【ソース有】【チュートリアル】ECSでlaravel(11)をSQLiteでとり急ぎ動作させる(改訂版) 〜 運用編
ここからproductionレベルで運用可能なようにサイクルを考えてみる。
本稿のまとめ
- EC2に適切なロールとポリシーをセットし、AWSキーを使わないようにしよう
- ECRの操作には適切な名前空間に限定したポリシーの割当てにする
-
aws sts get-caller-identity
で状態を確認する
-
pingcrm
というアプリをcloneし、ビルドしてECS Fargateで動作させてみる練習をしよう- 欠損モジュールをビルドする(
exif
,gd
)
- 欠損モジュールをビルドする(
- タスク定義をコマンドからIn/Outしよう
- 必要な権限の追加
-
aws ecs describe-task-definition --task-definition <定義名> --query taskDefinition --output json
-
jq
を用いた不要エントリの削除
-
aws ecs register-task-definition --cli-input-json file://task-definition.json
- larvelのエラーを意図的に作り出し、エラーが出ないようにしよう
ビルド環境の構築
ここではpingcrm
というinertia.jsで使われているデモをECSで動かすという事を目標にしてみよう。まあ、前回でlaravelアプリを動作させられているので、これといって難しさは無いと思うんだがモジュールのビルドなど、動作に少しコツが必要だ。
よりセキュアに
先ず、現在はEC2を使ってECRにイメージをプッシュしている環境である。ここでAWSキーを使って通信しているが、これは実は必要がない。
ここではキーを使わないデプロイ(等)の環境を設定していく
ECRリポジトリの作成
以下の2つのリポジトリを作成する
practice/pingcrm-nginx
practice/pingcrm-php-fpm
作業用EC2にロールを割り当てキーを抹消する
ここで手順通りに来ていると作業にて使うEC2
インスタンスが1つ起動していると思うがこのEC2からここのリポジトリにアクセスするためのroleを作る
ロールへ移動
ロールを作成
すると「信頼されたエンティティを選択」となる。信頼されたエンティティて...
これは何かというと、簡単にいえばここで作成するロールをどのサービスで使えるかを決めるという事だ。ここではEC2からこのロールを使うので当然EC2を選ばなくてはいけない
このようになっていれば「次へ」を押す
すると許可を追加
になるんだけど、まだ許可を考えていなかったので何もおさずに次へを押す。これは後からでも当てられるので、とりあえずガワを作るイメージ
そしてロール名をECSPracticeManagementRole
として保存する
とりあえずロールができる
ポリシーを作る
ポリシーの作成
とりあえずビジュアルモードで作っていく。registryみたいに入力するとElastic Container Registry
が選択できる
リストと読み取りは全部付けといて、書き込みはこの4点とする
こんな感じで
ap-northeast-1
pracitice/*
とした。practice名前空間をどうにかして当てたかったのはここに理由がある。
ECRPracticeManagePolicy
として保存した
ロールにポリシーを当てる
さきほど作成したポリシーが空のロールに
許可を追加して、ポリシーをアタッチする
EC2にロールを当てる
そしたら、起動中のEC2の設定に戻りセキュリティー→ IAMロールの変更をする
ECSPracticeManagementRoleを当てた
ここでずらずらと列挙されているロールこそが「信頼されたエンティティ」で結ばれているロールであって、ロールは多数作る事はできてもEC2と信頼関係がないとここに出てこないってこと。リレーションみたいなもんすね。
アクセスキーを無効化
そうするともう practice/ を触る事に関してはキーが必要ない。AmazonEC2ContainerRegistryFullAccess
という物騒な権限がついているアカウントのキーを無効にする
まあ無効化後削除しといていいと思う
EC2でロールの確認とECR操作
aws sts get-caller-identity
でまずロールを確認する
よさそう
そしたらログインする
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ****.dkr.ecr.ap-northeast-1.amazonaws.com
ログインできている
権限の詳細はまだ確認していないけどログインできている。まあ前にやった奴を取得チェックしてみてもいいだろう。
aws ecr describe-images --repository-name practice/my-ecs-nginx --region ap-northeast-1
practice/ 以下操作できるようにしてあるので、これは通るはずだ(practiceじゃないやつもやってみた方が万全だけど)
イメージの構築
前回同様さっさか構築していく。workingディレクトリつくってgit init
して
git submodule add https://github.com/inertiajs/pingcrm.git pingcrm
git submodule update --init --recursive
みたいな感じでinertiajs/pingcrm
を引きこんでくる。少し改変したsrv/Dockerfile
srv/Dockerfile
# --------------------------------------
# Stage 1: frontend build stage
# --------------------------------------
FROM node:18-alpine AS frontend-builder
WORKDIR /laravel
COPY ./pingcrm /laravel
RUN npm install && npm run build
RUN mkdir /public && cp -a public/* /public
# --------------------------------------
# Stage 2: PHP-FPM
# --------------------------------------
FROM php:8.4-fpm AS php-fpm
WORKDIR /srv
COPY ./pingcrm /srv
COPY /public /srv/public
RUN cp .env.example .env
RUN chown -R www-data:www-data storage database
RUN apt-get update && apt-get install -y \
git \
unzip \
zip
COPY /usr/bin/composer /usr/bin/composer
RUN composer install
EXPOSE 9000
# --------------------------------------
# Stage 3: Nginx
# --------------------------------------
FROM nginx:alpine AS nginx
WORKDIR /usr/share/nginx/html
COPY ./srv/default.conf /etc/nginx/conf.d/default.conf
COPY /srv/public/ /usr/share/nginx/html
EXPOSE 80
これでビルドできるはず、と思い
docker build -t pingcrm-php-fpm -f ./srv/Dockerfile --target=php-fpm .
すると、モジュールのエラーになる
nstalling dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Your lock file does not contain a compatible set of packages. Please run composer update.
Problem 1
- Root composer.json requires PHP extension ext-exif * but it is missing from your system. Install or enable PHP's exif extension.
Problem 2
- Root composer.json requires PHP extension ext-gd * but it is missing from your system. Install or enable PHP's gd extension.
To enable extensions, verify that they are enabled in your .ini files:
- /usr/local/etc/php/conf.d/docker-fpm.ini
- /usr/local/etc/php/conf.d/docker-php-ext-sodium.ini
You can also run `php --ini` in a terminal to see which files are used by PHP in CLI mode.
Alternatively, you can run Composer with `--ignore-platform-req=ext-exif --ignore-platform-req=ext-gd` to temporarily ignore these required extensions.
The command '/bin/sh -c composer install' returned a non-zero code: 2
これはexif
エクステンションが無いといっている。この辺解説が面倒なのでDockerfileをアップデートしておきました。まあ詳細は必要に応じてAIにでも聞いてみて
# --------------------------------------
# Stage 1: frontend build stage
# --------------------------------------
FROM node:18-alpine AS frontend-builder
WORKDIR /laravel
COPY ./pingcrm /laravel
RUN npm install && npm run build
RUN mkdir /public && cp -a public/* /public
# --------------------------------------
# Stage 2: PHP-FPM
# --------------------------------------
FROM php:8.4-fpm AS php-fpm
WORKDIR /srv
COPY ./pingcrm /srv
COPY /public /srv/public
RUN cp .env.example .env
RUN chown -R www-data:www-data storage database
RUN apt-get update && apt-get install -y \
git \
unzip \
zip \
libjpeg-dev \
libpng-dev \
libfreetype6-dev \
libz-dev
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install gd exif
COPY /usr/bin/composer /usr/bin/composer
RUN composer install
EXPOSE 9000
# --------------------------------------
# Stage 3: Nginx
# --------------------------------------
FROM nginx:alpine AS nginx
WORKDIR /usr/share/nginx/html
COPY ./srv/default.conf /etc/nginx/conf.d/default.conf
COPY /srv/public/ /usr/share/nginx/html
EXPOSE 80
aptで必要なビルドライブラリーのインストールとdocker-php-ext-install
コマンドでモジュールを作成している。これはdockerイメージ内でphpモジュールのビルドをするのに用意されているコマンド(シェルスクリプト)である。この辺も必要に応じてAIに尋ねるといいだろう。
いずれにせよこれでdocker-compose.ymlで起動する。ここでもイメージの指定は書き換えてある。
一見良さそうではあるが
sqlite
データベースが無いといわれる。まあそうだろう。そもそもsqlite
なんてのはましてECSでは絶対使わないからね。
ってわけでイリーガルな対応ではあるけど、ここで作る
# ...
COPY --from=composer /usr/bin/composer /usr/bin/composer
RUN composer install
RUN touch database/database.sqlite
RUN php artisan migrate:fresh --seed
EXPOSE 9000
# ...
これで起動するはずだ。
あとは前回と同じ手順でECSにデプロイして欲しい
docker tag pingcrm-php-fpm ****.dkr.ecr.ap-northeast-1.amazonaws.com/practice/pingcrm-php-fpm:latest
docker tag pingcrm-nginx ****.dkr.ecr.ap-northeast-1.amazonaws.com/practice/pingcrm-nginx:latest
docker push ****.dkr.ecr.ap-northeast-1.amazonaws.com/practice/pingcrm-php-fpm:latest
docker push ****.dkr.ecr.ap-northeast-1.amazonaws.com/practice/pingcrm-nginx:latest
pushされとる
【必須作業】タスク定義のIn/OutをJSONで行う
タスク定義というのは基本的に初回こそwebインターフェースでセットするのであるが、その後はJSONを書き換えて再投入という流れになるのが普通、というわけでこれをEC2から行う。
aws ecs describe-task-definition --task-definition <TASK_DEFINITION_NAME> --query taskDefinition --output json > task-definition.json
ここで
pingcrmというタスク定義をひっぱってくるには
aws ecs describe-task-definition --task-definition pingcrm --query taskDefinition --output json
なのだが
こんな感じでエラーになる。ここまででロールを作成してきているから、ここに新たなポリシーを割り当てて実行する。 https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-1#/policies から作成
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:DescribeTaskDefinition"
],
"Resource": "*"
}
]
}
こういうのはもう直接jsonで作成した方が間違いも少ないかと思う。
ECSPracticeServiceManagerPolicy
とした。作成したら正しくroleに割り当てること
これで
aws ecs describe-task-definition --task-definition pingcrm --query taskDefinition --output json > task-definition.json
としてtask-definition.jsonを作成した
jsonのimport
このjsonを何の変更もなしにimportしてみると
aws ecs register-task-definition --cli-input-json file://task-definition.json
これで出来る、しかし
このような余計なものが付いているのでjq
でカットする。
aws ecs describe-task-definition --task-definition pingcrm --query taskDefinition | jq 'del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .compatibilities, .registeredAt, .registeredBy)' > clean-task-definition.json
そうしたら
aws ecs register-task-definition --cli-input-json file://clean-task-definition.json
でimportできそうだが
もちろん権限不足なのでポリシーを変更する
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:DescribeTaskDefinition",
"ecs:RegisterTaskDefinition"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::****:role/ecsTaskExecutionRole"
}
]
}
こんな感じでecs:RegisterTaskDefinition
を追加しあとiam:PassRole
ってのも追加している。これは権限を使う権限というか...気になったら調査してみてください
現在の問題点
たとえばlaravelのエラーをここで改めて意図的に引き出してみる。
# <snip>
COPY /usr/bin/composer /usr/bin/composer
RUN composer install
#RUN touch database/database.sqlite
#RUN php artisan migrate:fresh --seed
EXPOSE 9000
# <snip>
このようにしてビルド&プッシュすると、前回のようにエラー画面が出るはずだ
これを、さっきのタスク定義を変更するとともに考えていく
まずproduction環境でエラー画面が出るのは言語道断
というわけで、ここでタスク定義にデバッグを表示しないようにセットしサービスを更新してみよう
"environment": [
{
"name": "APP_KEY",
"value": "base64:slBwwE6jbHjJSLHAlWbVvm9WUeqJrAQO/QaMrbdfcuA="
},
{
"name": "APP_DEBUG",
"value": "false"
}
],
こんな感じにしてimport
aws ecs register-task-definition --cli-input-json file://clean-task-definition.json
これで新たなリビジョンがセットされる。なお、登録とリビジョンを表示させる場合シェルスクリプトを用意しておくといいかも
#!/bin/bash
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <task-definition.json>"
exit 1
fi
TASK_DEFINITION_FILE=$1
# AWS CLI の `pager` を無効化
export AWS_PAGER=""
# タスク定義を登録
TASK_DEF_JSON=$(aws ecs register-task-definition --cli-input-json file://"$TASK_DEFINITION_FILE")
# 最新のリビジョンを取得
LATEST_REVISION=$(echo "$TASK_DEF_JSON" | jq -r '.taskDefinition.taskDefinitionArn')
if [ -z "$LATEST_REVISION" ]; then
echo "Error: Failed to register task definition."
exit 1
fi
echo "Latest Task Definition: $LATEST_REVISION"
ここではrev:7が最新である事がわかったりする
サービスの更新
サービスを更新して最新のrevにする
新しいデプロイを強制してもいいけど、基本的にサービスが更新されるとタスクも更新される、はず。
エラー画面が消滅して500になった
今回のまとめと課題
- laravelのエラーが500なのはいいとして何が起きてるのか全くわからない
- サービスの更新を手動でしたくない
- .envに関してファイルを削除する
- そもそも、.envというファイルを本番環境に置かない方がいい
- 全ては環境変数からコントロールできるし、それが正解
- ssm
- そもそも、.envというファイルを本番環境に置かない方がいい
- イメージビルドの自動化
など、まだまだネタが書けそうっすね
Discussion