EC2 にデプロイするCI/CD環境を作ってみた

# はじめに
AWS Code シリーズを使って Amazon EC2 にデプロイするCI/CD環境を構築してみました。
構築手順を忘れないために残しておきます。

構築する構成は以下です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/98e58705-f701-6964-11a3-ebafaf74b333.png)

※前提条件
本手順ではローカル開発環境（ IDE, Git, AWS CLI ) はセットアップされた状態でスタートします。

# CodeCommitの設定
AWS コンソールにログインし、CodeCommitのサービスに移動します。
リポジトリの作成をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/a445e6d4-f34a-6057-ec36-f0b88e9c9eea.png)
Code CommitにCLIで接続できるようにします。
今回は、認証情報ヘルパーを使った方法で設定しました。
手順は[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/setting-up-https-unixes.html#setting-up-https-unixes-credential-helper)を参照。


接続できるようになったら、ローカルにリモートリポジトリのクローンを作成します。
コンソール画面から`git clone`コマンドをコピーして、ターミナルで実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/33fa7cd3-503c-e6b1-f68a-cf71ba430259.png)

### CodeCommitにファイルを登録してみる
HTMLファイルの作成します。
**最終的なフォルダ構成** 
demoCICD
 ∟ src / index.html
 ∟ src / appspec.yml
 ∟ buildspec.yml


<details><summary>index.html(サンプル)</summary>

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <style>
        .sample{
            margin-top: 20%;
            margin-left: 20%;
        }
    </style>
</head>
<body>
    <div class="sample">
        <h1>CI/CD demo</h1>
        <h2>version 1</h2>        
    </div>
</body>
</html>
```
</details>

コミットしてpushしてみます。
```
git add -A 
git commit -m "init"
git push origin main
```
無事リポジトリに反映されるか確認しましょう。

# デプロイ先となるEC2を準備する
### EC2に紐づけるIAMロールの作成
IAMのサービスページに移動して作成します。EC2を選択して「次へ」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/3dbd4b7c-26d5-6bb5-68d9-303a884a299e.png)
S3アクセスが必要となるので、フルアクセスの権限をつける。（ユースケースに応じて権限は絞ってください。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/70e177dc-8d1b-5b32-2e8c-2e19fd656c84.png)
あとはロール名を決めて作成

### EC2を起動
今回は、マシンイメージ、インスタンスタイプなどはデフォルトで進めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/b5ec6517-774c-c89b-1ad4-18f94a775bbe.png)
ネットワーク設定の項目では、
- 「パブリックIPの自動割り当て」を有効化（今回はIPでブラウザからアクセスして動作確認します。）
- セキュリティグループは新規に作成します。
    - インバウンドセキュリティグループのルールを追加します。
    - 下記画面のようにHTTPで通信できるようにしておきます。
    - IPは会社のIPや自宅のIPなど適切に絞ってください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/e4015349-2803-b97c-9700-f4f9c15d64f8.png)
次に、「高度な設定」で先ほど作ったIAMロールを紐づけます。
「IAMインスタンスプロフィール」で設定します。
「キーペアなし」で作成します。
:::note warn
EC2への接続はsshではなく、ssmエージェントを利用した方法が推奨 [[参考](https://www.karakaram.com/aws-session-manager-tunneling-support-for-ssh/)]
:::


### EC2のセットアップ
インスタンスが起動したら、「接続」ボタンを押してインスタンスに接続します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/5c8d305d-8adf-dba1-e0cf-0b300dd4d887.png)

以下のコマンドを流していきます。
```
sudo yum update -y
sudo yum install httpd -y
# httpdの起動
sudo systemctl start httpd.service
# 再起動がかかった際に起動するようにしておく
sudo systemctl enable httpd.service
```
終わったら、EC2インスタンスのパブリックIPをコンソール画面からコピーして、ブラウザに貼り付けます。apacheのテストページが表示されるのを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/46ca14da-351a-9d7c-e929-133932be770d.png)

次にCodeDeployからEC2にデプロイするために、デプロイエージェントをインストールします。
[手順](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html)に沿ってコマンドを実行します。

# S3バケットの作成
artifactを格納するS3を作成します。
バケット名決めて作成するだけなので、割愛します。

# CodeBuildeの設定
Codebuildのサービスページに移動して、ビルドプロジェクトを作成していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/8408408a-b49a-ee9e-fff2-a6098db5978f.png)

作成したCodeCommitのリポジトリとブランチを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/13a307a0-7260-8139-c7b1-4778be25b767.png)

Linux環境で設定を進めます。イメージやバージョンは最新で進めます。
サービスロールは新規に作成を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/4210a1c3-a16c-c071-c044-22307ce5722d.png)

アーティファクトは先ほど作ったS3に格納するように設定します。
パッケージ化は「Zip」を選択し、他の項目はデフォルトで進めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/d23e54ef-5bfd-b182-e765-42d13b680186.png)

上記ビルドプロジェクトの過程で作成されたIAMロールに必要なポリシーをアタッチします。
IAMロールの設定画面から該当のIAMを選択し、「許可を追加」　> 「ポリシーをアタッチ」 > 下記スクショのポリシーを選択してアタッチします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/0a3543f9-2a62-862b-a0a5-673b214bedf1.png)

### buildspec.ymlを書く
フォルダ直下にbuildspec.ymlファイルを作成します。
buildspec.ymlの書き方は[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)を見て書き方を調べます。
今回ビルドフェーズで実行するコマンドは、CodeDeployでデプロイするリビジョンを引き渡します。[参考](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/application-revisions-push.html)
<details><summary>buildspec.yml(サンプル)</summary>

```yml
version: 0.2

