1つのコードベースから複数の環境にデプロイできる関係性で管理する

小島淳氏:まず、Factor1. コードベース。これは何かというと、バージョンを管理されている1つのコードベースと複数のデプロイです。何を言いたいかというと、1つのコードベースを使って、複数の環境に対応できるようにしましょうということです。

コードを管理するのは、アプリケーションだけじゃないんですよ。インフラストラクチャもきちんとInfrastructure as Codeとして管理して、ConfigもきちんとGitで管理しましょうということです。

実際に1つのコードベースと、複数のデプロイがどういう関係を持っているかが、次のページです。Twelve-Factorに適合した状態、左側を見てください。

1つのコードベースがあって、その1つのコードベースは「本番」「ステージング」「開発」と3つの環境に対してデプロイされるようになっていますよね。なので、この3つの環境の差異はなくなっているはずです。もちろん開発を進めていくので、開発が先行していきますが、この3つの環境はすでにローリングされているので、基本的にコードベースに対しての差異がありません。これがTwelve-Factorに適合した状態です。

ありがちな例として、コードベースが複数あってそれぞれに環境が用意されているということがあります。本番用のコードベース、ステージング用のコードベース、開発用のコードベースです。これをやってしまうことがけっこう多いんですが、メチャクチャコードの管理が大変です。

こういうことをやってしまうと、スパゲティ(コード)になりやすくなっちゃうんですね。なので、こういうことはやめましょう。1つのコードベースから複数の環境にデプロイできるような関係性をもって管理していきましょう。

1つのコードベースを複数のアプリケーションで共有している場合です。環境を本番環境・ステージング環境などと分けたときは1つのコードベースでいいんですが、今度は1つのコードベースで複数のアプリを管理している場合ですね。

これはパッケージ屋さんがよくやりがちです。1つのパッケージを使って、複数のアプリを管理するんですが、アプリBやアプリCに独自の修正が入るパターンがありますよね。その独自の修正を全部コードベースに内包してうまく運営できるんだったらいいんですが、「あれ、この機能どこのアプリが使っているんだっけ?」とスパゲティになっちゃいます。

そういうときは、1つのコードベースから共通のコードをライブラリに分解してパッケージにして配布するというやり方をします。

このパッケージにするのはけっこう重要で、自分たちが作ったコードをパッケージにしないと、あとで保守がメチャクチャ大変になります。大変になっている今のレガシーアプリケーションは、これがたぶん大元なっていると思うんですね。なので、しっかりとパッケージングして、共通コードをライブラリに分解して、配布して、それぞれのアプリケーションに対応するというやり方をします。

例えば共通コードのライブラリを配布するときに、僕らが何を使うかというと、Azure DevOpsを使います。Azure DevOpsのArtifactsというサービスがあるので、Artifactsを使ってパッケージの管理をしたり、パッケージリポジトリとして提供をしたりします。

「Azure Key Vault」を使って環境変数を安全に取り出す

Factor2はとばしてFactor3。Factor3は「アプリケーションの設定は、デプロイの間で異なる唯一のものである」。これは何を言っているかというと、環境変数ですね。定数ではなく環境変数に格納し、コードから分離します。だから環境変数は唯一異なるものなんですよということを言っています。

環境変数の分離の仕方はいくつかあります。App Serviceを使っている場合は、App Serviceの中に環境変数を設定できます。複数のApp Serviceを使っていて、その複数のApp Serviceで環境変数を一元管理した場合は、App Configurationというサービスを使います。仮想マシンの場合は、.envなどをアプリ内に配置するのが一般的な方法だと思います。

1つおすすめするのはKey Vaultですね。Azure Key Vaultを使ってみましょう。Azure Key Vaultは環境変数などのシークレットを安全に取り扱うためのキーストアです。

このキーストアはちょっと特殊で、Azure Active Directoryから発行されたManaged IDというものを認証・認可します。認証・認可したものが、このAccess Policyの定義によってアクセスできるキーコンテナが分かれていくので、「このアプリケーションが持つManaged IDはこのキーコンテナを使います」みたいにして、そこから環境変数やSecretを取り出せます。

