[AWS CodeDeploy]自動デプロイを実装する

## はじめに
この記事はプログラミング初学者による備忘録用の記事であり、また、少しでも他の初学者のお役に立てればと思い書いています。

今回は、ローカル開発環境からGitHubへgit pushし、mainブランチにマージした際にAWSのCodeDeployが自動デプロイする環境を構築しましたので、備忘録としてまとめておきたいと思います。

間違いなどがございましたら、ご指摘のほどよろしくお願い致します。


## 自動デプロイ用のS3バケットを作成する
自動デプロイでは、CircleCIでビルドした結果のファイルをS3にアップロードします。
従って、自動デプロイ用のS3バケットを作成します。

### 1.一般的な設定
**・バケット名**:
一意な名前を設定してください。

**・AWSリージョン**:
東京リージョンを指定して下さい。

**・オブジェクト所有者**:
今回は、ACL無効を選択しています。

 
![スクリーンショット 2022-03-22 17.51.03（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/35adc652-2e79-ff66-9cc8-5c6a283fe7b7.png)


### 2.バケットのブロックパブリックアクセス設定とバケットのバージョニング 
**・バケットのブロックパブリックアクセス設定**:
パブリックアクセスをすべてブロックはチェックをしたままとします。

詳細設定は特に不要です。

**・バケットのバージョニング**:
CodeDeployで使用するS3バケットは、**バージョニングを有効化**しておく必要があります。

バージョニングを有効化にすることで、S3バケット内のオブジェクト(ファイル)を世代管理します。

同じオブジェクト名で上書きしても、古い世代のオブジェクトが別途保持されるようになります。

![スクリーンショット 2022-03-22 17.31.28（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/ac43b532-b617-9bd6-2811-3dbce176af1e.png)

### 3.その他の設定
**・デフォルトの暗号化**:
無効にするを選択してください。

**・詳細設定**:
無効にするを選択してください。

![スクリーンショット 2022-03-22 17.32.50（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/4d218c05-0537-c29b-7898-0114d3802bca.png)

以上でバケットの作成は終了です。

## CodeDeploy用のIAMロールの作成
IAMサービスのアクセス管理から、ロールを選択し、ロール作成画面に移動してください。
### 1.信頼されたエンティティを選択

**・信頼されたエンティティタイプ**:
今回は、CodeDeployのロールを作成するので``AWSのサービス``を選択してください。

**・ユースケース**:
検索欄からCodeDeployを探し、``CodeDeploy``を選択して次のステップへ進みます。

![スクリーンショット 2022-03-22 17.39.19（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/8a127d91-1649-7608-893c-45667588eac2.png)

許可ポリシー一覧に、``AWSCodeDeployRole``が追加されていることを確認してください。

![スクリーンショット 2022-03-22 17.40.33（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/33d9f153-f587-2d07-f967-fc7af4933330.png)

### 2.ロールの詳細を確認する
**・ロール名**:
特定のロール名を入力してください。

**・ポリシー名**:
``AWSCodeDeployRole``が追加されていることを確認してください。

**・タグ**:
今回はタグの追加は行いません。


![スクリーンショット 2022-03-22 17.42.39（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/980979b7-acce-e5e1-90cf-2697059e15f0.png)

### 3.追加されたロールを確認する
信頼されたエンティティ欄に、``AWSのサービス: codedeploy``が表示されていればロールの作成は成功です。

![スクリーンショット 2022-03-22 17.43.27（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/650194a3-dd87-f0a6-33f9-75e2a4b3d281.png)

## CodeDeployのアプリケーションとデプロイグループを作成
AWSのデプロイ自動化サービスであるCodeDeployの「アプリケーション」と「デプロイグループ」を作成します。

### 1.アプリケーションとデプロイグループについて
**・アプリケーション**:
複数のデプロイグループを束ねる存在として、CodeDeployではアプリケーションという概念が存在します。

**・デプロイグループ**:
デプロイグループとは、デプロイ方法に関する設定のことを指します。
今回は、1つのデプロイグループを作成します。

:::note
複数のデプロイグループを用意して、デプロイ方法に関する設定を複数個用意して使い分けることなども可能です。
:::

### 2.アプリケーションの作成
CodeDeployサービスを選択後、アプリケーション作成画面に移動します。

**・アプリケーション名**:
100文字以内で特定のアプリケーション名を入力してください。

**・コンピューティングプラットフォーム**:
今回は、Amazon EC2にアプリケーションをデプロイするので、コンピューティングプラットフォームは``EC2/オンプレミス``を選択してください。

