CircleCI + AWS CodeDeployによるテスト・デプロイの自動化

担当しているプロジェクトがテストの実行もデプロイも手動だったので、
テストとデプロイの自動化に挑戦してみました。
バージョン管理はGitHub、サーバーはEC2を使っていることもあり、
今回のケースでは無料で実現できる`CircleCI`と`AWS CodeDeploy`を使います。


こちらの記事に沿う形で行いました。
[CircleCIとAWS（CodeDeploy/S3）で作るCI環境](http://qiita.com/hardreggaecafe/items/7ff3ada575a1100fa4a2)


# 構築したいこと

- 特定ブランチにpushすると、CircleCIでテストされ、OKなら指定のEC2インスタンスにデプロイする
- cronはgitで管理し、デプロイ毎にcronを更新する
- デプロイ毎に`pip install requirements.txt`など依存ライブラリのインストールを行う

# 0. 事前準備

- [ ] aws account作成
- [ ] GitHub account作成
- [ ] GitHub にデプロイしたいのリポジトリを作成（今回は`codedeploy-test`とする）

# 1. AWS IAM 設定

## 1.1 IAM Policy 作成

- 以下4つのIAM Policyを作成する。
- 今回は`s3`の`test-codedeploy-bucket`というbucketの`test/`下にrevision(ソースコード)を置くため、そこにアクセスできるようにする。権限は必要最小限にしたほうが良い。

```json:EC2用S3_Policy(※1)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::test-codedeploy-bucket/test",
                "arn:aws:s3:::test-codedeploy-bucket/test/*"
            ]
        }
    ]
}
```

```json:CodeDeploy用S3_Policy(※2)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::test-codedeploy-bucket/test",
                "arn:aws:s3:::test-codedeploy-bucket/test/*"
            ]
        }
    ]
}
```

```json:CircleCI用S３_Policy(※3)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::test-codedeploy-bucket/test",
                "arn:aws:s3:::test-codedeploy-bucket/test/*"
            ]
        }
    ]
}
```

```json:CircleCI用CodeDeploy_Policy(※4)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "codedeploy:RegisterApplicationRevision",
                "codedeploy:GetApplicationRevision"
            ],
            "Resource": [
                "arn:aws:codedeploy:ap-northeast-1:{aws account id}:application:test-app"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "codedeploy:CreateDeployment",
                "codedeploy:GetDeployment"
            ],
            "Resource": [
                "arn:aws:codedeploy:ap-northeast-1:{aws account id}:deploymentgroup:test-app/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "codedeploy:GetDeploymentConfig"
            ],
            "Resource": [
                "arn:aws:codedeploy:ap-northeast-1:{aws account id}:deploymentconfig:CodeDeployDefault.OneAtATime",
                "arn:aws:codedeploy:ap-northeast-1:{aws account id}:deploymentconfig:CodeDeployDefault.HalfAtATime",
                "arn:aws:codedeploy:ap-northeast-1:{aws account id}:deploymentconfig:CodeDeployDefault.AllAtOnce"
            ]
        }
    ]
}
```

## 1.2 IAM Role作成

以下2つのIAM Roleを作成する。これも権限は最小限にしたほうがよい。

- InstanceProfile(※8)
  - ポリシー
      - EC2用S3_Policy(※1)
  - 信頼関係

```json:InstanceProfile用信頼関係
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
``` 

- ServiceRole(※9)
  - ポリシー
      - CodeDeploy用S3_Policy(※2)
      - AWSCodeDeployRole
      - AmazonEC2FullAccess
  - 信頼関係

```json:ServiceRole用信頼関係
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "codedeploy.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

## 1.4 CircleCI用IAM User作成

- circleci_user
  - ポリシー
      - CircleCI用S３_Policy(※3)
      - CircleCI用CodeDeploy_Policy(※4)
  - アクセスキー(※5)を作成して覚えておく

# 2. AWS S3 bucket作成

- bucketの作成
  - 今回は `test-codedeploy-bucket` というbucket名にした。
  - region: ap-northeast-1(Tokyo)

# 3. EC2 instance作成

- 今回は t2-micro/AmazonLinux で作成
- IAM Roleに 1.2で作成したRole `InstanceProfile(※8)` を使用
- key pair作成(※6)
- ElasticIP付与
- Tagsでインスタンス名つける (今回は `test-server` )

# 4. CodeDeploy agentのインストール

- `鍵(※6)` を使ってSSHログインし、code deploy agentをインストールする。

```bash:test-server(EC2)
$ sudo yum update
$ cd ~
$ aws s3 cp s3://aws-codedeploy-ap-northeast-1/latest/install . --region ap-northeast-1
$ chmod +x ./install
$ sudo ./install auto
```

# 5. CodeDeploy Application作成

- Application Name: `test-app` ※7
- Deployment Group Name: `test-deploy-group` ※7 
- Add Instances: `test-server` 3.で設定したインスタンス名
- Deployment Config: `CodeDeployDefault.OneAtATime`
- Service Role: `ServiceRole(※9)`

# 6. CircleCIの設定

- Add ProjectsでGitHubのリポジトリを追加
- Settings >> {user name} >> codedeploy-test
  - Permissions >> AWS Permissions でアクセスキーとシークレットキー(※5)をセット(いらない？)
  - Continuous Deployment >> AWS CodeDeploy
      - Step 1: アクセスキーとシークレットキー(※5)をセット
      - Step 2: circle.ymlで記述するのでスキップ

# 7. CircleCIとCodeDeployの設定ファイルを作成する

- [GitHubに作成したサンプルリポジトリ](https://github.com/h-kanazawa/codedeploy-test)を記述します。

### ディレクトリ構成

```text:codedeploy-testリポジトリのディレクトリ構成
■ scripts
  □ setup.sh
  □ run.py
■ tests
  □ __init__.py
  □ test.py
□ README.md
□ appspec.yml
□ circle.yml
□ cron
```

### appspec.yml

- デプロイ対象とデプロイ先を記述する。
- インストール後(S3からソースコードをpullした後)に実行させることを `AfterInstall`に記述する。
  - 今回は`scripts/setup.sh`で`pip install` & `cron更新`を行わせる

```bash:appspec.yml
version: 0.0
os: linux
files:
  - source: / # デプロイ対象
    destination: /app # デプロイ先のディレクトリパス
hooks:
  AfterInstall:
    - location: scripts/setup.sh 
```

### circle.yml

- CI環境でテストする内容を`test`に記述する
- `CodeDeploy`によるデプロイ内容を`deployment`に記述する

```yaml:circle.yml
test:
  override:
    - python -m unittest -v tests.test
deployment:
  master:
    branch: master
    codedeploy:
      test-app: #※7
        deployment_group: test-deploy-group #※7
        application_root: /
        region: ap-northeast-1
        revision_location:
          revision_type: S3
          s3_location:
            bucket: test-code-deploy-bucket
            key_pattern: test/master/test-app-{BRANCH}-{SHORT_COMMIT}
  staging:
    branch: staging
    codedeploy:
      test-app:
        deployment_group: test-deploy-group
        application_root: /
        region: ap-northeast-1
        revision_location:
          revision_type: S3
          s3_location:
            bucket: test-codedeploy-bucket
            key_pattern: test/staging/test-app-{BRANCH}-{SHORT_COMMIT}
```

### インストール後に実行する内容

- 依存ライブラリのinstallとcronの登録・更新を行うscriptを作成する

```bash:setup.sh
#!/bin/bash
CURRENT_DIR=`dirname $0`
sudo pip install -r ${CURRENT_DIR}/../requirements.txt
sudo cp ${CURRENT_DIR}/../cron /etc/cron.d/test-cron
```

- 今回は `cron` が登録できたか確認するため、 `run.py` を実行させる

```:cron
* * * * * root /tmp/scripts/run.py >> /tmp/cron.log >2&1
```

# 実行結果

GitHub master/staging branchへのcommitによってCircleCIによるテストが行われ、
失敗すればデプロイされず、成功なら自動的にデプロイする仕組みを構築できました。


# 参考文献
- [AWS CodeDeployのドキュメント](http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/welcome.html)
- [circle ciのドキュメント](https://circleci.com/docs/continuous-deployment-with-aws-codedeploy/)
- 本記事がベースとしたQiitaの記事
  - [CircleCIとAWS（CodeDeploy/S3）で作るCI環境](http://qiita.com/hardreggaecafe/items/7ff3ada575a1100fa4a2)