👏

【AWS】Laravel9 * Vue * Nginx * RDS * Fargateの構築

2022/09/29に公開

Laravel9 * Vue * NginxをFargateで構築するまでの手順

ディレクトリ構造

❯ tree -L 3
.
├── docker
│   ├── laravel
│   │   ├── Dockerfile
│   │   └── php.ini
│   ├── mysql
│   │   ├── Dockerfile
│   │   └── my.cnf
│   └── nginx
│       ├── Dockerfile
│       ├── ecs
│       └── local
├── Dockerfile(laravel install用)
├── docker-compose.prod.yml
├── docker-compose.yml
├── ecs-task-definition-for-web.json
└── src

ファイルの中身確認

docker-compose.yml

  • nginxの- ./src:/applicationがないとVueをインストール後に実施するnpm install && npm run devが失敗する
version: '3'
services:
  nginx:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    container_name: nginx_ecs
    volumes:
        - ./src:/application
        - ./docker/nginx/local/conf.d/default.conf:/etc/nginx/conf.d/default.conf
    ports:
        - 8082:80
    expose:
        - 8082
    depends_on:
        - laravel
    environment:
      - DB_CONNECTION=mysql
      - DB_HOST=db
      - DB_PORT=3306
      - DB_DATABASE=${DB_NAME}
      - DB_USERNAME=${DB_USER}
      - DB_PASSWORD=${DB_PASSWORD}
      - "TZ=Asia/Tokyo"

  laravel:
    build:
      context: .
      dockerfile: ./docker/laravel/Dockerfile
    container_name: laravel_ecs
    volumes:
        - ./src:/application

  db:
    build:
      context: .
      dockerfile: ./docker/mysql/Dockerfile
    container_name: mysql_ecs
    ports:
      - ${DB_PORT}:3306
    environment:
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      TZ: 'Asia/Tokyo'
    volumes:
      - mysql-volume:/var/lib/mysql

volumes:
  mysql-volume:

docker/nginx/Dockerfile

FROM node:16 as node-build

COPY ./src /application
WORKDIR /application

RUN apt-get update \
		&& npm install \
		&& npm run prod

FROM nginx:1.19.2

WORKDIR /application

RUN apt-get update

# install Node.js
COPY --from=node-build /usr/local/bin /usr/local/bin
COPY --from=node-build /usr/local/lib /usr/local/lib
COPY ./docker/nginx/ecs/conf.d/default.conf /etc/nginx/conf.d/default.conf
COPY --from=node-build /application/public /application/public/

COPY ./src/package*.json /application/
COPY ./src/public /application/public

EXPOSE 80

docker/nginx/local/conf.d/default.conf

upstream php-fpm {
    server laravel:9000;
}

server {
    listen 80;
    server_name localhost;
    root /application/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off;}
    location = /robots.txt { access_log off; log_not_found off;}

    error_page 404 /index.php;
    
    location ~ \.php$ {
        fastcgi_pass php-fpm;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
    location ~ /\.(?!well-known).* {
        deny all;
    } 
}

docker/mysql/Dockerfile

FROM mysql/mysql-server:8.0

COPY ./docker/mysql/my.cnf /etc/my.cnf

docker/mysql/my.cnf

[mysqld]
# character
character_set_server = utf8mb4
collation_server = utf8mb4_0900_ai_ci

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# Error Log
log-error = mysql-error.log

# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0

# General Log
general_log = 1
general_log_file = mysql-general.log

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

docker/laravel/Dockerfile

FROM php:8.1-fpm-bullseye

COPY ./docker/laravel/php.ini /usr/local/etc/php/php.ini
COPY ./src /application
WORKDIR /application

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

RUN apt-get update && apt-get install -y \
		git \
		zip \
		unzip \
		&& composer install \
		&& docker-php-ext-install pdo_mysql bcmath

RUN php artisan cache:clear \
		&& php artisan config:clear \
		&& php artisan route:clear \
		&& php artisan view:clear
RUN chown -R www-data:www-data storage

docker/laravel/php.ini

