はじめに
なぜ本番環境でのDocker運用は難しいのか
開発環境でのDockerは、環境構築の簡略化や一貫性の確保に役立ちます。しかし、本番環境ではそれに加えて、可用性、スケーラビリティ、セキュリティ、そして保守性が求められます。
- 開発環境: docker-compose upで手軽に起動できることが重要。
- 本番環境: ダウンタイムなくサービスを継続し、予期せぬ障害にも対応できる堅牢な設計が必要。
開発で使っていたdocker-compose.ymlをそのまま本番環境に適用するのは危険です。本番運用に特化した構成を、最初からしっかりと設計する必要があります。
開発環境との違いを認識する
本番環境のDocker構成を考える上で、開発環境との主な違いを認識しておくことが重要です。
- イメージの最適化: 開発用のイメージはデバッグツールやビルドツールが含まれていてサイズが大きくなりがちです。本番では最小限のツールだけを含んだ軽量なイメージを使います。
- コンテナの役割分担: 開発では一つのコンテナが複数の役割を担うこともありますが、本番では一つのコンテナに一つの役割を持たせるべきです。
- データの永続化: 開発では一時的なデータでも問題ない場合がありますが、本番ではデータベースやログなど、重要なデータは必ず永続化させる必要があります。
- 監視とロギング: 本番では、アプリケーションの状態を常に監視し、ログを収集・分析する仕組みが必要です。
本記事で解説する本番環境構成の全体像
本記事で目指す本番環境の構成は、以下の要素で成り立っています。
- APIコンテナ: Laravel APIを実行。
- SPAコンテナ: Vue.jsでビルドされた静的ファイルを配信。
- データベースコンテナ: MySQLやPostgreSQLなど。
- ジョブキューコンテナ: Redisなど。
- スケジューラーコンテナ: バッチ処理を実行。
これらのコンテナをDocker Composeで連携させ、さらにログやバックアップの仕組みを組み込むことで、堅牢な本番運用を目指します。
本番環境向けDocker構成の基礎
開発用docker-compose.ymlからの脱却
開発環境のdocker-compose.ymlは、利便性を優先して記述されています。本番環境では、restart: alwaysの追加、リソース制限、セキュアな環境変数の設定など、より本番運用に特化した設定が必要です。
YAML
# 本番用 docker-compose.prod.yml
version: '3.7'
services:
# Laravel API
app:
build:
context: ./api
dockerfile: Dockerfile.prod
image: my-laravel-app:latest
ports:
- "8000:8000"
volumes:
- /path/to/laravel/storage:/var/www/html/storage
environment:
- APP_ENV=production
- DB_HOST=db
#...その他の環境変数
restart: always
networks:
- app-network
# Nginx (SPAの静的配信とAPIへのリバースプロキシ)
web:
image: nginx:1.25.3-alpine
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./frontend/dist:/var/www/frontend
depends_on:
- app
restart: always
networks:
- app-network
# MySQL
db:
image: mysql:8.0
volumes:
- db-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_DATABASE=${DB_DATABASE}
#...その他の環境変数
restart: always
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
db-data:
driver: local
Dockerfileの最適化(マルチステージビルド、コンテナの軽量化)
開発用のDockerfileは、Composerやnpmなどのビルドツールを含んでいるため、コンテナサイズが大きくなります。本番環境では、マルチステージビルドを利用して、最終的な実行に必要なファイルだけをコピーすることで、コンテナを軽量化します。
Dockerfile
# api/Dockerfile.prod
# Stage 1: ビルドステージ
FROM composer:2.5 as composer_stage
WORKDIR /app
COPY . .
RUN composer install --no-dev --optimize-autoloader --no-scripts
# Stage 2: 実行ステージ
FROM php:8.2-fpm-alpine
RUN apk add --no-cache nginx
WORKDIR /var/www/html
COPY --from=composer_stage /app .
COPY . .
RUN php artisan config:cache
RUN php artisan route:cache
RUN php artisan view:cache
#...その他の設定
EXPOSE 8000
CMD ["php", "-S", "0.0.0.0:8000", "-t", "public"]
#--no-devオプションで開発用パッケージを除外したり、php artisan cache:..コマンドでキャッシュを生成しておくことで、起動時間を短縮し、本番環境でのパフォーマンスを向上させます。
Nginxコンテナでの静的コンテンツ配信とリバースプロキシ設定
Laravel APIとVue.js SPAを一つのドメインで運用する場合、Nginxコンテナをリバースプロキシとして利用します。
Nginx
# nginx/nginx.conf
server {
listen 80;
server_name your-domain.com;
root /var/www/frontend;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://app:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
この設定では、http://your-domain.comへのアクセスはVue.jsの静的ファイルにルーティングし、/apiパスへのアクセスはLaravel APIコンテナ(サービス名app)にリバースプロキシします。
環境変数の安全な管理方法
本番環境の認証情報やシークレットキーは、.envファイルに直接記述せず、Docker SecretsやAWS Parameter Store、Vaultなどの専用のサービスを使って安全に管理すべきです。docker-compose.ymlではプレースホルダーを使い、デプロイ時に実際の値を渡します。
YAML
# 環境変数を安全に扱う例
# docker-compose.prod.yml
services:
app:
# ...
environment:
- DB_PASSWORD
secrets:
- db_password
secrets:
db_password:
external: true
ロギング戦略
コンテナログの標準出力化
Dockerの運用において、最も重要な原則の一つが「コンテナログを標準出力・標準エラー出力に統一する」ことです。Laravelはデフォルトでファイルにログを出力しますが、本番環境ではコンテナを再起動するとファイルが失われる可能性があるため、これを標準出力に切り替えます。
PHP
# config/logging.php
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['stderr'], // ここを'stderr'に変更
],
'stderr' => [
'driver' => 'monolog',
'handler' => StreamHandler::class,
'with' => [
'stream' => 'php://stderr',
],
],
//...
]
これにより、docker-compose logsコマンドでアプリケーションログを確認できるようになります。
ログ管理サービス(Fluentd, Logstashなど)への連携
コンテナの標準出力に集約されたログは、単体では管理が困難です。そこで、FluentdやLogstashといったログコレクターを導入し、ログをElasticsearchなどの集中ログ管理システムに転送します。
YAML
# ログ管理サービスとの連携例
# docker-compose.prod.yml
services:
app:
# ...
logging:
driver: "json-file" # or awslogs, fluentd, etc.
options:
max-size: "10m"
max-file: "3"
fluentd:
image: fluent/fluentd:v1.16.2
# ...
docker-compose.ymlのloggingセクションでログドライバーを設定することで、Docker自身がログを収集し、指定された場所に転送するようになります。
Laravelのログ設定とコンテナログへの出力
先ほど説明したように、Laravelのログ設定をstderrに変更することで、アプリケーションログをコンテナの標準出力に出力します。これにより、すべてのログがコンテナのライフサイクルとは独立して管理されるようになります。
エラー監視(Sentry, Bugsnagなど)の導入
ログとは別に、アプリケーションで発生したエラーはSentryやBugsnagといった専用のエラー監視サービスに集約すべきです。これらのサービスは、エラーの発生箇所、スタックトレース、ユーザー情報などを詳細に記録し、開発者に通知してくれます。Laravelにはこれらのサービスと連携するためのライブラリが用意されており、簡単に導入できます。
定期的なバッチ処理の実装
cronを利用したコンテナ内でのバッチ処理
Dockerコンテナ内で定期的なバッチ処理を実行する最もシンプルな方法は、cronデーモンを利用することです。しかし、一つのコンテナに複数の役割を持たせるのは推奨されません。
LaravelのSchedule機能とコンテナの連携
Laravelには、バッチ処理を定義・実行するためのSchedule機能が組み込まれています。この機能を利用する場合、独立したスケジューラーコンテナを作成するのが理想的です。
YAML
# スケジューラーコンテナの追加
# docker-compose.prod.yml
services:
app:
# ...
scheduler:
build:
context: ./api
dockerfile: Dockerfile.prod
image: my-laravel-app:latest
command: php artisan schedule:work --no-daemon
restart: always
networks:
- app-network
commandでphp artisan schedule:workを実行することで、Laravelのスケジューラーが定期的にタスクを実行するようになります。このコンテナはAPIコンテナとは完全に分離されており、APIコンテナに影響を与えることなくバッチ処理を実行できます。
独立したワーカーコンテナの設計
大規模なアプリケーションでは、キュー(redisなど)に登録されたジョブを実行するために、さらに独立したワーカーコンテナを設計します。
YAML
# ワーカーコンテナの追加
# docker-compose.prod.yml
services:
app:
# ...
scheduler:
# ...
worker:
build:
context: ./api
dockerfile: Dockerfile.prod
image: my-laravel-app:latest
command: php artisan queue:work
restart: always
networks:
- app-network
このワーカーコンテナは、php artisan queue:workコマンドを実行し、ジョブキューからタスクを取得して実行する役割を担います。
バッチ処理の実行監視と通知
バッチ処理が正常に実行されているかを監視することも重要です。Laravel Scheduleはbeforeやafterフック、pingOnSuccessなどの機能を提供しており、これらのフックを使って監視サービス(Pingdomなど)に通知を送ったり、エラーが発生した場合にSlackやメールで通知する仕組みを構築できます。
データベースのバックアップとリストア
ボリュームの永続化とバックアップの基本
Dockerのデータベースコンテナは、コンテナが削除されてもデータが失われないように、ボリュームを利用してデータを永続化します。docker-compose.ymlで定義したdb-dataボリュームがその役割を担います。
しかし、このボリュームはDockerホスト上に存在するため、ホストが故障するとデータも失われます。そのため、定期的に外部ストレージにバックアップを保存する仕組みが必要です。
定期的なバックアップスクリプトの作成
Dockerのホストマシン上で、mysqldumpコマンドを使ってデータベースのダンプファイルを生成するシェルスクリプトを作成します。
Bash
#!/bin/bash
# バックアップディレクトリの作成
BACKUP_DIR="/path/to/backup"
TIMESTAMP=$(date +"%Y%m%d%H%M%S")
FILENAME="$BACKUP_DIR/db-backup-$TIMESTAMP.sql"
# Dockerコンテナからmysqldumpを実行
docker-compose exec -T db mysqldump -u root -p"$DB_ROOT_PASSWORD" --all-databases > "$FILENAME"
# 圧縮
gzip "$FILENAME"
echo "Database backup completed: $FILENAME.gz"
このスクリプトをcronやsystemd timerなどを使って、定期的に実行します。
バックアップファイルの外部ストレージ(S3など)への保存
生成したバックアップファイルは、AWS S3、Google Cloud Storage、あるいはrcloneなどを利用して、定期的に外部のオブジェクトストレージに転送します。これにより、ホストマシンの故障からデータを保護できます。
障害発生時のリストア手順
万が一、データベースに障害が発生した場合のリストア手順も明確にしておく必要があります。
- 最新のバックアップファイルを外部ストレージから取得する。
- docker-compose downでサービスを停止する。
- docker-compose up -dで新しいデータベースコンテナを起動する。
- docker-compose exec -T db mysql -u root -p"$DB_ROOT_PASSWORD"コマンドで、取得したバックアップファイルをインポートする。
- docker-compose up -dでサービスを再開する。
この手順を事前にドキュメント化し、テストしておくことが重要です。
まとめとデプロイの自動化
本番運用構成の全体レビュー
本記事で解説した本番運用のためのDocker構成は、以下の要素で構成されています。
- コンテナの分離: API、SPA、DB、スケジューラー、ワーカーなど、役割ごとにコンテナを分離。
- 軽量なイメージ: マルチステージビルドを活用した軽量なDockerイメージ。
- 安全な環境変数管理: .envファイルではなく、専用のサービスで環境変数を管理。
- 標準出力へのロギング: Laravelのログを標準出力に集約し、ログコレクターで集中管理。
- 外部ストレージへのバックアップ: データベースのバックアップを定期的に外部ストレージに保存。
CI/CDパイプラインへの組み込み
これらの本番環境構成は、CI/CDパイプラインに組み込むことで、さらに効率的な運用が可能になります。
GitHub ActionsやCircleCIなどのCI/CDツールを使えば、コードがプッシュされるたびに自動でテストを実行し、問題がなければDockerイメージをビルドしてレジストリにプッシュします。そして、本番サーバー上では、最新のイメージを取得してデプロイを自動化する仕組みを構築できます。
デプロイツール(GitHub Actions, CircleCIなど)の活用
GitHub Actionsのワークフロー例:
YAML
name: Deploy to Production
on:
push:
branches:
- main
jobs:
build_and_deploy:
runs-on: self-hosted # 本番サーバーにGitHub Actionsランナーを設置
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Build Docker images
run: docker-compose -f docker-compose.prod.yml build
- name: Deploy containers
run: docker-compose -f docker-compose.prod.yml up -d --force-recreate
このワークフローは、mainブランチにプッシュされたときにトリガーされ、本番サーバー上でDockerイメージをビルドし、コンテナをデプロイします。
継続的な改善と監視の重要性
一度環境を構築したら終わりではありません。本番環境は常に変化するため、監視と改善が不可欠です。PrometheusやGrafanaなどの監視ツールを導入し、CPU使用率、メモリ消費量、レスポンス時間などを常にチェックすることで、サービスの安定稼働を維持しましょう。
安心安全のホワイト高還元SESに転職を考えている方へ
新しい挑戦に踏み出すことは、人生において重要な一歩です。 転職活動は自分自身を知り、成長する貴重な機会でもあり、夢や成長を追求するためには必要な要素の一つ になるかと思います。 どんな選択をされるにせよ、その決断があなたに取って素晴らしい未来を切り開くことを願っています! グラディートと一緒に誇れるエンジニアを目指しましょう!
■『株式会社グラディート』では受託開発・SES・ブランディングデザイン・事業コンサルティングなどを事業として行う都内のIT企業です。現在、不遇な待遇で困っているエンジニアさんは、ぜひ一度グラディートに相談してみてね!(年収査定・SESへの転職相談も承っております!)
株式会社グラディート採用情報はこちら▼
https://en-gage.net/gradito/
株式会社グラディート公式サイトはこちら▼
https://www.gradito.co.jp/
