Laravel + Nuxt +Fargate にCodePipelineで継続的デプロイを実現する

# 前提
[Laradock + Nuxt（SSRモード）をAWS Fargateで実行する](https://qiita.com/gemetasu1103/items/3248c86059f21a8ac545)

こちらでFargateで起動できていること

# やりたいこと
githubのソースコードがmasterブランチにpushされた際にCodeBuildでDockerイメージをビルドし
Fargateにデプロイを自動化する
できればテストも実施する

# 参考

[LaravelアプリケーションをローカルでもAWSでもDockerで動かす](https://qiita.com/imunew/items/1e4826030d725beb4710)

[LaravelアプリケーションをCodePipeline/CodeBuildでECSに自動デプロイする](https://qiita.com/imunew/items/687221e02d977564d610)

[Laravelのセッション管理にRedisを指定し、AWSのElastiCacheを利用する](https://www.prj-alpha.biz/entry/2018/05/06/230232)

# 手順

1. buildspec.ymlを追加する
2. githubリポジトリを作成する
3. AmazonAuroraを構築し、テーブルの作成まで行えるようにする 
4. CodePipelineを作成する
5. CodePipelineでデプロイまでできるか確認する

# buildspec.ymlを追加する

ソースコードプロジェクトのディレクトリ直下に
build時の実行内容を記載する

```sh
touch buildspec.yml
```

#### buildspec.yml

```yml
version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      ## awsにログインする
      - $(aws ecr get-login --no-include-email --region ${AWS_DEFAULT_REGION})
      ## s3からenvファイルを取得する
      - aws s3 cp s3://${BUCKET_NAME}/staging/env .env
      - echo ls -l -a 
      ## nginxとphp-fpmコンテナイメージのECRリポジトリURIを生成する
      - REPOSITORY_URI_PHP_FPM=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_NAME_PHP_FPM}
      - REPOSITORY_URI_NGINX=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_NAME_NGINX}
      ## ここは何をしてる？
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t ${REPOSITORY_URI_PHP_FPM}:latest -f docker/php-fpm/Dockerfile .
      - docker build -t ${REPOSITORY_URI_NGINX}:latest -f docker/nginx/Dockerfile .
      - docker tag ${REPOSITORY_URI_PHP_FPM}:latest ${REPOSITORY_URI_PHP_FPM}:$IMAGE_TAG
      - docker tag ${REPOSITORY_URI_NGINX}:latest ${REPOSITORY_URI_NGINX}:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push ${REPOSITORY_URI_PHP_FPM}:latest
      - docker push ${REPOSITORY_URI_PHP_FPM}:$IMAGE_TAG
      - docker push ${REPOSITORY_URI_NGINX}:latest
      - docker push ${REPOSITORY_URI_NGINX}:$IMAGE_TAG
      - echo Writing image definitions file...
      ## コンテナ定義の更新するためのファイルを作成する
      - IMAGE_DIFINITION_PHP_FPM="{\"name\":\"${PHP_FPM_CONTAINER_NAME}\",\"imageUri\":\"${REPOSITORY_URI_PHP_FPM}:${IMAGE_TAG}\"}"
      - IMAGE_DIFINITION_NGINX="{\"name\":\"${NGINX_CONTAINER_NAME}\",\"imageUri\":\"${REPOSITORY_URI_NGINX}:${IMAGE_TAG}\"}"
      - echo "[${IMAGE_DIFINITION_PHP_FPM},${IMAGE_DIFINITION_NGINX}]" > imagedefinitions.json
artifacts:
    files: imagedefinitions.json
```

## コンテナ起動時に`php artisan migrate`を実行するように設定を行う

マイグレーションを行うためにコマンドを起動時に実行するコマンドを追加

/docker/php-fpm/startup.sh

```sh
#!/bin/bash

+ # migrate lunch
+ php artisan cache:clear
+ php artisan migrate

# php-fpm sever lunch
php-fpm
```



# githubリポジトリを作成する

push先のgithubリポジトリを作成し、ローカルのプロジェクトに移動する
ソースコードをpushする

```sh
cd ./{プロジェクト名}/
echo "# guild-app" >> README.md
git init
git add .
git commit -m "first commit"
git remote add origin https://github.com/hajime1103/guild-app.git
git push -u origin master
```

# AmazonAuroraを構築し、テーブルの作成まで行えるようにする

1. DB配置用のプライベートサブネットを作成する
2. プライベート用のルートテーブルを作成する
3. RDSサービスでサブネットグループを作成する
4. RDSサービスでパラメータグループを作成する
5. AuroraDBの作成を行う
6. laravelのdatabase.phpの変更を行う



## DB配置用のプライベートサブネットを作成する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/40bdcce5-7289-a34a-a7ba-d3aed5aa78ae.png)

## プライベートサブネット用のルートテーブルを作成する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/c880a983-dde0-9e29-f864-6c96a813ab0c.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/c859bbff-6d56-c1cb-3182-1bb72e6964f9.png)

## RDSでサブネットグループを作成する

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/380e5fdc-f05a-9bfe-b4a3-acf3abf4a74d.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/7f7e85b8-1c7b-15c7-d4cb-ceb468815f26.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/59fc4e2c-398f-3bdd-6b2b-2a360ad10174.png)

## RDSでAuroraのパラメータグループを作成する
パラメータグループとDBクラスターパラメータグループの両方を作成する

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/9142a586-2611-60a2-64a9-ec61fac587fe.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/946867fc-2d0c-24a9-85c1-2f21ef16af1c.png)

## AuroraDBの作成を行う
![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/8ebefb07-7194-2acf-01f4-6fb5bb045147.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/70ae2ac2-683c-1798-e410-03a1b7dc1abe.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/13291343-e3a0-95b4-f9d3-70d06f95ff91.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/70385cb2-3a41-dae4-5ce8-09bd2be1a104.png)
※セキュリティグループを新たに作成する場合は、許可するインバウンド設定をFargateからでもアクセスできるようにしておく

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/1046e93f-cfb9-e739-8cb5-d2ac9d6deab6.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/62ea6f5a-2aaf-f17d-5446-738ec9551834.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/1ee88907-c7a8-48c5-0103-872d6d66f788.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/8fc84af5-98c0-5217-250e-f987533f6c3d.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/d59f3074-03fe-5840-6d46-e1e6b2fc1a6a.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/d41bb82f-6904-9c97-d040-6f687ffff4b1.png)

## laravelのdatabase.phpの変更を行う

Auroraはデフォルトで読み込みと書き込み専用が作成されるため
エンドポイントを切り替える必要がある

参考サイト
[Laravel の DB のマスター/スレーブでマスターで SELECT したりスレーブでトランザクションしたり](https://qiita.com/ngyuki/items/a3ab2ea615e8ba73a491)

**/config/database.php**

```
- 'host' => env('DB_HOST', '127.0.0.1'),
- 'port' => env('DB_PORT', '3306'),
+ 'read' => [
+     'host' => env('DB_SLAVE_HOST', '127.0.0.1'),
+     'port' => env('DB_SLAVE_PORT', '3306'),
+ ],
+ 'write' => [
+     'host' => env('DB_MASTER_HOST', '127.0.0.1'),
+     'port' => env('DB_MASTER_PORT', '3306'),
+ ],
```

本番環境/ステージング環境用のenvファイルをあわせて修正し、S3バケットにアップロードする

```
DB_CONNECTION=mysql
DB_MASTER_HOST=｛書き込み専用エンドポイント｝
DB_MASTER_PORT=｛ポート番号｝
DB_SLAVE_HOST=｛読み込み専用エンドポイント｝
DB_SLAVE_PORT=｛ポート番号｝
DB_DATABASE=XXXX
DB_USERNAME=XXXX
DB_PASSWORD=XXXX
```

# CodePipelineを作成する

CodePipelineを新規作成する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/0dd5dd33-6286-e4b3-1c29-a5b6efd49538.png)

リポジトリはgithubを選択する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/eda01994-8d2a-e326-61cc-177e4cf7487f.png)

