两回合制 PHP 项目首回合部署方案
在两回合制部署(如灰度发布、蓝绿部署、A/B 双版本并存)中,首回合部署的核心目标是:让新版本能在不影响现有服务的前提下上线,并具备随时回滚的能力。

下面给出一个完整的 PHP 项目首回合部署方案。
部署模型选择
常见两回合制部署有两种形态:
| 模型 | 首回合含义 | 适用场景 |
|---|---|---|
| 蓝绿部署 | 首回合把新版本部署到"绿"环境,流量仍走"蓝" | 要求零停机、可秒级回滚 |
| 灰度/金丝雀 | 首回合只放小比例流量到新版本 | 大流量、需真实流量验证 |
首回合的共同点:
- 新版本部署到独立环境/路径(不覆盖线上)
- 数据层兼容(新旧版本能同时读写)
- 流量切换开关就绪但默认不切或小比例切
- 回滚路径已准备好
首回合部署流程(以蓝绿为例)
环境隔离
/var/www/
├── blue/ # 当前线上版本 (v1)
├── green/ # 首回合要部署的新版本 (v2)
└── shared/ # 共享资源
├── .env
├── storage/ # 日志、缓存、上传
└── sessions/
Nginx 通过软链切换:
root /var/www/current/public;
代码交付(不覆盖线上)
# 拉取新代码到 green cd /var/www/green git fetch origin git checkout v2.0.0 # 复用共享资源(软链) ln -sfn /var/www/shared/.env .env ln -sfn /var/www/shared/storage storage ln -sfn /var/www/shared/sessions ../../shared/sessions
依赖安装(隔离于 blue)
composer install --no-dev --optimize-autoloader --no-interaction
⚠️ 不要执行
composer update,使用composer.lock保证可复现。
数据层兼容处理(关键)
首回合最大风险是数据库结构变更,原则:
- 只加不改:新增字段、新增表;不删字段、不改字段类型
- 可空默认值:新字段必须有默认值或允许 NULL
- 双写/双读兼容:旧代码遇到新字段无感知
// 迁移脚本示例:只做加法
Schema::table('orders', function (Blueprint $table) {
$table->string('channel')->nullable()->after('status');
});
执行迁移时:
php artisan migrate --force # Laravel # 或 php bin/console doctrine:migrations:migrate --no-interaction # Symfony
缓存/配置预热(针对 green)
php artisan config:cache php artisan route:cache php artisan view:cache php artisan opcache:compile # 若使用
注意:Laravel 的 config:cache 会写进 green 自己的 bootstrap/cache,不影响 blue。
健康检查(切换前必做)
通过内网直接访问 green,不经过线上入口:
curl -H "Host: www.example.com" http://127.0.0.1:8081/health # 或指向 green 的本地 Nginx curl http://green.internal/health
健康检查应包含:
- HTTP 200
- 关键依赖(DB/Redis/MQ)可连
- 版本号正确
// /health 路由
Route::get('/health', function () {
DB::connection()->getPdo();
Redis::ping();
return response()->json([
'status' => 'ok',
'version' => config('app.version'),
]);
});
首回合收尾:不切或小比例切
蓝绿:current 软链保持不变,流量仍走 blue;等待第二回合正式切换。
# 此时保持: # current -> /var/www/blue # 不做任何切换
灰度:在 Nginx/SLB 层放小比例流量:
upstream backend {
server 127.0.0.1:8080 weight=95; # blue
server 127.0.0.1:8081 weight=5; # green
}
首回合部署脚本示例(可落地)
deploy.sh:
#!/usr/bin/env bash
set -euo pipefail
NEW_DIR="/var/www/green"
SHARED="/var/www/shared"
REPO="git@git.example.com:app.git"
TAG="$1"
echo "==> 1. 拉取代码"
cd "$NEW_DIR"
git fetch --tags
git checkout "$TAG"
echo "==> 2. 挂载共享资源"
ln -sfn "$SHARED/.env" .env
ln -sfn "$SHARED/storage" storage
echo "==> 3. 安装依赖"
composer install --no-dev --optimize-autoloader --no-interaction
echo "==> 4. 数据库迁移(向后兼容)"
php artisan migrate --force
echo "==> 5. 预热缓存"
php artisan config:cache
php artisan route:cache
echo "==> 6. 健康检查"
for i in {1..5}; do
if curl -sf http://green.internal/health >/dev/null; then
echo "健康检查通过"
break
fi
sleep 2
[ "$i" = "5" ] && { echo "健康检查失败"; exit 1; }
done
echo "==> 首回合完成:green 就绪,等待第二回合切换"
首回合必须注意的坑
| 项 | 说明 |
|---|---|
| Session 共享 | 若用文件 Session,务必走 shared 目录或改 Redis,否则切换时用户掉线 |
| 上传目录 | 必须共享,不能跟着代码版本走 |
| 日志 | 新旧版本日志分离,便于排查,但要有统一聚合 |
| Cron / Queue | 首回合只跑旧版本的 worker,避免新任务对旧代码不可读 |
| 迁移不可逆 | 首回合迁移只能"加",为了第二回合能回滚,禁止 dropColumn |
| OPcache | 若开启,务必检查 green 独立 OPcache,防止缓存串版本 |
| .env 差异 | 新版本新配置项要有默认值或提前写入 shared/.env |
| CDN / 静态资源 | 带 hash 文件名,避免新旧资源冲突 |
首回合结束的"验收清单"
- [ ] green 代码部署完成,软链共享资源正常
- [ ] composer 依赖安装完成,无版本漂移
- [ ] 数据库迁移已执行,旧版本仍能正常运行
- [ ] 缓存已预热
- [ ] 内网健康检查通过
- [ ] 日志可正常写入
- [ ] Cron/Queue 仍指向 blue
- [ ] 流量仍集中在 blue(或仅 5% 在 green)
- [ ] 回滚脚本已准备(只需切软链或权重)
- [ ] 监控面板已加入 green 维度
总结一句话
首回合 = "部署但不上量":把新版本完整地放到旁路环境,验证通过、数据兼容,流量不动;第二回合才做原子切换或逐步放量,回滚能力在首回合就必须具备,否则两回合制就失去了意义。
如果你能说明具体场景(Laravel/Symfony/自研?K8s 还是裸机?灰度还是蓝绿?),我可以把方案细化到可直接执行的脚本。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播