顧客・案件・請求をExcelや共有フォルダで管理していて、「そろそろ社内向けの管理画面を作りたい」「作るならAWSで安定して動かしたい」と考える会社は少なくありません。この連載では、Laravel(=PHPのWebアプリケーション用フレームワーク)と Vue.js(=画面を部品単位で組み立てるJavaScriptのフレームワーク)で管理画面を作り、AWS の ECS(=コンテナを動かすサービス)で本番運用するところまでを、1回1機能で積み上げていきます。
「Laravel と Vue で管理画面を作って、AWS のコンテナで動かしたい。でも、画面づくりとインフラの両方があって、どこから手を付ければいいのか分からない…」
結論から言うと、Laravel+Vue.js の管理画面を ECS で動かす作業は、「画面と機能を作る」「コンテナにする」「AWS に置く」「自動でデプロイ・監視する」の4段階に分けると、1つずつ小さく進められます。第1回の今回は、連載の全体像と目次を示したうえで、Laravel 12 公式の Vue スターターキットを土台に、Docker(=アプリを「コンテナ」という箱に入れて動かす仕組み)で開発環境を立ち上げるところまでを作ります。
Laravel+Vue.js の管理画面を ECS で動かす連載の全体像
最終的に完成させるのは、次のことができる社内向けの管理画面です。
- ログインと、ロール(管理者/担当者/閲覧のみ)ごとの権限
- 顧客マスタ、案件(顧客にひもづく)、請求(案件にひもづく)の登録・一覧・検索・並べ替え・ページ送り
- 請求書の発行などの重い処理を裏側で順番に実行(キュー)、月次の締め処理などを定期実行(スケジューラ)
- 本番用のコンテナイメージ(nginx + PHP-FPM)を1つ作り、画面・キュー処理・定期実行で使い回す
- Terraform(=AWS の構成をコードで書いて作るツール)で、ネットワーク・ロードバランサー・ECS・データベース(RDS for MySQL)などを構築
- GitHub Actions から、長期のアクセスキーを持たずに AWS へ自動デプロイ
- ログ・監視・オートスケーリング・切り戻しの運用手順
完成形の構成:画面・アプリ・AWS の3層
| 層 | 役割 | 主な技術 |
|---|---|---|
| 画面(ブラウザ) | 一覧・登録・編集フォーム、権限に応じたボタンの出し分け | Vue 3、Inertia 2、TypeScript、Tailwind CSS 4 |
| アプリ(サーバー) | 入力チェック・権限チェック・データ保存・キュー処理・定期実行 | Laravel 12(PHP 8.4) |
| 実行環境(AWS) | コンテナの実行、データベース、秘密情報の保管、ログ・監視 | ECS on Fargate、ALB、RDS for MySQL 8.4、SQS、Secrets Manager、SSM Parameter Store、CloudWatch |
Inertia(イナーシャ)は、Laravel のルーティング(=URLと処理の対応付け)をそのまま使いながら、画面だけを Vue で描くための橋渡し役です。画面用の API を別に設計しなくてよいので、社内向けの管理画面と相性がよい組み合わせです。
設計の方針は次の4つです。
- 1つのアプリイメージを3つの役割で使う:画面を返す web、キューを処理する worker、定期実行の scheduler は、起動コマンドだけが違う同じイメージにします。更新のたびに3つの中身がずれる心配がなくなります。
- 秘密情報はイメージにもGitにも入れない:DBパスワードや暗号化キーは AWS の Secrets Manager / SSM Parameter Store に置き、起動時に環境変数として渡します。
- マイグレーション(=データベースのテーブル変更)はデプロイの前に1回だけ:画面のコンテナが起動するたびに流すと、複数台が同時に実行して衝突することがあるためです。
- ログはファイルではなく標準出力へ:コンテナの中のファイルは入れ替えのたびに消えるため、ログは CloudWatch Logs に集めます。
連載の目次(予定)
各回のタイトルは予定です。回を追うごとに、前回までのコードに差分を積み上げていきます。
| 回 | タイトル(予定) |
|---|---|
| 第1回 | 全体像と開発環境:Laravel 12+Vue 3 スターターキットを Docker で動かす(この記事) |
| 第2回 | 顧客マスタの一覧と登録:FormRequest でバリデーションする |
| 第3回 | ロールと Policy:誰が何をできるかをコードで決める |
| 第4回 | 案件管理と一覧画面の作り込み:検索・並べ替え・ページング |
| 第5回 | 請求と非同期処理:キューとスケジューラをローカルで動かす |
| 第6回 | 本番用コンテナイメージを作る:マルチステージ Dockerfile |
| 第7回 | Terraform の土台とネットワーク:state 管理と VPC |
| 第8回 | RDS for MySQL と秘密情報:パスワードをコードに書かない |
| 第9回 | ECR・ECS on Fargate・ALB:管理画面をインターネットに公開する |
| 第10回 | キューワーカーとスケジューラを ECS で動かす |
| 第11回 | GitHub Actions で CI/CD:OIDC でキーを持たずにデプロイ |
| 第12回 | 運用編:ログ・監視・スケーリング・切り戻し |
第1〜5回で管理画面の機能を作り、第6回でコンテナ化、第7〜10回で AWS の構成、第11〜12回で自動化と運用を扱います。AWS の部分は Terraform のコードと terraform validate / terraform plan による確認までを記事で示し、実際の構築(apply)は読者の AWS アカウントで行う前提とします。
Laravel 12 公式の Vue スターターキットを土台にする理由
Laravel 12 では、React・Vue・Livewire 向けの新しいスターターキット(=ログイン画面などがあらかじめ揃ったひな形)が公開されました。Vue 版は Inertia 2・Vue 3・TypeScript・Tailwind CSS・shadcn-vue(画面部品のセット)の組み合わせで、ログイン・会員登録・パスワード再設定・プロフィール設定の画面と、そのテストが最初から入っています。
📰 出典:Laravel 12.x Release Notes
管理画面の「ログインまで」を一から作る手間を省けるため、この連載ではスターターキットを採用しました。一方で、次の点は理解しておく必要があります。
- スターターキットは「コードを受け取って自分たちで持つ」方式:以前の Breeze / Jetstream のように、後から更新をまとめて取り込む仕組みではありません。取り込んだ時点のコードを自分たちで保守する前提になります。
- サンプルは 2025年5月初めの時点のスターターキットに固定:新しく始める場合は、Laravel のインストーラーで
laravel newを実行し、スターターキットに Vue を選ぶと同じ構成が入ります。ただし実行した時期によって中身が少しずつ変わるため、連載のサンプルでは 2025年5月初めのスターターキットを土台にし、PHP・JavaScript の依存パッケージの版も lock ファイルで固定しています。
📰 出典:Laravel 12.x Starter Kits
Laravel 12 は、公式のサポートポリシーでバグ修正が2026年8月まで、セキュリティ修正が2027年2月までとされています。業務システムは数年単位で使うものなので、採用するバージョンのサポート期限は最初に確認しておきましょう。
Docker で作る開発環境の構成
開発用のパソコンに PHP や MySQL を直接入れず、すべて Docker のコンテナで動かします。担当者のパソコンごとに環境が違って「自分の環境では動く」が起きるのを防ぐためです。
| サービス名 | 使うイメージ | 役割 |
|---|---|---|
| app | PHP 8.4 + Composer(自作の開発用イメージ) | php artisan serve で管理画面を 8000 番ポートで動かす。Composer やテストの実行にも使う |
| mysql | MySQL 8.4 | 開発用のデータベース |
| node | Node.js 22 | 画面のビルド(npm run build)と、開発中の自動反映(Vite の開発サーバー) |
MySQL は長期サポート版(LTS)の 8.4 系で、本番の RDS for MySQL とそろえます。
サンプルコードの主なファイルは次のとおりです(スターターキット由来のファイルは省略)。
code/
├── app/ bootstrap/ config/ database/ resources/ routes/ tests/ … スターターキット
├── compose.yaml … 開発環境(app / mysql / node)
├── docker/dev/php/Dockerfile … 開発用の PHP 8.4 イメージ
├── Dockerfile … 本番用(第6回で完成させるひな形)
├── .env.example … 環境変数の見本(ダミー値のみ)
├── .gitignore / .dockerignore
├── vite.config.ts … 画面ビルドの設定
└── infra/terraform/ … AWS の構成(第7回から)
compose.yaml と開発用 Dockerfile を用意する
compose.yaml:3つのサービスをまとめて起動する
compose.yaml に、3つのサービスの起動方法を書きます。
compose.yaml(抜粋)
name: laravel-ecs
services:
app:
build:
context: ./docker/dev/php
args:
UID: ${UID:-1000}
GID: ${GID:-1000}
command: php artisan serve --host=0.0.0.0 --port=8000
ports:
- "8000:8000"
volumes:
- ./:/var/www/html
# DB 接続情報は .env から読む(environment で渡すと phpunit.xml の sqlite 設定より優先されてしまうため)
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:8.4
environment:
# ローカル専用の値。本番のパスワードは Secrets Manager で管理する(第8回)
MYSQL_DATABASE: ${DB_DATABASE:-laravel}
MYSQL_USER: ${DB_USERNAME:-laravel}
MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootsecret}
TZ: Asia/Tokyo
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-u", "root", "-p$${MYSQL_ROOT_PASSWORD}"]
interval: 5s
timeout: 5s
retries: 20
node:
image: node:22-bookworm
working_dir: /var/www/html
user: "${UID:-1000}:${GID:-1000}"
command: sh -c "npm ci && npm run dev"
environment:
# 5173 番が他のプロジェクトで使用中なら .env に VITE_PORT=5174 のように書いて変える
VITE_PORT: ${VITE_PORT:-5173}
ports:
- "${VITE_PORT:-5173}:${VITE_PORT:-5173}"
volumes:
- ./:/var/www/html
volumes:
mysql-data:
ポイントは次の3つです。
- DB の接続情報は
.envから読む:Docker Compose は、同じフォルダの.envを読み込んで${DB_DATABASE}などを置き換えます。Laravel も同じ.envを読むので、アプリとデータベースで接続情報が食い違いません。 - mysql にヘルスチェックを付ける:
depends_onのcondition: service_healthyで、MySQL が接続を受け付けられる状態になってから app を起動します。 - app に DB の環境変数を直接渡さない:
environment:で渡すと、テストのときにphpunit.xmlの設定(テストは SQLite のメモリ上のDBで実行)より優先され、テストが開発用DBを書き換えてしまいます。
docker/dev/php/Dockerfile:開発用の PHP 8.4 イメージ
公式の PHP 8.4 イメージに、MySQL への接続などに必要な拡張と Composer(=PHPのパッケージ管理ツール)を追加します。
docker/dev/php/Dockerfile
# 開発用 PHP イメージ(compose.yaml の app / composer 実行用)
# 本番用は リポジトリ直下の Dockerfile(第6回で作り込む)
FROM php:8.4-cli-bookworm
RUN apt-get update \
&& apt-get install -y --no-install-recommends git unzip libzip-dev libicu-dev \
&& docker-php-ext-install pdo_mysql zip bcmath intl pcntl \
&& rm -rf /var/lib/apt/lists/*
# Composer 2.8 系(2.8.0 は 2024-10-02 公開)
COPY --from=composer:2.8 /usr/bin/composer /usr/bin/composer
# ホストの UID/GID に合わせて作成したファイルの所有者ずれを防ぐ
ARG UID=1000
ARG GID=1000
RUN groupadd -g ${GID} app && useradd -m -u ${UID} -g ${GID} app
USER app
WORKDIR /var/www/html
コンテナの中で作られたファイル(vendor/ やログ)がパソコン側で root の持ち物にならないよう、パソコンのユーザーIDと同じIDのユーザーでコマンドを実行しています。
.env.example と .gitignore:秘密情報の置き場を最初に決める
Laravel は .env ファイルから設定値を読み込みます。.env には実際の値を書き、Git にはコミットしません。リポジトリに置くのは、キー名とダミー値だけを書いた .env.example です。
📰 出典:Laravel 12.x Configuration(Environment Configuration)
.env.example(抜粋)
APP_NAME="Admin Console"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000
APP_LOCALE=ja
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=ja_JP
# ローカルは compose.yaml の mysql サービスに接続する(値はローカル専用のダミー。本番は Secrets Manager から注入)
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret
SESSION_DRIVER=database
QUEUE_CONNECTION=database
CACHE_STORE=database
APP_KEY は Cookie やセッションの暗号化に使う鍵で、空のままにしておき、各自の環境で php artisan key:generate を実行して作ります。本番の APP_KEY は第8回で SSM Parameter Store に置き、コンテナの起動時に渡します。
.gitignore には、スターターキットの設定に加えて、連載で使う秘密情報や Terraform の状態ファイルを追加しています。
.gitignore(連載で追加した部分)
# 環境変数(秘密情報)は .env.example 以外コミットしない
.env.*
!.env.example
# Terraform
.terraform/
*.tfstate
*.tfstate.*
*.tfvars
!*.tfvars.example
vite.config.ts と /up:開発サーバーとヘルスチェック
vite.config.ts:コンテナの Vite 開発サーバーにブラウザからつなぐ
Vite(ヴィート)は、Vue や TypeScript のファイルをブラウザが読める形にまとめるビルドツールです。開発中は Vite の開発サーバーを動かしておくと、ファイルを保存しただけで画面が更新されます(HMR=ホットモジュールリプレースメント)。
コンテナの中で開発サーバーを動かす場合は、外(パソコンのブラウザ)から接続できるように設定を追加します。
vite.config.ts(連載で追加した部分)
export default defineConfig({
// plugins: [...] はスターターキットのまま
// Docker の node コンテナで `npm run dev` するための設定(第1回)
server: {
host: '0.0.0.0', // コンテナの外(ホストのブラウザ)から接続できるようにする
port: Number(process.env.VITE_PORT ?? 5173),
strictPort: true,
// public/hot に書かれる URL を、ブラウザから届く localhost にする(0.0.0.0 のままだと読み込めない環境がある)
hmr: { host: 'localhost' },
},
// resolve: {...} はスターターキットのまま
});
開発サーバーが起動すると public/hot に開発サーバーのURLが書き込まれ、Laravel はそこから画面のファイルを読み込みます。hmr.host を指定しないと http://0.0.0.0:5173 と書かれ、ブラウザによっては読み込めません。
📰 出典:Vite 公式ドキュメント Server Options
/up:ロードバランサーが見るヘルスチェック
Laravel 12 は、bootstrap/app.php の health: '/up' で、アプリが正常に起動しているかを返すURLを最初から持っています。第9回で、AWS のロードバランサー(ALB)がこの /up を定期的に確認し、応答しないコンテナにはアクセスを振り分けないように設定します。
bootstrap/app.php(抜粋)
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
📰 出典:Laravel 12.x Deployment(The Health Route)
動作確認の方法
サンプルコードのフォルダで、次の順に実行します。PHP・Composer・Node.js をパソコンに入れる必要はありません。
cp .env.example .env
docker compose run --rm --no-deps app composer install
docker compose run --rm --no-deps app php artisan key:generate
docker compose run --rm --no-deps node sh -c "npm ci && npm run build"
docker compose up -d mysql app
docker compose exec app php artisan migrate
docker compose exec app php artisan --version # Laravel Framework 12.x
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8000/up # 200
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8000/dashboard # 302(/login へ)
docker compose run --rm --no-deps app php artisan test
筆者の環境では、次のことを確認しました。
- Laravel Framework 12 系が起動し、マイグレーションで users などのテーブルが作られる
/up・/loginが 200、未ログインの/dashboardは/loginへ 302- スターターキットに含まれる認証・設定画面のテスト27件がすべて成功し、
npm run buildも成功する docker compose up -d nodeで開発サーバーが起動し、public/hotにhttp://localhost:<ポート>が書き込まれる
ブラウザで http://localhost:8000 を開き、「Register」から会員登録するとダッシュボードが表示されます。確認が終わったら docker compose down -v で停止します(-v を付けると MySQL のデータも消えます)。
つまずきやすい点
- ポートが他のプロジェクトとぶつかる:8000・3306・5173 番がすでに使われていると起動に失敗します。筆者の環境でも 5173 番が別のプロジェクトで使われていたため、
VITE_PORTで変えられるようにしました。MySQL の 3306 番も、compose.yamlのportsの左側を変えれば回避できます。 - 開発サーバーを止めたのに画面が真っ白になる:Vite の開発サーバーを止めたときに
public/hotが残っていると、Laravel は止まった開発サーバーから画面を読み込もうとします。public/hotを削除するか、npm run buildをやり直してください。 - 依存パッケージの更新を勝手にしない:
composer updateやnpm updateを実行すると、記事と違うバージョンが入ることがあります。連載のサンプルでは lock ファイル(composer.lock・package-lock.json)で版を固定し、composer install・npm ciだけを使います。
発注者向けメモ:スターターキットと開発環境で確認しておきたいこと
スターターキットを使うと、ログイン画面や会員登録を一から作る工数は減ります。ただし、スターターキットは「コードを受け取って自社で保守する」方式なので、将来の更新は自分たち(または保守を担う開発会社)が取り込む前提になります。
開発会社に依頼するときは、次の点を確認しておくと安心です。
- どのバージョンを使い、いつまでサポートされるか:Laravel 12 はセキュリティ修正が2027年2月までです。サポート期限の前にバージョンアップの工数がかかることを、保守計画に入れておきます。
- 開発環境が手順書どおりに再現できるか:Docker で作っておくと、担当者が替わっても同じ環境をすぐ用意でき、引き継ぎが楽になります。
- 秘密情報をどこで管理するか:
.envをメールやチャットで送り合う運用は漏えいの原因になります。本番の値は誰が、どこに保管するのかを決めておきます。
打ち合わせでは、次のように聞いてみてください。
- 「ログイン画面などはスターターキットを使いますか?使う場合、将来の更新は誰がどのように取り込みますか?」
- 「使うフレームワークのサポート期限と、バージョンアップの目安の時期を教えてください」
- 「開発環境は Docker などで再現できるようにしてもらえますか?手順書も納品物に含まれますか?」
- 「パスワードや鍵などの秘密情報は、開発中と本番でそれぞれどこに保管する予定ですか?」
まとめと次回予告
第1回では、Laravel+Vue.js の管理画面を ECS で動かす連載の全体像と目次を示し、Docker で開発環境を立ち上げました。
- 連載は「機能を作る → コンテナにする → AWS に置く → 自動化と運用」の順に、1回1機能で進める
- 土台は Laravel 12 公式の Vue スターターキット(Inertia 2・Vue 3・TypeScript・Tailwind CSS 4)。コードは自分たちで保守する前提
- 開発環境は Docker Compose で app(PHP 8.4)・mysql(MySQL 8.4)・node(Node.js 22)の3サービス
- 秘密情報は
.env(コミットしない)、見本は.env.example。ヘルスチェック用の/upは Laravel 12 に最初からある
次回は「顧客マスタの一覧と登録:FormRequest でバリデーションする」です。customers テーブルを作り、一覧・登録・編集・削除の画面と、入力チェック、そのテストまでを実装します。
この連載の記事一覧
この記事は連載「Laravel+Vue.jsで作る管理画面をECSで動かす」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第1回】全体像と開発環境:Laravel 12+Vue 3 スターターキットを Docker で動かす(この記事)
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第2回】顧客マスタの一覧と登録:FormRequest でバリデーションする
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第3回】ロールと Policy:誰が何をできるかをコードで決める
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第4回】案件管理と一覧画面の作り込み:検索・並べ替え・ページング
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第5回】請求と非同期処理:キューとスケジューラをローカルで動かす
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第6回】本番用コンテナイメージを作る:マルチステージ Dockerfile
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第7回】Terraform の土台とネットワーク:state 管理と VPC
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第8回】RDS for MySQL と秘密情報:パスワードをコードに書かない
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第9回】ECR・ECS on Fargate・ALB:管理画面をインターネットに公開する
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第10回】キューワーカーとスケジューラを ECS で動かす
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第11回】GitHub Actions で CI/CD:OIDC でキーを持たずにデプロイ
- 【Laravel+Vue.jsで作る管理画面をECSで動かす 第12回】運用編:ログ・監視・スケーリング・切り戻し










コメント
コメント一覧 (1件)
[…] 前回(第1回:全体像と開発環境)では、Laravel 12 公式の Vue スターターキットを土台に、Docker で開発環境を立ち上げました。今回はそのコードに顧客マスタの機能を追加します。 […]