CodeDeployとCircleCIを利用したBlue/Greenデプロイメント

2016.08.09

はじめに

こんにちは、中山です。

今回CircleCIとCodeDeployを組み合わせてイミュータブルなBlue/Greenデプロイ環境を作ってみました。内容的には以下のエントリを組み合わせたものになります。

構成図

デプロイの流れが異なるのでDevとOpsで構成図を分けました。今回説明を簡略化させるためにDev用コード(CodeDeploy)、Ops用コード(Terraform)を混ぜていますが、本番環境では別リポジトリに分離して管理することをオススメします。

Devの場合

circleci-dev

Devの場合のデプロイフローは以下のとおりです。

  1. コードの修正後GitHubに git push
  2. Web HookでCircleCIに通知
  3. circle.yml の中でリビジョンのアップロードとCodeDeployの実行

Dev用のブランチには以下の2つを利用します。

  • release/blue : Blueリリース用ブランチ
  • release/green : Greenリリース用ブランチ

今回テスト目的なので、リリースブランチのみにします。つまりアプリケーションのテストは含めていません。

circle.yml の内容は以下のとおりです。

<snip>
deployment:
  blue:
    branch: release/blue
    codedeploy:
      bg-deploy-app:
        application_root: /app
        revision_location:
          revision_type: S3
          s3_location:
            bucket: bg-deploy-app
            key_pattern: apps/{SHORT_COMMIT}
        region: ap-northeast-1
        deployment_group: Blue
        deployment_config: CodeDeployDefault.AllAtOnce
  green:
    branch: release/green
    codedeploy:
      bg-deploy-app:
        application_root: /app
        revision_location:
          revision_type: S3
          s3_location:
            bucket: bg-deploy-app
            key_pattern: apps/{SHORT_COMMIT}
        region: ap-northeast-1
        deployment_group: Green
        deployment_config: CodeDeployDefault.AllAtOnce
<snip>

release/blue または release/green ブランチにプッシュされたらデプロイが実行されます。CodeDeployの設定内容(詳細はドキュメント参照)は以下のとおりです。

設定 内容
codedeploy CircleCIでCodeDeployの設定をする際に指定します。
bg-deploy-app CodeDeployのアプリケーション名です。
application_root appspec.yml の設置場所です。リポジトリのトップディレクトリからの相対パスを指定します。少しハマったのですが、先頭のスラッシュは必須です。
revision_location リビジョンの設定です。
revision_type リビジョンの保管場所です。現時点ではS3のみサポートされています。
s3_location 以降の設定でリビジョンを保存するS3バケットの場所を指定します。
bucket リビジョンを保存するバケット名を指定します。
key_pattern リビジョンを保存するS3バケットのキー名を指定します。ここにはいろいろと変数を指定可能です。 {SHORT_COMMIT} はコミットハッシュ値のショートバージョンが参照できます。
region そのまんまリージョンです。
deployment_group そのまんまデプロイメントグループ名です。
deployment_config どうやってデプロイするかを指定します。

Opsの場合

circleci-ops

Opsの場合のデプロイフローは以下のとおりです。

  1. コードの修正後GitHubに git push
  2. Web HookでCircleCIに通知
  3. circle.yml の中でTerraformの実行

Ops用のブランチには以下の2つを利用します。

  • ops : 開発用ブランチ
  • release/ops : リリース用ブランチ

circle.yml については、以前のエントリに書いたので割愛します。

コード

GitHubに置いときました。ご自由にお使いください。

実行方法

GitHubとCircleCIとの連携など、初期セットアップは以下のエントリを参照してください。

以下の手順では初期セットアップはすでに導入済みで、Blue環境がASGの下にぶら下がっているという前提で進めます。

Blueへのデプロイ

まず素の状態で動作しているBlueに対してデプロイしてセットアップしましょう。今回はデプロイ自体を目的にしているので、空コミットでプッシュします。Blueのデプロイメントグループに対してデプロイしたいので、ブランチを release/blue に切り替えて作業してください。

$ git checkout -b release/blue
Switched to a new branch 'release/blue'
$ git commit -m 'Deploy blue' --allow-empty
[release/blue 66d1f2d] Deploy blue
$ git push origin release/blue
Counting objects: 1, done.
Writing objects: 100% (1/1), 185 bytes | 0 bytes/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To https://github.com/knakayama/blue-green-deployment-with-codedeploy-and-circleci.git
   be42e77..66d1f2d  release/blue -> release/blue

デプロイに成功後、ELBのエンドポイントにアクセスすると以下の画面が表示されます。

blue

作業完了後、masterブランチに結果を反映しておきます。

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff release/blue
Already up-to-date!
Merge made by the 'recursive' strategy.

