Laravel Sail + ECS + ECRでデプロイフロー構築

# Background
## Objective
- ローカルでLaravel Sailアプリ作成
- GitHubでリポジトリ管理
- ECR, ECSを用いたデプロイ

## Environment
- PHP 8.2.8
- Laravel 10.18
- Composer 2.5.8

# Deploy
## Create Laravel Sail App
1. 開発ディレクトリで以下コマンド実行
```
laraevl new deploy-back
cd deploy-back
composer require laravel/sail --dev
php artisan sail:install
composer install
./vendor/bin/sail up
```
2. `localhost`にアクセスして画面が表示されればOK
![](https://storage.googleapis.com/zenn-user-upload/ef5eaccac95e-20230819.png)

## Push to GitHub
1. GitHubにリポジトリ作成(以下リポジトリ名を `deploy-back` とする)
2. 先ほど作成したlaravelアプリのディレクトリで以下コマンド実行
```
git init
git add .
git commit -m 'first commit'
git remote add origin {作成したリポジトリURL}
git push origin master
```
3. GitHubを確認してプッシュできていればOK

## Create AWS VPC
デプロイ環境を構築するためにAWSでVPCを作成
1. VPC > Create VPCからVPC~IGWまで一括で作成
![](https://storage.googleapis.com/zenn-user-upload/9708cde126e4-20230819.png)

## Create AWS Security Group
テスト用のセキュリティグループを作成
1. VPC > Security Group > Create security groupから作成
2. 先程作成したVPCを選択して、Inbound rules, Outbound rulesに適切な設定をする
![](https://storage.googleapis.com/zenn-user-upload/2ba6e137f226-20230819.png)

## Create AWS ECR Repository
1. AWS > Elastic Container Registry > Private > Create repositoryでリポジトリを作成
![](https://storage.googleapis.com/zenn-user-upload/ac3f7a58f4cb-20230819.png)
2. Private, Repository name = deploytestで作成
![](https://storage.googleapis.com/zenn-user-upload/35f49b137557-20230819.png)

## Create Dockerfile for ECR
laravelディレクトリ内にECRデプロイ用のDockerfileを作成
1. `php artisan sail:publish`でdockerディレクトリ配下をリポジトリ管理対象にする
2. `/docker/8.2`配下にapache設定用の`000-default.conf`ファイル作成
```
// 000-default.conf
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/public

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    <Directory /var/www/html/public>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
```
3. `/var/www/html`直下に以下の内容でDockerfileを作成
```
// Dockerfile
FROM php:8.2-apache
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]

ENV COMPOSER_ALLOW_SUPERUSER=1 \
  COMPOSER_HOME=/composer

COPY --from=composer:2.2 /usr/bin/composer /usr/bin/composer

RUN apt-get update && \
  apt-get -y install git unzip libzip-dev libicu-dev libonig-dev libpq-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev  && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  docker-php-ext-install intl pdo_mysql zip bcmath

RUN pecl install xdebug && \
    docker-php-ext-enable xdebug

RUN docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/ && \
    docker-php-ext-install -j$(nproc) gd

RUN mv /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled
RUN /bin/sh -c a2enmod rewrite
WORKDIR /var/www/app
COPY . /var/www/app
COPY ./docker/8.2/000-default.conf /etc/apache2/sites-available/000-default.conf
COPY ./docker/8.2/php.ini /usr/local/etc/php/php.ini

RUN composer require predis/predis

RUN composer install \
  && chmod -R 777 storage bootstrap/cache public/
RUN php artisan config:cache
RUN php artisan route:cache
RUN php artisan view:cache
RUN curl -sL https://deb.nodesource.com/setup_lts.x | bash -
RUN apt-get install -y nodejs
RUN npm install npm@latest
RUN npm run build
```

## Push to ECR
1. ECRでリポジトリ作成後に詳細画面に行けるので、右上にあるView push commandsからプッシュ用のコマンドを確認
![](https://storage.googleapis.com/zenn-user-upload/ab93ed1ae42a-20230819.png)
2. 書かれているコマンドをlaravelディレクトリで実行
![](https://storage.googleapis.com/zenn-user-upload/75dea722eda0-20230819.png)
3. 成功してリポジトリのImagesにlatestで表示されればOK

## Create ECS
Elastic Container Serviceから各設定をする

### Task definition
Elastic Container Service > Task definitions > Create new task definitionから作成
ECSで起動するためのタスクを作成する
#### Task definition famimly
![](https://storage.googleapis.com/zenn-user-upload/61e8d6903c74-20230819.png)
#### Container
![](https://storage.googleapis.com/zenn-user-upload/c4a4b805bf44-20230819.png)
他の設定はデフォルトのまま作成

### Cluster
Elastic Container Service > Clusters > Create clusterからクラスターを作成
1. クラスター名、作成したVPCとSubnetsを設定して作成
![](https://storage.googleapis.com/zenn-user-upload/4de0bce8dfac-20230819.png)
2. 作成すると以下のような詳細画面に遷移できる
![](https://storage.googleapis.com/zenn-user-upload/bbc7eaace8c1-20230819.png)

### Service
クラスター詳細画面からService > Createでサービスを作成
#### Environment
Launch type, FARGATEを選択
![](https://storage.googleapis.com/zenn-user-upload/06a75c6ed380-20230819.png)
#### Deployment configuration
Service, 作成したタスク定義、作成するサービス名を設定
![](https://storage.googleapis.com/zenn-user-upload/a2c2a25b96bc-20230819.png)
#### Networking
作成したVPC, Subnets, Security groupを設定、Public IPはONにする
![](https://storage.googleapis.com/zenn-user-upload/b8427ac44479-20230819.png)

上記設定でサービスを作成するとサービスが立ち上がる
![](https://storage.googleapis.com/zenn-user-upload/4aa0e72c1182-20230819.png)

サービス詳細からタスクのコンテナを確認するとPublic IPが確認できる
![](https://storage.googleapis.com/zenn-user-upload/f95c78026539-20230819.png)

Public IPにアクセスしてLaravelアプリが立ち上がっていればOK
![](https://storage.googleapis.com/zenn-user-upload/8f7dcacb3975-20230819.png)

# Deploy Flow
## Create buildspec.yml for Code Build
laravelディレクトリでCode Build用にbuildspec.ymlファイルを作成する
`/var/www/html`直下に`buildspec.yml`を以下の内容で作成
```
version: 0.2
# Codebuild用設定ファイル
phases:
  pre_build:
    commands:
      - aws --version
      - echo Logging in to Amazon ECR....
      - aws ecr get-login-password --region {region} | docker login --username AWS --password-stdin {Account ID}.dkr.ecr.ap-northeast-1.amazonaws.com
  build:
    commands:
      - echo Build started on `date`
      - docker build -f ./Dockerfile -t {ECR name}:latest .
      - docker tag {ECR name}:latest {Account ID}.dkr.ecr.ap-northeast-1.amazonaws.com/{ECR name}:latest
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker image ls -a
      - docker push {Account ID}.dkr.ecr.ap-northeast-1.amazonaws.com/{ECR name}:latest
      - printf '[{"name":"%s","imageUri":"%s"}]' {ECR name} {Account ID}.dkr.ecr.ap-northeast-1.amazonaws.com/{ECR name}:latest > imagedefinitions.json
artifacts:
  files:
    - imagedefinitions.json
```
`{ }`の中身については各自の環境に合わせて設定
- {region}: ap-northeast-1など
- {Account ID}: AWSのアカウントID
- {ECR name}: 作成したECRのリポジトリ名(deploytest)

## Create Code Build
CodeBulid > Build projects > Create build projectから作成
### Source
デプロイしたGitHubのlaravelアプリのリポジトリを選択
![](https://storage.googleapis.com/zenn-user-upload/417ada449327-20230819.png)
### Primary source webhook events
Webhookの発火条件を指定（PUSH:GitHubにプッシュされた時に発火）
![](https://storage.googleapis.com/zenn-user-upload/83bf526c9ff2-20230819.png)
### Environment
Privilegedにチェック（これがないとbuildspec.ymlでimageを作成できない）
![](https://storage.googleapis.com/zenn-user-upload/0a80831d7b3f-20230819.png)
### Buildspec
作成したbuildspec.ymlの名前を入れる
![](https://storage.googleapis.com/zenn-user-upload/3426d6bf9f11-20230819.png)
上記設定でBuild project作成

## CodePipeline
1. Codepipeline > Create new pipelineから作成
### Choose pipeline setting
パイプライン名とロールを設定
![](https://storage.googleapis.com/zenn-user-upload/5ed422c3a7d2-20230819.png)
### Source
GitHubを選択してLaravelアプリのリポジトリ、ブランチを設定
![](https://storage.googleapis.com/zenn-user-upload/151436be2315-20230819.png)
### Add build stage
Build providerにCodeBuildを設定し、作成したBuild projectを設定
![](https://storage.googleapis.com/zenn-user-upload/6259fb7a01bb-20230819.png)
### Add deploy stage
Deploy providerにECSを設定し、作成したクラスター、サービスを設定
Image definition fileにはbuldspec.ymlのartifacts > filesに記載したファイル名を入れる
![](https://storage.googleapis.com/zenn-user-upload/4af32fd33fed-20230819.png)
上記設定でCodePipeline作成

## Check
試しにGitHubにプッシュして動作確認し、問題なければデプロイ成功
![](https://storage.googleapis.com/zenn-user-upload/dc55818a7587-20230819.png)