【AWS】AWS超初心者が、頑張ってDockerで作ったLaravelプロジェクトをECR、ECS、EC2を使ってAWS上で動かしてみた

皆さんこんにちは！

巷で有名なDockerとAWS

２つともつい2，3週間前に学習を始めたのですが、最初は苦労したものの少し慣れてくると便利だなと感じました。

あと、Amazonってすごい！

今回はそんなDockerで作ったLaravelプロジェクトをAWS上で動かしてみたいと思います！

DockerでLaravelプロジェクトを作成する方法は下記の記事を参考にして作成してください！

[【超入門】20分でLaravel開発環境を爆速構築するDockerハンズオン](https://qiita.com/ucan-lab/items/56c9dc3cf2e6762672f4)

Dockerに触れたことがない人でも簡単に作れるのでおススメの記事です！

#IAMユーザー権限の追加#

IAMユーザーの場合、以下の権限を追加しておいてください。

・AmazonRDSFullAccess
・AmazonEC2FullAccess
・IAMFullAccess
・AmazonEC2ContainerRegistryFullAccess
・AmazonECS_FullAccess
・ComputeOptimizerReadOnlyAccess

#ディレクトリ構成#

```
├─backend
│  └─laravel
│      ├─app
│      ├─bootstrap
│      ├─config
│      ├─database
│      ├─public
│      ├─resources
│      ├─routes
│      ├─storage
│      ├─tests
│      └─vendor
├── logs
│   ├── access.log
│   ├── error.log
│   ├── mysql-error.log
│   ├── mysql-query.log
│   ├── mysql-slow.log
│   └── php-error.log
├── docker
│   ├── mysql
│   │   └── my.cnf
│   ├── nginx
|   |   |__ Dockerfile
│   │   └── default.conf
│   └── php
│       ├── Dockerfile
│       └── php.ini
├── docker-compose.yml
```

#Docker設定#

##docker-compose.yml##

```
version: "3"
services:
  app:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
      args:
        - TZ=${TZ}
    ports:
      - ${APP_PORT}:8000
    volumes:
      - ./src:/work
      - ./logs:/var/log/php
      - ./docker/php/php.ini:/usr/local/etc/php/php.ini
    working_dir: /work
    environment:
      - DB_CONNECTION=mysql
      - DB_HOST=db
      - DB_DATABASE=${DB_NAME:-homestead}
      - DB_USERNAME=${DB_USER:-homestead}
      - DB_PASSWORD=${DB_PASS:-secret}
      - TZ=${TZ:-Asia/Tokyo}

  web:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    depends_on:
      - app
    ports:
      - ${WEB_PORT:-80}:80
    volumes:
      - ./src:/work
      - ./logs:/var/log/nginx
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    environment:
      - TZ=${TZ:-Asia/Tokyo}

  db:
    image: mysql:8.0
    volumes:
      - db-store:/var/lib/mysql
      - ./logs:/var/log/mysql
      - ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
    environment:
      - MYSQL_DATABASE=${DB_NAME:-homestead}
      - MYSQL_USER=${DB_USER:-homestead}
      - MYSQL_PASSWORD=${DB_PASS:-secret}
      - MYSQL_ROOT_PASSWORD=${DB_PASS:-secret}
      - TZ=${TZ:-Asia/Tokyo}
    ports:
      - ${DB_PORT:-13306}:3306

volumes:
  db-store:
```

##docker/php/Dockerfile##

```
FROM php:7.4-fpm-alpine
COPY ./src /work
COPY ./docker/php/php.ini /usr/local/etc/php/php.ini
LABEL maintainer="ucan-lab <info@u-can.tech>"

SHELL ["/bin/ash", "-oeux", "pipefail", "-c"]

# tinker(psysh)
ARG PSYSH_DIR=/usr/local/share/psysh
ARG PSYSH_PHP_MANUAL=$PSYSH_DIR/php_manual.sqlite
ARG PHP_MANUAL_URL=http://psysh.org/manual/ja/php_manual.sqlite

# timezone
ARG TZ=Asia/Tokyo

# composer
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer

RUN apk update && \
  apk add --update --no-cache --virtual=.build-dependencies \
    autoconf=~2.69 \
    gcc=~9.2 \
    g++=~9.2 \
    make=~4.2 \
    tzdata=2020a-r0 \
    git=~2.24 && \
  apk add --update --no-cache \
    icu-dev=~64.2 \
    libzip-dev=~1.5 \
    oniguruma-dev=~6.9 && \
  cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
  echo ${TZ} > /etc/timezone && \
  pecl install xdebug && \
  git clone https://github.com/phpredis/phpredis.git /usr/src/php/ext/redis && \
  apk del .build-dependencies && \
  docker-php-ext-install intl pdo_mysql mbstring zip bcmath redis && \
  docker-php-ext-enable xdebug && \
  mkdir $PSYSH_DIR && curl $PHP_MANUAL_URL -o $PSYSH_PHP_MANUAL && \
  curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer && \
  composer config -g process-timeout 3600 && \
  composer config -g repos.packagist composer https://packagist.jp && \
  composer global require hirak/prestissimo
```

##docker/php/php.ini##

```
zend.exception_ignore_args = off
expose_php = on
max_execution_time = 30
max_input_vars = 1000
upload_max_filesize = 64M
post_max_size = 128M
memory_limit = 256M
error_reporting = E_ALL
display_errors = on
display_startup_errors = on
log_errors = on
error_log = /dev/stderr
default_charset = UTF-8

[Date]
date.timezone = Asia/Tokyo

[mysqlnd]
mysqlnd.collect_memory_statistics = on

[Assertion]
zend.assertions = 1

[mbstring]
mbstring.language = Japanese
```

##docker/nginx/Dockerfile##

```
FROM nginx:1.17-alpine
COPY ./docker/nginx/default.conf /etc/nginx/conf.d/default.conf
COPY ./backend /work

EXPOSE 80
```

##docker/nginx/default.conf##

```
server {
    listen 80;
    root /work/laravel/public;
    index index.php;
    charset utf-8;
    error_log /var/log/nginx/error_test.log;

    location / {
        root /work/laravel/public;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}
```

めちゃめちゃ長いので気合い入れていきましょう！

それでは説明していきます！！

#DockerイメージをECRにプッシュ#

まず最初にECRと言う場所にDockerイメージをプッシュします。

イメージ的にはGithubにソースコードをプッシュするときと同じ感覚です。

###コンテナのビルド###

以下のコマンドでappコンテナとwebコンテナをビルドしてください。

```
docker build -t app -f docker/php/Dockerfile .
docker build -t web -f docker/nginx/Dockerfile .
```

###ECRのリポジトリ作成###

まず最初にECR用のプロファイルを作成してください。

```
aws configure --profile ecr
```

これでアクセスキー、シークレットキを入力してください。

リージョンは``ap-northeast-1``

``Default output format``には``text``と入力してください。

もしシークレットキーを忘れてしまった方や、アクセスキーの場所が分からない方は下記の記事が参考になるかと。

[DockerプロジェクトをAWSのECRにプッシュしようとしたらエラーが起きたときに話](https://qiita.com/akki-memo/items/34f31bc1e6b109b66db0)

そしたら下記のコマンドを入力しECRへログインしてください。

```
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [aws_account_id].dkr.ecr.ap-northeast-1.amazonaws.com
```

``Login Successded``が表示されたらOK

次はリポジトリの作成を行います。

今回はappコンテナ用とwebコンテナ別々に作成していきます。

```
aws ecr create-repository --repository-name app --image-scanning-configuration scanOnPush=true --region ap-northeast-1
aws ecr create-repository --repository-name web --image-scanning-configuration scanOnPush=true --region ap-northeast-1
```

「ECS」→「AmazonECR Repositories」に以下のように作成されてたら成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/0c61f12f-5136-f053-af4c-cc06d00ff80f.png)


###Dockerイメージをプッシュ###

先ほどビルドしたイメージをECRへプッシュします

```
#appコンテナをappディレクトリにプッシュ
docker tag app:latest 905806622013.dkr.ecr.ap-northeast-1.amazonaws.com/app:latest
docker push 905806622013.dkr.ecr.ap-northeast-1.amazonaws.com/app:latest

#webコンテナをwebディレクトリにプッシュ
docker tag web:latest 905806622013.dkr.ecr.ap-northeast-1.amazonaws.com/web:latest
docker push 905806622013.dkr.ecr.ap-northeast-1.amazonaws.com/web:latest
```

無事プッシュされたら成功です。

また、時間がかかる場合があり時間がかかりすぎてしまうと、タイムアウトになりプッシュが中断されるのですが、その場合はもう一度プッシュすればOKです！

#ECSの設定#

次は先ほどECRにプッシュしたイメージをECSで使っていきます。

その前にまずは、PCからECSへ接続するためにSSHキーが必要なるのでSSHキーの作成を行います。

##SSHキーの作成##

1. サービス検索で「EC2」と検索
2. 「ネットワーク＆セキュリティ」の「キーペア」
3. 「キーペアを作成」をクリック
4. キーペアキーを入力（なんでもいい）
5. ファイル形式は**pem**を選択
6. 「キーペアを作成」をクリック
7. キーペアを作成すると自動でそのファイルが保存されるので、わかりやすいところに置いておく（後で使うため）

##タスク定義##

次にサービス検索欄で「ECS」と入力し、左のメニューバーの「タスク定義」をクリック

1.「新しいタスク定義の作成」をクリック
2.「EC2」を選択
3. 「タスク定義名」を適当に入力
4. ネットワークモードは「default」
5. 少し下に行き「コンテナの定義」で「コンテナの追加」をクリック

以下の表を参考にコンテナを作成してください。

###appコンテナ###

|コンテナ名    |app                  |
|----------|--------------------------|
|イメージ  |イメージURL（ECR→app→イメージURLをコピーしてここに貼り付け）         |
|メモリ制限|ハード制限（300）|
|**環境**    |                  |
|エントリポイント|sh,-c|
|コマンド|php artisan config:cache && chmod -R 777 storage/* && php-fpm|
|作業ディレクトリ|/work/laravel|
|環境変数|Key→APP_ENV,TYPE→Value,値→production|

###webコンテナ###

|コンテナ名    |web                  |
|----------|--------------------------|
|イメージ  |イメージURL（ECR→web→イメージURLをコピーしてここに貼り付け）         |
|ポートマッピング|ホストポート→80,コンテナポート→80,プロトコル→tcp|
|メモリ制限|ハード制限（300）|
|**環境**    |                  |
|作業ディレクトリ|/work/laravel|
|環境変数|Key→APP_ENV,TYPE→Value,値→production|
|**ネットワーク設定**    |                  |
|リンク    |app                  |

**環境変数**は``.env``ファイルではなく``.env.production``を読み込むために行っている作業です。

コンテナ追加を終えたら「作成」をクリック

##クラスターの作成##

次に左のメニュー欄から「クラスター」選択。

1.「クラスターの作成」をクリック
2.「EC2　Linux + ネットワーキング」を選択
3.「クラスター名」を入力
4.「インスタンスの設定」は以下のように設定

|インスタンスの設定    |                  |
|----------|--------------------------|
|プロビジョニングモデル|オンデマンドインスタンス|
|EC2インスタンスタイプ|t2.micro|
|インスタンス数|1|
|キーペア|先ほど作成したキーペアを選択|

5.「作成」をクリック

無事クラスターが作成されればOK

##ALBの作成##

ここで一旦、今作ったクラスターを元にALBというものを作成していきます。

まずは今作ったクラスターの画面に行き、「ECSインスタンス」をクリック

するとコンテナインスタンス、EC2インスタンスなどの項目が書いてあると思います。

その中の「EC2インスタンス（例：i-123456789abs）」をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/1edea802-5953-8cd8-7df0-f6ca040bd630.png)


ページ先の「インスタンスID」をクリック

すると、パブリックアドレス、プライベートアドレスなどが表示される画面たどり着きます。（以下、ページ①とします）

このページは残しておいて、別のタブでサービス検索欄で「EC2」と検索。

左のメニューから「ロードバランシング」にある「ロードバランサー」をクリック

そこから「ロードバランサーの作成」をクリック

1. 「ApplicationLoadBalancer（HTTP,HTTPS）」を選択
2. 適当に名前を入力
3. アベイラビリティーゾーンにページ①の「VPC ID」と同じものを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/85ce7d4d-3a39-76d5-f3ca-0243df5acff0.png)

4. 次の手順を2回押し「3. セキュリティグループの設定」画面へ
5. 新しいセキュリティグループを作成する（名前は適当に）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/91690202-857e-e176-724a-71926b690b86.png)


6. 「ルーティングの設定」で名前だけ適当に入力し次の画面へ
7. 「ターゲットの登録」で下のインスタンスからページ①のインスタンスを選択し「登録済みに追加」クリックし次の画面へ
8. 「作成」をクリック

無事作成されればOK！

##タスクの実行##

左のメニューの「クラスター」画面に行き、先ほど作ったクラスターの画面に行ってください。

そこから「タスク」→「タスクの実行」をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/32aea6cc-63d8-ab50-55cb-b4f6932c68b9.png)

|タスクの実行||
|----------|--------------------------|
|起動タイプ|EC2|
|タスク定義|先ほど作成したタスクを選択|
|タスクの数|1|

「タスクの実行」をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/c6ac5e0e-e67b-2587-3735-85ce4caa56aa.png)


このように「ステータス」が「RUNNING」になれば成功です！

ここが「STOPPED」になる場合は、おそらくタスク定義の設定ミスかDocker自体に問題があります。

もう一度見直してみてください。

#ページの表示#

最後にサービス検索欄から「EC2」と検索

左のメニューから「ロードバランシング」にある「ロードバランサー」をクリック

先ほど作成したALBの「説明」にあるDNS名をコピーしてアクセスするとLaravelのトップ画面が表示されます！！

もし、下記のような画面が出ても大丈夫です！

気にせず先へ進んでください！

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/142e02aa-3283-36aa-8b68-401f85fc96da.png)


##502エラー##

ここで、502エラーが表示される場合は、「ターゲットグループ」から先ほどALBを作成する際に作成したグループを選択し、「Targets」の欄が空の状態になっていると思います。

なので、ページ①のインスタンスを追加してあげてください。

すると表示されます！

#SSH接続#

SSH接続を行うためには、22番ポートからのアクセスを許可しなければいけません。

まずはその設定を行います。

##22番ポートの受付##

ページ①のセキュリティタブをクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/5cdf6218-9abe-b89f-66e2-f8ab90ec1319.png)

その中のセキュリティグループをクリック

「インバウンドルールを編集」をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/60e2e8ac-51ad-3a96-5838-999950285017.png)

このようにして「SSH」を追加してください。

<font color="red">※注意 HTTPの設定も残してください！</font>

そしたらコマンドプロンプトにて以下のコマンドを実行。

ただし、Macユーザーは先ほどダウンロードしたキーファイルの権限を変更してください。

```
chmod 400 xxx.pem
```

Windowsユーザーの方はこの動作はいりません。

```
ssh -i "C:~\example-ec2-key.pem" ec2-user@ec2-xxx.ap-northeast-1.compute.amazonaws.com
```

``ec2-xxx.ap-northeast-1.compute.amazonaws.com``は、ページ①の「パブリック IPv4 DNS」をコピペしてください。

```

   __|  __|  __|
   _|  (   \__ \   Amazon Linux 2 (ECS Optimized)
 ____|\___|____/

For documentation, visit http://aws.amazon.com/documentation/ecs
6 package(s) needed for security, out of 13 available
Run "sudo yum update" to apply all updates.
```

このような画面がでればOK！！

試しにコンテナにアクセスしてみましょう！

```
docker ps
```

``app``コンテナの**CONTAINER ID**をコピーしてください。

そして以下のコマンドを実行

```
docker exec -it CONTAINER ID bash
root@***:/work/laravel#
```

こんな感じでコンテナには入れたらOK！

試しに``ph artisan``コマンドを打って見て下さい！

```
root@61e66fbbc18e:/work/laravel# php artisan list
```

ちゃんと表示されましたか？？

抜け出すには``exit``と入力して下さい。

#番外編#

###No application encryption key has been specifiedエラー###

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/69e9e49f-8ad4-1b60-036e-dc947b7d2a66.png)


先ほどこのような画面になってもOKと言いました。

でも実際はダメですよね（笑）。

なので、このエラーを解決しましょう！！

まず、このエラーはAPP_KEYが無いよと言う意味です。

Laravelを振れたことがある方は一度は見たことのあるエラーですね。

じゃあキーを発行してあげましょう！

まずはコンテナに入るためにSSH接続

```
ssh -i "C:~\example-ec2-key.pem" ec2-user@ec2-xxx.ap-northeast-1.compute.amazonaws.com
```

```
root@61e66fbbc18e:/work/laravel# php artisan key:generate --show
base64:***
```

ここで、タスク定義を行うときに環境変数を設定したのを覚えていますか？

AWS上では``.env``ファイルではなく、``.env.production``を読み込ませるために``.env.production``を作成しましょう！

```
root@61e66fbbc18e:/work/laravel# touch .env.production
```

そしたら、``.env.example``の内容を``.env.production``にコピペして``APP_KEY``のところだけ今発行したキーを貼り付けましょう。

最後にビルドしてプッシュしてください！

```
docker build -t app -f docker\php\Dockerfile .
docker tag app:latest 905806622013.dkr.ecr.ap-northeast-1.amazonaws.com/app:latest
docker push 905806622013.dkr.ecr.ap-northeast-1.amazonaws.com/app:latest
```

``app``コンテナだけで大丈夫です！

無事プッシュが完了したら、クラスターから先ほどと同じようにタスクの実行を行い（前のタスクは停止してください）、「ロードバランサー」のDNSに接続して見て下さい！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/67754e49-6a5a-476c-dbf7-a4676f919e86.png)

見慣れた画面。。。

ここまで長かった。。。。。。

#2021/02/27 追記 RDS(MySQL)への接続#

作成したLaravelとMySQLのデータベースの連携方法を以下の記事にまとめたので、興味のある方は是非ご覧ください！

[【AWS】EC2インスタンスで作成したLaravelとRDS（MySQL）を連携させる方法](https://qiita.com/akki-memo/items/05c975f76dc62c6821d2)

いかがだったでしょうか？？？

大変ですよね（笑）

ただこの記事を参考にして少しでも時間を削って皆様のお役に立てればなと思います。

ページの最後に参考記事を載せておくので、なにか分からない点がありましたらそちらの記事をご覧ください。

また、何かご不明な点があれば遠慮なくコメント欄にてお申し付けください。

大変でしたが達成感はあると思います！！

これを励みに皆さん勉強頑張りましょう！！！！！

つい最近「ココナラ」で環境構築のお手伝いをするサービスを始めました。

気になる方はぜひ一度ご相談ください！

以上、「【AWS】Dockerで作ったLaravelプロジェクトをECR、ECS、EC2を使ってAWS上で動かしてみた」でした！

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい！！

Thank you for reading

#参考記事#

・[【AWS】初めてのECR](https://qiita.com/3utama/items/b19e2239edb6996a735f)
・[【Docker × ECS入門】docker compose upでECSデプロイ](https://qiita.com/Rubyist_SOTA/items/1ead200bf602569804ea)
・[LaravelアプリケーションをローカルでもAWSでもDockerで動かす](https://qiita.com/imunew/items/1e4826030d725beb4710)
・[ALB（Application Load Balancer）を導入する](https://qiita.com/kobayashi-m42/items/9f6dd14ad1e4dc7cab4c)
・[LaravelアプリケーションをAWS上のDockerで動かす](https://qiita.com/kobayashi-m42/items/95ba3f611b3def27241e)
・[LaravelアプリをDockerfile, ECR, ECS, RDSを使用してデプロイする。](https://qiita.com/ken-s/items/3548568eaff4e60c2cb9)