Laravelプロダクト Fargate化への道
↓の Scrap を記事化しました
#phperkaigi
での LT で話すことの詳細版です
前提
- コードリポジトリが docker-compose 化されている
- AWS を利用している
- PHP 7 系以上
- Laravel 5.8 以上
- セッションストアにRedisを使用
- フロントエンドは Vue.js で開発
Step 1 アプリケーションのイメージ化
(docker-compose.yml)
・・・
build:
context:
dockerfile:
args:
volumes:
・・・
↓
・・・
image:
・・・
(1.1) 調整した事項など
- ログ出力をファイルから標準出力へ
- この点、文化として浸透していない現場がそれなりにありそう
- 「セッションをファイルで持つ」のを Redis 等の共有ストレージに変えておく、
のようなスケールアウトを意識した初動にしておいた
- composer install を Dockerfile でやるように
- docker build 前に Vue.js のビルドを済ませるように
(1.2) 躓いていたこと
-
ディレクトリがない編
- storage 配下のディレクトリが足りてなくて
- 特に、 storage/framework/cache/data
- bootstrap/cache ディレクトリに書き込み権限がなくて
- storage 配下のディレクトリが足りてなくて
-
docker build 時には Redis と繋がってないので
- php artisan cache:clear をDockerfile中でやるとエラーになる
- php artisan config:clear しないと環境変数が古いまま
-
Laravel Mix の「左辺と右辺、同じはダメ」制約
- 頭に MIX_ を付けないとダメなのか、単に左辺と右辺で違ってればいいのか、まだちゃんとわかっていない
-
少し変数名を間違えてただけで、画面に期待されていた文字列が載ってこない
- ビルド前に気が付けない
Step 2 イメージ化したアプリケーションを動かす
-
docker-compose up
してコンソール出力とにらめっこ
(2.1) 調整した事項など
- docker-compose.yml から
build:・volumes: ディレクティブをなくして image: にして起動- volumes: はマウントにすれば実現可能だが、FargateのEFSマウントにはまだ諸問題あるので避けた
- 先に docker build を済ませてから
(2.2) 躓いていたこと
-
接続できない
- 良くあったのは、"配列でない値を配列スタイルでアクセスした場合"
https://www.php.net/manual/ja/migration74.incompatible.php
(PHP 7.3 -> 7.4 アップグレード対応から始めていた)
- 良くあったのは、"配列でない値を配列スタイルでアクセスした場合"
-
環境変数の不足
- 「何が不足しているのかわからないが落ちる」地獄
結局は S3 へのアクセスキー/シークレットキーだった - 開発者から環境変数の追加を伝達されない、という問題ケースもあったり
- 「何が不足しているのかわからないが落ちる」地獄
Step 3 インフラ構築(ECS は cluster まで)
概要構成図
以下の順に module 化 しつつ順次 apply
-
network
- vpc
- subnet
- nat gateway
- (vpc endpoint : 後で追加した)
- Gateway
- Interface
-
security
- public
- private
- storage
- (vpc_endpoint)
-
ecs-fargate
- ecs cluster
- alb
- iam role
-
data-store
- rds
- elasticache
(3.1) 調整した事項など
- VPC Endpoint
- Security Group
- 後発でネットワーク系を弄るの怖かった...
(3.2) 苦戦したこと
- RDS cluster の構築
- Redis cluster の構築
- 細々と名前の制約に引っかかった
数々の terraform apply 爆死
- 終盤で ECR pull ができなくなって大苦戦
- PV 1.4 が標準になったことによる影響?
- プライベートサブネットに 443 ポートの疎通を入れてクリアー
Step 4 ecspresso で ECS サービス・タスク定義構築
構築・運用ツールとして ecspresso を採用
大いに見て参考にした書籍 (¥500)
- 手始めは
maintenance
service- nginx-proxy
- 次にDBマイグレーションをする用の
artisan
service- laravel-app
- そして、年末年始に学習しておいた、nginx - laravel 一体タスクを実装
(4.1) ecspresso 利用のだいたいの流れ
-
init
は特に使わず json でサービス定義・タスク定義を記述 -
verify
でシンタックスチェック -
--dry-run
でどういうタスク定義が作られるか・適用されるかを最終確認 -
初回は
create
でサービス作成 -
2回目以降は
deploy
-
作り直すときは一旦
scale --tasks=0
->delete
-
マイグレーションは
run --skip-task-definition --overrides="{\"containerOverrides\":[{\"name\":\"laravel-app\",\"command\":[\"make\",\"migrate\"]}]}"
-
使っていない
maintenance
やartisan
service はscale --tasks=0
で「止める」- 使うときに
scale --tasks=1
してる
- 使うときに
-
(その他) 今のところ
-
rollback --deregister-task-definition
は使っていない - 古くてもう使わないタスク定義の登録解除は手動
-
(4.2) ecspresso-handbook 読者コミュニティ
独占的に質問してました。 @fujiwara さんに感謝。
Step 5 デプロイ簡素化
今のところ、CircleCI 版のみ。
絶賛 Bitbucket Pipelines 版を構築中。
(5.1) 意図的に「全自動」にしていないワークフロー構成
不測の事態が発生したときの「振られ幅」を小さめに抑える意図
ワークフローの処理内容概略
check
- 開発メインストリーム ブランチのみで filter
- test-fresh-seed
- local 用の 環境変数を direnv で load
- Vue.js build
- docker build
- DB migrate:fresh --seed を走らせる
- (なぜか成功しないのでコメントアウト中。
名前解決できてなさそう?)
- (なぜか成功しないのでコメントアウト中。
- 完了時に Slack に通知
- test-fresh-seed
ecr-update
- ステージング・本番環境用ブランチのみで filter
- パラメータ run_push を true にした API からのリクエストのみで発動
- build
- Vue.jsビルド用の環境変数を direnv で load
- Vue.js build
- docker build
- docker tag
- ECR への push まで
- docker tag
- aws ecr login
- docker push
- 完了時に Slack に通知
- build
ecs-deploy
- ステージング・本番環境用ブランチのみで filter
- パラメータ run_deploy を true にした API からのリクエストのみで発動
- 要承認 : CircleCI の管理画面で実施可能
- 開始時に Slack に通知
- ecspresso のインストール
- デプロイ等用の環境変数を direnv で load
- make jsonnet (タスク定義 ecs-task-def.json 作成)
- make verify
- make dry-deploy
- make deploy <- デプロイ本体
- 完了時に Slack に通知
ecs-rollback
(今のところ使用機会が発生していない)
- ステージング・本番環境用ブランチのみで filter
- パラメータ run_rollback を true にした API からのリクエストのみで発動
- 要承認 : CircleCI の管理画面で実施可能
- 開始時に Slack に通知
- ecspresso のインストール
- デプロイ等用の環境変数を direnv で load
- make jsonnet (タスク定義 ecs-task-def.json 作成)
- make verify
- make dry-rollback
- make rollback (ecspresso rollback --deregister-task-definition) <- ロールバック本体
- 完了時に Slack に通知
db-migrate
- ステージング・本番環境用ブランチのみで filter
- パラメータ run_migrate を true にした API からのリクエストのみで発動
- 要承認 : CircleCI の管理画面で実施可能
- 開始時に Slack に通知
- ecspresso のインストール
- デプロイ等用の環境変数を direnv で load
- make jsonnet ( "artisan" タスク定義 ecs-task-def.json 作成)
- make verify
- make mg-run (php artisan migrate 実行) <- マイグレーション実行本体
- 完了時に Slack に通知
(5.2) 工夫・気をつけていること
- 自分の作業マシンに近い(はずの) Amazon Linux 2 の Docker Image でデプロイ環境の土台を作り込んでいる(でかいけど成功してるのでまあ良いとしている)[1]
- デプロイ用IAMユーザーで運用
- ベースにした情報は下記
記
今後の課題など
-
Vue.js のビルドに時間がかかるのはキャッシュするなどして短縮したい
- 毎度、デプロイ用のエグゼキューター(Amazon Linux2 をカスタマイズした Docker イメージ)を pull している・・・
-
CloudWatch にヘルスチェック ログが流れないように
- ノイズ感甚だしい
-
バッチ処理実行コンテナ
- ecschedule を運用してみる?
-
NGINX の resolver を入れたい
- サービスディスカバリ機能で DNS キャッシュのために疎通できなくなるのを避けたい
-
ブルーグリーンデプロイ
- もっと気軽に検証できるように
-
ECS Exec
- php artisan cache:clear をオンデマンドでしたい!!
3/26(金)にできた。感動。- Zenn Scrap "Try ECS Exec"
- php artisan cache:clear をオンデマンドでしたい!!
-
接続元IP制限
- ステージング環境は内部公開にとどめたい場合
その他
思い出の何コマか
@chaspy_ さんとの 2月3日頃
@raki さんとの 3月18日頃
@toricls さんとの3月26日
-
Dockerfile をもとに各現場用の ECR イメージを作っている(DockerHub から ビルドの都度 pull はしていない)。 ↩︎
Discussion