![スクリーンショット 2022-03-22 22.14.33（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/3d4f9758-9942-20dd-2b1a-90d40decacf9.png)

アプリケーションを作成後、下のような画面が表示されアプリケーションの作成が成功しています。

![スクリーンショット 2022-03-22 22.17.11（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/20f31b66-1563-bf48-a33a-4cd15c984b35.png)

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

次に、デプロイグループの作成を実行します。

**・デプロイグループ名**:
特定のデプロイグループ名を入力してください。

**・サービスロール**:
自動デプロイを実装する際に作成したIAMロールを指定します。

**・デプロイタイプ**:
インプレースを選択してください。
インプレースでは、``起動中のEC2インスタンス``に対してデプロイを行います。


![スクリーンショット 2022-03-22 22.21.55（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/b60b5c76-5a37-3a42-cb20-9d822712d72a.png)

**・環境設定**:
環境設定では、このデプロイに追加する``Amazon EC2 Auto Scaling グループ``、``Amazon EC2 インスタンス``、および``オンプレミスインスタンス``の任意の組み合わせを選択します。

今回は、``Amazon EC2 インスタンス``を選択します。

タググループでは、<font color=red>**デプロイ対象のEC2インスタンスを、タグを使って指定することが可能です。**</font>

今回対象となるEC2インスタンスにはNameタグを付けており特定の値が存在します。
従って、``キー``に**Name**、``値 - オプショナル``に**デプロイ対象となるEC2インスタンス名**を入力しています。

![スクリーンショット 2022-03-22 22.30.20（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/5d4f67e6-d931-5917-b5da-150ae9511e5f.png)

**・AWS Systems Managerを使用したエージェント設定**:
AWS Systems Managerを使用して、CodeDeploy エージェントのインストールを設定し、更新をスケジュールできます。
更新の期間はデフォルトで14日間となっています。

:::note warn
注意点
CodeDeployで使用するには、Amazon EC2 およびオンプレミスインスタンスに``CodeDeployエージェント``がインストールされている必要があります。
:::

:::note
AWS Systems Managerの詳細は、[「AWS Systems Manager とは」](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/what-is-systems-manager.html?icmpid=docs_acd_console)をご確認ください。
:::

<br>

**・デプロイ設定**:
デフォルトの``CodeDeployDefault.AllAtOnce``を選択してください。

``CodeDeployDefault.AllAtOnce``の場合、以下のように作用します。

>インプレースデプロイの場合:<br>一度に可能な限り多くのインスタンスへアプリケーションリビジョンをデプロイするよう試みます。アプリケーションリビジョンが 1 つ以上のインスタンスにデプロイされる場合、デプロイ全体のステータスは Succeeded として表示されます。アプリケーションリビジョンがいずれのインスタンスにもデプロイされない場合、デプロイ全体のステータスは Failed として表示されます。
``引用:``[CodeDeploy でデプロイ設定を使用する](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/deployment-configurations.html)

<br>

**・Load balancer**:
ロードバランシングは``無効の状態``にしてください。

:::note
ロードバランシングとは、複数のEC2インスタンスがあり、それらをロードバランサー(負荷分散装置)で接続している場合に使用できるデプロイ方法です。
デプロイを開始するEC2インスタンスをロードバランサーから切り離し、リクエストを受け付けないようにします。
その間にデプロイを行い、デプロイの完了後、再度ロードバランサーに組み入れリクエストの受け付けを再開します。
これらを繰り返すことでサービスを停止させずにデプロイを実行することができます。
:::


![スクリーンショット 2022-03-22 22.43.45（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/b7b39cd8-8471-fb2c-a017-1f5c9baa3135.png)

:::note
アプリケーションとデプロイグループに関する詳細は、[「EC2/オンプレミスコンピューティングプラットフォームのデプロイを作成する」](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/deployments-create-console.html)をご確認ください。
:::

## EC2用のIAMロールの作成
自動デプロイでは、S3に保存されたビルド結果のファイルをEC2が取得します。
従って、**EC2がS3からS3オブジェクトを取得できる権限**を持っている必要があります。

下記では、デプロイ用のS3からファイルを取得可能な権限を持つIAMロールを作成し、EC2への割り当てを行なっています。

### 1.IAMロールの作成
**・信頼されたエンティティを選択**:
タイプの選択で``AWSのサービス``を指定します。

**・ユースケース**:
ユースケースでは``EC2``を選択してください。

![EC2用IAMロール作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/f3899364-2982-1a48-34b9-1c35d30ad06f.png)

### 2.ポリシーの追加
今回は何も選択せずに次のステップへ進んでください。

