スタンドアロンコンテナから別々のWebおよびデータコンテナへの移行

こんにちは、Jay @pfaffman さん
この「2 つのコンテナ」トピックに関する投稿、およびサム氏による関連記事を含め、ご投稿いただきありがとうございます。

質問です。

ご指摘のように、data 用のコンテナと web-only 用のコンテナという 2 つのコンテナを設定しようとしていますが、macOS 上でこれを動作させる際にいくつかの障害に直面しています。

しかし、macOS や Ubuntu 上でこの「2 つのコンテナ構成」のデバッグを心配する前に、正しい理由で行っていることを確認したいと考えています。

「2 つのコンテナ」の構成を行う理由は、プラグインのインストール時など、Web アプリを再構築する際にサイトがダウンしないようにするためです。また、独自開発のプラグインを微調整する際、変更が正しく機能することを確認するには再構築が必要になることが時々あります(これはまた別の話です)。また、自分自身としても「速くて使いやすい」Web 開発環境を満足いくように構築することに苦労していますが、これもまた別の話題です。

そこで質問ですが、「2 つのコンテナ」構成は、Web 専用部分のアプリを再構築する際のダウンタイムを大幅に削減するのでしょうか?

これがこの問題を考える正しいアプローチだと言えますよね?

プラグインをインストールしたり微調整したりする際、data の yml ファイルではなく、web-only の yml ファイルだけを再構築すればよいのでしょうか?

私たちは LAMP フォームの背景から来ており、プラグインへの変更は主にライブサイト上でランタイムに行うことができます(何かミスしない限りダウンタイムはありません)。また、VueJS の Web アプリでは、デスクトップ上でビルドし、新しいアプリをアップロードして配置するだけで、ほぼダウンタイムなしでアップグレードや更新が可能です。しかし、Discourse ではダウンタイムが発生してしまいます。私たちは数秒であってもダウンタイムを避けたいと考えています。

「2 つのコンテナ」ソリューションは、(1) アプリの再構築(プラグイン、コードの微調整など)または (2) フルバックアップからの復元時に、ダウンタイムを大幅に改善するのでしょうか?

この質問をしたことでまた「叩かれる」気がするのですが、Discourse を本番環境で運用し、ほぼダウンタイムなしで変更を加える方法を探しているためです。LAMP や VueJS アプリでは簡単にできることが、Discourse ではまだ見つかっていません。

そのため、まだ動作させることができていない「2 つのコンテナ」方式への関心と葛藤があります。

ありがとうございます!

はい。新しいコンテナの構築中に、既存の Web コンテナは引き続き稼働します。したがって、ダウンタイムは新しい Web サーバーを起動するまでの時間のみとなり、通常は 1 分未満です。ただし、これは完全なゼロダウンタイムではありません。完全なゼロダウンタイムを実現するには、新しいコンテナが起動して動作を開始する前に旧コンテナを停止できるよう、前面にリバースプロキシを導入する必要があります(また、新しいコンテナ向けのデータベースマイグレーションが旧コンテナに支障をきたす場合、何らかの追加の手順を踏まない限り、そこでダウンタイムが発生します)。

バックアップからの復元については、差はありません。

「いいね!」 4

Jay @pfaffman さん、ありがとうございます。

あなたは間違いなく、ここでの最高峰で価値あるリソースです!

まだ理解が限られている私の、もしかしたら突飛なアイデアについてどう思いますか?

フロントエンドにリバースプロキシとして nginx を設定する(このチュートリアルを参照):

その後、discourse_docker(スタンドアロン)を設定した 2 つのディレクトリ/インスタンスを用意します。例:

  1. /var/discourse1
  2. /var/discourse2

両方のインスタンスで、discourse_docker(スタンドアロン)が異なるソケットをリスンするように設定し、それぞれのインスタンスでこのテンプレートを修正します:

 - "templates/web.socketed.template.yml"

つまり、要するに、プロダクション環境を(静かな時間帯に)再構築し、異なるコンテナで異なるソケット(nginx.https.sock2)をリスンするように変更するだけです。これによりソケットの競合は発生しません。これはスタンドアロンモードでも構築可能です(目的は、dataweb-only の 2 つのコンテナの必要性を排除することです)。

