TUTORIAL

自动化部署 CI/CD 配置

自动化部署 CI/CD 配置

自动化部署 CI/CD 配置

手动部署是什么体验?SSH 登录服务器、拉代码、装依赖、跑迁移、重启服务、祈祷没出错——每次上线都像拆炸弹。CI/CD(持续集成/持续交付)把这些步骤自动化:代码推到仓库后,流水线自动跑测试、构建、部署,全程无需人工干预。本篇以 GitHub Actions 为例,搭建一条从提交到上线的完整自动化流水线。

一、CI:持续集成与自动化测试

CI 的核心是"频繁集成、自动验证"——每次代码推送到仓库,自动运行测试,确保改动没破坏现有功能。这能把"合并后才发现冲突"的痛点前置到"提交时即发现"。CI 流水线通常包含:代码检出、安装依赖、代码规范检查、单元测试。任何一步失败就中止,不允许问题代码进入主分支。

# .github/workflows/ci.yml —— CI 流水线
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      # 1. 检出代码
      - uses: actions/checkout@v4

      # 2. 设置 PHP 环境
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.1'
          extensions: pdo_mysql, mbstring, gd, redis
          coverage: xdebug

      # 3. 安装 Composer 依赖(带缓存加速)
      - name: Get Composer Cache Directory
        id: composer-cache
        run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
      - uses: actions/cache@v3
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-
      - run: composer install --prefer-dist --no-interaction

      # 4. 代码规范检查(PSR-12)
      - run: ./vendor/bin/phpcs --standard=PSR12 app/

      # 5. 单元测试(带覆盖率)
      - run: ./vendor/bin/phpunit --coverage-text
        env:
          DB_HOST: 127.0.0.1
          DB_DATABASE: test_db

      # 6. 测试通过后通知(可选)
      - name: Notify Success
        if: success()
        run: echo "CI 通过,可以合并"

依赖缓存是加速 CI 的关键——Composer 下载依赖很慢,但依赖版本不变时可以复用缓存。上面配置用 hashFiles('**/composer.lock') 做 cache key,lock 文件没变就直接用缓存,从几分钟降到几秒。尧图项目加上缓存后,CI 平均耗时从 4 分钟降到 90 秒,开发者反馈"推代码马上知道结果"。

二、CD:构建镜像与推送

测试通过后进入 CD 阶段——构建可部署的产物。容器化项目就是构建 Docker 镜像并推送到镜像仓库。这一步只在 main 分支推送时触发(不是每次 PR 都构建),产物打上版本标签,既用于本次部署也留作回滚备份。

# .github/workflows/deploy.yml —— CD 流水线
name: Deploy

on:
  push:
    branches: [main]    # 只有 main 分支触发部署

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    needs: test          # 依赖 CI 测试通过(引用 ci.yml 的 job)
    steps:
      - uses: actions/checkout@v4

      # 1. 生成版本号:git 短哈希 + 时间戳
      - name: Generate Version
        id: version
        run: echo "tag=$(date +'%Y%m%d')-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT

      # 2. 登录镜像仓库(阿里云 ACR)
      - name: Login to Registry
        uses: docker/login-action@v3
        with:
          registry: registry.cn-hangzhou.aliyuncs.com
          username: ${{ secrets.REGISTRY_USER }}
          password: ${{ secrets.REGISTRY_PASS }}

      # 3. 构建并推送镜像
      - name: Build and Push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            registry.cn-hangzhou.aliyuncs.com/ldpk/app:${{ steps.version.outputs.tag }}
            registry.cn-hangzhou.aliyuncs.com/ldpk/app:latest

      # 4. 把版本号传给部署步骤
      - name: Save Version
        run: echo ${{ steps.version.outputs.tag }} > version.txt
      - uses: actions/upload-artifact@v3
        with:
          name: version
          path: version.txt

注意 secrets.REGISTRY_PASS 这类敏感信息绝不能写在配置文件里,要用 GitHub Secrets 加密存储。尧图所有项目的数据库密码、API 密钥、SSH 私钥都放在 Secrets 里,配置文件只引用 ${{ secrets.XXX }},泄露风险降到零。镜像标签同时打版本号和 latest——版本号用于回滚,latest 供总是拉最新的场景。

三、服务器部署与回滚

镜像推到仓库后,最后一步是登录服务器拉取新镜像并重启。推荐用蓝绿部署或滚动更新实现零停机——新旧版本平滑切换,用户无感知。同时保留快速回滚能力:出问题秒切回上个版本。

  # deploy.yml 的部署 job(接上文)
  deploy:
    runs-on: ubuntu-latest
    needs: build-and-push
    steps:
      # 1. 取版本号
      - uses: actions/download-artifact@v3
        with: { name: version }

      # 2. SSH 登录服务器执行部署
      - name: Deploy to Server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/ldpk
            TAG=$(cat version.txt)
            export TAG=$TAG
            # 拉取新镜像
            docker-compose -f docker-compose.prod.yml pull
            # 滚动重启(Nginx 前置负载均衡,逐个替换容器)
            docker-compose -f docker-compose.prod.yml up -d --remove-orphans
            # 健康检查:等新容器就绪
            sleep 10
            curl -sf http://localhost/health || exit 1
            # 清理旧镜像
            docker image prune -f
            echo "部署成功:$TAG"

  # 回滚 job(手动触发)
  rollback:
    runs-on: ubuntu-latest
    if: github.event_name == 'workflow_dispatch'
    steps:
      - name: Rollback
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/ldpk
            # 切回上个版本镜像
            export TAG=$(cat .last_version)
            docker-compose -f docker-compose.prod.yml up -d
            echo "已回滚到:$TAG"

健康检查是部署的安全网——新容器启动后先探测 /health 接口,返回正常才算部署成功,否则立即告警。尧图曾因漏配健康检查,新版本启动失败但流水线显示成功,半小时后才发现站点挂了。加上检查后这类问题零发生。回滚要设计成"一键触发"——在 GitHub Actions 界面手动跑 rollback workflow,输入要回滚到的版本号即可。尧图要求每次部署后保留上个版本镜像 7 天,确保随时能回滚。这套 CI/CD 流水线让尧图的部署频率从"一周一次"提升到"一天多次",且每次部署都是按钮一按的事,再也不用半夜 SSH 抢修了。

返回教程列表