GitHub ActionsとAWSのCodeDeployを連携させ、GitHubからAWS EC2への自動デプロイを実現する

# 経緯  
  AWSの勉強がてらにローカルマシン上で作成したNode.jsアプリをAWSに移植した。移植後、修正プログラムのデプロイを自動で行いたいと考え、AWSのCodeDeployの設定にチャレンジしてみた。AWS初心者かつ色々勘違いしたまま進めたためだいぶ遠回りしたが、最終的にうまくいったので手順を記録しておく。
# 利用サービス  
  * GitHub  
  * GitHub Actions
  * AWS EC2 ・・・「Ubuntu 18.04.4 LTS」でインスタンスを作成  
  * AWS CodeDeploy    

# 概要  
  * AWS側の設定手順は基本的に公式の「[AWS CodeDeployユーザーガイド](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/welcome.html)」に沿って実施したが、全体概要がつかみづらく、必要／不要な作業の区別がつけづらかった。最終的に必要だった作業項目については[手順](#手順)に記載する。
  * GitHubとの連携設定はユーザーガイドの「[CodeDeploy と GitHub の統合](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/integrations-partners-github.html#github-deployment-steps)」に記載されているが、この設定を行っても**GitHubのmasterへのpush時に都度CodeDeployを手動で実行しないとpushした内容がAWS EC2にデプロイされない**。pushしたタイミングでAWS EC2に自動デプロイするためには[AWS CodePipeline](https://aws.amazon.com/jp/codepipeline/)を利用するか、GitHub ActionsでCodeDeployのデプロイを実行するよう設定する必要がある。AWS CodePipelineは無料利用枠の条件があるため、今回はActionsを利用した。Actionsの設定は「 [GitHub ActionsでAWS Code Deployを回す](https://nonsensej.xyz/articles/2020/03/15/GitHub-Actions-deploy)」の内容をそのまま真似させてもらった。  
 
# 前提  
 * GitHubにデプロイ対象となるレポジトリを作成済み。  
 * AWS EC2インスタンス作成済み（デプロイ先の環境）。対象は1インスタンスのみ。
 * 本手順では作業端末からAWSコマンドを実行しない（AWSの設定はすべてコンソールで実施する）ため、「AWS CLI」のインストールは不要  
 * デプロイタイプは「インプレース」を使用。ロードバランサーは使用しない。  

# 手順  
## 1. IAM ユーザーの設定  
### 1-1. IAM ユーザー用のポリシーの作成  
 * IAMユーザーがCodeDeploy及び関連サービスにアクセスするためのポリシーを作成する。
 * ポリシーは「[IAM ポリシーの作成 (コンソール)](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_create-console.html)」の手順で作成する。ポリシーの定義は「AWS CodeDeployユーザーガイド＞[ステップ 1:IAM ユーザーをプロビジョニングする](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/getting-started-provision-user.html)」のJson定義をそのまま利用する。ポリシー名は「CodeDeployAccess」など、任意の名称を設定する。  

### 1-2. IAM ユーザーの作成＆ポリシーのアタッチ  
  * CodeDeoloy用のIAM ユーザーを作成する。
  * 後でIAM ユーザのアクセスキーIDとシークレットアクセスキーが必要になるので、ユーザ作成時の「AWS アクセスの種類を選択」では「プログラムによるアクセス」を選択する。
  * 「アクセス許可の設定」で「既存のポリシーを直接アタッチ」を選択し、手順1で作成したポリシーをアタッチする。
  * ユーザー作成時にアクセスキーIDとシークレットアクセスキーが表示されるので、メモしておく（シークレットアクセスキーは後から確認不可）。  
  * （既存ユーザーを利用する場合）IAMのコンソールで対象ユーザーを検索し、アクセス権限タブの「アクセス権限の追加」からポリシーをアタッチすればよい。シークレットアクセスキーが不明な場合は「認証情報」タブから「アクセスキーの作成」を行い、新たなキー情報を取得する。  

## 2. EC2の設定  

### 2-1. インスタンス用ポリシー及びロールの作成    
  * 「AWS CodeDeployユーザーガイド＞ステップ 4: IAM インスタンスプロファイルを作成する＞[Amazon EC2 インスタンス用の IAM インスタンスプロファイルの作成 (コンソール)](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/getting-started-create-iam-instance-profile.html)」の手順でインスタンスにアタッチするポリシー及びロールを作成する。

### 2-2. CodeDeploy エージェントのインストール  
  * 「AWS CodeDeployユーザーガイド＞[Ubuntu Server 用の CodeDeploy エージェントのインストールまたは再インストール](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install-ubuntu.html)」の手順でインスタンスにCodeDeploy エージェントをインストールする。  
  * インストール後、「AWS CodeDeployユーザーガイド＞[CodeDeploy エージェントの実行の確認](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-verify.html)」の手順でCodeDeployエージェントが正常に動作していることを確認する。

### 2-3. インスタンスの設定  
  * EC2のコンソールで対象のインスタンスを選択し、アクション＞インスタンスの設定＞IAM ロールの割り当て/置換を開く。IAM ロールの検索欄で手順2-1で作成したインスタンス用ロールを選択し、適用する。  
  * ロール適用後、インスタンスの説明タブで今設定したIAM ロールをクリックする。ロールの概要ページで信頼関係タブを開き、[信頼されたエンティティ] に [ID プロバイダー ec2.amazonaws.com] と表示されていることを確認する。  
  * インスタンスのタグタブを開き、`キー：Name`、`値：CodeDeployDemo （任意の値で良い）`を追加する。  
  
## 3. CodeDeployの設定  
### 3-1. CodeDeoloy用サービスロールの作成  
  * 「AWS CodeDeployユーザーガイド＞ステップ 3: サービスロールを作成する＞[サービスロールの作成 (コンソール)](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/getting-started-create-service-role.html#getting-started-create-service-role-console)」の手順でサービスロールを作成する。 

### 3-2. アプリケーション及びデプロイグループの作成  
  * 「AWS CodeDeployユーザーガイド＞[インプレースデプロイ (コンソール) 用のアプリケーションを作成](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/applications-create-in-place.html)」の手順に従い、アプリケーションの作成を行う。  
  * 引き続きデプロイグループの作成が必要となるが、その際設定する「サービスロール」には手順3-1で作成したCodeDeoloy用サービスロールを設定する。  
  * 「環境設定」では「Amazon EC2 インスタンス」を選択する。タググループの情報入力を求められるので、手順2-3で設定したタグのキー及び値を入力する。  
  * 「デプロイ設定」では「CodeDeployDefault.AllAtOnce」を選択する。  
  * 「ロードバランサー」では「ロードバランシングを有効にする」のチェックを**外して**おく。
  * 上記設定が完了したら「デプロイグループの作成」をクリックする。  

## 4. レポジトリの設定  

### 4-1. AppSpec Fileの追加  
  * 「AWS CodeDeployユーザーガイド＞[EC2/オンプレミス デプロイ用の AppSpec ファイルを追加する](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/application-revisions-appspec-file.html#add-appspec-file-server)」の手順に従い`appspec.yml`を作成する。`appspec.yml`の中身は一旦以下の内容で良い。ユーザーガイドに`appspec.yml`のテンプレートや設定例があるが、それらをそのまま貼るとエラーになるので注意。　　

  ```
  # This is an appspec.
  version: 0.0
  os: linux
  files:
    - source: /
      destination: /home/ubuntu/src/github.com/hogehoge/ ※インスタンスのどのディレクトリにデプロイするか指定する
  ```  
  * デプロイ対象のレポジトリのルートディレクトリに`appspec.yml`をpushする。  

## 5. デプロイの作成と実行結果の確認  

### 5-1. デプロイの作成  
  * 「AWS CodeDeployユーザーガイド＞[EC2/オンプレミス コンピューティングプラットフォームを選択します。 のデプロイの作成 (コンソール)](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/deployments-create-console.html)」の手順に従いデプロイを作成する。  
  * 「リビジョンタイプ」では「アプリケーションは GitHub に格納されています」を選択する。  
  * 「GitHubトークン名」にはGitHubアカウント名を入力し、「GitHubに接続」ボタンをクリックする。GitHub側で承認画面が表示されるので、承認する。　　
  * 「リポジトリ名」には`GitHubアカウント名／リポジトリ名`を設定する。
  * 「コミットID」にはデプロイしたい時点のコミットIDを入力する。本手順では4-1で`appspec.yml`をmasterブランチに登録した際のコミットIDを設定した。
  * 「追加のデプロイ動作設定」では「コンテンツオプション」の「コンテンツの上書き」をチェックする。上書きを許可しないとデプロイ対象のファイル名が被っただけでデプロイに失敗してしまう。　　
  * 上記設定が完了したら「デプロイの作成」をクリックする。  

### 5-2 デプロイ結果の確認  
  * CodeDeploy コンソールのナビゲーションペインで「デプロイ」 を展開し、「デプロイメント」を選択する。
  * 「デプロイ履歴」で実行されたデプロイのIDをクリックし、デプロイの詳細画面を開く。ステータスが成功となっていない場合は、詳細画面を下までスクロールし、「デプロイのライフサイクルイベント」の「View events」のリンクからデプロイのイベント一覧を開く。失敗したイベントの「エラーコード」列の内容から失敗した原因を調査する。

ここまでの設定が完了すれば、以後コンソールから手動でデプロイを実行することが可能になる（都度、デプロイしたいコミットIDを設定する必要あり）。
GitHubへのプッシュ時に自動でデプロイされるようにするためには、さらに以後の設定を行う。  

## 6. GitHub、Actionsの設定  
### 6-1. デプロイ対象レポジトリの「Secrets」の設定  
  * ブラウザでGitHubのデプロイ対象レポジトリを開き、Settings＞Secrets＞「Add a new secret」をクリックする。  
  * Secrets設定画面で`AWS_ACCESS_KEY_ID`と`AWS_SECRET_ACCESS_KEY`という名前でIAMユーザーのアクセスキーID、シークレットアクセスキーを登録する。  

### 6-2. GitHub Actionsの設定  
  * デプロイ対象のレポジトリの「Actions」タブを開き、「Set up a workflow yourself」をクリックする。  
  * ymlファイルの編集画面が開くので、以下の内容でymlファイルを作成する。デフォルトで`main.yml`となっているが、ファイル名は自由に設定可能。  

  ```
name: AWS Code Deploy

on:
  push:
    branches: [ master ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: chrislennon/action-aws-cli@v1.1

    - name: Set AWS region
      run: aws configure set region 【AWSのリージョン】　※東京なら"ap-northeast-1"

    - name: Run AWS CodeDeploy
      run: aws deploy create-deployment --application-name "【CodeDeployで作成したアプリケーション名】" --deployment-group-name "【CodeDeployで作成したデプロイグループ名】" --github-location repository="【GitHubアカウント名/デプロイ対象レポジトリ名】",commitId="${{ github.sha }}"
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

  ```  
  * ymlファイルの作成が完了したら編集画面でコミットを実行する。
  * コミットが完了するとこの時点でActionsが動くので、レポジトリの「Actions」タブから実行結果を確認する。ワークフローが正常に完了していれば、`appspec.yml`で指定したインスタンスのデプロイ先ディレクトリに、直前にコミットした`main.yml`がデプロイされていることが確認できる。　　
 
以上の設定が完了すると、以後masterブランチにpushしたタイミングでデプロイが実行されAWSに修正が反映されるようになる。　　

# あとがき
今回は非常にシンプルな構成、ルールでデプロイしているだけだが、それでも自動でデプロイされるようになるまで非常に苦労した。今後はデプロイ設定やymlファイルのカスタマイズを試し、より高度なデプロイ設定ができるようチャレンジしていきたいと思う。