# <application-name>と<backet>は書き換えてください。
phases:
  build:
    commands:
      - aws deploy push --application-name <application-name> --s3-location s3://<backet>/artifact.zip --source src
artifacts:
  files:
    - '**/*'
  base-directory: src

```
</details>

# CodeDeployの設定
CodeDeploy の設定をしていきます。
### IAMrole の作成
CodeDeployで使うIAMroleをまず作ります。
IAMrole作成画面で、信頼されたエンティティとしてCodeDeployを選択して作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/285833dc-184a-ab11-17e4-8406f8b6a3df.png)

### CodeDeploy でアプリケーションを作成
「アプリケーションの作成」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/13d65a7b-0f8c-ddcf-5438-0f4ab133c7e1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/64978343-7c23-ed8e-512e-d42a1410ba7d.png)

次にデプロイグループを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/abb2c6d9-0463-7fb6-06fe-56bb0e235fb6.png)
先ほど作成したIAMroleを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/408a0eca-f0d0-cb1e-174e-49304c3da262.png)
今回は「デプロイタイプ」としてインプレースを選択し、EC2に付けた`Project`タグと`Environment`タグを指定してデプロイします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/a7de0858-a2f6-981d-e430-63838cfd305f.png)

今回は手動でインストールしたので、「なし」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/650e58b2-f012-9202-e464-f6cf31a4f12a.png)

ロードバランシングのチェックは外します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/d5508f4b-26e8-3773-40ad-797855c04923.png)

### appspec.ymlを書く
CodeDeployで読み込むappspec.ymlを作成します。
srcフォルダにappspec.ymlファイルを作成します。
appspec.ymlの書き方は[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/reference-appspec-file.html)を見て調べます。

<details><summary>appspec.yml(サンプル)</summary>

```yml
version: 0.0
os: linux
files:
  - source: /
    destination: /var/www/html/
```
</details>

### アプリケーションをデプロイしてみる
「アプリケーションをデプロイする」からデプロイしてみるのですが、CodeBuildでアーティファクトを作成しておく必要があるので、一旦CodeBuildに遷移して、ビルドの開始を押します。
成功したら再度CodeDeployに戻って、作業を続けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/d8e30745-f00d-3867-7ccb-ca8274626237.png)
デプロイの作成画面でデプロイグループとリビジョンの場所を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/b0e6f4cf-f01f-6965-f25b-972470724042.png)


# CodePipeline を作る
あとわずかです。
今まで作ったコンポーネントをCodePipelineで繋げていきます。
パイプライン作成画面で、CodePipeline用のロールを作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/f231ea4c-c2f4-4e7d-4706-cb86c42e1f1b.png)
ソースステージを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/b65f42f5-b915-24f0-ba98-ad3c5f413feb.png)
ビルドステージを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/efc5ef49-8692-69d0-cb36-0041d6df0827.png)
デプロイステージを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/d00d87cd-220b-2e86-e287-a2e7ddc0335c.png)

完成です。

# 動作確認
index.html　に変更を加えたら、下記コマンドをターミナルから実行して、git pushしていきます。
```
# ステージングエリアに追加
git add -A

# ローカルリポジトリに追加
git commit -m "comment"

# リモートリポジトリにプッシュ
git push origin main
```
無事最後まで流れて成功しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/8965db10-b27b-b195-72a2-77b3805c4be5.png)

デプロイしたEC2のパブリックIPにブラウザからアクセスして、HTMLも無事表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644638/4a662472-a0cf-75e5-ae40-176f48b5f37f.png)

# さいごに

実際にシステム開発する際には商用、検証、ステージングなどでAWSアカウントを分離してCI/CDパイプラインを構築するケースが多いかと思います。
参考になるリンクを記載しておきます。

- [Create a pipeline in CodePipeline that uses resources from another AWS account](https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-create-cross-account.html)
- [Building a Cross-account CI/CD Pipeline](https://cross-account-cicd-pipeline.workshop.aws/)