You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GitHub push/PR合并成功后触发服务器webhook实现CI/CD的方法

触发时机认知纠正

你此前的认知有误。GitHub Actions的事件触发逻辑完全是在对应操作生效后才执行:

  • push事件:仅当目标分支的代码已经完整推送到GitHub远端仓库、分支引用更新完成后,才会触发工作流运行,不存在“push未生效就先跑Actions”的情况
  • PR合并场景:只要监听pull_request事件的closed类型,同时增加github.event.pull_request.merged == true的判断,触发时机就是PR合并动作完全完成、代码已经合入目标分支之后,完全满足你“操作生效后再触发部署”的要求。
符合约束的CI/CD实现方案

以下方案均不依赖DockerHub,仅使用Ubuntu系统自带工具与GitHub Actions,适配你当前Docker Compose部署的Django+gunicorn+PostgreSQL+Traefik栈。

方案1:SSH直连部署(推荐,维护成本最低)

不需要在服务器额外部署常驻webhook服务,直接通过GitHub Actions在触发条件满足后,经SSH连接到服务器执行部署操作,是最简洁的实现方式。

实施步骤

  • 服务器侧准备
    • 新建独立的部署系统账号,不要直接使用root,给该账号开放项目目录的读写权限、Docker执行权限
    • 生成专属的SSH部署密钥对,将公钥写入部署账号的~/.ssh/authorized_keys文件
    • 确认服务器安全组允许SSH端口入站访问
  • GitHub仓库配置
    进入仓库Settings -> Secrets and variables -> Actions,添加以下加密变量:
    • SERVER_SSH_KEY:部署密钥对应的私钥内容
    • SERVER_HOST:Ubuntu服务器的公网IP
    • SERVER_USER:部署用的系统用户名
    • PROJECT_PATH:项目在服务器上的绝对存储路径,例如/data/django-app
  • 工作流编写
    在仓库内新建.github/workflows/dev-deploy.yml文件,内容参考如下:
name: Dev Environment Auto Deploy
on:
  push:
    branches: [ "dev" ]
  pull_request:
    branches: [ "dev" ]
    types: [ closed ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Verify deploy condition
        id: condition
        run: |
          if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.pull_request.merged }}" != "true" ]]; then
            echo "skip=1" >> $GITHUB_OUTPUT
          else
            echo "skip=0" >> $GITHUB_OUTPUT
          fi

      - name: Execute remote deploy
        if: steps.condition.outputs.skip == 0
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SERVER_SSH_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          cat > ~/.ssh/config <<EOF
          Host target
            HostName ${{ secrets.SERVER_HOST }}
            User ${{ secrets.SERVER_USER }}
            StrictHostKeyChecking no
          EOF

          ssh target << 'REMOTE_SCRIPT'
            cd ${{ secrets.PROJECT_PATH }}
            git pull origin dev
            # 仅构建业务镜像,公共镜像无需重新编译
            docker compose build --no-cache gunicorn
            # 执行Django必要变更
            docker compose run --rm gunicorn python manage.py migrate --noinput
            docker compose run --rm gunicorn python manage.py collectstatic --noinput
            # 重启变更的服务
            docker compose up -d
            # 清理无用镜像释放磁盘空间
            docker image prune -f
          REMOTE_SCRIPT

方案2:自定义Webhook部署(匹配你最初的构思)

如果希望在服务器侧保留统一的部署入口,可以用Ubuntu原生工具实现轻量webhook服务,不需要引入第三方服务。

实施步骤

  • 服务器侧配置
    • 编写部署脚本/usr/local/bin/app-deploy.sh,内容和方案1中远程执行的脚本一致,赋予可执行权限
    • 用系统自带工具实现简单的HTTP接口:可以用socat监听端口,收到带正确预共享密钥的POST请求就调用部署脚本,也可以直接通过已部署的Traefik做路由转发,不需要额外装Nginx
    • 编写systemd服务配置,让webhook进程开机自启、异常自动重启
    • 务必给webhook接口加签名/密钥校验,禁止无认证调用,防止被恶意扫描触发部署
  • GitHub Actions配置
    触发条件和判断逻辑和方案1完全一致,在确认满足部署条件后,直接在工作流中用curl向服务器的webhook地址发起带密钥的POST请求即可。不要直接使用GitHub仓库原生的webhook功能——原生webhook会在PR创建、更新、关闭等所有相关事件发生时发请求,需要自己写大量过滤逻辑,误触发概率高。
部署注意事项
  • PostgreSQL是有状态服务,执行docker compose up -d时不要加删除卷的参数,避免数据库数据丢失,正常重启流程不会影响存储卷内的数据
  • Traefik配置无变更的话,部署过程中不会重启,gunicorn服务重启时Traefik会自动摘除流量,等服务健康检查通过后再导入流量,部署过程基本无感知
  • 不需要重新构建PostgreSQL、Traefik等公共基础镜像,仅构建自己的Django+gunicorn业务镜像即可,大幅缩短部署时间
  • 如果项目有前端构建、依赖安装等步骤,直接放在服务器侧的部署脚本里执行即可,不需要在Actions中构建后传输文件,流程更简单易维护

内容的提问来源于stack exchange,提问作者Dmitriy Lunev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 04:48:04