どうもこんにちは。@stormcat24です。前回寄稿した記事から1年半ぶりになります。
主な仕事は業務中のTwitterで、その傍らでAmebaFRESH!というサービスを絶賛開発してたりしています(この記事が公開される頃にはリリースしてるかしら?)。何かネイティブやりに意気揚々と異動してきたんですが、いつの間にかサーバサイドエンジニアになってました。まあサーバサイドといっても、自分はかなりWebオペレーション寄りなんですけど。
で、今回はFlexible Blue Green Deploymentの仕組みを作ったよっていうお話です。
そもそもBlue Green Deploymentとは
Blue Green DeploymentとはImmutable Infrastructure(不変なインフラストラクチャ)思想の一貫で、サーバの内容やアプリケーションを更新したい場合に、稼働しているサーバ群を変更するのではなく新規にまるっとサーバ群ごと作りなおして切り替えるという手法です。そのためにはサーバ群が2系統必要なので2系統をそれぞれBlueとGreenと呼び、それを更新の度にBlueとGreenを切り替えるのでBlue Green Deploymentと呼ばれています。この手法だとロードバランサーを切り替えるだけで稼動系を変更できるので、ゼロダウンタイムリリースのテクニックとしても有用です。リリース後に問題があった場合に旧稼動系に戻すといったことも容易にできるようになります(DB変更がある場合は問題は複雑ですが)。
Immutable Infrastructureを支える技術
かつてサーバを構築する作業というものはそれなりの時間とコストを伴うものでしたが、AWSやGCP等のクラウドプラットフォーム全盛時代である今日では、サーバを作ったり破棄したりという作業がカジュアルにできるようになりました。Blue Green Deploymentを実現する上においての重要な要素は、このImmutable Infrastructure思想に合致する要素を一つでも多く満たすということに尽きます。
- インフラはプログラムによって全て構築可能であること
- インフラの構築時間が短いこと(数分)
- インフラは自動でスケールアウト・スケールインできること
- インフラはいつでも破棄されても良いように、消失してはいけないデータを残さないようになっていること
- アプリケーションはポータビリティ性が高く、かつスケールする仕組みであること
Terraform
はHashicorp社が開発しているオーケストレーションツールで、インフラの構成を独自のDSLによって定義し、インスタンスの破棄や作成等を行ってくれます。AWSであればCloudFormationでも同様のことができますが、Terraformの方は特定のプロバイダに依存しない仕組みになっています。Docker
AmebaFRESH!ではDockerをかなり積極活用しています。ってか、むしろFull Dockernizedされています。Dockerのメリットはそのポータビリティの高さにあります。イメージさえ作ってしまえば、Dockerホスト上に乗せるだけ。コンテナ型仮想化技術によって論理的に独立した空間であるため、ホスト環境に依存した問題が発生しにくく、そして起動が早いというメリットがあります。
これまでは環境を作る上でChefやAnsibleでのProvisioningが効率化の上で必要不可欠なことでした。しかし、Dockerの場合はアプリケーションもミドルウェアもコンテナとなんてポータブルなので、実際のホスト環境にProvisioningを施す必要性があまりありません。そのため、今回はAnsibleは利用せずにcloudinitだけで対応しています。WebサーバやGoで書かれたアプリケーションサーバ、バッチサーバや各種エージェント等諸々全てがDockerコンテナとして稼働しています。
EC2 Container Service(ECS)
ECSはEC2のインスタンス群をDockerのクラスタにしてしまおうというサービスです。ECS上のEC2ではecs-agentというECSクラスタ上でコンテナやリソースを監視するエージェント(実はこれもDockerコンテナ)が稼働しており、クラスタ内のどのインスタンスにコンテナを割り当てるか?等の制御を行います。簡単に言うとDockerのスケジューラーです。td-agent(Fluentd)
サーバに残ってしまうものの代表格で、かつ消失が許されないものといえばログです。保持しておくべきログはtd-agentによって然る場所に転送されます。このtd-agentもDockerコンテナとして稼働させます。転送したいログはDockerホストに各コンテナからマウントされたものをtd-agentコンテナから任意の場所に転送します。ちなみにDokcer1.8ではFluentd Logging Driverがサポートされました。この機能を利用すればtd-agentコンテナを立てることなく、任意のコンテナから直接ログを転送することが可能です。
Ameba FRESH!のBlue Green Deployment
AmebaFRESH!でのBlue Green Deploymentの構成は以下のようになっています。これはわかりやすくした図ですが、実際は複数のMicroservicesで構成されておりこれのセットがPublic/Internal問わずいくつか存在しています。AutoScaling Group + Lambda
AmebaFRESH!ではAWSを利用しているので、AutoScaling Groupによってインスタンスの数を容易に変更することができます。ただ、負荷が高騰し、AutoScalingでのインスタンス増加が発動してもECSクラスタ上のタスクの数までは変わりません。インスタンスの数とともにタスクの数も一緒にスケールさせてあげないといけません。そこでECSのタスクの数をコントロールするためにLambdaを利用しています。LambdaはNodeやJavaで書かれたジョブを、稼働させた分だけしか料金がかからないサービスで、常にEC2を稼働させて同じようなことをさせるよりも効率的です。AutoScalingでの増減が発生したら、LambdaでEC2のDesiredCount(稼働させたいECSタスクの数)も変更してあげましょう。
Elastic Load Balancing(ELB)
AWSなのでロードバランサーにはELBを利用しています。ELBはAutoScaling Group単位での付け替えが可能なので、スケールアップ・スケールインしやすいBlue Green Deployment構成を実現することができます。ecs-formation
今回はECSのDockerクラスタやコンテナ構成を定義して管理するためにecs-formationというツールを書きました。docker-compose互換のYAMLファイルでECSのTask Definitionsを定義し、Task Definitionsの反映やECS Serviceの更新、ECSクラスタ単位でのBlue Green Deploymentをサポートしています。
旧稼動系を柔軟に縮退させる
Blue Green Deploymentはとても旨味のある仕組みですが、2系統のサーバ群が必要になるので単純にやるとコストが2倍になります。そこで今回はAutoScaling Groupを利用することでその旨味を活かしながらも、コスト面にも気を配った構成になっています。これをFlexible Blue Green Deploymentと勝手に呼んでいます。Blue Green Deploymentでは新しい稼動系がリリース後安定的に稼働していてロールバックの必要が無いようであれば、旧稼動系をずっと置いておく必要はありません。AutoScaling Groupであれば負荷がないので勝手にスケールインしていきますが、ゼロにしちゃってもいいよってケースもあるのでそこは柔軟に制御できるようにしてます。
ちなみに以下のようにカジュアルにやってます。ウチはChatOps推進なので、Jenkinsでポチポチなんてやりません。以下のように指示すると・・・
こんな感じでbotがAutoScalingのMaxSizeを調整してくれます。(ちなみにkirikoというのはウチのチームのBotです)
逆に新しいアプリケーションに更新する場合は、次に新系統となるクラスタをあらかじめウォームアップしておくこともできます。
旧稼働系の考慮すべきこと
Blue Green Deploy後に旧稼働系となったクラスタですが、そのまま放置しているとシステムに思わぬ副作用を及ぼすこともあります。例えば、以下のような考慮が必要でしょうか。- 他のサーバへの不必要な通信を止める
- ジョブキューへのロングポーリングを止める(旧稼働系にメッセージを吸われるので)