AWSで割とモダンな技術使う大学生日記①

## 前置き
terraformで構成しようとしたが、まずはAWSを理解するためコンソールから構築
本記事は私自身が少し悩んだ、ネットワークの構成やトラブルシューティングについて重点的に記載されております。ハンズオン形式の記事はQiita等にも多くございますのでそちらを参照しつつ、こちらの記事でより理解を深めていただければな、と思います。


|使用技術             |
|:-----------------------------------|
|Laravel6.x                          |
|AWS ECS(Fargate), RDS, ElasticCache |
|Docker, docker-compose              |

## ネットワーク構成
VPCは10.1.0.0/16を利用。
サブネットの利用感は下記みたいな感じ

| サブネット       |       用途 |
|:-----------------|------------------:|
| 10.1.0.0/24, 10.1.1.0/24|Publicサブネット。LBに使うサブネット               |
| 10.1.2.0/24, 10.1.3.0/24|Privateサブネット。LBからECSに行くサブネット        |
| 10.1.4.0/24, 10.1.5.0/24|ECS~RDS間のサブネット。ECSからDBに接続するサブネット |
| 10.1.6.0/24             |EC2~RDS間のサブネット。EC2からDBに接続するサブネット |

migrationはDockerfileには置かないのでDB接続用のEC2インスタンスを用意する。
(userのデータとかも取得できるので)

ルートテーブルでInternetGatewayと接続するのはLBとDB接続用のEC2インスタンスのみ。
appサーバーとDBサーバー(RDS)はローカルでしか接続できないように設定する。

<font color="Red">
※ネットワークの軽い知識があれば上記のサブネットや、CIDRを理解できます。
30分とかで理解できると思うのでYouTubeとかでおさらいしておいてください。
</font>


## Fargateタイプにおいてのコンテナ間通信について
webコンテナは`Nginx`(読み方はエンジンエックスっていうらしい、最近までエヌジンクスって読んでた)を利用していてappコンテナは`PHP`でフレームワークは`Laravel`。
そのためwebコンテナからappコンテナへの通信が必要。
ローカル内では下記のように`.conf`ファイルを記述する。
ポートはwebはHTTP通信のため`80`、appは`9000`。
(docker-compose.ymlで名前をそれぞれ`web`と`app`で指定しておく)

```default.conf
01:server {
02:    listen 80;
03:    root /work/public;
04:    index index.php;
05:    charset utf-8;
06:
07:    location / {
08:        root /work/public;
09:        try_files $uri $uri/ /index.php$is_args$args;
10:    }
11:
12:    location ~ \.php$ {
13:        fastcgi_split_path_info ^(.+\.php)(/.+)$;
14:        fastcgi_pass app:9000;
15:        fastcgi_index index.php;
16:        include fastcgi_params;
17:        fastcgi_param SCRIPT_FILENAME /work/public/index.php;
18:        fastcgi_param PATH_INFO $fastcgi_path_info;
19:    }
20:}
```

上記のような設定だとAWS ECSのFargate起動タイプだとヘルスチェックで
`Taskが実行できません。`
というエラーが出ます。そこで`14行目`を下記のように変更します。

```default.conf
14:        fastcgi_pass localhost:9000;
```

上記のようにする理由としては、Fargate起動タイプは同じローカルネットワーク内でtaskが実行されるためlocalhostは共有している。そのためコンテナ間通信は`localhost:ポート番号`で指定する。


## トラブルシューティング
#### ヘルスチェックが通らない。
ECSを起動する際に、ヘルスチェックが通らず、taskが実行できなかったとき。
Dockerfileへの知見が甘かったため、
待つポート番号やコピーするディレクトリを間違えていたりした。
Dockerfileの最後の行に`EXPOSE <ポート番号>`を指定すること。
また、ローカルでコンテナを起動し期待するディレクトリにソースが入っているかを確認するといい。

#### ヘルスチェックは通るがLBのDNSに接続してもログインページが表示されない
ECS起動後にヘルスチェックが通るがログインページは表示されない場合。

```
ヘルスチェックは通っている
          ↓
ローカル内での通信はできている
          ↓
ネットワークの構成を確認し、落ちているところを確認
```
主に原因はネットワーク。
SGのインバウンドルールは適切か。ルートテーブルは適切か。再度確認する。
この過程が割と大事でCloudFomationとかで構成すると割と曖昧になりがちなネットワーク構成を理解できる。私の場合は、LBのターゲットグループを複数作成しており、利用したいターゲットグループの優先順位が一番ではなかったため、ログインページが表示されていなかった。

