しばやん雑記

Azure Serverless とメイドさんが大好きなフリーランスのプログラマーのブログ

障害に強い Azure の運用を考える (2021 年版)

Azure の日本リージョン 7 周年の日に Japan East のストレージ障害が発生するという、なんともアレな出来事がありましたが、障害発生後はアーキテクチャを見直すいい機会だと思うので色々書きます。

まだ RCA は公開されていないですが、おそらくぶちぞう RD がブログに書くのでリンクを貼ります。

4 年前の同じような時期にも Japan East で大規模なストレージ障害が発生したので、同じようなものを書きましたが流石にいろいろと進化しているので古さを感じます。

今回の障害は影響範囲はさほど大きくなかったようで、主に Azure Storage と Virtual Machines がステータスには上がってきていました。実際には Application Insights や Log Analytics にも影響がありましたが、Azure Storage は全ての基本なので仕方ない感はあります。*1

全てを書くのは無理なので、いくつかサービスをピックアップして紹介します。

要するに App Service / Azure Functions を使えという話ではありますが、Storage 周りにも 4 年前とは違ってサービスや機能が増えているので多めです。VM は察してください。

App Service / Azure Functions

以前に書いた App Service の設計パターンまとめにほとんど書いているのですが、いくつかピックアップしてもうちょっと具体的に紹介します。構築前に読んでおくといいことがあると思います。

これを書いた時から大きくは変わっていないので、そのまま使えるはずです。変わったら更新します。

基本は App Service Diagnostics に従う

既に何回も書いていますが App Service Diagnostics が非常に優秀なので、まずはここから始めていけば良いです。最近だと Risk alerts という形で推奨事項を確認できます。

f:id:shiba-yan:20210226212217p:plain

可用性に対するリスクが検出されているので、以下のように複数インスタンスで実行させることや、Auto heal を有効化することが推奨されています。

f:id:shiba-yan:20210226212309p:plain

ベストプラクティスの塊なので、いろいろ考える前に App Service Diagnostics を確認しましょう。

Health Check を有効化する

Health Check は去年に GA した新しい機能ですが、アプリケーション側で適切にヘルスチェック用のエンドポイントを実装していれば、App Service が自動的に再起動や VM の入れ替えなどを行ってくれるので、回復性が高まります。詳しくはドキュメントを参照してください。

最近の Azure Portal では Essential の部分に Health Check が表示されているので、重要度の高い機能であることが理解できると思います。設定自体は以下のように非常にシンプルです。

注意点としては不良 VM の入れ替えなどは 1 インスタンスでは実行してくれないので、2 インスタンス以上で動作させておく必要があることでしょう。該当する App Service では警告メッセージが出ます。

f:id:shiba-yan:20210226153718p:plain

Load Balancer から削除されるまでの時間は、実質的にはヘルスチェックが 2 分に 1 回行われるので、連続して何回失敗したときに行うかという設定になります。ヘルスチェックが 2 分に 1 回は若干長いと思うのですが、今のところ変更は出来ません。

2 インスタンスで実行している App Service に対して、特定のインスタンスに対してヘルスチェックを失敗させるようにしたアプリケーションをデプロイして、動作を確認したのが以下のグラフです。

f:id:shiba-yan:20210226211129p:plain

DC83 のインスタンスはヘルスチェックが通らなくなりましたが、App Service によって自動的に新しいインスタンスが用意されて入れ替えが行われました。この時ヘルスチェックの失敗回数が設定した回数を上回っているので、ヘルスチェックが落ちているインスタンスは LB から削除されています。

共有ストレージの利用を最低限にする

App Service / Azure Functions は Azure Storage ベースの共有ストレージがマウントされているので、使用している Azure Storage に障害が発生すると当然ながら影響を受けます。従ってアプリケーションが共有ストレージに頻繁に読み書きする場合は、サービスの提供を継続できません。

ごく小規模のアプリを除いては、共有ストレージは読み込み専用で利用するのが最近のお勧めになります。特にアプリケーションのデプロイには Run From Package を使うのは非常に良いです。

Run From Package でデプロイされたアプリケーションは、一度起動されるとデプロイやインスタンスが入れ替わらない限りはローカルストレージにキャッシュされたパッケージが使われるので、ストレージ障害の影響を最小限に抑えられる可能性があります。

マルチインスタンス・マルチリージョン構成を組む

障害が App Service のインスタンスと共有ストレージの両方に運悪く影響した場合は、基本的は障害の復旧を待つしかなくなってしまいます。Azure Storage は様々なサービスのバックエンドで使われているので、どうしても影響範囲が広くなりがちです。

