「本番に上げるたびに手が震える」そんな経験はありませんか?ゼロダウンタイムデプロイの仕組みと実践手順を、Laravel・Next.jsの具体例とともに解説します。
「デプロイ、怖いな」と感じたことはありませんか?
こんな悩みを抱えているエンジニアやWeb担当者の方は多いのではないでしょうか。
git pullしてからphp artisan migrateを叩くまでの数十秒、サービスが落ちている気がする- デプロイのたびにユーザーからエラーの報告が来る
- 金曜の夕方にリリースして、週末に障害対応することになった
- ロールバックの手順が曖昧で、本番トラブル時に焦ってしまう
「デプロイ=怖いもの」という認識は、手順が整備されていない環境では当然の感覚です。しかし適切な仕組みを導入すれば、デプロイはルーティンの作業へと変わります。この記事では、ゼロダウンタイムデプロイの考え方と、実際のプロジェクトで使える具体的な実装手順を解説します。
なぜデプロイ中にダウンタイムが発生するのか
まず、従来型の「怖いデプロイ」がなぜ危険なのかを整理しましょう。
典型的なデプロイ手順はこうです。
# よくある「怖いデプロイ」の流れ
git pull origin main
composer install
npm run build
php artisan migrate # ← ここでDBの状態が変わる
php artisan cache:clear
systemctl restart php-fpm
この手順の問題点は、コードの更新とDB変更がアトミックに行われないことです。git pull でコードが最新になった瞬間から migrate が完了するまでの間、旧コードと新スキーマ、または新コードと旧スキーマが混在する「中間状態」が生まれます。この数秒〜数十秒の間にリクエストが来ると、予期しないエラーが発生します。
さらに php-fpm の再起動中は新規リクエストが受け付けられず、ユーザーには502エラーが返ります。トラフィックの少ない深夜にデプロイするのはこのためですが、それ自体がエンジニアの負担になっています。
flowchart TD
A[git pull 実行] --> B[新旧コード混在期間]
B --> C[composer install]
C --> D[マイグレーション実行]
D --> E[⚠️ 新コード+旧スキーマの危険な瞬間]
E --> F[php-fpm 再起動]
F --> G[502エラー発生帯]
G --> H[デプロイ完了]この構造的な問題を解決するのが、ブルーグリーンデプロイや**シンボリックリンク方式(Deployer方式)**です。
実践:Deployerを使ったゼロダウンタイムデプロイ
Deployerとは
DeployerはPHP製のデプロイツールで、Laravelプロジェクトとの相性が抜群です。仕組みの核心は「シンボリックリンクの切り替え」にあります。新しいリリースを別ディレクトリに完全に準備してから、最後の一瞬にリンクを切り替えるため、切り替え中の「中間状態」が事実上ゼロになります。
/var/www/myapp/
├── current -> releases/20240601_120000/ ← Nginxはここを見ている
├── releases/
│ ├── 20240601_120000/ ← 現在稼働中
│ └── 20240601_115500/ ← 前バージョン(ロールバック用)
└── shared/
├── .env
└── storage/
インストールと基本設定
# Composerでインストール
composer require --dev deployer/deployer
# Laravel用テンプレートで初期化
./vendor/bin/dep init
生成される deploy.php を実案件に合わせて設定します。
<?php
// deploy.php
namespace Deployer;
require 'recipe/laravel.php';
// プロジェクト設定
set('application', 'my-laravel-app');
set('repository', '[email protected]:yourorg/your-repo.git');
set('branch', 'main');
set('keep_releases', 5); // 過去5世代を保持
// shared_files: リリース間で共有するファイル
set('shared_files', ['.env']);
set('shared_dirs', ['storage']);
// writable: Webサーバーに書き込み権限を付与するディレクトリ
set('writable_dirs', ['bootstrap/cache', 'storage']);
// 本番サーバーの設定
host('production')
->set('hostname', 'your-server.example.com')
->set('remote_user', 'deploy')
->set('deploy_path', '/var/www/myapp');
// デプロイ後のタスク定義
after('deploy:failed', 'deploy:unlock');
// マイグレーションを安全なタイミングで実行
task('deploy:migrate', function () {
run('cd {{release_path}} && {{bin/php}} artisan migrate --force');
});
// タスクの順序(重要)
after('deploy:update_code', 'deploy:migrate');
デプロイの実行
# 本番へのデプロイ(これだけ!)
./vendor/bin/dep deploy production
# ロールバックも一発
./vendor/bin/dep rollback production
Deployerは内部でコードのクローン、依存パッケージのインストール、マイグレーション、キャッシュクリアを順番に実行し、すべてが成功した時点でシンボリックリンクを切り替えます。途中でエラーが発生した場合は自動的にロールバックされるため、中途半端な状態で本番が壊れることがありません。
実案件での話:深夜リリースから昼間リリースへ
ある神奈川県内のEC事業者様のケースをご紹介します。Laravelで構築された受発注システムで、月に2〜3回のリリースがあるプロジェクトでした。
課題: デプロイのたびに「念のため深夜2時に作業」というルールが慣習化しており、担当エンジニアの疲弊が深刻でした。手順書もなく、コマンドをターミナルに直打ちしていたため、過去に一度ヒューマンエラーで本番DBのマイグレーションが中断し、数時間の障害が発生した経験がありました。
解決策: Deployerの導入とGitHub Actionsとの連携。main ブランチへのマージをトリガーにCIが走り、テスト通過後に自動でステージング環境へデプロイ。本番へのデプロイはSlackの承認ボタン一押しで実行できる体制を構築しました。
結果: 深夜作業がゼロになり、デプロイは平日昼間に実施できるようになりました。ロールバックも dep rollback production 一発で完了するため、万一の際の復旧時間が大幅に短縮されています。
よくある失敗パターンと対処法
サーバー管理、丸ごとお任せください
サーバー保守・運用
監視・障害対応・パフォーマンス改善まで、安定稼働をサポートします
※ 通常1営業日以内にご返信します
まとめと次のステップ
ゼロダウンタイムデプロイは「大企業の贅沢な仕組み」ではありません。Deployerを使えば、中規模のLaravelプロジェクトでも数時間で導入できます。一度仕組みを整えると、デプロイの心理的コストが劇的に下がり、リリース頻度を上げやすくなります。それがサービス改善のサイクルを加速させることにつながります。
まず取り組むべきステップは以下の通りです。
Deployerの設定ファイルの書き方やGitHub Actionsとの連携について「自社の環境に当てはめるとどうなるか」が判断しにくい場合は、ぜひFivenine Designにご相談ください。既存の構成を拝見した上で、無理のない移行プランをご提案します。