本文目录导读:

设计一个 PHP 项目的 CI/CD 流水线,需要考虑 PHP 生态的特殊性(如依赖管理、扩展、环境配置)以及现代 DevOps 实践(如容器化、自动化测试、灰度发布)。
以下是针对 PHP 项目 的 CI/CD 流水线设计方案,分为流水线架构、核心阶段拆解、关键技术选型和高级优化四个部分。
总体流水线架构(三层)
建议采用 GitLab CI / GitHub Actions 作为引擎,结合 Docker 作为构建环境,形成以下闭环:
graph LR
A[开发提交代码] --> B(CI 阶段: 代码静态分析)
B --> C(CI 阶段: 单元测试)
C --> D(CI 阶段: 构建工件)
D --> E(CD 阶段: 部署到测试环境)
E --> F{人工确认/门禁}
F --> G(CD 阶段: 部署到生产)
G --> H[监控回滚]
核心阶段设计与关键步骤
流水线通常包含以下 5 个主要 Stage。
阶段一:静态分析与规范检查(SAST)
目的:尽早发现代码异味、安全问题,避免带病进入测试。
- 语法检查:
php -l(逐文件扫描,防止语法错误)。 - 代码规范(PSR-12):
phpcs或php-cs-fixer。 - 静态分析(发现潜在Bug):
phpstan(建议level: 8) 或psalm,这是 PHP 项目质量的核心,能捕获类型错误和未定义变量。 - 安全扫描(SAST):
php-security-audit或集成 Snyk 检查composer.lock中的依赖漏洞。 - 推荐工具:
phpunit+roave/security-advisories。
阶段二:单元测试与集成测试
目的:验证业务逻辑。
- 依赖安装:使用
composer install --prefer-dist --no-interaction --no-scripts确保可复现性。 - 测试执行:
- 单元测试:
phpunit --testsuite unit。 - 集成测试(如有数据库):启动一个临时的 MySQL/Redis 容器,运行
phpunit --testsuite integration。
- 单元测试:
- 覆盖率门槛:配置
Xdebug或PCOV,使用 SonarQube 或 Coveralls 收集覆盖率,若低于 80% 则阻止合并。
阶段三:构建与打包(关键步骤)
目的:生成发布工件,确保“一次构建,到处运行”。
-
方案 A:传统部署(不适合规模大场景)
- 使用
git archive打包当前版本代码。 - 强烈建议: 如果你使用框架,不要把
.env文件打进包,使用环境变量注入。
- 使用
-
方案 B:Docker 镜像构建(现代首选)
- 基础镜像:建议使用
php:8.3-fpm-alpine。 - Dockerfile 关键点:
- 多阶段构建:第一阶段安装
composer和源码,第二阶段仅拷贝运行依赖,减小体积。 - 安装 PHP 扩展(如
pdo_mysql、redis)时组合docker-php-ext-install。
- 多阶段构建:第一阶段安装
- 构建命令:
docker build -t $IMAGE_TAG -f docker/Dockerfile.prod . - Push 镜像:推送到私有仓(如 Harbor、ECR)。
- 基础镜像:建议使用
重要建议:在 CI 阶段完成
composer install --no-dev和npm run prod(前端资源编译),确保产物里不含开发环境依赖。
CD 部署阶段设计(环境策略)
环境划分与门禁
推荐采用 “三环境”流程:
- 开发环境(Dev):代码合并到
develop分支,自动触发部署,此环境无人工干预。 - 预发布环境(Staging/Pre-prod):代码合并到
main分支或打releaseTag,部署后运行冒烟测试(curl健康检查 + 关键接口测试)。 - 生产环境(Prod):手动点击“执行”或通过 Prometheus + Alertmanager 检测到 Staging 无异常后,人工授权触发。
部署策略(针对 PHP)
- 方案 1:容器化部署(推荐)
- 如果使用 Kubernetes:使用
kubectl set image deployment/php-app php-app:$IMAGE_TAG进行滚动更新。 - 如果使用 Docker Compose:拉取新镜像,执行
docker-compose up -d --no-deps php-fpm。
- 如果使用 Kubernetes:使用
- 方案 2:传统服务器(Rsync/SFTP)
- 将 CI 打包好的
dist.tar.gz传输到服务器/var/www/html/releases/{build_id}/。 - 符号链接切换:
ln -sfn /var/www/html/releases/{build_id} /var/www/html/current。 - 回滚机制:仅需将软链接指向上一个 release 即可实现秒级回滚。
- 将 CI 打包好的
数据库变更(PHP 项目痛点)
- 必须包含 数据库迁移(Migration) 步骤。
- 建议:使用
phinx或doctrine/migrations。 - 流水线流程:在部署应用代码之前,先执行
vendor/bin/phinx migrate,若迁移失败,则中止部署,避免新旧代码不兼容。
关键技术选型与脚本示例
示例(GitLab CI,.gitlab-ci.yml 核心片段):
stages:
- test
- build
- deploy
# 全局缓存 PHP 依赖
cache:
paths:
- vendor/
variables:
MYSQL_ROOT_PASSWORD: root
APP_ENV: testing
# 1. 测试阶段
test:php:
stage: test
image: php:8.3-cli
services:
- mysql:8.0
script:
- apt-get update && apt-get install -y libzip-dev unzip
- docker-php-ext-install pdo_mysql zip
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
- composer install --prefer-dist --no-interaction
- cp .env.ci .env # 使用 CI 专用配置
- php artisan migrate --force
- vendor/bin/phpunit --coverage-text --colors=never
only:
- merge_requests
- main
# 2. 构建镜像阶段
build:docker:
stage: build
image: docker:20.10.16
services:
- docker:20.10.16-dind
scripts:
- docker build -t $REGISTRY_SERVER/php-app:$CI_COMMIT_SHORT_SHA .
- docker push $REGISTRY_SERVER/php-app:$CI_COMMIT_SHORT_SHA
only:
- main
# 3. 部署到预发布
deploy:staging:
stage: deploy
image: alpine:3.15
before_script:
- apk add --no-cache curl
script:
- curl -X POST --fail "$DEPLOY_HOOK_URL" # 调用 K8s Webhook 或 ArgoCD
environment:
name: staging
only:
- main
# 4. 生产部署(需手动批准)
deploy:production:
stage: deploy
image: alpine:3.15
script:
- echo "Deploying to Production"
- curl -X POST --fail "$PROD_DEPLOY_HOOK_URL"
environment:
name: production
when: manual # 关键:设置手动触发
only:
- tags # 只有在打 Tag 时才允许触发
高级优化与避坑指南
性能优化
- 构建缓存:
composer.lock未变化时,跳过composer install;Docker build中使用--cache-from标记复用层缓存。 - 并行执行:将 PHPCS、PHPStan、PHPUnit 拆分为 3 个并行的 GitLab Jobs,减少总时长(Go Test 和 Static Analysis 无依赖)。
PHP 特有坑
- 时区问题:在 Dockerfile 中必须设置
RUN echo "date.timezone=UTC" > /usr/local/etc/php/conf.d/timezone.ini,否则测试会因时区差异报错。 - OPcache:部署新版本后,需要在生产服务器上执行
php artisan cache:clear或重启 PHP-FPM,否则旧代码仍在内存中。 - Composer 内存不足:构建时设置
COMPOSER_MEMORY_LIMIT=-1。
安全门禁
- Secret 管理:避免在 CI 中写明文密码,使用 GitLab CI Variables(勾选 Masked)或 Vault(如 HashiCorp Vault)。
- 依赖锁定:必须将
composer.lock提交到 Git 仓库,这样才能保证生产环境和测试环境的依赖包版本一致。
一个健壮的 PHP CI/CD 流水线可以归结为三个核心点:
- 严格的门禁(静态分析 + 覆盖率)= 质量保障。
- Docker 化 + 不可变工件 = 交付一致性。
- 软链接切换 + 版本化发布目录 = 快速回滚。
如果你的项目是 Laravel,强烈建议在 CI 阶段执行 php artisan route:cache、config:cache 和 event:cache,并将这些缓存文件包含在构建产物中,这样可以显著减少线上应用启动时间,避免“并发缓存锁”问题。