### セッションの共有
可用性のために、複数のタスクを実行し、AutoScallingをオンにしていると思うので
セッションの共有をしておかなければいけない。
`Laravel`のセッションはデフォルトで`file`を選択しており`Project/storage/sessions`に保存されている。実行されるタスクが複数でセッションの保存先が`file`の場合、それぞれのappコンテナの中に保存されてしまうので、セッションの共有をしなければいけない。
今回は、AWSのマネージドサービスである`ElasticCache`を利用する。

<font color="Red">
※実装の工数上、現状(2020/09/06)では未構築のため後日追記する。
その際には上記のネットワーク構成にも修正を加える予定。
</font>


## Dockerfileとdocker-compose.yml
いや、GitHub載せろよ。って思うよね。
僕的に、Qiita見ながらコピペできるのが理想なので`Dockerfile`と`docker-compose.yml`はここに書きます。`default.conf`は上記に記載済み。Fargate起動タイプの時は`localhost`指定してください。
ディレクトリ構成も載せておきます。

```ディレクトリ構成
|
├── docker
|   |
│   ├── nginx
|   |   ├── Dockerfile
│   │   └── default.conf
│   └── php
│       ├── Dockerfile
│       └── php.ini
├── docker-compose.yml
|
├── .env
|
├── .gitignore
|
└── live
```

```docker-compose:docker-compose.yml
version: "3"
services:
  app:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
      args:
        - TZ=${TZ}
    ports:
      - ${APP_PORT}:9000
    volumes:
      - ./live:/work
      - ./logs:/var/log/php
      - ./docker/php/php.ini:/usr/local/etc/php/php.ini
    working_dir: /work
    environment:
      # ここは要設定
      - DB_CONNECTION={DB_CONNECTION}
      - DB_HOST=${DB_HOST}
      - DB_DATABASE=${DB_DATABASE}
      - DB_USERNAME=${DB_USERNAME}
      - DB_PASSWORD=${DB_PASSWORD}
      - TZ=${TZ:-Asia/Tokyo}

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

volumes:
  db-store:

```

```dockerfile:docker/nginx/Dockerfile
FROM nginx:1.19-alpine

COPY ./docker/nginx/default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
```

```dockerfile:dokcer/php/Dockerfile
FROM php:7.4-fpm-alpine

ARG PSYSH_DIR=/usr/local/share/psysh
ARG PHP_MANUAL_URL=http://psysh.org/manual/ja/php_manual.sqlite

ARG TZ

ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer

RUN set -eux && \
  apk update && \
  apk add --update --no-cache --virtual=.build-dependencies \
    autoconf \
    gcc \
    g++ \
    make \
    tzdata && \
  apk add --update --no-cache \
    icu-dev \
    oniguruma-dev \
    libzip-dev && \
  cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
  echo ${TZ} > /etc/timezone && \
  pecl install xdebug && \
  apk del .build-dependencies && \
  docker-php-ext-install intl pdo_mysql mbstring zip bcmath && \
  docker-php-ext-enable xdebug && \
  mkdir $PSYSH_DIR && wget $PHP_MANUAL_URL -P $PSYSH_DIR && \
  curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer && \
  composer config -g repos.packagist composer https://packagist.jp && \
  composer global require hirak/prestissimo

RUN apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev && \
  docker-php-ext-configure gd \
    --with-jpeg=/usr/include/ \
    --with-freetype=/usr/include/ && \
  NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) && \
  docker-php-ext-install -j${NPROC} gd && \
  apk del --no-cache freetype-dev libpng-dev libjpeg-turbo-dev


RUN chmod -R 777 /work/storage \
        /work/bootstrap/cache

RUN php artisan key:generate
RUN php artisan config:clear
RUN php artisan config:cache
# RUN php artisan migrate
# RUN php artisan db:seed

EXPOSE 9000
```

php.iniは自分で検索してください。笑
`.env`ファイルがないと起動できないので各々の用途に合わせて記述してください。
一応下記に書いておきます。

```.env
TZ=Asia/Tokyo
APP_PORT=9000
DB_CONNECTION=mysql
DB_HOST=<RDSのエンドポイント>
DB_PORT=3306
DB_DATABASE=<DB名>
DB_USERNAME=<username, rootでもいい>
DB_PASSWORD=<セキュアなやつにしてね>
```

以上です。
まだ、CI/CDとかできていないので別記事にでも書こうと思っております。
AWS第一弾でした。
`docker/php/Dockerfile`は割と書きすぎてる気がするのでこれいらんよ、とかあったら教えて欲しいです。未熟者の記事ですが読んでいただいてありがとうございます。