例として(議論/説明用)、discourse1 内の web.socketed.template.yml では:

  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 80;/
     to: |
       listen unix:/shared/nginx.http.sock;
       set_real_ip_from unix:;
  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 443 ssl http2;/
     to: |
       listen unix:/shared/nginx.https.sock ssl http2;
       set_real_ip_from unix:;

discourse2 では:

 - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 80;/
     to: |
       listen unix:/shared/nginx.http.sock2;
       set_real_ip_from unix:;
  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 443 ssl http2;/
     to: |
       listen unix:/shared/nginx.https.sock2 ssl http2;
       set_real_ip_from unix:;

しかし、discourse テンプレートが魔法のような処理を行う代わりに、/etc/nginx/conf.d/discourse.conf 内でソケットを手動で切り替え、nginx を再起動します。その際、web.socketed.template.yml テンプレート内の replace: ディレクティブを削除します。

この提案された(もしかしたら突飛な)構成では、競合しない 2 つの異なるソケットをリスンする 2 つのスタンドアロンコンテナを持ち、接続したいソケットに nginx を設定し、nginx を再起動するだけで済みます。

これは明確で簡単であり、ライブインスタンスに新しい投稿がまったくない静かな期間において、単一の discourse インスタンス(アプリ)あたり 2 つのコンテナ(data と web-only)の複雑さを望まない(または必要としない)人々にとって有用かもしれません。

もちろん、データ観点から最も堅牢な構成、つまり繁忙サイトの「完璧さ」を求める場合は、「2 コンテナ」ソリューションが最適です。なぜなら、data インスタンスと web-only インスタンス(現在は socksock2 の 2 つの異なるソケットをリスン)を維持したいからです。

nginx フロントエンド付きの「2 コンテナ」ソリューションにおいて、「標準構成」は両方の web-only コンテナが同じソケットをリスンするように設定することですが、これでは同時に両方を実行できません。しかし、(例としてのみ)異なるソケットをリスンするように設定すれば、両方を同時に実行でき、nginx 設定ファイル(および nginx の再起動)を使って 2 つの間を切り替えることができます。

この理解は正しいでしょうか?

私は(ゆっくりですが、確実にかつ希望を持って)これを理解し始めているのでしょうか?

ありがとうございます!

追記のみ: 私のデスクトップ Mac の 1 台で「2 コンテナ」設定が動作しています:

Screen Shot 2020-04-11 at 12.41.24 PM

インストールにおける唯一の注意点として、スクリプトが何らかの理由でこれらのディレクトリを作成しないため、手動でこれらを作成し(所有権と権限を設定する必要があります):

~discourse/discourse/shared/data
~discourse/discourse/shared/web-only

また、当初はデータベースのパスワードを空欄にして試しましたが、それは機能しませんでした(手順にはパスワードを設定するよう記載されていますが、私は単に実験していました)。

次に、nginx フロントエンドを設定し、web-only アプリに対して websocket を使用してその構成へ移行するテストを行います。

それは受け入れるのに大変なことですね。でも、2 つの Discourse ディレクトリを持つ必要はありません。containers ディレクトリに複数の yml ファイルを作成するだけで大丈夫です。ファイル名は好きなように付けてください。

「いいね!」 2

確認ありがとうございます。本日試行錯誤した結果、私どもの環境もまさにその通り(単一ディレクトリ)に設定されています。

2 つのコンテナ構成(2CC)ではすべて円滑に進んでいますが、macOS 上の nginx リバースプロキシの設定で苦労しています。

/shared ディレクトリ内の Unix ドメインソケットへの接続が確立できません。ソケット自体はコンテナ外からアクセス可能です。nginx、Python、socat(テスト用)を試してみましたが、常に「61 Connection refused」エラーが発生します。ふむふむ。

一日中「Connection refused」エラーでつまずいています!!

明日は明日の風が吹くさ。

小さな質問があります。
もし単一のコンテナ構成(app.yml のみ)で、./launcher bootstrap app を実行した場合、ウェブサイトやフロントエンドは停止しますか?

停止する場合、なぜ ‘bootstrap web-only’ ではウェブサイトが停止しないのでしょうか?

停止しない場合、コンテナの再構築時の時間短縮という観点から、2 台のコンテナ構成のメリットは何でしょうか?つまり、単一のコンテナをブートストラップしている間でもウェブサイトを稼働させられるのであれば、なぜ 2 つのコンテナを分ける必要があるのでしょうか?

