一般的な更新およびRuby on Railsでのオンザフライ(ゼロダウンタイムデプロイメント)

まず、定義について説明しましょう。 オンザフライでの更新とは、通常の動作が中断されないシステムを更新することを意味します。クライアントが動作し、訪問者が移動し、エラー、応答時間の増加、または「会計」サインが見られません。



なぜこれが必要なのですか? この質問をしている場合-あなたは必要ありません。 サインを掛け、夕食に座ってください。



これはどのように行われますか? 難しいです。 なんで? 2つの主な理由があります。

-システムを即座にアトミックに(つまり、2つのHTTPリクエスト間で)更新することはできません。 素朴なアプローチでは、たとえばデータベースが更新されてコードがまだ利用できない場合、ユーザーは少なくとも長い応答時間、またはエラーにさえ気づくでしょう。

-システムの状態と構成は、クライアントとサーバーの両方に存在します。 例:セッションのデータ、フォームフィールドの名前、リンクのアドレス、ユーザーが開いたページのJavaScriptの状態。



一般的な決定



一般的に、ソリューションは次のように定式化できます。バージョンN + 1のコード バージョンNおよびN + 1の状態との互換性を確保してから、 状態を N + 1に更新する必要があります。



実際には、ここでのそのような互換性は、非常に多くの(明らかにそうではない)困難をもたらします。 Ruby On Railsアプリケーションの典型的なケースを見てみましょう。



データベーススキーマの変更



テーブルへのフィールドの追加は、理論的には以前のバージョンのコードと互換性があります。 実際にも、特に悪いメタプログラミングがなければ。



古いコードがこのフィールドを使用している場合、フィールド削除すると明らかに非互換性があり、いずれの場合も明らかではありません:ActiveRecordはフィールドのリストをキャッシュし、たとえばINSERT要求ですべてのフィールドをリストします。 出力:最初に、コードを中間コードに更新します。a)削除されたフィールドは使用しません。 b)キャッシュからこのフィールドを削除し、データベースを更新してから、コードを最終的なものに更新します。



フィールドの名前変更はもう少し複雑になります。

-新しい名前でフィールドを作成します

-コードを中間コードに更新します。a)両方の(古いおよび新しい)フィールドからデータを読み取りますb)両方のフィールドにデータを書き込みます

-データを古いフィールドから新しいフィールドに移行します

-古いフィールドを正しく削除するには、前の段落を参照してください。



インデックスの追加と削除は以前のバージョンのコード互換性があります。a)明示的なインデックスでヒントを使用しない場合b)インデックスを削除しても古いコードの実行はそれほど遅くなりません。



データのセマンティクスを変更する場合、一般的なケースを特定すること困難です。 すべては特定のアプリケーションのサブジェクト領域に依存します。 おそらく、単純で典型的な場合-フィールドタイプの変更-は、名前の変更と同じ方法で実行されます。



クライアントとサーバーの相互作用を変更する



フォームフィールドの名前変更するか、より重要な変更を追加コードで処理する必要があります(おそらくコントローラーで)。これは、古いフォームと新しいフォームからの入力値とフィールド値を受け入れることができます。 ブラウザウィンドウは長時間開いたままになる可能性があるため、しばらくの間、このコードをアプリケーションに残しておく必要があります。



セッションおよびCookieのデータのセマンティクスの変更も、両方の形式を理解する別個のコードで処理する必要があります。 セッションは長持ちし、クッキーはさらに長持ちします。 顧客のバスケットデータを失ったり、再度ログインパスワードを入力させたりしたくないですか? (ハブラ、恥を知れ!)



特定のページ/アプリケーションのアクションのアドレスの変更は、常に下位互換性を保つ必要があります。 古いルートをそのままにして、リダイレクトを割り当てます。 WebアプリケーションのURLは、システムの最も安定した部分である必要があります。これは、ユーザーとユーザーをもたらす検索エンジンによって使用されるパブリックAPIです。 したがって、この記事で説明する部分に問題はありません。



アセットパイプラインを使用する場合、以前のバージョンのコードのアセットを削除する必要はありません。 簡単です。



再起動



コードの互換性がすべてではありません。 新しいコードを作品にどのように入力しますか? Webサーバーまたはアプリサーバーはいくつありますか? オプションを検討してください。



バランサーの背後に複数のサーバーが隠れている場合、さらに読むことはできません-あなたはすでにすべてを知っています:)他の人にとっては、すべても非常に明白です: 暗い夜にシステムの負荷が最も少ない時間選択し、各サーバーを順番に更新して、バランサーから削除します更新時。



NginxまたはApache httpdの背後に1つのPassengerサーバーがある場合、Unicornに移動する必要があります。 ゼロダウンタイムでの再起動が宣言されているPassenger 3でさえ、十分に単純です。最初に古いワーカーを殺し、次に新しいワーカーを殺します。 その結果、訪問者は、少なくともアプリケーションの開始時間以上の長い応答時間を取得します。



Unicornを使用して、複数のサーバーのスクリプトを「ミニチュア」で再現できます。 before_fork



TTOU



シグナルを古いマスタープロセスに送信する必要があります。この場合、新しいワーカーごとに1つの古いワーカーがオフになります。 最後に、古いマスターにQUIT



を送信する必要があります。それだけです。 ワーカー数が2倍になるのに十分なメモリがある場合は、再起動の最後に、簡単に行うことができ、古いプロセスを徐々にではなくすぐに表示できます。



ヒント:rubyエンタープライズエディションを使用していない場合でも、 preload_app true



オプションを使用します。そうしないと、エラーのために新しいワーカーが起動時に落ちるのが遅すぎます。



おわりに



もう一度考えてみてください。これが本当に必要なのでしょうか? そう? おそらく同じように、最新の+100500 TEDの問題をスタブページに挿入し、 cap deploy



を実行してお茶を飲みますか? そうそう...ユーザー、売り上げ、利益...



All Articles