web-dev-qa-db-ja.com

ダウンタイムなしの導入の実現

私は、ダウンタイムなしの展開を実現するために、オフ時間中はより少なく、「より遅い」時間中より多く、または理論的にはいつでも展開できるようにしています。

私の現在のセットアップ、やや簡略化:

  • WebサーバーA(.NETアプリ)
  • WebサーバーB(.NETアプリ)
  • データベースサーバー(SQL Server)

現在の展開プロセス:

  1. WebサーバーAとBの両方のサイトを「停止」します
  2. デプロイされているアプリのバージョンのデータベーススキーマをアップグレードする
  3. WebサーバーAの更新
  4. WebサーバーBの更新
  5. すべてをオンラインに戻す

現在の問題

これにより、毎月少量のダウンタイムが発生します-約30分。私はこれを営業時間外に行うので、大きな問題ではありませんが、それは避けたいものです。

また、実際に「戻る」方法はありません。私は通常、ロールバックDBスクリプトを作成しません-アップグレードスクリプトのみを作成します。

ロードバランサーの活用

一度に1つのWebサーバーをアップグレードできるようになりたいです。 WebサーバーAをロードバランサーから外し、アップグレードしてオンラインに戻し、WebサーバーBに対して繰り返します。

問題はデータベースです。ソフトウェアの各バージョンは、データベースの異なるバージョンに対して実行する必要があるため、私は一種の「行き詰まっている」のです。

可能な解決策

私が検討している現在の解決策は、次のルールを採用することです。

  • データベーステーブルは削除しないでください。
  • データベースの列は削除しないでください。
  • データベース列の名前を変更しないでください。
  • 列を並べ替えないでください。
  • すべてのストアドプロシージャにはバージョンを付ける必要があります。
    • 意味-「spFindAllThings」は、編集すると「spFindAllThings_2」になります。
    • その後、再度編集すると「spFindAllThings_3」になります。
    • 同じルールがビューに適用されます。

一方、これは少し極端に思えます-問題を解決すると思います。アプリケーションの各バージョンは、中断することなくDBにアクセスします。コードはビュー/ストアドプロシージャからの特定の結果を期待しています-これにより、その「契約」が有効に保たれます。問題は-それがずさんに浸透することです。アプリをしばらくデプロイした後で古いストアドプロシージャをクリーンアップできることはわかっていますが、汚れているように感じます。また、これはほとんどの場合発生するこれらのルールに従うすべての開発者に依存しますが、誰かが間違いを犯すと思います。

最後に-私の質問

  • これはだらしないかハックですか?
  • 他の誰かがこのようにしていますか?
  • 他の人はこの問題をどのように解決していますか?
40
MattW

これは、データベースに基づくソフトウェアアップグレードに対する非常に実用的なアプローチです。それは2003年にMartin FowlerとPramod Sadalageによって 記述された であり、その後 Refactoring Databases:Evolutionary Database Design で書かれました。

だらしないように見えると言うと、私はあなたの意味を理解できますが、故意に、そして事前に考えて、使用されていない構造をコードベースとデータベースからリファクタリングして、明らかに使用されなくなった場合、それよりもはるかに堅牢です。アップグレードおよびロールバックスクリプトに基づくより簡単なソリューション。

14
Mike Partridge

「ゼロダウンタイム」は、このようなアプローチの多くの考えられる理由の1つにすぎません。この方法でデータモデルに下位互換性を維持すると、さまざまな問題に対処するのに役立ちます。

  • 多くのソフトウェアパッケージがデータベースにアクセスしている場合、スキーマの変更がそれらに影響を与えるかどうかをすべて確認する必要はありません(同じデータベースにアクセスするプログラムを作成する複数のチームを持つ大規模な組織では、スキーマの変更が非常に難しくなる場合があります)。

  • 必要な場合は、いずれかのプログラムの古いバージョンをチェックアウトして、おそらく新しいデータベースで再度実行します(古いプログラムが新しい列を正しく処理することを期待していない限り)

  • 現在のデータベースバージョンへのアーカイブデータのインポート/エクスポートがはるかに簡単

