CodeCommit + CodeDeploy + CodePipelineでEC2にデプロイ~CodeDeployの設定~



|    目次    |
|:------------------|
| 1. [CodeCommitの設定](https://qiita.com/k_senbei/items/dd1b912debfa38136b9d)  |
| 2. CodeDeployの設定(この記事)   |
| 3. [CodePipelineの設定・デプロイ実行](https://qiita.com/k_senbei/items/f748f5a30414696ac645)  |

AWSのCodeCommit、CodeDeploy、CodePipelineを組み合わせてEC2にデプロイするまでをまとめました。
この記事ではCodeDeployでデプロイアプリケーションを作成する手順を説明します。

## 前提

* デプロイ先のEC2インスタンスは作成済みとします。
* ELB(ロードバランサー)はApplication Load Balancerを使用する想定とします。
* Ruby on Railsアプリケーションをデプロイする想定とします。
* EC2 : Amazon Linux2

## CodeDeploy

EC2インスタンスやオンプレミスインスタンス、AWS Lambda、Amazon ECSに対するアプリケーションのデプロイを自動化するデプロイメントサービスです。
EC2に対するデプロイは無課金で実行できます。

##### 用語

* **AppSpec file** : デプロイの内容を定義したファイル。YAMLもしくはJSONで記述する。
* **リビジョン** : S3もしくはGithubにアップロードした、アプリケーションコードとAppSpec fileをバンドルしたアーカイブファイル。

## デプロイ要件

CodeDeployのデプロイタイプは「インプレースデプロイ」と「Blue/Greenデプロイ」が選択でき、今回は「インプレースデプロイ」を選択します(デプロイタイプについては後ほど説明します)。
デプロイ作業中にロードバランサーのトラフィック制御は行いません。

EC2へのインプレースデプロイは以下のフローで行われます。

1. アプリケーションにAppSpec fileを含めたアーカイブファイルをAmazon S3、もしくはGithubにアップロード。
2. アップロードしたリビジョンの情報を次のデプロイ対象としてCodeDeployに提供。
3. デプロイ先EC2に常駐しているCodeDeployAgentプロセスがCodeDeployをポーリングして、デプロイ対象リビジョンの置き場所・デプロイ実施時期などの情報を取得する。
4. 3で取得した情報に基づいて、EC2のCodeDoployAgentがS3やGithubからデプロイ対象リビジョンをpullして、AppSpec fileの手順に従ってデプロイを実行する。

## 準備

#### S３にリビジョンを置くバケットを作成

1. Amazon S3コンソール([https://console.aws.amazon.com/s3/](https://console.aws.amazon.com/s3/))を開きます。
1. [バケットを作成する]を選択します。
1. [バケット名]を入力して[バケットの作成]を選択します。

#### CodeDeployからEC2にアクセスためのサービスロールを作成

1. IAMコンソール([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/))を開きます。
1. ナビゲーションペインの[ロール]を選択し、[作成]を選択します。
1. [ロールの作成]ページで[AWSサービス]を選択し、[このロールを使用するサービスを選択]リストからCodeDeployを選択します。
1. [ユースケースの選択]でCodeDeployを選択し、[次へ]を選択します。
1. アクセス権限ポリシーのを確認して[次へ]を選択します。
1. [ロールの名前]にサービスロール名(例:AWSCodeDeployRole)を入力し、[作成]を選択します。

#### EC2からS3にアクセスするためのインスタンスプロファイルを作成
　
<ol><li>IAMコンソール([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/))を開きます。
</li><li>ナビゲーションペインの[ポリシー]を選択し、[作成]を選択します。
</li><li>[JSON]タブに以下を貼り付けて[確認]を選択します。

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}
```
</li><li>[ポリシー名]に適当な名前を入力して[作成]を選択します。
</li><li>ナビゲーションペインで[ロール]を選択し、[作成]を選択します。
</li><li>[AWSサービス]を選択し、[このロールを使用するサービスを選択]リストからEC2を選択します。
</li><li>[ユースケースを選択]でEC2を選択し[次へ]を選択します。
</li><li>先ほど作成したポリシーを選択し、[次へ]を選択します。
</li><li>[タグの追加]は何もせずに[次へ]を選択します。
</li><li>[ロールの名前]に適当なロール名を入力して[作成]を選択します。
</li><li>EC2コンソールから、使用するEC2インスタンスを選択し、`アクション > セキュリティ > IAMロールを変更`とメニューを選んで、先ほど作成したS3アクセスのロールを指定して保存する(EC2へのロールアタッチ)。
</ol>

#### EC2にCodeDeployAgentをインストール・起動

EC2にSSH接続して、下記コマンドを実行します。

```
$ sudo yum update

# CodeDeployAgentはRubyで動作するので、Rubyをインストール
$ sudo yum install ruby

$ sudo yum install wget
$ cd /home/ec2-user

# このURLは東京リージョンのCodeDeployリソースキットファイルが置いてある場所です
$ wget https://aws-codedeploy-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/install

$ chmod +x ./install
$ sudo ./install auto
```

下のコマンドを叩いてCodeDeployAgentが起動していればOKです。

```
$ sudo systemctl status codedeploy-agent
```

## CodeDeployのデプロイアプリケーション作成・設定

#### デプロイアプリケーション作成

1. CodeDeploy コンソール (https://console.aws.amazon.com/codedeploy) を開きます。
1. ナビゲーションペインから[アプリケーション]を選択し、[アプリケーションの作成]を選択します。
2. [アプリケーション名]を入力し、[コンピューティングプラットフォーム]で[EC2/オンプレミス]を選択します。
3. [アプリケーションの作成]を選択します。

#### デプロイグループの作成

1. 作成したデプロイアプリケーションのページの[デプロイグループ]タブを選択し、[デプロイグループの作成]を選択します。
2. 必要項目を入力します。
  * デプロイグループ名 : 任意の名前（例:SampleAppDeploymentGroup）
  * サービスロール : 先に作成したAWSCodeDeployRole
  * デプロイタイプ : インプレース
  * 環境設定 : Amazon EC2 インスタンス(タググループでEC2インスタンスに使用したタグのキーと値を指定)
  * デプロイ設定 : CodeDeployDefault.OneAtATime
  * ロードバランサー : [ロードバランシングを有効にする]チェックボックスを外す
  * 詳細-オプション : 任意で自動ロールバックの設定、アラームの設定が可能
1. [デプロイグループの作成]を選択します。

デプロイタイプとデプロイ設定の選択肢と内容は下の通りです。

**デプロイタイプ**

| インプレース | Blue/Green |
|:------------|:-----------|
|既存EC2インスタンスのアプリケーションを上書きする。   |新規のEC2インスタンスを作成し、そちらにアプリケーションをデプロイする。アプリケーションが稼働したらロードバランサーのルーティングを新環境向けに切り替える。   |

**デプロイ設定**

| OneAtATime | HalfAtATime | AllAtOnce |
|:--|:--|:--|
| 1度に可能な限り多くのインスタンスに対してデプロイを実施します。  | 1度に全体の半数のインスタンスに対してデプロイを実施します。   | 1度に1つのインスタンスに対してデプロイを実施します。  |


## AppSpec file作成

AppSpec fileはデプロイの内容を定義するファイルです。
アプリケーションの配置先、パーミッション、デプロイのライフサイクルイベントで実行する処理を定義できます。
EC2へのインプレースデプロイで使用可能なライフサイクルイベントは5つあります。

* **ApplicationStop**
  - アプリケーションの停止などで使用する。
* **BeforeInstall**
  - ファイルの暗号化、現在のバージョンのバックアップ作成などで使用する。
* **AfterInstall**
  - アプリケーションの設定、ファイルの許可の変更などに使用する。
* **ApplicationStart**
  - アプリケーションの起動などで使用する。
* **ValidateService**
  - デプロイが正常に完了したことの検証に使用する。


ApplicationStopイベントは注意が必要で、新しくデプロイするアプリケーションのAppSpec fileではなく、**最後に正常にデプロイされたアプリケーション**のAppSpec fileの内容で実施されます。従って初回デプロイ時には何も実行されません。

#### appspec.ymlを作成

appspec.ymlをアプリケーションソースコードのルートに作成し、デプロイの定義を書いてきます。
Railsアプリケーションのappspec.ymlの内容は[こちらの記事](https://blog.shikisoft.com/automate-ruby-on-rails-deployments-aws-codedeploy/)が参考になります。

```YAML:appcpec.yml
version: 0.0  # 0.0固定
os: linux     # デプロイ先サーバーのOS
files:        # アプリケーションの配置場所
  - source: /
    destination: /var/www/sample_app/current
permissions:  # 配置ディレクトリのパーミッション
  - object: /var/www/sample_app/current
    owner: {ユーザー名}
    group: {ユーザーグループ名}
    pattern: "**"
    mode: 775
    type:
      - file
      - directory
hooks:        # デプロイのライフサイクルイベント
  ApplicationStop:
    - location: deployment_scripts/stop_application.sh
      runas: {シェルスクリプトの実行ユーザー名}
  AfterInstall:
    - location: deployment_scripts/install_gems.sh
      runas: {シェルスクリプトの実行ユーザー名}
    - location: deployment_scripts/compile_assets.sh
      runas: {シェルスクリプトの実行ユーザー名}
    - location: deployment_scripts/run_db_migrations.sh
      runas: {シェルスクリプトの実行ユーザー名}
  ApplicationStart:
    - location: deployment_scripts/start_application.sh
      runas: {シェルスクリプトの実行ユーザー名}
```

#### デプロイタスクを作成

アプリケーションルートにdeployment_scriptsディレクトリを作成し、デプロイタスクのスクリプトを配置します。
デプロイタスクは任意のスクリプト言語で記述可能です。

```bash:deployment_scripts/stop_application.sh
#!/bin/bash

source /home/{ユーザー名}/.bash_profile

cd /var/www/sample_app/current

# Pumaを停止
RAILS_ENV=production bundle exec pumactl -F config/puma.rb stop

```

## まとめ

これでCodeDeployを使用したデプロイの準備ができました。
今のままでもAWS CLIを使ってリビジョンをS3にアップロード → デプロイ実行を行うことはできますが、
デプロイの度に毎回手動でコマンドを叩くのは面倒です。

次回で、CodePipelineを使用してCodeCommitのリポジトリが変更されたタイミングでデプロイを自動実行する仕組みを作っていきます。

## 参考記事

* [CodeDeploy公式ドキュメント](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/welcome.html)
* [AWS S3＋CodeDeployでデプロイしてみたよ](https://qiita.com/tailix/items/124bb7a17c312f0ac45b)
* [CodeDeployのApplicationStopイベントフックはどう実行される?](https://dev.classmethod.jp/cloud/aws/codedeploy-applicationstop-hook-not-executed-for0first-deployment/)
* [AWS CodeDeploy の AppSpec を読み解く](https://dev.classmethod.jp/cloud/aws/code-deploy-appspec/)