🏛️

Laravelプロダクト Fargate化への道

2021/03/27に公開

↓の Scrap を記事化しました
https://zenn.dev/sogaoh/scraps/699d70879b6ed3

#phperkaigi での LT で話すことの詳細版です
https://fortee.jp/phperkaigi-2021/proposal/7aa8bb61-b649-4a29-a96e-f8edef026f98

前提

  • コードリポジトリが 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 ディレクトリに書き込み権限がなくて
  • 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) 躓いていたこと

  • 接続できない

  • 環境変数の不足

    • 「何が不足しているのかわからないが落ちる」地獄
      結局は 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 爆死
https://twitter.com/search?q=%40sogaoh apply爆死&src=typed_query&f=live

  • 終盤で ECR pull ができなくなって大苦戦
    • PV 1.4 が標準になったことによる影響?
    • プライベートサブネットに 443 ポートの疎通を入れてクリアー

Step 4 ecspresso で ECS サービス・タスク定義構築

構築・運用ツールとして ecspresso を採用
https://github.com/kayac/ecspresso

大いに見て参考にした書籍 (¥500)
https://zenn.dev/fujiwara/books/ecspresso-handbook

  • 手始めは maintenance service
    • nginx-proxy
  • 次にDBマイグレーションをする用の artisan service
    • laravel-app
  • そして、年末年始に学習しておいた、nginx - laravel 一体タスクを実装

https://booth.pm/ja/items/2539342

(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\"]}]}"

  • 使っていない maintenanceartisan service は scale --tasks=0 で「止める」

    • 使うときに scale --tasks=1 してる
  • (その他) 今のところ

    • rollback --deregister-task-definition は使っていない
    • 古くてもう使わないタスク定義の登録解除は手動

(4.2) ecspresso-handbook 読者コミュニティ

独占的に質問してました。 @fujiwara さんに感謝。

https://zenn.dev/fujiwara/scraps/4ebaf21cd88db7

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 に通知

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 に通知

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]

https://hub.docker.com/repository/docker/sogaoh/amzn2-laravel-frontend-builder
https://github.com/sogaoh/dockerhub/tree/master/amzn2-laravel-frontend-builder

  • デプロイ用IAMユーザーで運用
    • ベースにした情報は下記


https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs_managed_policies.html
https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/ecr_managed_policies.html#AmazonEC2ContainerRegistryPowerUser
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/codedeploy_IAM_role.html

今後の課題など

  • Vue.js のビルドに時間がかかるのはキャッシュするなどして短縮したい

    • 毎度、デプロイ用のエグゼキューター(Amazon Linux2 をカスタマイズした Docker イメージ)を pull している・・・
  • CloudWatch にヘルスチェック ログが流れないように

    • ノイズ感甚だしい
  • バッチ処理実行コンテナ

  • NGINX の resolver を入れたい

    • サービスディスカバリ機能で DNS キャッシュのために疎通できなくなるのを避けたい
  • ブルーグリーンデプロイ

    • もっと気軽に検証できるように
  • ECS Exec

  • 接続元IP制限

    • ステージング環境は内部公開にとどめたい場合

その他

思い出の何コマか

@chaspy_ さんとの 2月3日頃
https://twitter.com/chaspy_/status/1356861816941383682

@raki さんとの 3月18日頃
https://twitter.com/raki/status/1372201607216033793

@toricls さんとの3月26日
https://twitter.com/toricls/status/1375337191912525829

脚注
  1. Dockerfile をもとに各現場用の ECR イメージを作っている(DockerHub から ビルドの都度 pull はしていない)。 ↩︎

Discussion