![スクリーンショット 2022-03-23 11.31.41（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/2eba488d-1c27-cb18-d059-c0bcb55f3801.png)

### 3.詳細の確認
詳細確認画面では、ロール名のみ入力してロールの作成を実行します。タグの追加等は行いません。

**・ロール名**:
特定の名前を入力してください。

![スクリーンショット 2022-03-23 11.33.01（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/0247bded-7e3d-60f7-81c8-d238220df3d3.png)

以上で、EC2用のIAMロールの作成は完了です。

## EC2用のIAMロールにインラインポリシーの追加
### 1.インラインポリシーの追加の選択
EC2用のIAMロールの詳細画面に移動し、``アクセス許可を追加``メニューから``インラインポリシーを作成``を選択してください。

![インラインポリシーの追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/024dc037-5d11-251b-b074-a66720caf52d.png)

### 2.JSONを利用してインラインポリシーを作成する
``~各項目の説明~``

**・Version**:
``2012-10-17``はポリシー言語の現行バージョンであり、常にVersion要素を含め、**2012-10-17を設定する必要があります**。
このようにしない場合、このバージョンで導入されたポリシー変数などの機能は使用できません。

**・Effect**:
Effect 要素は必須であり、ステートメントの結果を許可または明示的な拒否のどちらにするかを指定します。
Effectの有効値は、``Allow``と``Deny``です。

**・Action**:
Action要素では、許可または拒否される特定のアクションを記述します。

**・Resource**:
Resource要素は、ステートメントで取り扱う一連のオブジェクトをARNを使用して指定します。

:::note
リソース ARN の一部にワイルドカードを使用することができます。
ARNセグメント (コロンで区切られている部分) でワイルドカード文字 (* と ?) を使用でき、アスタリスク (*) は 0 個以上の文字の任意の組み合わせを表し、疑問符 (?) は任意の 1 文字を表します。

例)以下の例では、特定の Amazon S3 バケット内のすべての項目を示しています。
"Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
:::


![スクリーンショット 2022-03-23 11.37.23（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/d25eed42-1331-fbca-2d2b-3878ab5854c7.png)

下記のコードをご利用ください。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::CodeDeploy用のS3バケット名",
                "arn:aws:s3:::CodeDeploy用のS3バケット名/*"
            ]
        }
    ]
}
```

### 3.インラインポリシー名を入力し作成を実行する
ポリシーの確認画面で、S3サービスが許可されていることを確認した後に特定の名前を入力してポリシーの作成を実行します。

![スクリーンショット 2022-03-23 11.38.26（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/dcabae99-1fcc-cc95-9acb-dd49db150476.png)

IAMロール画面で、インラインポリシー(特定の名前)が表示されていればインラインポリシーの追加は完了です。

![スクリーンショット 2022-03-23 11.39.11（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/d7f35676-bce4-f604-89fc-12a4739202ff.png)



### 4.IAMロールをEC2へアタッチする

実行中のEC2インスタンスを選択し、``アクション``->``セキュリティ``->``IAMロールの変更``の順で選択してIAMロールの変更画面に移動してください。

![スクリーンショット 2022-03-23 11.40.51（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/e9b8d32e-d587-8f65-10f6-9aaadab5c061.png)

IAMロールの変更画面の、``IAMロールを選択``メニューバーをクリックすると先ほど作成したEC2用のIAMロールが表示されるので選択して保存を実行します。

![スクリーンショット 2022-03-23 11.41.10（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/4ce0ecfa-ce86-c851-65ee-eb68c108f86a.png)

以上で、EC2へのIAMロールの割り当ては完了です。


## CodeDeployエージェントのインストール
CodeDeployを使ってEC2にデプロイを行うためには、EC2に``CodeDeployエージェント``をインストールする必要があります。

### 1.EC2へsshログイン後、Rubyをインストールする
まずは下記コマンドを実行してEC2へsshログインしてください。

```
ssh -i ~/.ssh/xxx-xxx-xxx.pem ec2-user@パブリックIPアドレス
```
sshログイン後、下記コマンドを実行してRubyをインストールしてください。

```
$ sudo yum install ruby -y

// 実行結果
完了しました!と表示されれば成功です
```

### 2.インストーラーのダウンロード
``/home/ec2-user``ディレクトリにいる状態で、下記コマンドを実行してインストーラーをダウンロードしてください。

```
$ wget https://aws-codedeploy-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/install

//実行結果
2022-03-23 07:25:09 (30.7 MB/s) - `install' へ保存完了 [17482/17482]
上記のような結果が表示されていれば成功です。
```
### 3.CodeDeployエージェントのインストール
下記のコマンドを実行してください。ただし実行結果は何も表示されません。

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