このKey Vaultをしっかりと使うことによって、かなり安全にSecretや機密情報を取り扱うことができるので、ぜひここらへんをやってみてはどうかと思います。

キャッシュやセッションやログはステートレスにする

いくつかとばして、Factor6。Factor6はアプリケーションを1つ、もしくは複数のステートレスなプロセスとして実行します。キャッシュ、セッション、ログなどステートを持たないということです。よく忘れがちなのはログです。「キャッシュとセッションはステートを分けています。でも、ログはですね……」みたいに、よく聞きます。これらは全部分けましょうね。

クラウドにするときに、僕らもいろいろコンサルティングやサポートをするんですが、このプロセスの分離ができていないお客さまが非常に多いです。できていない理由もいろいろあるんですが、なかなかそこの理解が進まないがために、クラウドに入ってもうまく使えないという例をいくつか見ています。ぜひこのプロセスを分離するところをしっかりと理解してもらいたいなと思います。

例えば.NET Coreの場合、分散キャッシュの方法として、メモリやSQL ServerやRedisなどを使うことができます。これらを使うことによってセッションを外に出すことが可能です。

PaaSに限らず、クラウドを使うということはスケールを考えるということですよね。セッションやステート情報などストアを内部にもってしまうと、スケールアウトしたときにその情報の差異が出てしまうので、外に出しておくのが基本的な構成です。

基本的な構成なので、この2つは覚えておいてください。ステートフルでなくて、ステートレスにしておきます。マイクロサービスと言う人もいるんですが、そうではなくてステートレスにしておきます。マイクロサービスの話をするのはだいぶ早いかなと思います。ステートレスです。

当たり前なんですが、インスタンス(IaaSやPaaS)にはデータを置きません。データを内包してしまうと、プロセス管理が複雑になってしまうので、例えばセッションやSQL Databaseを使ってストアを外に分離したり、Blob(Binary Large Objects)を使ってファイルを外に分離したりします。

セッションスティッキーを使わなくてはいけない場合もけっこう出てきます。そのときはApplication Gateway・Front Doorというサービスを使いますが、残念ながらAzureは、セッションスティッキーの管理はあまり得意ではないです。なので、セッションスティッキーをベースとしたセッション管理はやめておいたほうがいいかなと思います。

あくまでもスティッキーなので、スティッキーとしては使えますが、セッションのデータベースとしては使ってはいけません。使ってはいけないというか、使えません。なので、きちんと別のKey Value Storeを用意しておくべきです。例えばRedis Cacheのようなサービスを用意しておくといいと思います。これはどのクラウドでも同じな気はします。

ポートバインディングを通してサービスを公開できないアプリケーションサーバーは注意

Factor7がポートバインディングです。ポートバインディングを通してサービスを公開するのはけっこう当たり前のようにやるんですが、このポートバインディングを通してサービスを公開できないアプリケーションサーバーをもつのは僕はあんまりよくないと思います。

ちなみに.NETの場合はよくIISって言われるんですが、.NET Coreの場合は、中のアプリケーションサーバーとして「Kestrel」が入っているので、そのKestrelを使ってアプリケーションを公開することが可能です。例えば.NET Coreの場合はKestrelをそのままアプリケーションサーバー、Webサーバーとして外部に公開できます。

ただ、そういうことをやる前に、Webサーバーをリバースプロキシとして1個置いて、アプリケーションサーバーごとにKestrelのポートを変えると、マイクロサービスのようなかたちで複数の分散処理をするときも意外とキレイにできるので、こういうかたちにしておいてもらいたいと思います。

なので、こういうかたちでアプリケーションサーバーを構成できないアプリケーションを使う場合は注意が必要です。どうしてもズレが発生します。

あとプロキシとの連絡。昔はリバースプロキシとアプリケーションサーバーの間は、同じサーバーを入れるパターンがあったと思います。でも、そうではなくて、分けるんだったら、TCP・UDPのみのプロトコルで通信します。Unix Socketやファイルシステムを使ったものは分けられなくなるからやめておいてください。

いつでも再起動される可能性があるプロセスに備える