可用性を高めるためには、最低限 2 インスタンス以上で Always On を有効にした状態で稼働させるか、更に高可用性が必要なアプリケーションの場合は Japan East と Japan West の両方に同一構成のアプリケーションを用意するのが最適解となります。

Traffic Manager や Front Door を入れておくことで、フェールオーバーも簡単に行えますし、最近は CI / CD や ARM Template / Terraform などを使うことで同一構成を別リージョンに丸ごと作ることも簡単です。

Storage (Blob / Queue / Table)

Azure Storage で障害が発生した場合 Blob や Queue を使うアプリケーションでは回避が難しいです。

特に Azure Functions はバックエンドを Storage に完全に依存しているので、アーキテクチャを見直してマルチリージョン構成にする必要も出てくるでしょう。

ZRS 構成を組む

昔は GRS とか RA-GRS を使うぐらいしか選択が無かったですが、今は ZRS が使えるようになったので LRS に近い形で高可用性を実現できるようになっています。

GRS の場合はフェールオーバーが正直だるい感じですが、ZRS は特定のゾーンが落ちた場合には Azure によって自動的に DNS レベルで再設定が行われるので、クラウドデザインパターンに則ってリトライを実装しておけば幸せになれそうです。

Azure CDN / Front Door と組み合わせる

単純に Blob を使ってコンテンツを配信している場合には、前段に Azure CDN や Front Door を用意してキャッシュさせることで多少時間稼ぎが出来ますし、オリジンを切り替えることで同一 URL のまま別ストレージアカウントを見るようにできます。

Blob は Object Replication を使うと、GRS よりも分かりやすく別リージョンへ Blob のレプリケーションを実現できるので、コンテンツ系の場合はこの方法が RA-GRS などを使うよりは良いでしょう。

レプリケーション先は読み込み専用になるため、フェールオーバー後にプライマリに昇格みたいな動作にはならないですが、コンテンツ配信系のサービスでは問題にならないケースもあるかと思います。

Cosmos DB の利用を検討する

Queue と Table に関しては Cosmos DB とその Change Feed を使うことで、同等の処理を実現できます。Cosmos DB の可用性は非常に高く、必要に応じてマルチリージョン構成も簡単に組めるため、価格以外では Azure Storage よりもメリットがあります。

具体的には Queue には Change Feed を使い、Table には Cosmos DB の Table API を使う形になります。最初から Cosmos DB を使うと決めている場合は SQL API がお勧めですが、一応 Table と互換性のある SDK が用意されているので、既存のコードも比較的簡単に移行できるはずです。

Virtual Machines

基本的には VM を素の状態で運用するのは可用性が下がるだけなので、独自に高可用性のアーキテクチャを組めない場合は PaaS / Serverless といったマネージドサービスを使った方が良いです。

App Service / Azure Functions で実現出来ないか検討する

以前の App Service / Azure Functions は仮想ネットワーク周りの機能が足りておらず、実現可能なアーキテクチャに限界がありましたが、Regional VNET Integration や Private Link で出来ることが増えています。

したがって最初から VM を使う方法を選ぶのではなく、App Service / Azure Functions で実現出来ないかを検討するのがベストです。非常に大量のインスタンスが必要なケースや gRPC などの、どうしても App Service で実現できない要件の場合に、初めて他のソリューションを検討しても遅くはありません。

可用性オプションを適切に利用する

どうしても VM が必要な場合には、要件に合わせた可用性オプションを利用していきましょう。Japan East には Availability Zone が 3 つ用意されているので、AZ を有効にした VM をデプロイすることで、障害の影響を最小限に抑えることができます。

他にもゾーンまではいかなくとも Availability Set を使えば同じデータセンター内でも、物理的に分離されたハードウェア上に分離してデプロイすることも出来ます。ドキュメントは各種用意されているので、VM を使う場合は熟読が必要でしょう。

VMSS を使うことで比較的簡単に実現は出来ますが、それなりに管理コストがかかることを覚悟する必要があるでしょう。素の VM を適切に運用するのはかなり難しいです。

その他のサービス

可用性のチェックリストが Well-Architected Framework カテゴリに用意されているので、参考までに目を通しておくとよいと思います。代表的なサービスが載っています。

Azure には最初から高可用性を実現するためのサービスや機能は揃っていますが、それを上手く使えるかどうかはアーキテクチャ次第です。適当に作るだけで実現出来るような甘い世界ではないので、障害発生を無駄にせずベストプラクティスやデザインパターンを学んでいきましょう。

*1:ステータスに載らないのは許されないが、Azure Service Health には載っていた