CodePipelineでS3にデプロイしてCloudFrontでコンテンツを配信する

CodeStarでさくさくCI/CD作りもいいのだが、とりあえず一旦はCodeCommitからDeployまでCodePipelineで連携する方法を理解しておこうと思ったので、軽く試してみた。

CloudFrontで配信するところまでやってみる。

## やること
- CodePipelineを利用して、CodeCommit, CodeBuildを連携させ、ReactクライアントをS3にアップロードする（デプロイ）。
- S3に配置されたReactクライアントをCloudFrontで配信する。

## やる順番
1. CodeCommitでリポジトリを作成
2. CodeBuildでビルドの設定（テストの設定とかはしない）
3. CodePipelineでCommitからDeployまでを一貫して行う(S3へビルドファイルをアップロード)
4. CloudFrontでコンテンツを配信(細かい設定はしない)

## CodeCommitでリポジトリを作成

AWSコンソールのCodeCommitを開き、リポジトリを作成する。

仮にリポジトリ名testを作成すると以下のような「接続のステップ」が表示される。

<img width="1108" alt="Screenshot 0032-01-14 at 10.14.19 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/e9af036e-69fb-8c28-91c7-51ab6222a1c1.png">

ソースコードをプッシュするには、まずこのリポジトリをクローンする必要がある。
右上の「URLのクローン」から「HTTPSのクローン」を選択すると、URLがコピーされるのでローカルでgit cloneする。

git cloneの際に尋ねられるユーザー名等はIAMの認証情報から取得する。
IAMのアクセス管理>ユーザーからアカウントを選択して、「AWS CodeCommit の HTTPS Git 認証情報」の「認証情報を生成」から証明書をダウンロードする。

中のユーザー名とパスワードを使ってgit cloneできるようになる。

cloneしたディレクトリにソースコードを置いてプッシュすればCodeCommitにソースコードが表示される。

## CodeBuildでビルドの設定

AWSコンソールのCodeBuildを開き、ビルドプロジェクトを作成する。

公式の解説ページも参考に。
[CodeBuild でビルドプロジェクトを作成する](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/create-project.html)

「ビルドプロジェクトを作成」へ入ると、

- プロジェクトの設定
- 送信元
- 環境
- Buildspec
- アーティファクト
- ログ

の設定項目が目につくが、ここでは「送信元」、「環境」、「Buildspec」のみに触れる。

「アーティファクト」と「ログ」の設定は触れずにビルドプロジェクトを作成する。

### CodeBuild 送信元設定

<img width="838" alt="Screenshot 0032-01-14 at 10.39.50 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/4a3007dd-3f20-cdef-f457-938bbc970a7d.png">

ここで指定するソースプロバイダは、CodeCommitの入力アーティファクトを出力するソースコードを指す。

CodePipeline（CodeCommit, CodeBuild, CodeDeployをCI/CD機能）では入力アーティファクトと出力アーティファクトが各フェイズで受け渡される。
アーティファクトとはそれぞれのフェイズの成果物のことで、CodeCommitの出力アーティファクトはコードそのものであり、CodeBuildはそれを入力アーティファクトとして受け取って、ビルドしたファイルを出力アーティファクトとしてCodeDeployへ渡す。

[入力および出力アーティファクト](https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/welcome-introducing-artifacts.html)

### CodeBuild 環境設定

<img width="554" alt="Screenshot 0032-01-14 at 10.56.50 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/3715b4cc-23cb-f75f-bf9b-709224e62a97.png">

ビルド環境設定では、ビルドを実行するためにCodeBuildが使用するオペレーティングシステム、プログラミング言語ランタイム、およびツールの組み合わせを設定することができる。

特にこだわりがないのであれば、
OS、ランタイム、イメージ、イメージのバージョン、環境タイプは上記のように設定すればたいして困らないと思う。

サービスロールではCodeBuildの実行に必要なポリシーが組まれたロールが作成される。
すでにある場合は既存のものを使える。

[CodeBuild のビルド環境リファレンス](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-env-ref.html)
[CodeBuild に用意されている Docker イメージ](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-env-ref-available.html)

### CodeBuild Buildspec設定

<img width="834" alt="Screenshot 0032-01-14 at 11.25.09 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/3001eac3-1917-9aec-ec9f-a2b6f43cb4a7.png">

ビルドコマンドの挿入の選択肢もあるが、ここではbuildspecファイルを使ったビルド設定について触れる。
ここまでにCodeBuildがビルドを行うソースコードの設定と、ビルドを行う環境の設定について書いたが、Buildspecではビルドの実行時に実行する細かい処理についての設定を行うことができる。

例えば、reactクライアントをCodeCommitへのプッシュをトリガーとしてビルドしたい場合は、以下のようにディレクトリ内にbuildspec.ymlを配置する。

<img width="1027" alt="Screenshot 0032-01-14 at 11.32.04 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/5ba78ded-166e-5065-c9a5-ddd87649d0f9.png">

これがこのリポジトリのルートディレクトリだとすると、Buildspecの項目におけるBuildspec名にはファイルそのものを指定すれば良い。
もしリポジトリ内の特定のディレクトリに存在するBuildspecファイルを指定したい場合は、そのパスを書く（`client/buildspec.yml`のように）。

また、このymlファイルの名前が`buildspec`である必要はない（`clientspec.yml`とかでも良い）。

**buildspec.ymlの例**
例ではreactクライアントのビルドを念頭に置いているため、runtime-versionsはnodejs10.xを指定しており、　typescriptをインストールしている。