上記コマンドを実行後、次に下記のコマンドを実行してください。

```
$ sudo ./install auto

// 実行結果
完了しました!
I, [2022-03-23T07:27:17.811937 #9225]  INFO -- : Update check complete.
I, [2022-03-23T07:27:17.812057 #9225]  INFO -- : Stopping updater.

上記のように表示されていればインストールは成功です。
```

### 4.CodeDeployエージェントの実行状態を確認
CodeDeployエージェントを使ってデプロイを行うためには、**CodeDeployエージェントを起動させておく必要があります**。

CodeDeployエージェントはインストール後に自動的に起動しますので、起動状態を下記のコマンドで確認してください。

```
$ sudo service codedeploy-agent status

// 実行結果
The AWS CodeDeploy agent is running as PID 9309
上記のように表示されていればCodeDeployは実行中の状態であり問題ありません
```

:::note
CodeDeployエージェントに関する詳細は、[CodeDeploy エージェントのインストール](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install.html)をご確認下さい。
:::


## CircleCI用のIAMユーザー作成
今回の自動デプロイ実装では、CircleCIで以下を実行する必要があります。

* デプロイ用のS3バケットにビルド結果をアップロードする
* CodeDeployにデプロイ開始を指示する

従って、これらを実行可能な権限を持つIAMユーザーを作成します。

### 1.IAMユーザー詳細の設定
**・ユーザー名**:
特定のユーザー名を入力してください。

**・AWSアクセスの権限を選択**:
今回は、アクセスキーIDとシークレットアクセスキーが必要なので``アクセスキー・プログラムによるアクセス``を選択します。

![circleci用のIAMユーザー作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/135176fd-72c3-e8be-188e-566efd14b4f3.png)

### 2.アクセス許可の設定
``既存のポリシーを直接アタッチ``を選択後、``AWSCodeDeployDeployerAccess``という管理ポリシーを選択してください。

![スクリーンショット 2022-03-23 16.50.24（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/f7a285e4-1819-4bb5-95f8-df0cce60138f.png)

### 3.ユーザーの詳細を確認
最後に確認画面でユーザー詳細を確認して間違いがなければユーザーを作成してください。

※タグの追加は行っていません。
![スクリーンショット 2022-03-23 16.51.26（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/52f8772b-a785-98a6-60e0-7a363e8e4afa.png)

作成後、下のような画面でアクセスキーIDやシークレットアクセスキーが表示されていればIAMユーザーの作成は成功です。

:::note warn
重要
.csvのダウンロードを実行してファイルを保存してください。</font>
:::

![スクリーンショット 2022-03-23 17.03.49（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/8742bef3-92ff-e4f3-7252-5cf94d6efefa.png)



## CircleCIへ環境変数を登録する
先ほど作成したIAMユーザーの``アクセスキーID``と``シークレットアクセスキー``を発行し、``S3バケット名``も含めてCircleCIの環境変数に登録します。
環境変数に登録することで、CircleCIではそれら環境変数を使ってAWS CLI(ターミナルのコマンドで操作)にてCircleCIで実行させたい2つの処理を実行することが可能となります。

### 1.Project Settingsの選択
CircleCIのPipelines画面の右上にあるProject Settingsを選択してください。

![環境変数の登録.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/09be493a-e882-e46f-1f6a-f65ea8f0c0e1.png)

### 2.Environment Variablesの選択

``Project Settings``画面で``Environment Variables(環境変数)``を選択してください。

![スクリーンショット 2022-03-23 17.00.39（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/71314fb3-ee94-c1b0-2a7d-c4aa27f5f5a6.png)

### 3. 環境変数の登録

``Add Environment Variables``を選択して環境変数を登録します。

選択後、下のような画面が表示されるので、``アクセスキーID``、``シークレットアクセスキー``、``デプロイ用のS3バケット名``を登録します。

|Name|Value|
|:--|:--|
|AWS_ACCESS_KEY_ID|CircleCi用IAMユーザーのアクセスキーID|
|AWS_SECRET_ACCESS_KEY|CircleCi用IAMユーザーのシークレットアクセスキー|
|AWS_S3_BUCKET_NAME|デプロイ用のS3バケット名|

![スクリーンショット 2022-03-23 17.42.56（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/0d7e880c-3e7c-15db-9330-f05b14b43f72.png)

登録後、下のような画面で登録した環境変数が表示されていれば大丈夫です。

![スクリーンショット 2022-03-23 17.05.36（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/6d172f24-22d9-f167-20a3-ba5168c602ce.png)