「いいね!」 1

新しい .yml ファイルを作成し、例えば new_image と名付ければ停止しません。

適切に設定されていれば、ブートストラップ処理はどのサービスも開始も停止もしません。ブートストラップされたイメージは実行されていません。そのため「ブートストラップ済み」と呼ばれます。

ただし、新しい yml ファイルを作成する必要があります。なぜなら、新しいイメージ名を持つ新しいイメージを作成する必要があるからです。

つまり、新しいイメージ名を使用すると、新しいブートストラップ済みイメージはまだ実行されていません。「ブートストラップ済み」の状態だけです。

新しい名前(app ではない)で新しいイメージを構築し、その構築処理が完了します。これを new_image と呼びましょう。

次に、実行中のイメージ(これを old_image と呼びます)を置き換えたい場合は、以下のように実行できます。

./launcher stop old_image; ./launcher start new_image

あなたのケースでは以下のようになります。

./launcher bootstrap new_image          # データイメージとウェブ「app」イメージが実行中
./launcher stop app; ./launcher start new_image

データコンテナはすでに構築されているため、時間を節約できます。(1) データイメージの構築が不要、(2) ウェブイメージの再構築時にダウンタイムが発生しない(他のイメージが稼働している間にブートストラップ処理を行える)ためです。

これは非常に高速ですが、コンテナの前にリバースプロキシを使用する場合ほど高速ではありません。

最初のメッセージでの質問に戻ると、app という名前のイメージが実行されています。もし同じ名前の app イメージを再度ブートストラップしようとすると、同じ名前のイメージを再構築することになります。これはあなたの直感が示す通り、時間の節約にはなりません。

これで理解できましたか?

もしまだ不明な点があれば、質問してください。誰でも学べますし、学ぶことはエキサイティングです。実際には理解しやすい内容ですが、概念に不慣れな場合は少し時間がかかるかもしれません。

「いいね!」 2

正直に言って、何も理解できませんでした。試しましたが、失敗しました。

私の質問はシンプルです。
2 つのコンテナ構成があり、「web-only」コンテナを「再構築」ではなく「ブートストラップ」した場合、現在の web コンテナは動作し続けます(ウェブサイトが正常に動作しているように表示されるため)。

一方、「app.yml」のような単一コンテナ構成の場合、この「app」コンテナをブートストラップした場合、ウェブサイトは引き続き動作するのでしょうか?

もし説明がシンプルなら、なぜそうなるのでしょうか?

「いいね!」 1

もしかしたら、誰かを雇って手伝ってもらうべきでは?

「いいね!」 1

問題ありません。

ただ小さな疑問でした。その一部は、おそらく「はい/いいえ」で答えられるかもしれません。

大したことはありません。

「いいね!」 1

私の提案は、自分で試してみることです。

実際に自分でテスト環境で試す方が、フォーラムで質問をして回答を待つよりも時間はかかりません。

自分で試せば疑問の答えが得られます。本番アプリを壊さないよう、テスト環境で試すことをお勧めします。

Discourseはオープンソースで、共同設立者の寛大なご厚意により無料でダウンロードして設定できます。つまり、このオープンソースを活用して、好きなだけテストアプリを作成、破棄、再作成、破棄することが可能です。

もし基本的なシステム管理タスクに手間をかけたくない場合は、@codinghorror が地元の才能ある人材を雇うという素晴らしい提案をしています。

「いいね!」 1

はい(ただし、ブートストラップがデータベースを、実行中のコンテナが使用できなくなるように移行する場合は除きます)。古いコンテナがシャットダウンし、新しいコンテナが起動する間、ダウンタイムが発生します。

いいえ。同じファイルに同時に 2 つのデータベースプロセスがアクセスすることはできません。

「いいね!」 3

ああ!!
この点を明確にしてくれてありがとう。

「いいね!」 1

つまり、データベースを構築しているコンテナ内に置かないことで、別の Web コンテナが稼働している間に、データベースを操作する新しい Web コンテナを構築できます。

「いいね!」 1

このアプローチに関する簡単な疑問ですが、この設定でリビルドをどう進めればよいでしょうか?

@pfaffman さんが追加した、2 つのコンテナ構成(「app」: 「data」と「web_only」)をゼロから採用すると仮定します。