AWS CodeBuildを作成し、プロジェクトを新規作成する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/5591d6b8-73b5-6c46-ec3a-ae0e50c5ac42.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/37a24b8d-f646-76dd-d299-117e1a8bfbb7.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/3611f33c-ea91-4ded-5bbd-9293f530e48f.png)

環境変数の定義

| 環境変数名           |        概要       | 
|:-----------------|:-----------------|
| AWS_ACCOUNT_ID   | ECRのリポジトリID |
| BUCKET_NAME        | .envファイルを配置したS3バケット |
| IMAGE_NAME_PHP_FPM | php-fpmコンテナECRのリポジトリ名 |
| IMAGE_NAME_NGINX | nginxコンテナECRのリポジトリ名 |
| PHP_FPM_CONTAINER_NAME | ECSタスクのphp-fpmコンテナ名 |
| NGINX_CONTAINER_NAME | ECSタスクのnginxコンテナ名 |

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/6e415141-05f1-3fe1-719b-5f64c5a7339d.png)

デプロイ先を「AmazonECS」を選択する

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/4ea2a11d-4ea4-e670-5a27-9909420482dc.png)

デプロイの設定を行う

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/6b863159-5704-9541-5300-fb8a6e8a70d2.png)








## ビルドを実行する

ビルドを実行したら下記のエラーが発生

```
An error occurred (AccessDeniedException) when calling the GetAuthorizationToken operation: User: arn:aws:sts::636990608596:assumed-role/codebuild-GuildApp-CodeBuild-service-role/AWSCodeBuild-c714c40b-8e5e-466d-9d97-fda2158797d4 is not authorized to perform: ecr:GetAuthorizationToken on resource: *
 [Container] 2019/03/26 16:04:29 Command did not exit successfully $(aws ecr get-login --no-include-email --region ${AWS_DEFAULT_REGION}) exit status 255
[Container] 2019/03/26 16:04:29 Phase complete: PRE_BUILD Success: false
[Container] 2019/03/26 16:04:29 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: $(aws ecr get-login --no-include-email --region ${AWS_DEFAULT_REGION}). Reason: exit status 255
```

ロールが足りなかったみたい

[【備忘録】CodeBuildでaws ecr get-loginコマンド実行時にエラーが発生する](https://qiita.com/NaokiIshimura/items/e73898244d784d7fbce7)

.envファイルをS3から取得するためとECRにアクセスするため
下記のロールをアタッチした
AmazonEC2ContainerRegistryPowerUser
AmazonS3ReadOnlyAccess


# CodePipelineでデプロイまでできるか確認する

![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/976d6746-754f-0171-9aaa-7e0f6df78d37.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/305479/f441f9bb-b439-3adf-bea6-a661c2491a82.png)