ここまで既にだいぶ長かったですが、あとは味付け程度の流れになります。
アプリケーションを更新する場合は当然デプロイして、インスタンスやコンテナの中身を入れ替えることになるのですが、今回はスタイリッシュに Blue-Green Deployment してみましょう。
目次
(0) はじめに(1) Docker
(2) イメージ自動生成
(3) サービス起動
(4) デプロイ ← イマココ!
(5) Auto Scaling
(6) 費用と性能
Blue-Green Deployment とは
AWSで良い記事がありました。(さすがの @riywo 先生)あとは画像検索でググったりしてください。その上でサラッと説明します。
古来のデプロイ方法は組織ごとに色々あるでしょうが、例えば何かしらのデプロイツールを使って、稼働中のインスタンスにSSHでコマンドを送信して、分散対象から外し、新しいコードを取得し、アプリケーション・デーモンを再起動し、分散対象に戻す。元イメージも別途更新しておく。みたいなことをやったりしていたはずです。
腕の見せ所ではあるのですが、それだと工程が複雑で面倒だよねってことで、もっとガバっと入れ替えたりゴボッと戻したりできた方がいいじゃん、ってことで生まれたのが Blue-Green です。なんで青緑かというと、インフラ的に Yellow とか Red は監視系で縁起が悪いので、地球にやさしい色2つが選ばれたんじゃないかと思います。知らんけど。
で、稼働中のインスタンス・セットを青とすると、サービスに影響ないところで最新バージョンのセットを緑として用意するわけです。用意ができたら、リクエストの転送方向をカチッと切り替えることでデプロイ完了とします。何かしら失敗したら、カチッと戻せるところも強みです。その次のデプロイは緑から青へ移すことになります。
今回の構成で具体的にいうと、ALB は1つで変化なく、Listener が転送先の切り替え役で、TargetGroup のblue/green の2つが新旧セット役、となります。実際はさらにRDSなどデータベースがあり、デプロイした瞬間にデータの整合性に問題が起きない確証が必要で、確証がとれないならメンテインしてからデプロイすることになります……がそのへんは昔と同じですね。
旧式のBlue-Greenだと、瞬間的ではないDNSでの切り替えとか、本当に倍の台数用意したりとか、限られた台数の中で2~3回に分割して更新したりとか、あったかもですが、ALBの構成はこの辺を完全に払拭するので考えられてるなぁと思う次第。
あとデメリットに稼働リソースの重複による価格増とかありますが、そんなことを真剣に問題視する人なんていないですよね?一日が1440分で、一日に10回デプロイするとして、1回あたり重複が5分としても、10回で50分。課金・稼働時間的に1490分となり、5%にも満たない増加率です。それが倍と言われるとイマイチな仕組みと言わざるを得ないですが、シンプルさと運用しやすさからくる安心・安全度を微々たるお金で買えるなら安いものです。
実際に取り入れるかはまた別問題ですが、知っておくだけでも損はないのでレッツラゴーです。
CodeDeploy で Blue-Green Deployment
CodeDeploy を使って、ECS に対してデプロイを発動します。進行図
TargetGroupが青と緑で切り替わるようになるよ!Role作成
CodeDeployが必要とする権限をRoleで作成します。これは既存ポリシーで事足りるので楽です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
resource "aws_iam_role" "ecs-codedeploy" { count = "${local.on_common ? 1 : 0}" name = "ecsCodeDeployRole" assume_role_policy = <<eof { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "codedeploy.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } eof } resource "aws_iam_role_policy_attachment" "ecs-codedeploy" { count = "${local.on_common ? 1 : 0}" role = "${aws_iam_role.ecs-codedeploy.name}" policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS" } |
アプリケーション作成
ECSを扱うデプロイをするよ、っていう宣言みたいなものです。
1 2 3 4 5 6 |
resource "aws_codedeploy_app" "web" { count = "${local.on_ecs_web ? 1 : 0}" compute_platform = "ECS" name = "${format("%s-%s-%s_ecs", local.env, local.service_name, "web")}" } |
デプロイグループ作成
具体的なデプロイ内容は、デプロイグループというリソースに詰め込みます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
resource "aws_codedeploy_deployment_group" "web" { count = "${local.on_ecs_web ? 1 : 0}" app_name = "${aws_codedeploy_app.web.name}" deployment_group_name = "${format("%s-%s-%s", local.env, local.service_name, "web")}" service_role_arn = "${data.terraform_remote_state.common.aws_iam_role_ecs_codedeploy_arn}" deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" auto_rollback_configuration { enabled = true events = ["DEPLOYMENT_FAILURE"] } blue_green_deployment_config { deployment_ready_option { action_on_timeout = "CONTINUE_DEPLOYMENT" } terminate_blue_instances_on_deployment_success { action = "TERMINATE" termination_wait_time_in_minutes = "5" } } deployment_style { deployment_option = "WITH_TRAFFIC_CONTROL" deployment_type = "BLUE_GREEN" } ecs_service { cluster_name = "${aws_ecs_cluster.web.name}" service_name = "${aws_ecs_service.web.name}" } load_balancer_info { target_group_pair_info { prod_traffic_route { listener_arns = ["${aws_alb_listener.web.arn}"] } target_group { name = "${aws_alb_target_group.ecs-blue-web.name}" } target_group { name = "${aws_alb_target_group.ecs-green-web.name}" } } } } |
ecs_service で対象のECSサービスを指定、
deployment_style で Blue-Green を指定、
load_balancer_info でALB Listener と切り替える TargetGroup を指定、
あたりが主要ポイントでしょうか。termination_wait_time_in_minutes がデプロイ成功後に5分待機してからオリジナル・セットを削除する設定で、CodeDeployDefault.ECSAllAtOnce はListenerを更新してトラフィックの100%一気に切り替えるとか、色々あるのでドキュメントも眺めてみてください。
動作確認
どのタイミングでデプロイを発動するかは、扱う人によって変わると思います。コードが更新されたらテストからデプロイまで自動化するかもですし、dev/staging/production など環境によっても変えたいかもしれません。今回の内容だと、管理画面でポチることになります。ECS -> クラスター -> サービス へ移動し、「更新」へ行くと、「新しいデプロイの強制」という項目があります。これにチェックを入れて、他必要な変更もしつつ完了することでデプロイが発動します。
発動すると、ECSのサービス -> デプロイタブ もしくは、CodeDeploy -> デプロイ にて、実行状況を確認することができます。CodeDeployの方では発動はできないので、詳細を見る場合にのみ行くことになりそうです。ECSデプロイでは、発動もRollbackもできるので、基本はこちらでの操作となるでしょう。
デプロイが完了すると、ALB Listener のデフォルトターゲットが切り替わっているのが確認できます。そして、5分後には Original が削除され、Replacement だけが残り、全てが完了となります。
ひとくちにデプロイと言ってもアプリケーション・コードの更新だけではなく、データベースの更新などが発生するデプロイもあるので、実際にはこれだけで済むことはないでしょう。かといって、デプロイのためのインスタンスを用意して──ってのもできれば避けたい時代なので、Lambdaなどを駆使して手作業をできるだけ排除していくような方針で設計していくことを心がけると、
業務時間内にインターネッツを徘徊する時間を確保── じゃなくて、安心・安全・迅速なアプリケーション開発と運用ができるようになるでしょう!
あとは、デプロイの成否を社内チャット・グループに送信するとか、肉付けをみんなで楽しんでください。
次へまいります:Autoscaling