Factor9は廃棄容易性ですね。これは「突然死に対して堅牢であるべき」ですね。常に情報をキャッチアップしておくのが大事です。コードのデプロイや、プロセスの再配置(スケーリング)のためのプロセスはいつでも再起動される可能性があります。なので、この状態を常にキャッチアップするのが重要です。

コンテナの場合は、シェアードナッシングという話もあるんですが、この中で重要なのは、SIGTERMを正しくハンドリングするということです。SIGTERMを正しくハンドリングしないと、突然死に対応できません。ここらへんの対応ってみんな意外とやっていないんですよ。やっていないので、さっきのApplication Insightsみたいなのを入れて、ここらへんの情報を常に監視をするのが大事です。

Azureで取り扱うコンテナサービスはいくつかあるんですが、例えばWeb Apps、Web App for Containerというものがあります。これがWeb Appsのコンテナ版なので、一番使いやすいと僕は思っています。

コンテナ単体で使いたいときは、Azure Container Instancesというものがあります。ACIってやつですね。大規模なマイクロサービスでやるときはKubernetes、AKSを使います。いろいろなサービスがあるので、自分が提供しようとしているサービスに合わせてコンテナのサービスを選ぶといいと思います。

「開発」「ステージング」「本番」は同じ状態に保つ

最後に本番/開発一致。本当はFactorは12個あり、時間がないから端折りますが、これは開発・ステージング・本番環境をできるだけ一致させた状態で保ちましょうということです。Factor1やFactor3、Factor4が連動します。

同じ環境を提供すれば、ブルーグリーンデプロイメントみたいなことを簡単にできます。Web Appsは、Swapという機能があるので、ボタン一発ですごく簡単にできます。これでスロットを切り替えることによって、ブルーとグリーンを切り替えられます。

実際にブルーグリーンデプロイメントをやるのは、コンテナなどを使うのがよくある例なんですが、けっこう難しいです。このブルーグリーンデプロイメントの仕組みを作るのはかなり難しいです。

全部自分で作るというのは大変なので、App Serviceを使うのがやっぱり一番いいとは思うんですが、もし全部使うんだったら、ロードバランサーよりも、プロキシの中に入っているサーキットブレーカーを使います。

サーキットブレーカーパターンというデザインパターンあるので、そのサーキットブレーカーパターンを使って経路を書いてあげるのがやり方だと思います。もちろん、ロードバランサーが主軸になりますが、そこからサーキットブレーカーを使って経路を変えて、ブルーグリーンのようなかたちで入れ替える。そういったものがブルーグリーンデプロイメントです。

ログはファイル出力ではなく標準出力にする

最後にまだログがありましたね。ログが忘れがちですよね。ログはさっき三宅(@kazuyukimiyake)さんもおっしゃっていましたが、Application InsightsやAzure Monitorを使ってファイル出力ではなく標準出力にします。

ロガーを作って標準出力にする場合の.NETのコードがあるんですが、App Insightsのライブラリを入れてApp Insightsに送り込めます。

自分でこのロガーを要所要所に作ってください。そうしないとApp Insightsに情報が送られません。ロガーを作って、Application Insightsにログを送って、そこでアプリケーションの計測をします。そして監視をかけるやり方です。ぜひこれは覚えておいて、必須でやってください。

サーバーログは、仮想マシンの場合、FluentdとかバーってAgentを入れて、ダーッとログを収集して、ElasticsearchやKibanaや、場合によってはZabbixなどの情報を入れてダッシュボードを作りますが、Azureの場合はAzure Monitor一発で終わっちゃいます。

Azure Monitorはかなり優秀なので、ぜひ使ってみてください。Application Insightsを利用して、トレーシングまでかけることができるので、Azure Monitorはいろいろと使えると思います。

それと、オートスケールをやるときは、必ずAzure Monitorのフックが必要になるので、オートスケールをやるならAzure Monitorが入ってくるということを覚えていてください。

ガーッと話しちゃいましたが、The Twelve-Factor Appのクラウドネイティブ化はぜひやってもらいたいなと思います。細かいことほかにもたくさんあるんですよ。全部伝えきれないんですが、今ホワイトペーパーを作っていて、そのうちマイクロソフトさんと一緒にリリースされると思うので、ぜひホワイトペーパーも見てもらいたいなと思っています。