「rebuild」がコンテナの実行状態と密結合しているのはなぜですか?

公平を期すために、まず私はこのプラットフォームとコードベースに新しく、内部でのリビルドの仕組みについては全くわからないことを付け加えておきます。ただし、現在の私の理解では、リビルドは以下の手順で行われます:

  1. 現在のコンテナを停止する
  2. ソースツリーからのデータを使って新しいコンテナを構築する
  3. 新しいコンテナの起動を待つ

DevOps の観点からすると、なぜ古いコンテナが稼働している間に(別のブランチや一時ディレクトリなどで)新しいコンテナを構築できないのでしょうか?そうすれば、新しいコンテナと古いコンテナの入れ替えが(少なくともダウンタイムの面で)はるかに高速になり、数分ではなく数秒のオーダーで済むはずです。

もしコンテナが再構築時に破棄されないストレージボリュームを使用しているなら、設定変更やデータベース変更(例えば新しいメッセージなど)をこのユースケースのために特別に処理する必要すらあるかどうかもわかりません。つまり、コンテナの構築がコンテナの状態とこれほど密結合である必要はないはずです。

これは単にまだ誰も注目していない問題なのでしょうか、それとも、別のコンテナを構築する前に必ず一つを停止しなければならないという既存のアーキテクチャ上の決定があるのでしょうか?

Rebuild は包括的な更新機能であり、以下を行うことができます:

  • Discourse ソースの更新
  • Ruby のメジャーバージョンなどの OS レベルの依存関係の更新
  • PostgreSQL の新バージョン(互換性のないバージョン)への更新。この場合、データディスクフォーマットの新バージョンへの更新も自動的に処理されます
  • Docker イメージの更新。例として、今年初めに Ubuntu 16.04 から最新の Debian へ変更しましたが、ユーザーには透過的であり、./launcher rebuild app と入力するだけです。

Rebuild は常に必要なわけではありません。大規模な依存関係の更新が発生する年に数回のみ必須となります。それ以外の更新については、管理 UI の Web 更新ボタンをクリックすることで、ダウンタイムゼロで更新できます。

より「DevOps」的な観点については、以下をお試しください:

さらに多くの情報は #howto:sysadmin でご覧いただけます。

私の認識が間違っているかもしれませんが、@CodeGnome さんのゼロダウンタイムでの再構築に関する質問は、さらに調査する価値があると思います。

Docker の仕組みを正しく理解しているなら、Discourse の以下の要素は、既存のコンテナが稼働している間に、新しいコンテナでバックグラウンドで再構築できるはずです。

  • Discourse ソースの更新
  • Ruby のメジャーバージョンなど、OS レベルの依存関係の更新
  • Docker イメージの更新。例を挙げると、今年初めに Ubuntu 16.04 から最新の Debian へ変更しましたが、ユーザーにはすべて透過的に処理され、./launcher rebuild app と入力するだけで済みました。

PostgreSQL の変更による影響については、データ量が多いため、より手がかかります。これは、おそらく新旧のコンテナ間でボリュームが共有されているためです。

再構築の開始時にサイトをリードオンリーモードにし、既存のコンテナは現在のボリュームを維持したまま、新しく構築中のデータベースは新しい Docker ボリューム内で動作させることは可能でしょうか?

Discourse のソースコード、OS レベルの依存関係、Docker ベースイメージ、Ruby Gems などの更新について、ビルドを 2 ステップに分け、1 ステップ目で上記のタスクを実行することで対応可能です。

この最初のステップは環境に依存しないため、CI 環境でも実行できます(これにより、ステージング環境と本番環境でほぼ同一のイメージを使用でき、異なる日時にビルドを再実行することによる潜在的なエラーを回避でき、ダウンタイムの削減にもつながります)。

DB マイグレーションと assets:precompile タスクは、依然としてターゲットマシンで実行する必要があります。DB マイグレーションはほとんどの場合高速ですが、一方、assets:precompile タスクは問題となります。なぜなら、このステップが最も時間を要するからです。その理由として、一部のアセットが DB で定義された環境情報(一部の CSS ルールなど)を認識して実行する必要があるためではないかと考えられます。

もしこのタスクを 2 つに分け、環境に依存しないアセットを先に実行して CI 環境で処理し、2 ステップ目では DB などの環境情報に依存するアセットのみをコンパイルすることができれば、非常に素晴らしいことです。ただし、技術的に実装がどの程度難しいかはわかりません。

アプリコンテナのブートストラップを 2 ステップで行うことについては、以下のトピックで議論しています。

私が行った変更は、Discourse の Web テンプレートを 3 つのファイルに分割したことだけですが、タスク自体は同じです。それでも、Discourse チームがこの仕組みをサポートしてくれれば、Web テンプレートの将来の変更に合わせて毎回更新する必要がなくなるため、非常に助かります。