```yml
version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 10
    commands:
      - npm install -g typescript
  pre_build: # ビルド実行前に実行する処理等の設定
    commands:
      - cd client
      - npm install
  build: # ビルド実行処理等の設定
    commands:
      - npm run build
  # post_build:
  #   commands:
  #     - some command
  #     - some command
artifacts:
  files:
    - 'client/dist/*' # 出力するビルドファイル
  discard-paths: yes # 出力するビルドファイルからパスを省く（client/dist/などを省く）
```

[CodeBuild のビルド仕様に関するリファレンス](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)

## CodePipelineでCommitからDeployまでを一貫して行う

AWSコンソールのCodePipelineを開き、パイプラインを作成する。

パイプライン名とサービスロールを設定すると、

- ソースステージ
- ビルドステージ
- デプロイステージ

の設定に入る。

### ソースステージの設定

<img width="803" alt="Screenshot 0032-01-14 at 11.54.58 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/50b283a2-c7e2-c000-664e-22ae4d6914e3.png">

CodeCommitのリポジトリ名とブランチ名を指定する。
これはビルドステージへの出力アーティファクトとなる。

### ビルドステージの設定

<img width="784" alt="Screenshot 0032-01-14 at 11.57.00 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/bf546672-e6b0-4476-9996-713ff4d0923f.png">

CodeBuildで設定したプロジェクトを指定する。

### デプロイステージの設定

<img width="796" alt="Screenshot 0032-01-15 at 12.01.07 AM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/743b0ece-5871-175b-8e76-db186a78d573.png">

CodeDeployはS3のデプロイに対応していないため、S3デプロイを行うためにはCodePipelineを使う必要がある。

デプロイプロバイダーにS3を指定し、デプロイ先のバケットを選択する。
ここでは「デプロイする前にファイルを抽出する」にチェックを入れている（何もしないとzipがバケットに配置されるが、ここでは解凍された状態で配置したいため。デプロイパスはバケット内に展開されるディレクトリの構成を設定できる）。

全ての設定を終えて確認画面から「パイプラインを作成する」と、パイプライン一覧に新規パイプラインが表示される。
ソースコードに変更を加え、CodeCommitへプッシュすると、自動的にCodeBuildが起動し、S3へのビルドファイルのデプロイが行われる。

デプロイ設定で指定したバケット内にビルドしたファイルが表示されるはず。

<img width="1314" alt="Screenshot 0032-01-15 at 12.07.45 AM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/b2517ace-1763-85c4-6f57-b176b5827aba.png">

＊バケットポリシーでs3:GetObjectを許可しておかないと、事項のCloudFront経由でアクセスした時に403が返ってきてしまうので注意。また、CloudFront Origin Access IdentityをポリシーのPrincipalに追加してあげる必要もあり。

## CloudFrontでコンテンツを配信

CloudFrontとはオリジンサーバーが直接アクセスに対応する機会を減らし、キャッシュ化されたエッジロケーションのリソースに対してユーザをルーティングする機能のこと。
[Amazon CloudFront とは](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/Introduction.html)

AWSコンソールでCloudFrontを開き、CreateDistributionを選択する。

<img width="1150" alt="Screenshot 0032-01-15 at 11.33.52 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/18336d15-764e-fe96-a331-713a258b5969.png">

WebのGet Startedからディストリビューションの設定を行う。

**Origin Setting**では以下の2点を設定する。

- Origin Domain Name
CodePipelineでビルドファイルをデプロイしたS3バケットを選択する。

- Restrict Bucket Access
バケットのリソースへのアクセスを、S3のURLを使わずに常にCloudFrontのURLを利用したアクセスのみに絞りたい場合はこの項目をYesに設定する。
そして同時にOrigin Access IdentityでCreate a New IdentityかUse an Existing Identityを選択します（新規で作成する場合は前者を、既存のものを再利用するのであれば後者を選択する）。
Grant Read Permission on BucketもYesにしておきましょう。

**Default Cache Behavior Settings**は飛ばして、
**Distribution Settings**では以下の項目だけを設定する。

- Default Root Object  
index.htmlを指定する。
これはバケット内のインデックスドキュメントの設定。
設定しないと`${URL}/index.html`としてアクセスしなければならない。

Create Distributionをクリックして一覧に表示されるディストリビューションのStatusがDeployedになるまで待つ。

<img width="1229" alt="Screenshot 0032-01-15 at 11.44.21 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/43cc5abb-1f91-da3b-85a4-42e8155c0602.png">

DeployedとなったらDomain Nameに表示されているURLにアクセスしてページが動いているかを確認する。

完。


## まとめ

ここまででCodeCommitに新しい変更をコミットしていくと、S3バケットに自動的にビルドファイルがアップロードされるようになる。

厳密なデプロイはそのあとにCloudFrontのディストリビューションのInvalidate(CloudFrontのエッジロケーションのキャッシュを削除して更新されたコンテンツを再配布する)を行ったときに行われる。

InvalidateはCloudFrontのディストリビューションの一覧画面からディストリビューションを選び、Invalidationタブを選択した画面で行える。

<img width="1224" alt="Screenshot 0032-01-15 at 11.55.39 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/b4917eea-6a0e-0c61-2fb1-c5ecc5fdbee7.png">

Create InvalidationからInvalidateする項目を指定してInvalidateボタンをクリックするだけ。
バケット内の全てのファイルを指定してInvalidateする場合は*(アスタリスク)を指定すれば良い。

今後デプロイの手順としては、

1. CodeCommitへコードのプッシュ
2. CloudFrontでInvalidateを実行

をするだけでよくなる。

おしまい。