Greenへのデプロイ

続いてGreenへデプロイします。先程と同じようにまずブランチを切って作業を実施します。Green用リリースブランチは release/green です。

$ git checkout -b release/green
Switched to a new branch 'release/green'

続いて app/index.html を以下のように修正してください。

$ git diff
diff --git a/app/index.html b/app/index.html
index de45436..5e9c4db 100644
--- a/app/index.html
+++ b/app/index.html
@@ -5,11 +5,11 @@
     <style type="text/css">

     body {
-      background-color: #00FFFF;
+      background-color: #00FF00;
     }
     </style>
   </head>
   <body>
-    <h1>This is Blue v1.0.</h1>
+    <h1>This is Green v1.0.</h1>
   </body>
 </html>

コミット後、プッシュします。

$ git add app/index.html
$ git commit -m 'Deploy green'
[release/green 52e6355] Deploy green
 1 file changed, 2 insertions(+), 2 deletions(-)
$ git push origin release/green
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 611 bytes | 0 bytes/s, done.
Total 5 (delta 2), reused 0 (delta 0)
To https://github.com/knakayama/blue-green-deployment-with-codedeploy-and-circleci.git
   6558892..52e6355  release/green -> release/green

念のためGreenサーバにSSHでログインして確認します。以下のように表示されたらOKです。

$ ssh -i keys/key_pair ec2-user@<public-ip> 'curl -fsSL localhost'
<!DOCTYPE html>
<html>
  <head>
    <title>Blue</title>
    <style type="text/css">

    body {
      background-color: #00FF00;
    }
    </style>
  </head>
  <body>
    <h1>This is Green v1.0.</h1>
  </body>
</html>

ELBの切り替え

この時点では、ELBの向き先がBlueになっています。これを切り替えます。Terraformのコードは ops ブランチでテスト可能です。まずはここでテストしてみます。以下のコマンドを実行してブランチを切り替えてください。

# masterは最新の状態にしたいのでrelease/greenをマージ
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff release/green
Merge made by the 'recursive' strategy.
 app/index.html | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
# masterからブランチを切る
$ git checkout -b ops
Switched to a new branch 'ops'

modules/asg/blue/blue.tfmodules/asg/green/green.tf をそれぞれ以下のように修正してください。

$ git diff
diff --git a/modules/asg/blue/blue.tf b/modules/asg/blue/blue.tf
index c1e20e0..b470338 100644
--- a/modules/asg/blue/blue.tf
+++ b/modules/asg/blue/blue.tf
@@ -8,14 +8,14 @@ resource "aws_autoscaling_group" "blue" {
   health_check_grace_period = 300
   health_check_type         = "ELB"
   force_delete              = true
-  load_balancers            = ["${var.load_balancer_id}"]
+
+  #load_balancers            = ["${var.load_balancer_id}"]

   tag {
     key                 = "Name"
     value               = "Blue"
     propagate_at_launch = true
   }
-
   lifecycle {
     create_before_destroy = true
   }
diff --git a/modules/asg/green/green.tf b/modules/asg/green/green.tf
index afcda96..262e856 100644
--- a/modules/asg/green/green.tf
+++ b/modules/asg/green/green.tf
@@ -8,14 +8,14 @@ resource "aws_autoscaling_group" "green" {
   health_check_grace_period = 300
   health_check_type         = "ELB"
   force_delete              = true
-
-  #load_balancers            = ["${var.load_balancer_id}"]
+  load_balancers            = ["${var.load_balancer_id}"]

   tag {
     key                 = "Name"
     value               = "Green"
     propagate_at_launch = true
   }
+
   lifecycle {
     create_before_destroy = true

修正したらコミット後、 ops ブランチにプッシュします。

$ git add modules
$ git commit -m 'Change ELB'
[ops 29420fb] Change ELB
 2 files changed, 4 insertions(+), 4 deletions(-)
$ git push origin ops

成功するとCircleCIの画面で以下のように表示されます。

circleci

テストに成功したので実際にデプロイしましょう。リリースは release/ops ブランチで実施します。

$ git checkout -b release/ops
Switched to a new branch 'release/ops'
$ git push origin release/ops
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/knakayama/blue-green-deployment-with-codedeploy-and-circleci.git
 * [new branch]      release/ops -> release/ops

デプロイに成功後、ELBのエンドポイントにアクセスすると以下が画面が表示されます。

green

まとめ

いかがだったでしょうか。

CodeDeployとCircleCIを組み合わせてBlue/Greenデプロイを作成してみました。CircleCIのようなCIサービスを使いこなして、チーム内でデプロイ作業を安全に行いたいですね。もっと触ってみようと思います。

本エントリがみなさんの参考になれば幸いです。