ちょっと話題の記事

DB変更リリースの停止時間を最小化するには?「DevOps with Database on AWS」 #AWSDevDay

「DevOpsってアプリの話ばっかりだけど、データベースもがんがん変更はいるよね。で、実際どうすんの?」という疑問に真正面から答える有用なセッションでした。
2018.11.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

「DevOpsってアプリの話ばっかりな気がするけど、DBもアプリ以上に変更入るよなぁ」

データベースの変更をいかにノーダウンタイムで本番リリースするか?ステート(データ)を持つという宿命上、そう簡単にいかないことは皆さん想像つくかと思いますが、その制約の中でも工夫次第では停止時間を極小化する方法が有るんだなぁと気付かされました。

AWS Dev Day Tokyo 2018 | AWS、「DevOps with Database on AWS」のセッションレポートです。

(祭) ∧ ∧
 Y  ( ゚Д゚)
 Φ[_ソ__y_l〉     無停止DBリリースダワッショイ
    |_|_|
    し'´J

セッション内容

タイトルは「DevOps with Database on AWS」

DevOps のアプローチでシステム開発に取り組む上で、データベースに対する変更をどのように扱うかは難しい問題です。このセッションでは、ライフサイクルが異なるアプリケーションとデータベースに対する変更を適切に管理することを目的として、代表的なデータベーススキーマ変更の手法を紹介し、それを CI/CD パイプラインに組み込む方法について考えます。

講演者は、こちらのお二人です。

  • 山内 晃(アマゾン ウェブ サービス ジャパン株式会社 技術統括本部 ソリューションアーキテクト)
  • 大村 幸敬(アマゾン ウェブ サービス ジャパン株式会社 技術統括本部 ソリューションアーキテクト

講演資料

DevOps With Databaseはなぜ難しいのか

まず最初に、なぜ、DatabaseのDevOpsが難しいのかをお話していきます。

変更対象が異なる

アプリケーションとデータベースでは変更対象が異なる。アプリケーションでは影響範囲を限定しやすいが、データベースの変更は影響範囲が大きくなりがち。

所要時間が異なる

また、デプロイの所要時間も異なる。アプリケーションなら長くて数分程度だが、データベースはデータベースのサイズにより数日かかる場合もある。

変更管理が異なる

アプリケーションの変更管理では、gitを始めとしたバージョン管理ツールの定番が存在するが、データベースの変更管理は、そういった定番が存在しない。プロジェクト独自で実施している場合が多い。

これらの面から、アプリケーションの変更とデータベースの変更は、違いが多い。大きくは下記3点。

  1. スキーマ変更に時間がかかる
  2. バージョン管理が難しい
  3. スキーマ変更はアプリケーションの停止を伴う

これらが、データベースを含むDevOpsを難しくしている要因です。このセッションでは、これらについての解決策を順にお伝えしていきます。

方法①「スキーマ変更にかかる時間を短縮するには」

データベースのスキーマ変更とは、データ定義言語(DDL)によるオブジェクト定義の変更です。大きく分けて2つのパターンがある。

  • メタデータだけを変更するDDL
    • カラム名変更、インデックス削除、ビュー作成など
    • →通常、瞬時に終了するのであまり気にすることはない
  • 既存データのコピーを伴うDDL
    • カラム追加・削除、インデックス追加、プライマリキー追加など
    • →データ量に依存して時間がかかる

既存データのコピーを伴うDDLについて MySQL5.7でのカラム追加はこのように動作します。

この例だと、元テーブルのコピー中は常に更新はブロックされるため、テーブルのサイズが大きいと稼働中アプリケーションへの影響は非常に大きくなる。そのため、スキーマ変更の確実な実施には、以下の手順を踏む必要があり手間が非常にかかる。

  • アプリケーション全体の停止
  • スナップショットを取得
  • DDL実行(問題があったら、スナップショットから復元)
  • アプリケーションを再開

これではDevOpsパイプラインへ組み込むことが困難。オンラインで(更新をブロックせずに)スキーマ変更するにはどうしたらよいか?

オンラインでのスキーマ変更(MySQL)

PostgreSQLには、この機能はない。

オンラインでのスキーマ変更(Oracle Database)

オンラインでのスキーマ変更(SQL Server)

これら、データベースエンジン自体の仕組みでオンライン変更できる方法もあるが、データベースに依存しない方法もある。

新テーブルにカラム追加して元テーブルと結合してビューで参照させる

この場合、通常の更新は元テーブルに実施しておき、追加カラムをもつテーブルを新規作成しヴューで参照させるやり方。停止時間は短縮できるが、繰り返すと結合が多くなる。

また、旧来からあるやり方として予備カラムを定義しておくという方法もある。

JSON型を使う

変化球だが、こういう方法もある。注意点も多いので気をつけて利用する。

方法②「安全にバージョン管理するには?」

みなさん、Martin Fowlerが提唱したEvolutionary Database Designはご存知でしょうか? あまり知られていないかと思いますが、データベースのバージョン管理を考えていく中で一つの基礎となりうるものなので、最初に紹介させていただきました。

皆さん、データベースのスキーマ管理されていますか? パラパラって感じですね(笑)

一つのやり方としては、、DDL文をコードとあわせてリポジトリにコミットしてDBAが適用という方法もありますが、なかなかスピードがでない。ので、最近はDBマイグレーションツールを利用することが多いかと思います。

  • フレームワークに付属
    • Ruby on Rails(Ruby)
    • Laravel(PHP)
    • Entity Framework(.NET)
    • Django(Python)
  • 専用ツール
    • Flyway
    • Liquibase
    • MyBatis
    • Phinx
    • sql-migrate

様々なツールがあるが、考え方としてはアプリケーションのコードと合わせてデータベースの状態もこういったツールを利用して、足並みを合わせて管理していく必要があるということです。

方法③「リリース方法を工夫して停止時間を短縮するには?」

DevOpsにおいて、無停止リリースは非常に大事です。事前のユーザー調整無くリリースできることはDevOpsに必須ですよね。リリースを繰り返すことで早いフィードバックを得ることが必要なのに、データベースの変更がその足かせになるのは良くない。

アプリケーションの無停止リリースは非常に多く語られているが、データベースについてここらへんの話は少ないんですよね。なので、今日はあえてその点について踏み込んでいきます。

最初に言っておきますが「銀の弾丸」は無いです。

リリースへの影響

ステートレスなアプリケーションの変更は、順に停止・起動することで無停止入れ替え可能。

ステートフルなデータストアの変更は、単一のデータストアを共有しているため、並列で用意しておくことが難しい。

無停止リリースのパターンA:「DBを共有」

DBとアプリの変更を個別に実施する方法。DBとアプリケーションが疎結合で有ることが前提になり新旧アプリでDBを共有することになるが、一般的にはこの方法を推奨する

パターン A-1:DBスキーマ変更を先行

一般的に一番多いのがこのパターンかと思います。DBを先に変更しその後にアプリケーションを変更する方法です。旧アプリが新DBに対応しておく必要があるが、カラム追加などでは問題ない場合が多いですね。

パターン A-2:アプリリリースを先行

こちらのパターンは少ないと想います。例えばカラム削除などは、このパターンが必要になりますね。

どちらかというとA-1のパターンのほうが多いかと思いますので、それに合わせてアプリケーションの変更を考慮するほうが良いかと思います。

無停止リリースのパターンB:「異なるDBの使用」

もう一つのパターンとしてDB自体を切り替える方法もあります。アプリケーションやDBの変更が大きく、データコピーによる停止が許容できない、またはアプリとDBが密結合で個別リリースできない場合などがあるかと思います。

はっきり言って難しい問題です。スキーマをそれぞれ用意するのは簡単ですが、新旧DBのデータを一致させること、これは簡単ではない。

ただいくらかリリースのパターンはあるので、それを説明していきます。

パターン B-1:「レプリケーション」

事前にGreen環境のスキーマを変更しておき、旧環境DBから新環境DBに対してレプリケーションを実施しておく方法です。任意のタイミングでアプリの書き込みを停止して、DBを切り替える方式。

トランザクション管理の複雑さをDBに任すことができるのがメリットとなります。ただ、切り戻しを考えた場合、リリース後に旧DBへのレプリケーションが必要になったりなどの考慮点が必要になったり、難しい問題も多いです。

また、RDBMSの種類によっては、異なるスキーマ間でレプリケーションの可否が異なるので、事前に変更対象のスキーマに対してレプリケーションが行えるかの検証が必要となります。

パターン B-2:「Double Write」

こちらの場合は、アプリケーション側で新旧データベースにそれぞれ書き込みを実施する方法で、無停止を実現する方法です。2つのデータベースへの書き込みが必要なため、速度の低下や2-phase-commitの実施などが必要になる点で注意が必要となります。

まとめ「無停止によるDBスキーマ変更の体系がわかりやすくまとめられている」

あまり話題に上がらないDevOps文脈におけるデータベースの変更について、全体像が非常に簡潔に整理しまとめられていて、ものすごく参考になりました。

ステート(データ)を持たざるを得ないデータベースにおいて、BlueGreen的なやり方はほぼ不可能かとなんとなく考えていましたが、パターンB-1みたいにレプリケーションを貼る方法も確かにあるなぁというのは至極納得です。スキーマ変更の影響度によってRDBMSのレプリケーション機能が使えない場合、性能要件がきつくなければ、DMS(Database Migration Service)も検討できるなぁと妄想したりしました。

「銀の弾丸はない」というメッセージは最初にありながらも、様々な手段のメリットデメリットが整理されていて、非常に有用なセッションでした。定期的に見返しつつ、今後のDB移行手段の検討において参考にさせてもらおうと思います。

それでは、今日はこのへんで。濱田(@hamako9999)でした。