AWS Code Deploy を雑に触った

AWS Code Deploy で EC2 インスタンスにコードを雑にデプロイしてみた。

IAM ロールの準備

Code Deploy がインスタンスとかを操作するために必要なロール(サービスロール)を作成します。

# ロールを作成
aws iam create-role --role-name CodeDeployServiceRole --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "codedeploy.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}'

# ロールに Code Deploy のためのポリシーをアタッチ
aws iam attach-role-policy --role-name CodeDeployServiceRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole

EC2 インスタンスに付与するインスタンスプロファイルを作成します。

# ロールを作成
aws iam create-role --role-name CodeDeployDemo-EC2-Instance-Profile --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}'

# ロールにインラインポリシーを設定
aws iam put-role-policy --role-name CodeDeployDemo-EC2-Instance-Profile \
    --policy-name CodeDeployDemo-EC2-Permissions --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}'

# インスタンスプロファイルを作成
aws iam create-instance-profile --instance-profile-name CodeDeployDemo-EC2-Instance-Profile

# インスタンスプロファイルにロールを追加
aws iam add-role-to-instance-profile --instance-profile-name CodeDeployDemo-EC2-Instance-Profile \
    --role-name CodeDeployDemo-EC2-Instance-Profile

Code Deploy の設定とインスタンスの作成

Code Deploy のアプリケーションを作成します。 これは後述のデプロイグループをまとめるだけのものっぽいです。

aws deploy create-application --application-name myapp

次にデプロイグループを作成します。どのインスタンスにどのようにデプロイするかの設定です。

他にもいろいろ指定できて、インプレースではなく Blue/Green にしたり、ロードバランサへのデタッチ/アタッチを自動化したりもできます。今回は何も指定していないので一番シンプルなインプレースでロードバランサ無しです。

--service-role-arn で指定しているのは↑で作成したサービスロールです。--ec2-tag-filters で対象となる EC2 インスタンスのタグを指定します。

aws deploy create-deployment-group \
    --application-name myapp \
    --deployment-group-name mydep \
    --service-role-arn arn:aws:iam::999999999999:role/CodeDeployServiceRole \
    --ec2-tag-filters Key=Name,Value=mydep,Type=KEY_AND_VALUE

インスタンスを作成します。↑で作成したインスタンスプロファイルを指定したり、↑で指定した通りにタグを付与したりします。ついでにユーザーデータで Code Deploy のエージェントをインストールしたり Apache をインストールしたりします。

aws ec2 run-instances \
  --image-id "ami-91c4d3ed" \
  --key-name "oreore" \
  --instance-type "t2.nano" \
  --block-device-mappings "DeviceName=/dev/sda1,Ebs={DeleteOnTermination=true,VolumeType=gp2}" \
  --associate-public-ip-address \
  --iam-instance-profile "Name=CodeDeployDemo-EC2-Instance-Profile" \
  --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=mydep}]" \
  --user-data '#!/bin/bash
set -eu

yum -y install ruby
yum -y install wget
wget https://aws-codedeploy-ap-northeast-1.s3.amazonaws.com/latest/install
chmod +x ./install
./install auto

yum -y install httpd
systemctl start httpd
systemctl enable httpd
'

aws ec2 describe-instances --filters Name=instance-state-name,Values=pending \
  --query "Reservations[].Instances[].PublicIpAddress" \
  --output text
#=> 192.0.2.123

デプロイ

appspec.yml ファイルでインスタンス内にコードをどのようにデプロイするか指定します。 この例だと index.html をドキュメントルートにおいて、デプロイ後に after.sh を実行します。

version: 0.0
os: linux
files:
  - source: /index.html
    destination: /var/www/html/

hooks:
  AfterInstall:
    - location: /after.sh
      timeout: 300
      runas: root

index.htmlafter.sh も適当に作ってアーカイブにまとめて S3 にアップします。

tar czvf app.tar.gz appspec.yml index.html after.sh
aws s3 cp app.tar.gz s3://oreore-deploy/v1.tar.gz

デプロイを開始します。

aws deploy create-deployment --application-name myapp --deployment-group-name mydep \
    --s3-location bucket=oreore-deploy,bundleType=tgz,key=v1.tar.gz
#=> d-111111111

終わるのを待って、結果を確認します。

aws deploy wait deployment-successful --deployment-id d-111111111
aws deploy get-deployment --deployment-id d-111111111

ブラウザで見ると・・・ index.html の内容が見えます、デプロイ成功です。

適当にファイルを編集してもう一度デプロイすると・・・

tar czvf app.tar.gz appspec.yml index.html after.sh
aws s3 cp app.tar.gz s3://oreore-deploy/v2.tar.gz

aws deploy create-deployment --application-name myapp --deployment-group-name mydep \
    --s3-location bucket=oreore-deploy,bundleType=tgz,key=v2.tar.gz
#=> d-222222222

aws deploy wait deployment-successful --deployment-id d-222222222
aws deploy get-deployment --deployment-id d-222222222

ブラウザで見ると index.html への編集内容が反映されています。

さいごに

(試していないけど)Blue/Green デプロイがわりと簡単にできるのが良さそう。自前でやろうとするとわりと面倒だろうので。

ただ、インプレースでデプロイする分にはどうかな・・サーバでスクリプト1本走らせればいいだけなので、わざわざ Code Deploy を使うまでも無いような気もする。

例えば AWS Systems Manager の Run Command で次のようにしても似たようなことはできそう。

aws ssm send-command \
  --region ap-northeast-1 \
  --document-name AWS-RunShellScript \
  --targets Key=tag:Name,Values=mydep \
  --timeout-seconds 30 \
  --max-concurrency 1 \
  --max-errors 1 \
  --parameters '{
    "commands":[
        "aws s3 cp s3://oreore-deploy/v2.tar.gz /tmp",
        "sudo tar xvzf v2.tar.gz -C /var/www/html/",
        "rm -f v2.tar.gz"
    ],
    "workingDirectory":["/tmp"],
    "executionTimeout":["3600"]
  }'

ただ、Code Deploy ならどのバージョンがデプロイされているか分かるし、ロールバックもマネジメントコンソールから簡単にできたりと、メリットは多い。