これがリストの追加ルールです

  • 新しい各列はNULL可能であるか、意味のあるデフォルト値を提供する必要があります

(これにより、新しい列を認識していない古いプログラムでも、データベースに新しいレコードを作成するときに何も壊れません)。

もちろん、このアプローチには実際の欠点が1つあります。データモデルの品質が時間とともに低下する可能性があります。また、データベースにアクセスするすべてのアプリケーションを完全に制御でき、たとえば列の名前を変更するときに、それらすべてのアプリケーションを簡単にリファクタリングできる場合は、よりクリーンな方法でリファクタリングすることを検討してください。

5
Doc Brown

それは一種のデプロイメントごとに異なります。

もちろん、テーブルや列を削除することはできません。インターフェイスの互換性を損なうものを変更することはできません。いつでも抽象レイヤーを追加できます。ただし、その抽象化とバージョンのバージョン管理を行う必要があります。

自問する必要があるのは、リリースごとにスキーマが変更されるため、下位互換性がないということです。

このようにスキーマを変更するリリースがほとんどない場合、データベースの問題はミュートです。アプリケーションサーバーのローリングデプロイを実行するだけです。

ダウンタイムを最小限に抑えた展開で最も役立つのは、次の2つです。

  1. 後方互換性のために努力する-少なくとも単一のリリース内で。常に達成できるとは限りませんが、特に各リリースが小さい場合は、リリースの90%以上で達成できると思います。
  2. プレリリースおよびポストリリースデータベーススクリプトを用意します。これにより、アプリのコードがデプロイされる前に新しいオブジェクトを作成し、アプリのコードがデプロイされた後に古いオブジェクトを削除することで、名前の変更やインターフェースの変更を処理できます。 null可能ではない新しい列を追加する場合、デフォルト値を満たすトリガーを使用して、プレリリーススクリプトでnull可能として追加できます。その後、リリース後、トリガーをドロップできます。

うまくいけば、残りのデプロイをメンテナンスウィンドウのために保存できます。

ダウンタイムを必要とするいくつかのデプロイへの対処に役立つ可能性のある他のアイデア:

  • コードに下位互換性を組み込むことはできますか?たとえば、コードが複数のタイプの結果セットをサポートできる方法はありますか?列をintからdoubleに変更する必要がある場合、アプリのコードで列を文字列として読み取り、解析できます。ちょっとハッキーですが、リリースプロセスを自分で進めるための一時的なコードであれば、それは世界の終わりではないかもしれません。
  • ストアドプロシージャは、スキーマの変更からアプリコードを分離するのに役立ちます。これはこれまでのところしか実行できませんが、少しは役立ちます。
4
Brandon

あなたは少し余分な努力のためにこのようにそれを潜在的に行うことができます。

  1. エクスポートしてデータベースをバックアップする
  2. バックアップをインポートしますが、リリースバージョン(例: myDb_2_1
  3. MyDB_2_1でデータベースリリースを実行する
  4. WebサーバーAのアプリプールを「停止」するか、ロードバランサーから削除する
  5. WebサーバーAを更新し、実装後のテストを実行し、必要に応じてロールバックする
  6. セッションはWebサーバーBをブリードし、WebサーバーAをループに戻します
  7. WebサーバーBをアップグレードしてからロードバランサーに戻す

当然、Web更新では、新しいDbスキーマを指す新しい構成エントリが必要になります。月に1回リリースを行っていて、小規模なチームであれば、下位互換性のないDB変更を実際にいくつ作成しているのでしょうか。テストによってそれを制御できる場合は、ダウンタイムなしで、またはおそらく最短で5分のダウンタイムで自動展開を行うことができます。

2
LeoLambrettra