すべてのリビルド操作などは「web_only」向けに行われますが、「data」コンテナに対して何か操作を行う必要があるのでしょうか、それとも「web_only」のブートストラップがそれを処理してくれるのでしょうか?(データコンテナが一度も停止しないため、確認のため質問しています。)

私たちは「3 つのコンテナ」とリバース nginx プロキシを使用しています:

  1. data
  2. socket1
  3. socket2

現在 socket1(あなたが言う「web-only」)が動作しており、何かを再構築してテストしたいとします。socket1 を実行したまま socket2 を再構築します。これらのコンテナはそれぞれ Unix ドメインソケットを使用しているため、共有ソケットが異なるファイル(場所)にある場合、両方を同時に実行できます。

次に、socket2 が構築され、準備が整ったと仮定します。

私は次のように行います:

ln -sf /var/discourse/shared/socket2/nginx.http.sock /var/run/nginx.http.sock

これで socket2 でライブ動作しています。

例えば問題が発生した場合、次のようにすれば済みます:

ln -sf /var/discourse/shared/socket1/nginx.http.sock /var/run/nginx.http.sock

これで以前の状態に戻り、socket1 で動作しています。

余談ですが、bash ログインプロファイルにいくつかのコードを追加して、サーバーにログインするたびにどのソケット(コンテナ)が実行されているかを表示するようにしています:

Last login: Fri May 15 09:39:39 2020 from 159.192.33.138
srw-rw---- 1 root docker  0 May  5 07:38 /var/run/docker.sock
lrwxrwxrwx 1 root root   44 May 15 09:16 /var/run/nginx.http.sock -> /var/discourse/shared/socket/nginx.http.sock

これが役立つことを願っています。

私見ですが、これは(一般的に)「やるべきこと」であり(本番環境ではステージングや開発ではなく)私の「デフォルトの標準」ですが、これは私の意見に過ぎません。人それぞれ異なる場合があります(YMMV)。設定には少し手間がかかりますが、本番環境ではその価値があると思います。


注意点:


元のテンプレート yml ファイルのバックアップを安全な場所に保管しておくことをお勧めします。テンプレートが上書きされる可能性があるため問題になる場合があります。そのため、私たちは「すべての yml を保存し」、「最初に pull」し、その後に「yml を確認」してからブートストラップを行う傾向があります(過剰なまでの慎重さから)。

「いいね!」 2

データコンテナの再構築は、Postgres(直近で発生)またはRedis(過去6ヶ月以内に発生)のアップグレードがある場合を除き、不要です。

基本的には、ご覧の指示で「web_only」を「app」に置き換えるだけです。詳細については、Managing a Two-Container Installation - Documentation - Literate Computing Support にメモを記載しています。

「いいね!」 5

つまり、このアプローチでは 2 つの Web インスタンスを持ち、アイドル状態のほうで同じデータを使ってテストを行うということでしょうか?興味深いですね。「ストレージ節約」戦略を確立し次第、ぜひ試してみたいと思います :stuck_out_tongue:

ありがとうございます。記事を最初から最後まで読みました。一つだけ小さな質問があるのですが、ここでの「data」は、記事に書かれている通り「web_only」ではなく「data」を再構築するということでしょうか?それとも、私が見落としている別のテクニックがあるのでしょうか(例えば、大きなアップグレード時にはそれらを結合してから再度分離する必要があるなど)?

はい、一方のソケットベースのコンテナでテスト・追加・再構築を行い、他方で実行しています。

はい、どちらも同じデータコンテナを使用します。データコンテナは、どのWebアプリケーションと「通信」しているかについては「気にしません」。

実はとてもシンプルです。コンテナを外部に公開されたTCP/IPポートではなく、共有Unixドメインソケット上で実行する方法の基本を掴めば、すぐに理解できるでしょう。

私どもでは、それほど多くのストレージを消費しているとは感じていません。それに、ディスク容量は安価であるため、本番環境では容量制限を設けて運用していません。

「いいね!」 1

詳細をありがとうございます。テスト環境で2つの「アプリコンテナ」をData Oneに接続する設定を試したところ、問題なく動作しました。

ただし、PRD環境ではData Containerを再構築できないため、その点を解決しないまま変更を加えるのは避けたいと考えています。現在、discourse@discourse FATAL: terminating connection due to administrator commandというエラーで運用が停止してしまいます。