【ソース有】【チュートリアル】ECSでlaravel(11)をSQLiteでとり急ぎ動作させる(改訂版) 〜 イメージの作成まで
基本的には前回の続き。ただし今回一応サラの環境で出来るようにするから、前のものを読まなくても大丈夫なようにしたい。
必要なこと
- laravelの動作を単体で確認できる環境を作る
- dockerイメージを作る
- dockerイメージの出来をローカルで確認する
- dockerイメージをECRに転送する
- タスク定義作る
- ECS Farget (サービス)でよきにはからう
今回は 2. まで行う
作成環境
EC2のt4g.small
。まあまあ容量食うのでボリュームは20Gほど用意した。
ワーキングディレクトリ
mkdir ecs-laravel
cd ecs-laravel
をワーキングディレクトリとする
1. laravelの動作を単体で確認できる環境を作る
ワーキングディレクトリの下にlaravelアプリケーションを配置する。
laravel.buildからコードを作成する
ここではある程度作られたソースコードをcloneしてもいいのだけど、1から出来るイメージを作るために、敢えてlaravel.build
を用いてお手軽に完成品となるものを作成してみる。
curl -s "https://laravel.build/laravel-app?with=mysql" | bash
実行イメージ
するとworkingディレクトリの下にlaravel-appという名前でアプリケーションの雛形がセットされる
laravelアプリケーションを起動する
ここでは完成品の動作確認をするフェーズなのだが、そもそも完成していないのでこれを完成させていくという事になる。
docker-compose.yamlで不要なmysqlを削除し、.envを変更して起動
cd laravel-app
docker-compose.ymlというのがあるので、以下のようにmysqlの部分をカットする
# 略
IGNITION_LOCAL_SITES_PATH: '${PWD}'
volumes:
- '.:/var/www/html'
networks:
- sail
# depends_on:
# - mysql
depends_on:
- mysql
# mysql:
# image: 'mysql/mysql-server:8.0'
# ports:
# - '${FORWARD_DB_PORT:-3306}:3306'
# environment:
# MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
# MYSQL_ROOT_HOST: '%'
# MYSQL_DATABASE: '${DB_DATABASE}'
# MYSQL_USER: '${DB_USERNAME}'
# MYSQL_PASSWORD: '${DB_PASSWORD}'
# MYSQL_ALLOW_EMPTY_PASSWORD: 1
# volumes:
# - 'sail-mysql:/var/lib/mysql'
# - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
# networks:
# - sail
# healthcheck:
# test:
# - CMD
# - mysqladmin
# - ping
# - '-p${DB_PASSWORD}'
# retries: 3
# timeout: 5s
networks:
sail:
driver: bridge
#volumes:
# sail-mysql:
# driver: local
さらに .env をmysql
からsqlite
に変更する
DB_CONNECTION=sqlite
#DB_CONNECTION=mysql
#DB_HOST=mysql
#DB_PORT=3306
#DB_DATABASE=laravel
#DB_USERNAME=sail
#DB_PASSWORD=password
このように変更する。
なお、こちらの完成品はport8000で動作させるようにするため
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:sBAiRUN+hy/Yp4FglKlR1Saloc8YFRgRkYbgZ3y6sf8=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_PORT=8000 #<---------------------------------------
APP_PORT=8000
を与えている。
この段階で
./vendor/bin/sail up
し、ホスト:8000にアクセスすると
laravelが起動している
となる。Ctrl+C で停止し、よさそうであれば
./vendor/bin/sail up -d
として裏に回すか、あるいは別端末で操作する。
その後
./vendor/bin/sail artisan --version
などして、sail
環境からartisan
が起動できる事を確認する
DBのmigration
./vendor/bin/sail artisan migrate:fresh --seed
と入力してみると
このようになる。
これはdatabase/database.sqlite にデーターが格納されるのだが、laravel.build
から展開した場合はこの辺が割と整っているのでこの辺りを全て使っていく。
laravel breezeの展開
さらに、laravel breezeをインストール、展開する。何故これが必要かというと、laravelのwelcomeページは全てがオールインワンになっており確認としては全く訳に立たないからである。
というわけでサクっとscaffoldする場合breezeが良い。とり急ぎbladeでいいと思う
./vendor/bin/sail composer require laravel/breeze --dev
./vendor/bin/sail artisan breeze:install blade
./vendor/bin/sail artisan migrate:fresh --seed
これで
ログインのフォームが用意される。今回はこれをECS上で確認する事が目標となる。
sail(開発環境)の終了
sailは「開発用のdocker環境」なので、開発が終わったら終了する。ここではbreezeを展開するという開発っぽいのに使っただけだが、必要に応じて起動したり終了してもらえたらよいと思う。
./vendor/bin/sail down
で終了できる
要するに、ここで完成したlaravelアプリケーションをデプロイするという作業を行うわけだ。
2. dockerイメージを作る
ここからいよいよ本題。ここまでは単純にlaravelアプリを作ってきただけ。
なお、ここではnginx
とphp-fpm
を分離して動作させる環境を作成していく。
docker-compose.ymlを作る
ワーキングディレクトリにこのようなdocker-compose.yamlを作る
services:
php-fpm:
image: php:8.4-fpm
networks:
- app-network
nginx:
image: nginx:alpine
ports:
- "80:80"
depends_on:
- php-fpm
networks:
- app-network
networks:
app-network:
こうした時に
ecs-laravel
├── docker-compose.yml # AWSに送信するイメージ生成確認用のdocker-compose
└── laravel-app
└── docker-compose.yml # 開発用のdocker-compose(sailが使う)
このような構成になっているはずである。この状態で
docker-compose up
して一応起動する事を確認する
当該のIP:80にアクセスすると
nginxのトップ画面が表示される
ソースコード入りカスタムイメージを作っていく
現在は
image: php:8.4-fpm
# ...
image: nginx:alpine
などの指定によりオフィシャルイメージ(これはdocker hubから取得される)を「そのまま」 使って起動しているだけに過ぎず、何のソースコードも挿入されていない。これを「カスタム」して自分のイメージを作る作業が本稿の要旨である。
ここでカスタムイメージ用のディレクトリを作成する。基本的にはphp-fpm
とnginx
の2つのイメージを分割して作成した方がわかりやすいので、とりあえず2つディレクトリを切っていく
mkdir php-fpm nginx
1つずつ見ていくことにしよう
nginxを元にしたカスタムイメージの作成
FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY ./laravel-app/public /usr/share/nginx/html # laravelアプリケーションのpublicのみ転送している
EXPOSE 80
nginxにはlaravelのpublicファイルのみ基本的には必要であるので、上記の例でわかるようにlaravel_appの中のpublicの中身のみ転送している。というのもnginxではphpは実行できないため、各種phpファイルは必要がないからだ。
このDockerfileを作ったらビルドする
docker build -t my-ecs-nginx -f ./nginx/Dockerfile . --no-cache
このようにビルドされてくる。このイメージをdocker-composeで使うように指示する
services:
php-fpm:
image: php:8.4-fpm
networks:
- app-network
nginx:
#image: nginx:alpine
image: my-ecs-nginx
ports:
- "80:80"
depends_on:
- php-fpm
networks:
- app-network
networks:
app-network:
docker-composeを再起動する
docker-compose down
docker-compose up -d
確認
http://server/robots.txt とかにアクセスして内容が取れていればok
robots.txtの内容が表示されている
トップページはまだnginxのdefaultのままである。これは設定を何も変更していないためだ。
これを変更するために、nginxの設定ファイルを取得した後、変更しimageに詰め直していく。
nginxのイメージをシェルで入って確認する
docker-composeが起動している状態にて
docker-compose exec nginx sh
とする(bashが入ってないため)なお、
cat /etc/nginx/conf.d/default.conf
などしてnginxの設定ファイルの場所とか存在とかを確認しておく。
nginxのデフォルトconfが出力されている
さらに
find /usr/share/nginx/html/
などして、転送されたファイルの一覧を一応確認してみよう
これらが転送されたファイルであり、また現在のドキュメントルートである。現在 / でアクセスした場合 index.html が表示されている
index.htmlの中身
http://server/robots.txt でアクセスした場合は /usr/share/nginx/html/robots.txt が取れるというわけだ。なお、http://server/index.php でアクセスした場合はソースコードがそのまま落ちてくる。
あらかた確認が終わったらexitで出る
dockerコンテナーから /etc/nginx/conf.d/default.conf をホストに持ってくる
ここからホストにdockerイメージのファイルを転送する。これはまずdocker psで確認して
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aacd50756247 my-ecs-nginx "/docker-entrypoint.…" 2 hours ago Up 2 hours 0.0.0.0:80->80/tcp, :::80->80/tcp ecs-laravel_nginx_1
a5a73c914865 php:8.4-fpm "docker-php-entrypoi…" 2 hours ago Up 2 hours 9000/tcp ecs-laravel_php-fpm_1
ここでNAMESを見るとecs-laravel_nginx_1
なので
docker cp ecs-laravel_nginx_1:/etc/nginx/conf.d/default.conf ./nginx/default.conf
のようにすると転送できる。
このconfigをカスタムする。ここでは全文を貼り付ける
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
index index.php;
location / {
root /usr/share/nginx/html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /srv/public$fastcgi_script_name;
include fastcgi_params;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
このように、index index.php
にするのと、phpの設定を生やす。
まず
fastcgi_pass php-fpm:9000;
ここで外部ホストと通信している事を確認する。つまり php-fpm:9000 というホストと通信している設定である。
さらに
fastcgi_param SCRIPT_FILENAME /srv/public$fastcgi_script_name;
これはphp-fpm側のパスが /srv/public に配置されているという想定になる。すなわち、php-fpm側のソースは全て /srv に転送される事を想定している。
nginxの設定ファイルを詰めなおす
FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY ./laravel-app/public /usr/share/nginx/html
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
このように、nginx/default.conf を /etc/nginx/conf.d/default.conf にコピーしているだけ
php-fpmのカスタムイメージを作成し、全て転送する
上記の例でみたように /srv にファイルを送信する
FROM php:8.4-fpm
WORKDIR /srv
COPY ./laravel-app /srv
EXPOSE 9000
こんな感じになる
laravelを起動する
まずdocker-compose.ymlを修正
services:
php-fpm:
#image: php:8.4-fpm
image: my-ecs-php-fpm # カスタムイメージを使う
networks:
- app-network
nginx:
#image: nginx:alpine
image: my-ecs-nginx
ports:
- "80:80"
depends_on:
- php-fpm
networks:
- app-network
networks:
app-network:
それぞれビルドして
docker build -t my-ecs-nginx -f ./nginx/Dockerfile . --no-cache
docker build -t my-ecs-php-fpm -f ./php-fpm/Dockerfile . --no-cache
完了後
docker-compose down; docker-compose up -d
などする。この段階で当該IPへアクセスすると
パーミッションエラーになるが、この段階でlaravelと連携できている事が確認できる
作成されたイメージの修正
php-fpm側の権限修正
FROM php:8.4-fpm
WORKDIR /srv
COPY ./laravel-app /srv
RUN chown www-data storage database -R # storageとdatabaseの権限を変更
EXPOSE 9000
というような事して再度ビルド
docker build -t my-ecs-php-fpm -f ./php-fpm/Dockerfile .
ダウン→アップ
docker-compose down; docker-compose up -d
すると
このように一応のトップページが現われる
実際に動作してはいない
Loginなどを押すと404になるはずだ。
これを修正する。
を参考にすると
location / {
try_files $uri $uri/ /index.php?$query_string;
}
が必要なのがわかるので、これを付け足す
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.php?$query_string; # 追加
}
nginxをビルドして
docker build -t my-ecs-nginx -f ./nginx/Dockerfile . --no-cache
起動しなおす
docker-compose down; docker-compose up -d
これでアクセスするとログインフォームは出てくる
とりあえずログインフォームは出た
ここまでのソースコード
現在の構成の問題点を1つずつ潰す
sqliteの問題
まず、sqliteのデーターベースファイルであるdatabase/database.sqliteをそのまま転送してしまっているが、通常この手のシステム構成は外部DBシステム(RDSなど)を見にいくはずなので、イメージを作成する時に初期化などは必要がないはずである。
ただ、ここではデーターを転送するにあたって、laravelのツリーの中で一度migrationされた物理ファイルを作成したものを転送しているという割と見かけない構成になっている事を意識しておく必要があるだろう。
転送しないファイル
通常、 .env や vendor/ というファイルはオリジナルソースに含まれていないか、あるいは含まれていたとしても転送しない方がデプロイの常道であるため、これを外していく。ただ、いきなり全て外すと解説が面倒なのでちょっとずつやる
.envの問題 (applicationキー)
では.envを転送しないようにしてみよう。これはプロジェクトのrootに .dockerignoreというファイルを作成する
laravel-app/.env
これでphp-fpmをビルドして再起動する
docker build -t my-ecs-php-fpm -f ./php-fpm/Dockerfile . --no-cache
docker-compose down; docker-compose up -d
すると.envが転送されていないため、以下のようなエラーとなるはずだ
これに対応する
FROM php:8.4-fpm
WORKDIR /srv
COPY ./laravel-app /srv
RUN chown www-data storage database -R
RUN cp .env.example .env
EXPOSE 9000
そうすると予想通り?
APP_KEY
がセットされない問題が生じてくる。これはもちろん、Dockerfileの中でartisan key:generate
しても動作するのではあるのだが、通常これは外部環境変数の注入で対応するのがこの手のデプロイの常道であるはずだ。
docker-compose exec php-fpm php artisan key:generate --show
などしてキーを書き込まず
services:
php-fpm:
#image: php:8.4-fpm
image: my-ecs-php-fpm
networks:
- app-network
environment:
APP_KEY: base64:slBwwE6jbHjJSLHAlWbVvm9WUeqJrAQO/QaMrbdfcuA=
このような形でdocker-composeの環境変数で定義した方がより実践的だ。docker-composeを再度起動して正常に戻る事を確認しておく
vendorが転送されている問題
vendorに関しては元のlaravelツリーで構成してコピーしても動くといえば動くのだが、これに関してはやはりイメージ作成時に中でcomposer installするのが常道って感じがするので、そのようにする。
laravel-app/.env
laravel-app/vendor/
ここでvendorを転送しないようにし
docker build -t my-ecs-php-fpm -f ./php-fpm/Dockerfile . --no-cache
docker-compose down; docker-compose up -d
するとvendorを転送していないので
このようになる
composerの対応
これはphp-fpmのDockerfile中でcomposer installする。なお、composerを動作させるために必要なパッケージも導入している
FROM php:8.4-fpm
WORKDIR /srv
COPY ./laravel-app /srv
RUN chown www-data storage database -R
RUN cp .env.example .env
# 必要package
RUN apt-get update && apt-get install -y \
git \
unzip \
zip
COPY /usr/bin/composer /usr/bin/composer
RUN composer install
EXPOSE 9000
再度ビルド
docker build -t my-ecs-php-fpm -f ./php-fpm/Dockerfile . --no-cache
docker-compose down; docker-compose up -d
これで再度アクセスするとビルドされているはずだ。
フロントエンドの対応
ここまではバックエンド側を潰してきたが、npm installなどはフロントエンドでやるのが普通である。つまりpublic/buildなどの生成である。
.dockerignoreのアップデート
laravel-app/.env
laravel-app/vendor/
laravel-app/node_modules/
laravel-app/public/build/
としてそれぞれビルドしてみよう
docker build -t my-ecs-php-fpm -f ./php-fpm/Dockerfile . --no-cache
docker build -t my-ecs-nginx -f ./nginx/Dockerfile . --no-cache
docker-compose down; docker-compose up -d
すると
このようになる。
Vite manifest の問題
これは、たとえばフロントエンドでnpm installしnpm run buildしてpublic/buildを作成したとしても
"resources/css/app.css": {
"file": "assets/app-BwHaPEbd.css",
"src": "resources/css/app.css",
"isEntry": true
},
"resources/js/app.js": {
"file": "assets/app-CAkSn3BF.js",
"name": "app",
"src": "resources/js/app.js",
"isEntry": true
}
}
のようなmanifest.jsonをバックエンドに送らないといけない。これは別々にDockerfileを作っているとうまいことやり辛いので、ここでnginxとphp-fpmのDockerfileを統合したsrvというディレクトリを作る事にする。
mkdir srv
cp nginx/default.conf srv
srvの中にconfigをコピーしている
srv/Dockerfileに統合されたDockerfileを作成
# --------------------------------------
# stage: PHP-FPM
# --------------------------------------
FROM php:8.4-fpm AS php-fpm
WORKDIR /srv
# Laravel のコードをコピー
COPY ./laravel-app /srv
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 --from=composer /usr/bin/composer /usr/bin/composer
RUN composer install
EXPOSE 9000
# --------------------------------------
# Nginx: Stage
# --------------------------------------
FROM nginx:alpine AS nginx
WORKDIR /usr/share/nginx/html
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
として
docker build -t my-ecs-php-fpm -f ./srv/Dockerfile --target=php-fpm . --no-cache
docker build -t my-ecs-nginx -f ./srv/Dockerfile --target=nginx .
docker-compose down; docker-compose up -d
これを解決するために、フロントエンドビルドステージを加える
# --------------------------------------
# frontend build stage
# --------------------------------------
# Stage 1: フロントエンドをビルド
FROM node:18-alpine AS frontend-builder
WORKDIR /laravel
# Laravelアプリをコピー
COPY ./laravel-app /laravel
# npmでビルド
RUN npm install && npm run build
# 生成されたpublicを/publicにcopy
RUN mkdir /public && cp -a public/* /public
# --------------------------------------
# stage: PHP-FPM
# --------------------------------------
# 略
これは最初のstageでnpmのinstallとビルドを行い、そのイメージの /publicにコピーしただけ。これだと動かないので、それぞれ、その成果物を自身にコピーする
最終Dockerfile
# --------------------------------------
# frontend build stage
# --------------------------------------
# Stage 1: フロントエンドをビルド
FROM node:18-alpine AS frontend-builder
WORKDIR /laravel
# Laravelアプリをコピー
COPY ./laravel-app /laravel
# npmでビルド
RUN npm install && npm run build
# 生成されたpublicを/publicにcopy
RUN mkdir /public && cp -a public/* /public
# --------------------------------------
# stage: PHP-FPM
# --------------------------------------
FROM php:8.4-fpm AS php-fpm
WORKDIR /srv
# Laravel のコードをコピー
COPY ./laravel-app /srv
# /public をコピー
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
# --------------------------------------
# Nginx: Stage
# --------------------------------------
FROM nginx:alpine AS nginx
WORKDIR /usr/share/nginx/html
COPY ./srv/default.conf /etc/nginx/conf.d/default.conf
# /public をコピー
COPY /src/public/ /usr/share/nginx/html
EXPOSE 80
最終確認
必ず /login とかで行う事
ソース
結局
工数が多く1から組んでいると日が暮れてしまうので、ある程度のセットを自身のリポジトリーに用意して、laravelプロジェクトをくっつけてさくっとイメージをビルドできるようにしておいた方がよいかと思う。なお、ECSで2コンテナを通信する場合のホストはlocalhostになるので、これに対応をそのうち続編として書くつもりではある(需要ありそうならね)
Discussion