github+circleci+codedeployでs3リビジョンとec2デプロイを行う連携のメモ

# 何をするのか

githubのプッシュを発端にcircleciでテストとデプロイのキックからcodedeployでデプロイを行う連携のメモ
codedeployはec2をターゲットにデプロイを行い、デプロイ成果物はs3のバージョニング機能を使ってリビジョンを維持をします。

## IAMの設定

### IAMユーザーの作成

IAMはcircleciからs3とcodedeployへの権限が必要。とりあえず最低限の権限なら[この](https://dev.classmethod.jp/cloud/circleci-codedeploy/)のActionをAllowすればよいです。


ここでハマったのはポリシーのアタッチの際の文法ミス。例えばアプリケーション名がAppNameでデベロップメントグループ名がDevelopmentGroupNameのアプリのCreateDeploy権限は下記のような書き方になります。

```json

    {
      "Effect": "Allow",
      "Action": [
        "codedeploy:CreateDeployment"
      ],
      "Resource": [
        "arn:aws:codedeploy:ap-northeast-1:XXXXXXXXXXXX:deploymentgroup:AppName/DevelopmentGroupName"
      ]
    }

```

Resource属性の`arn:aws:codedeploy:ap-northeast-1:XXXXXXXXXXXX:deploymentgroup:AppName/DevelopmentGroupName`の書き順の最後がアプリケーション名/デベロップメントグループ名となることに注意しましょう。

きちんと[公式リファレンス](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/auth-and-access-control-permissions-reference.html)を確認しようと思いました。

### IAMロールの作成

codedeployとEC2の2ロール作成します。名前は任意のものでポリシーにそれぞれAWSCodeDeployRoleとAmazonS3ReadOnlyAccessをアタッチします。

## EC2の設定

注意して設定しなければならない項目はさっき作ったEC2ロールとタグです。ロールはcodedeploy agentの導入に必要です。タグはcodedeployからEC2を認識するために設定します。他は要件にあったものを適当に選択します。

作成したインスタンスが起動したらそれにcodedeploy agentを導入します。
EC2上でcodedeploy agentが動いているか確認するには`sudo service codedeploy-agent status`をcliで打ちましょう。

動作していなかった[これら](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install.html)の手順にしたがってインストールします。今回はAmazon LinuxをEC2のOSに設定したので[これ](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html)に従いました。

## s3の設定

特に気をつけることはありませんが、リビジョンをs3でもたせたい場合はバケットのバージョニング機能を有効化しましょう。私はs3にこの機能があることを知らず、circleciで`時刻.zip`というファイル名にするコマンドでセルフリビジョン管理してました。

## codedeployの設定

アプリケーションを作成します。アプリケーション名とコンピューティングプラットフォームは任意の物を設定して作成。デプロイのためにはデプロイグループを作成する必要があるので、そのままデプロイグループの作成に移行します。

例えばこんな感じでデプロイグループを作成します。詳細設定はデフォルトのままです。

| 項目 | 値 |
|:------------|:------------|
| デプロイグループ名　| my-app-deploy-group |
|サービスロール | ※2 |
| デプロイタイプ | インプレース |
| 環境設定 | Amazon EC2 インスタンス※3 |
| デプロイ設定 | CodeDeployDefault.AllAtOnce |
| ロードバランサー | 無効 |

※2：さっき作ったIAMロールを指定
※2：EC2設定時につけたタグで識別する

この時点でcodedeployは手動で動作させられるので、デプロイの作成を押下後にリビジョンをgithubのコードを指定することでデプロイが動作するはずです。

## githubとcircleciの連携

circleciにgithubアカウントでログイン後左メニューの`add project`から任意のレポジトリを選択する。すると、詳細な連携方法が表示されるのでそれに従いましょう。
自分の場合、コミットが300程度積んである既存のレポジトリに`.circleci/config.yml`を置いてpushしてもレポジトリが認識されないことがあった。この時はcircleciで読み込むブランチを変えたり読み込む環境変数を変えたりといったconfig.ymlの変更を何度か繰り返すと解決した。

## circleciの設定

レポジトリが認識されるとWORKFLOWSにレポジトリが表示されて設定を行えるようになります。circleciにレポジトリで使う環境変数を設定します。環境変数については公式リファレンスが[こちら](https://circleci.com/docs/ja/2.0/env-vars)。今回は非公開にしたい情報をブランチ毎に切り替えたかったので全てContextにしました。

左メニュー>SETTINGS>ContextsでCreate Contextを押下して任意の名前をつけます。
Contextには権限を設定できます。デフォルトでは誰でも使用できてしまうので限定しましょう。この権限のないユーザーがcircleci上でcontextを用いようとするとエラーとなります。

今回連携で必要な環境変数はこんな感じでした。

| 項目 | Key | Value |
|:------------|:------------|:------------|
| アプリケーション名　| APPLICATION_NAME | my-app |
| デプロイメントグループ | DEPLOYMENT_GROUP | my-app-grp |
| s3バケット名| BUCKET_NAME | my-app-bucket |
| s3バケット名（スキーマ付き）| FULL_BUCKET_NAME | s3://my-app-bucket |
| リージョン | REGION | ap-northeast-1 |
| アクセスキー | AWS_ACCESS_KEY_ID | ※1 |
| シークレットアクセスキー | AWS_SECRET_ACCESS_KEY | ※1 |

※1：AWSで作成したcircleci→codedeployにアクセスするIAMユーザーのもの

## config.ymlの設定

circleciでcodedeployをキックしてデプロイを行う記述がこんな感じです。テストコードは省略しています。config.ymlの書き方は[公式](https://circleci.com/docs/ja/2.0/configuration-reference/)を見るのが最良と思います。

```yaml

version: 2.1
jobs:
  deploy_something:
    docker:
      - image: circleci/python
        environment:
          TZ: Asia/Tokyo
    steps:
      - checkout
      - run:
          name: download pip
          command: curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
      - run:
          name: install pip
          command: sudo python get-pip.py
      - run:
          name: install aws-cli
          command: sudo pip install awscli
      - run:
          name: delete get-pip.py
          command: rm -f get-pip.py
      - run:
          name: set AWS region
          command: aws configure set region ${DEFAULT_REGION}
      - run:
          name: zipping
          command: zip -r ${APPLICATION_NAME}.zip .
      - run:
          name: s3 cp
          command: aws s3 cp ${APPLICATION_NAME}.zip ${FULL_BUCKET_NAME}
      - run:
          name: registar app
          #s3のリビジョン登録。バケット側でも有効化しないと意味ないです。
          command: aws deploy register-application-revision --application-name ${APPLICATION_NAME} --s3-location bucket=${BUCKET_NAME},bundleType="zip",key=${APPLICATION_NAME}.zip
      - run:
          name: deploy
          #デプロイ先にすでにファイルがある場合の動作を指定しないとエラー出ます。
          command: aws deploy create-deployment --application-name ${APPLICATION_NAME} --deployment-group-name ${DEPLOYMENT_GROUP} --file-exists-behavior "OVERWRITE" --s3-location bucket=${BUCKET_NAME},bundleType="zip",key=${APPLICATION_NAME}.zip
workflows:
  version: 2.1
  build_deploy:
    jobs:
      - deploy_something:
      # job名。releaseブランチの時のみこれが動作する。
          context: deploy_something_context
          filters:
            branches:
              only: release

```

aws-cliのコマンドリファレンスは[こちら](https://docs.aws.amazon.com/cli/latest/index.html)です。

circleciにはorbという機能があってversionを2.1にすることで使用できるのですが、s3以外のorbにメジャーバージョンがないので今回は使用していません。また、dockerイメージにaws-cliが含まれているものを最初から選べばconfig.ymlはもっと短くなるしEC2のロールも不要になるんですが非公式なイメージを使うことに不安を覚えたので今回は使っていません。

ここまでの設定をして、こんな感じでconfig.ymlを書いてpushすれば各ツールがをうまくやってくれるはずです。

## 参考資料
[\[公式チュートリアルではじめる\]CircleCI＋CodeDeployを使ったCD(継続的デプロイ)](https://dev.classmethod.jp/cloud/circleci-codedeploy/)

[CodeDeploy pushコマンドは裏で何をやっている?](https://dev.classmethod.jp/cloud/aws/codedeploy-push-behind-the-scenes/)

[EC2デプロイのためにCodeDeployを導入する](https://qiita.com/yukofeb/items/e077fc8755416c904032#appspec%E4%BD%9C%E6%88%90)

[CircleCIで特定ブランチで動くワークフローをさらに分岐させたい](https://qiita.com/yamashun/items/15965c9a789d7789f3f0)