## CircleCI用のIAMユーザーにインラインポリシーを追加
CircleCI用のIAMユーザーに対して、デプロイ用S3への書き込みを可能とするインラインポリシーを追加します。

### 1.IAMユーザーの詳細画面からインラインポリシーを追加する
該当するIAMユーザーの詳細画面で、``インラインポリシーの追加``を選択してください。


![インラインポリシーの追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/14128127-ed98-7340-72ef-ffb6e57c5c97.png)

### 2.JSONを利用してポリシーを作成する
``JSON``タブを選択した上で、下記のように入力し、ポリシーの確認を押してください。

:::note
ポリシー要素の詳細は、[IAM JSON ポリシー要素のリファレンス](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_elements.html)をご確認ください。
:::

![スクリーンショット 2022-03-23 17.10.19（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/bf290ce7-d1d8-ab29-fbc9-7235b62936a0.png)

下記のコードをご利用ください。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::CodeDeploy用のS3バケット名/*"
        }
    ]
}
```

### 3.作成したインラインポリシーの確認
確認画面で、許可されているサービス欄に``S3``があることを確認してください。

確認後、特定の名前を入力してポリシーの作成を実行します。

![スクリーンショット 2022-03-23 17.10.53（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/1d2f49a5-ee58-9827-d25d-475a54c06411.png)

実行後、IAMユーザー詳細画面でインラインポリシーが追加されていれば完了です。

![スクリーンショット 2022-03-23 17.11.46（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/958fa016-3a66-7c52-2865-a8aeff5a4129.png)

## AppSpec Fileとシェルスクリプトの作成
次に、``CodeDeployのデプロイ時の処理内容を定義するAppSpec File``と、AppSpec Fileから使用される``シェルスクリプト``を作成します。

ローカル環境のディレクトリ配下に``ppspec.yml``や``関連するシェルスクリプト``を作成していきます。

### 1.appspec.ymlの作成

ローカル環境に存在するデプロイ対象のディレクトリのトップに``appspec.yml``というファイル名を作成します。
作成後、下記のようにコードを追記してください。

```デプロイ対象のディレクトリ/appspec.yml
version: 0.0
os: linux
files:
  - source: /
    destination: /home/実行ユーザー名/特定のディレクトリ名

permissions:
  - object: /home/実行ユーザー名/特定のディレクトリ名
    owner: 実行ユーザー名
    group: 実行ユーザー名

hooks:
  AfterInstall:
    - location: ./scripts/after_install.sh
      timeout: 60
      runas: 実行ユーザー名
```

``~項目の説明~``

**・version**:
AppSpec Fileのバージョンを指定します。今回は``0.0``固定です。
</br>
**・os**:
デプロイ先のEC2インスタンスのOSを、``linux``か``windows``を指定します。
</br>
**・files**:
filesでは、``デプロイ元``と``デプロイ先``のパスを指定します。
1. source
デプロイ元をsourceで指定します。
sourceはファイルを参照し、指定されたファイルのみがインスタンスにコピーされます。
``/``を指定することで、**全てのファイルがデプロイ対象**となります。

2. destination
デプロイ先をdestinationで指定します。
destinationは、ファイルをコピーするインスタンス上の場所を識別します。
今回は、**EC2のデプロイ対象となるディレクトリ**を指定します。
</br>

**・permissions**:
permissionsでは、デプロイ先にコピーしたファイルの権限(パーミッション)をどのように変更するかを指定します。
1. object
objectには、**権限を変更する対象のディレクトリまたはファイル**を指定します。(必須)
2. owner
ownerには所有者を指定します。
3. group
groupにはグループを指定します。

<br>

**・hooks**:
hooksは、**CodeDeployによるデプロイ処理の過程で、特定のタイミングで何かしらの処理を実行したい場合に使用します**。

``今回のデプロイ条件``

* インプレースデプロイ(起動中のEC2インスタンスに対してデプロイを行う)
* ロードバランサーを使用しない


このような条件の場合、デプロイ処理は下記のような順序で進んでいきます。

1. ApplicationStop
2. DownloadBundle(デプロイ対象のファイルをEC2内の一時的な保存場所に置く。hooks利用不可。)
3. BeforeInstall
4. Install(EC2内の一時的な保存場所からファイルをデプロイ先のパスに配置する。hooks利用不可。)
5. AfterInstall
6. ApplicationStart
7. ValidateService

今回のhooksでは、デプロイ先のパスに最新のファイルが配置される``4.Install``の次の``5.AfterInstall``で、シェルスクリプトを実行する指示をしています。

:::note
hooksの詳細は、[AppSpec の「hooks」セクション](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html)をご確認ください。
:::

1. location
locationには、**実行したいシェルスクリプトのパスを指定**します。

2. timeout
timeoutに秒数を指定すると、実行するスクリプトの処理秒数が指定秒数を超えた場合にCodeDeployではデプロイ失敗と見なされるようになります。

3. runas
runasでは、**スクリプトを実行するユーザー名**を指定します。

:::note
AppSepc Fileの詳細は、[CodeDeploy AppSpec ファイルのリファレンス](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/reference-appspec-file.html)をご確認ください。
:::

## シェルスクリプトの作成
AppSpec Fileの``AfterInstallイベント``で使用されていたシェルスクリプトを作成します。

デプロイ対象となるディレクトリ配下に``scripts``ディレクトリを作成し、``after_install.sh``を作成してください。
作成後、下記のようにコードを追記してください。

```/特定のディレクトリ名/scripts/after_install.sh
#!/bin/bash

set -eux

cd ~/特定のディレクトリ名
php artisan migrate --force
php artisan config:cache
```

このシェルスクリプトは、**実行ユーザーとして起動されるので/home/実行ユーザー名/特定のディレクトリに移動して、php artisanから始まる2つのコマンドを実行するように指定します**。

:::note warn
注意点
ディレクトリの指定を間違えると、CodeDeploy側で``No such file or directory``といったエラーが発生するので要注意です。
:::

### シェルスクリプトの権限の変更


```
$ ls -l scripts
// 実行結果
-rw-r--r-- 1 root root 89  3月 25 12:11 after_install.sh
```
上記のように、``-rw-r--r--``となっていたら、実行権限である``x``が無いのでスクリプトとして実行できません。

実行権限を追加するため、下記コマンドを実行してください。

```
$ chmod +x scripts/after_install.sh // 権限を追加する

$ ls -l scripts

-rwxr-xr-x 1 root root 89  3月 25 12:11 after_install.sh
```

権限を追加後、``-rwxr-xr-x ``のようにxが追加されていれば問題ありません。


## CircleCIのdeployジョブの編集
ローカル開発環境の``/.circleci/config.yml``に、下記のような``deployジョブ``を追加します。


```/.circleci/config.php
# 略
  deploy:
    docker:
      - image: circleci/php:7.4-node-browsers

    environment:
      AWS_DEFAULT_REGION: ap-northeast-1

    steps:
      - checkout
      - run: sudo composer self-update --1
      - restore_cache:
          key: composer-no-dev-v1-{{ checksum "composer.lock" }}
      - run:
          working_directory: 特定のディレクトリ名
          command: composer install -n --no-dev --prefer-dist
      - save_cache:
          key: composer-no-dev-v1-{{ checksum "composer.lock" }}
          paths:
            - vendor
      - restore_cache:
          key: npm-v1-{{ checksum "package-lock.json" }}
      - run:
          working_directory: 特定のディレクトリ名
          name: npm ci
          command: |
            if [ ! -d node_modules ]; then
              npm ci
            fi
      - save_cache:
          key: npm-v1-{{ checksum "package-lock.json" }}
          paths:
            - node_modules
      - run:
        working_directory: 特定のディレクトリ名
        command: npm run prod
      - run:
          working_directory: 特定のディレクトリ名
          name: archive artifacts
          command: zip -ryq ファイル名.zip .
      - aws-cli/install
      - run:
          working_directory: 特定のディレクトリ名
          name: upload artifacts to s3
          command: aws s3 cp ファイル名(上記コマンドで作成したファイルを指定).zip s3://${AWS_S3_BUCKET_NAME}
      - run:
          working_directory: 特定のディレクトリ名
          name: deploy to prod
          command: |
            aws deploy create-deployment \
            --application-name CodeDeployのアプリケーション名 \
            --deployment-group-name CodeDeployのデプロイグループ名 \
            --s3-location bucket=${AWS_S3_BUCKET_NAME},key=ファイル名.zip,bundleType=zip

orbs:
  aws-cli: circleci/aws-cli@1.0.0

workflows:
  version: 2
  build_deploy:
    jobs:
      - build
      - deploy:
          requires:
            - build
          filters:
            branches:
              only:
                - main
```

### ・各項目の補足

#### 1.PHP関連パッケージのインストール

```.php
- restore_cache:
          key: composer-no-dev-v1-{{ checksum "composer.lock" }}
      - run:
          working_directory: 特定のディレクトリ名
          command: composer install -n --no-dev --prefer-dist
      - save_cache:
          key: composer-no-dev-v1-{{ checksum "composer.lock" }}
          paths:
            - vendor
```

上記の箇所では、PHP関連パッケージのインストールを実行しています。

:::note warn
注意点
* 開発環境用のパッケージは不要なため、--no-devオプションを付けています。
* buildジョブでのキーと異なるキー名を付けてください。
:::
<br>

#### 2.JavaScript関連パッケージのインストール

```.php
 - restore_cache:
          key: npm-v1-{{ checksum "package-lock.json" }}
      - run:
          working_directory: 特定のディレクトリ名
          name: npm ci
          command: |
            if [ ! -d node_modules ]; then
              npm ci
            fi
      - save_cache:
          key: npm-v1-{{ checksum "package-lock.json" }}
          paths:
            - node_modules
```

上記の箇所では、JavaScript関連パッケージのインストールを実行しています。

基本的には、bildジョブと同じステップ内容です。
<br>

#### 3.JavaScriptのトランスパイル

```.php
      - run: npm run prod
```
上記の箇所では、JavaScriptのトランスパイルを実行しています。

:::note warn
本番環境向けのため``npm run prod``を実行するようにしています。
:::
<br>

#### 4.ビルド後のファイルをzipファイル化する

```.php
- run:
          working_directory: 特定のディレクトリ名
          name: archive artifacts
          command: zip -ryq ファイル名.zip .
```
上記の箇所では、ビルド後のファイルをzipファイル化しています。

:::note warn
CodeDeployでデプロイするファイルは、zip, tar, tar.gzのいずれかの形式のファイルである必要があります
:::

3つのオプションは下記のような内容となっています。

1. ``r``オプション ・・・下の階層のディレクトリも含めてzipファイル化します。
2. ``y``オプション ・・・シンボリックリンクをリンク先の実体のファイルとしてzipファイル化するのではなく、シンボリックリンクのままzipファイル化します。
3. ``q``オプション ・・・動作中のメッセージを表示させないようにします。
<br>

#### 5.S3へファイルをアップロード

```.yml
      - run:
          name: upload artifacts to s3
          command: aws s3 cp laravel-ci.zip s3://${AWS_S3_BUCKET_NAME}
```

上記の箇所では、AWS CLIを使ってS3にファイルをアップロードしています。

:::note
コマンドの詳細は、[AWS CLI での高レベル (S3) コマンドの使用](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-services-s3-commands.html)をご確認ください。
:::

<br>

#### 6.CodeDeployのデプロイメントの作成

```.php
- run:
          working_directory: 特定のディレクトリ名
          name: deploy to prod
          command: |
            aws deploy create-deployment \
            --application-name CodeDeployのアプリケーション名 \
            --deployment-group-name CodeDeployのデプロイグループ名 \
            --s3-location bucket=${AWS_S3_BUCKET_NAME},key=ファイル名.zip,bundleType=zip
```
上記の箇所では、AWS CLIを使って``CodeDeployでのデプロイ処理を開始``させています。

``コマンドの説明``

**・ aws deploy create-deployment**
デプロイメントの作成を行なっています。

**・ --application-name**
CodeDeployのアプリケーション名を指定します。

**・ --deployment-group-name**
CodeDeployのデプロイグループ名を指定します。

**・ --s3-location bucket=${AWS_S3_BUCKET_NAME},key=ファイル名.zip,bundleType=zip**
``--s3-location``という必須オプションにbucket=${AWS_S3_BUCKET_NAME},key=laravel-ci.zip,bundleType=zipという引数を渡しています。

``bukcet``には、デプロイ元のファイルが保存されたS3バケット名(CircleCIに登録済の環境変数を使用する)を指定します。

``key``には、デプロイ元のオブジェクト名(先ほど作成したzipファイル名)を指定します。

``bundleType``には、デプロイ元のオブジェクトがどのような形式のファイルかを指定します。

<br>

#### 7.AWS CLIインストール(CircleCI Orbの使用)

```.yml
- aws-cli/install
orbs:
  aws-cli: circleci/aws-cli@1.0.0
```

上記のステップでは、AWS CLIという``AWSをコマンドで操作可能なツール``のインストールを行なっています。


orbsの箇所に記述されている``circleci/aws-cli@1.0.0``は、Orbと呼ばれるCircleCI専用のパッケージです。
circleci/aws-cli@1.0.0というCircleCI公式のOrbを、aws-cliという名前で使用することを明記しています。

:::note warn
AWS CLIを利用する際の注意点
AWS CLIを利用する場合、下記の3つの環境変数を設定しておく必要があります。
* AWS_ACCESS_KEY_ID
* AWS_SECRET_ACCESS_KEY
* AWS_DEFAULT_REGION

``AWS_ACCESS_KEY_ID``と``AWS_SECRET_ACCESS_KEY``は、CircleCIのプロジェクト環境変数に登録します。
``AWS_DEFAULT_REGION``はconfig.ymlの``environment:``で設定します。
:::

:::note
``orbs``に関する詳細は、[「circleci/aws-cli@1.0.0」](https://circleci.com/developer/orbs/orb/circleci/aws-cli?version=1.0.0)をご確認ください。
:::

#### 8.各ジョブの開始条件を定義

```.php
workflows:
  version: 2
  build_deploy:
    jobs:
      - build
      - deploy:
          requires:
            - build
          filters:
            branches:
              only:
                - main
```

``~各項目の説明~``

**・workflows**
各ジョブの開始条件を定義しています。

**・version**
バージョン指定では、必ず``2``を指定します。

**・build_deploy**
ワークフローの名前を指しています。(自由に定義可能)
今回は、buildジョブとdeployジョブで構成されるワークフローなのでbuild_deployという名前にしています。

**・requires**
特定のジョブを開始する際に、正常に終了している必要のあるジョブを指定します。
今回は、``deploy``ジョブを開始する前に``build``ジョブを終了している必要があるため、``build``ジョブを指定しています。
これにより、**buildジョブが正常終了してから、deployジョブが開始するようになります**。

※buildジョブ内でテストを行なってからdeployジョブを開始するように設定しています。

**・filters**
``fileters``、``branches``、``only``と指定することで、**特定のブランチへのプッシュの時**のみ、ジョブが動作するように設定することができます。
今回は、``main``ブランチを指定することで、**mainブランチにpush時(mainブランチにdevelopブランチをマージする)のみdeployジョブが開始されるようになっています**。

## deployジョブの実行結果を確認
上記の流れで設定することで、自動デプロイの実装が可能となります。

実装後、CircleCIのdeployジョブを実行し、CodeDeployによるデプロイ結果を確認してみます。

### 1.deployジョブの実行
1. ローカル開発環境のブランチ上で、コードを修正し、GitHubにpushしてください。
2. GitHub上でプルリクエストを立て、developブランチにマージし、ローカル開発環境で``origin develop``をpullしてください。
3. GitHub上でmainブランチにdevelopブランチをマージし、ローカル開発環境で``origin main``をpullしてください。

### 2.CircleCIのdeployジョブの確認
プルリクエストをマージしたら、CircleCIの画面でdeployジョブの実行結果を確認してください。
下記のように、deployジョブの実行が終了していれば成功です。

![スクリーンショット 2022-03-27 12.10.18（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/8c9c8d90-b449-b73a-6336-5e50ff4928db.png)

### 3.CodeDeployの結果を確認
CodeDeployのデプロイメント画面で、実行中もしくは実行が終了したデプロイメント(デプロイ処理)を確認します。
下記のように、ステータスが成功になっていれば自動デプロイが完了しています。

デプロイメントの詳細の確認する際は、デプロイIDをクリックすることで詳細画面に移動できます。

![スクリーンショット 2022-03-27 12.13.49（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/2cad3a03-9e8a-a528-b340-da29c02539ea.png)

## まとめ
長くなりましたが、以上で自動デプロイの実装は完了です。

今回、自動デプロイを実装することで、CircleCIでのブランチの扱い方や特定のジョブの設定、CircleCIとCodeDeployの関係性を深掘りすることができたと思います。

AppSepc Fileに関しては、まだまだ知識不足な点が多いため今後も継続して学習を進めていきたいと思います。

最後になりますが、もしエラーが発生した場合はコメントにてご報告して下さると非常に助かります。
私自身も、自動デプロイを実装するにあたり、数多くのエラーに悩まされたので少しぐらいは解決策を提示できると思います。
(後日、時間があれば実装の際に発生したエラーとその解決策も記載しておきます)

## 参考文献
[CodeDeploy を使用してデプロイを作成する](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/deployments-create.html)
[CodeDeploy でデプロイ設定を使用する](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/deployment-configurations.html)
[AWS Systems Managerを使用して CodeDeploy エージェントをインストールする](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install-ssm.html?icmpid=docs_acd_console)
[AWS Systems Manager とは](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/what-is-systems-manager.html?icmpid=docs_acd_console)
[IAM JSON ポリシー要素のリファレンス](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_elements.html)
[Amazon Linux または RHEL 用の CodeDeploy エージェントをインストールする](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html)
[CodeDeploy AppSpec ファイルのリファレンス](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/reference-appspec-file.html)
[AWS CLI での高レベル (S3) コマンドの使用](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-services-s3-commands.html)
[]()