[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"

Laravelのインストール

Laravelインストール用のDockerファイルを作成

以下のLaravelインストール用のDockerfileを作成してコマンド実行

docker build -t composer -f Dockerfile . 

Dockerfile

# 初回laravel install用
FROM php:8.1-fpm-bullseye
WORKDIR /application
RUN apt-get update && apt-get install -y \
            git \
            zip \
            unzip \
    && curl -sS https://getcomposer.org/installer | php \
    && mv composer.phar /usr/local/bin/composer

Laravelのインストール

docker run -v $(pwd):/application composer composer create-project --prefer-dist laravel/laravel src "9.1.*"

Localで動くか確認する

docker-compose up -d --build 
docker-compose exec app php artisan migrate:fresh
→http://localhost:8082が表示されればOK

docker-compose exec db mysql -u root -p
→DBに入れるかも確認

JetstreamとVueのインストール

docker-compose exec laravel composer require "laravel/jetstream=2.6.6"
("" は書き換えたほうがいい)
docker-compose exec laravel php artisan jetstream:install inertia
docker-compose exec nginx npm install -g npm@8.19.2
docker-compose exec nginx npm run dev

AWSの設定

  • VPC
  • サブネット(計4つ)
    • public2つ(web用)
    • private2つ(DV用)
  • ルートテーブル
    • web用
      • ルート設定
        • 0.0.0.0/0
        • ターゲットをig(インターネットゲートウェイ)
      • サブネットの関連付け
        • publicサブネット2つ
    • DB用
      • ルート設定
        • そのまま
        • ターゲットをig(インターネットゲートウェイ)
      • サブネットの関連付け
        • privateサブネット2つ
  • インターネットゲートウェイ
    • VPCに紐づける
  • セキュリティグループ(計2つ)
    • web用(public subnet2つを選択)
      • インバウンドルール
        • HTTP:0.0.0.0/0と::/0
        • HTTPS:0.0.0.0/0と::/0
        • SSH:0.0.0.0/0
    • DB用(private subnet2つを選択)
      • インバウンドルールにweb用のセキュリティグループを選択する
  • サブネットグループの作成
    • DB用に作成したprivateサブネットを2つ選択
  • RDSの作成
    • サブネットグループ:作成したDB用サブネットグループ
    • パブリックアクセス可能:なし
    • VPCセキュリティグループ:既存の選択
    • 既存のVPCセキュリティグループ:データベース用に作成したセキュリティグループ
    • アベイラビリティゾーン:ap-northeast-1a
    • データベースポート:3306
      ** - 最初のデータベース:db_ecs(重要!!)
      **
  • Route53
    • レコード名空白
    • レコードタイプ:A
    • エイリアス:選択
    • トラフィックのルーティング先:ALB,東京リージョン、先程作成したALBを選択
    • ルーティングポリシー:シンプルルーティング
  • SystemManager
    • パラメーターストアで秘匿情報を登録
    • 3つ作成
    • /ecs/db-username
      • 安全な文字列
      • 現在のアカウント
      • alias/aws/ssm
      • admin(RDS作成時に設定したユーザー名)
    • /ecs/db-password
      • 安全な文字列
      • 現在のアカウント
      • alias/aws/ssm
      • RDS作成時に設定したパスワード
    • /ecs/db-host
      • 安全な文字列
      • 現在のアカウント
      • alias/aws/ssm
      • 先程作成したRDSのエンドポイントを入力
  • ECR
    • laravelとnginxのリポジトリを作ってpush
    • M1Macなのでplatformの指定をする
docker build -t ecs-hands-on/nginx:latest -f ./docker/nginx/Dockerfile --platform amd64 .

docker build -t ecs-hands-on/laravel:latest -f ./docker/laravel/Dockerfile --platform amd64 .
  • ECSのクラスター
    • ネットワーキングのみ
  • ECSのタスク
    • ecs-task-definition-for-web.jsonを作成する
    • aws ecs register-task-definition --generate-cli-skeletonでテンプレを取得できる
  • ECSのタスク定義の登録
    • aws ecs register-task-definition --cli-input-json file://ecs-task-definition-for-web.json
    • 環境
      • エントリポイント:sh,-c
      • コマンド:php artisan config:cache && php artisan view:cache && php artisan route:cache && php artisan migrate --force && chmod -R 777 storage/* && php-fpm && composer install --optimize-autoloader --no-dev
      • 作業ディレクトリ:/application
  • ECSのサービス
$ aws ecs run-task \
--cluster ecs-hands-on \
--task-definition ecs-hands-on-for-command \
--overrides '{"containerOverrides": [{"name":"laravel","command": ["sh","-c","php artisan print:helloworld"]}]}' \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[<作成済みのサブ ネットID>],securityGroups=[<作成済みのセキュリティグループID>],assignPublicIp=ENABLED}"
  • ECSのサービスの更新
aws ecs update-service --cluster <クラスター名> --service <サービス名> --task-definition <タスク定義名>

エラー集

npmがないというエラー

OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "node": executable file not found in $PATH: unknown

原因①

  • nginxコンテナにnpmのインストールができていないのが原因
  • ちゃんと以下の内容を記載して、nodeとnpmをインストールする
# install Node.js
COPY --from=node-build /usr/local/bin /usr/local/bin
COPY --from=node-build /usr/local/lib /usr/local/lib

原因②

  • docker-compose.ymlでファイルのマウントできていないためdockerコンテナの中に入っても必要なpackage.jsonなどのファイルがないのが原因
volumes:
  - ./src:/application

Laravel9.*と指定するとエラーになる

以下コマンド実行時にdocker-compose up -dが失敗する

docker run -v $(pwd):/application ecs-hands-on/composer:latest composer create-project --prefer-dist laravel/laravel src "9.*"

原因

  • npm run prodとpackage.jsonに記載がないコマンドを実施していたため
  • laravel9.*としてViteをインストールする場合は、npm run buildにする
RUN apt-get update \
		&& npm install \
		&& npm run build //ここをbuildにする

npm installがエラーにならないが、インストールされない

❯ docker-compose exec nginx npm run prod

> prod
> npm run production


> production
> mix --production

[webpack-cli] Error: Cannot find module '/application/webpack.mix'
Require stack:
- /application/node_modules/laravel-mix/setup/webpack.config.js

原因

  • 先ほどと同じだが、docker-compose.ymlでマウントする
    volumes:
        - ./src:/application //これ
  • dockerコンテナの中に入ってlocalと似ているか確認すべき
❯ docker-compose exec nginx bash       
root@a3fd8208c548:/application# ls
README.md      config             phpunit.xml         tests
app            database           public              vendor
artisan        lang               resources           webpack.config.js
bootstrap      node_modules       routes              webpack.mix.js
composer.json  package-lock.json  storage
composer.lock  package.json       tailwind.config.js
root@a3fd8208c548:/application# 

DBに接続できない場合

  • php.iniの設定
  • Dockerfileで&& docker-php-ext-install pdo_mysql bcmath の記載
  • src->.envにDB情報の記載

ECRプッシュ時にDockerへログインできない

  • IAMの設定が正しくない可能性があるので、設定する
  • アクセスキーはIAMから取得する
aws configure --profile fargate
-- アクセスキー
-- シークレットアクセスキー
-- ap-northeast-1
-- json

export AWS_PROFILE=fargate //これ大事
aws configure list
→更新されている
  • これで確認できる
❯ cat ~/.aws/config

ECRへプッシュができない

  1. すべてのDockerのイメージやコンテナを削除する
  2. 以下のコマンド(セキュリティ的に悪いかは調べるべき)
export DOCKER_BUILDKIT=0
export COMPOSE_DOCKER_CLI_BUILD=0
export DOCKER_CONTENT_TRUST=0

ECRへプッシュができない②

standard_init_linux.go:228: exec user process caused:
standard_init_linux.go:228: exec user process caused: exec format error
  • ECRのプッシュ時にplatfomeの指定をする必要がある

RDSと接続できない

原因①

  • passwordの設定を間違えている!!!!!!!!!

参考書類

使って学ぶAWS ECS入門 FargateとLaravelでコンテナデプロイを体験
AWS FargateにRailsアプリケーションをデプロイする手順
Laravel 9 を ECS on Fargateで構築

Discussion