19 10 月 2026
你还在用 FTP 拖文件上线吗?改一行代码传半小时、忘了传哪个文件、半夜回滚找不到旧版本——这些痛,CI/CD 一次解决。这篇讲清楚概念、选型,以及一套跑在自己服务器上的完整自动化部署流水线。代码仓库基础见Git 版本控制与服务器部署。

一、CI/CD 是什么:两个词讲明白

  • CI(持续集成):每次代码提交,自动跑测试、构建,尽早发现问题
  • CD(持续部署/交付):构建产物自动发布到服务器,不用人肉上传

合起来的终极形态:git push = 上线。你只管写代码,测试、构建、发布、回滚全是流水线的。

git push ──> 自动测试 ──> 自动构建 ──> 自动部署 ──> 健康检查 ──> 失败自动回滚
   (你做的)      (CI)                    (CD)

对个人/小团队,CI/CD 的核心价值其实不是"快",是可重复、可回滚、不留脏手工。三个月后你敢不敢在生产环境改代码,取决于有没有这条流水线。

二、方案选型:别一上来 Jenkins

方案适合一句话
GitHub Actions / Gitee Go代码在公网平台免费额度够个人用,首选
Gitea + Drone/Woodpecker代码要私有、全自建轻量组合,吃配置少
Jenkins传统企业、复杂流水线功能全但重,小团队杀鸡用牛刀
Webhook + 脚本极简需求十行脚本的事,别上平台

本文推荐路径:代码在 GitHub/Gitee → 用平台自带的 Actions;代码必须私有自建 → Gitea + Woodpecker。两者思路完全一样,学会一个另一个零成本。

三、实战:GitHub Actions 部署到自己的服务器

场景:一个静态网站/Node 应用,代码在 GitHub,服务器上跑着,要 git push 后自动更新。

第 1 步:服务器准备

# 建一个专用于部署的用户(最小权限原则)
useradd -m -s /bin/bash deploy

# 生成部署专用密钥对(私钥后面放 GitHub,公钥留在服务器)
sudo -u deploy ssh-keygen -t ed25519 -f /home/deploy/.ssh/id_ed25519 -N ""
cat /home/deploy/.ssh/id_ed25519.pub >> /home/deploy/.ssh/authorized_keys

# 只给这个用户必要的 sudo(比如重启服务的权限)
echo "deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart myapp" | sudo tee /etc/sudoers.d/deploy

关键习惯:部署用专用账号 + 专用密钥,别用 root 直登。SSH 加固见SSH 安全加固进阶,密钥登录见SSH 密钥登录指南。

第 2 步:写工作流

仓库里建 .github/workflows/deploy.yml:

name: 部署到生产

on:
  push:
    branches: [main]        # 只有 main 分支推送才触发

jobs:
  test-and-deploy:
    runs-on: ubuntu-latest

    steps:
      # 1. 拉代码
      - uses: actions/checkout@v4

      # 2. 装依赖 + 测试(CI 部分,按项目实际改)
      - name: 安装依赖
        run: npm ci
      - name: 跑测试
        run: npm test

      # 3. 构建
      - name: 构建
        run: npm run build

      # 4. 部署:rsync 到服务器
      - name: 同步到服务器
        uses: burnett01/rsync-deployments@6.0
        with:
          switches: -avzr --delete
          path: dist/
          remote_path: /var/www/myapp/
          remote_host: ${{ secrets.SERVER_IP }}
          remote_user: deploy
          remote_key: ${{ secrets.DEPLOY_SSH_KEY }}

      # 5. 远程重启服务
      - name: 重启服务
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_IP }}
          username: deploy
          key: ${{ secrets.DEPLOY_SSH_KEY }}
          script: |
            sudo systemctl restart myapp
            curl -f http://127.0.0.1:3000/health || exit 1

第 3 步:配 Secrets

GitHub 仓库 → Settings → Secrets and variables → Actions,加三个:

Secret值
SERVER_IP服务器 IP
DEPLOY_SSH_KEY第 1 步生成的私钥全文
(按需)其他令牌数据库密码等

Secrets 里只放必要的——部署私钥、API 令牌。绝不把密码写进仓库文件,哪怕私有仓库。代码泄露的经典事故就是从"顺手提交了 .env"开始的,防范见Git 仓库与代码泄露防范。

第 4 步:推送,看它跑

git push origin main

GitHub 仓库 → Actions 标签页,看到流水线一个个步骤变绿。绿的尽头,服务器上的文件已经更新、服务已重启、健康检查已通过。这就是 git push = 上线。

四、部署策略:从能用到稳

1. 蓝绿部署(零停机)

不停旧服务,起新版本,切流量,停旧的:

# 新版本起在 3001 端口
docker run -d --name myapp-new -p 127.0.0.1:3001:3000 myapp:v2
curl -f http://127.0.0.1:3001/health   # 新版本健康检查
# Nginx 切到 3001(reload 不中断)
nginx -s reload
# 旧版本下线
docker stop myapp-old

Nginx 切换配置见Nginx 反向代理实战。

2. 回滚:流水线里最重要的分支

回滚策略要在出事前想好:

# 方案 A:保留上一个版本的构建产物,出事后重新部署旧 tag
git tag v1.2.1
# 回滚 = 部署 v1.2.1 的产物

# 方案 B:服务器上保留旧版本目录,软链接秒切
ln -sfn /var/www/releases/v1.2.0 /var/www/current
nginx -s reload

原则:回滚必须比部署更快更简单。设计流水线时先回答"出事了两分钟内能不能回到昨天",能,再上线。

3. 数据库迁移:最危险的一步

代码回滚容易,数据库 schema 回滚要命。纪律:

  • 迁移脚本必须向后兼容(先加字段后删字段,分版本做)
  • 迁移在部署前跑,且先备份(见MySQL 备份与恢复)
  • 迁移失败 = 部署中止,不要让半迁移状态上线

五、自建方案:Gitea + Woodpecker(代码不出门)

公司代码不能上公网平台时的组合:Gitea(自托管 Git 服务)+ Woodpecker CI(轻量 CI,一个容器)。

# Gitea
docker run -d --name gitea -p 3000:3000 -p 2222:22 \
  -v /data/gitea:/data gitea/gitea:latest

# Woodpecker(连 Gitea,跑流水线)
docker run -d --name woodpecker -p 8000:8000 \
  -e WOODPECKER_OPEN=true \
  -e WOODPECKER_GITEA=true \
  -e WOODPECKER_GITEA_URL=http://gitea:3000 \
  woodpeckerci/woodpecker-server:latest
# 再加一个 woodpecker-agent 容器真正干活

流水线文件 .woodpecker.yml 语法和 GitHub Actions 大同小异,会一个就会另一个。整套跑在 4核8G 的 NAT 型服务器上毫无压力——"吃配置不吃公网 IP",用 NAT 型比独立 IP 省一半以上。

六、安全清单:CI/CD 是信任链

自动化流水线是攻击者眼中的金矿——攻破它,你的每次部署都是它的后门:

  • [ ] 部署密钥专用,权限最小化(只能 rsync 指定目录 + 重启指定服务)
  • [ ] Secrets 只放平台 Secret 管理,永不提交进仓库
  • [ ] CI 里 npm ci 而非 npm install(锁文件保一致性),并开依赖漏洞扫描
  • [ ] 外部 Action/插件锁版本(@v4.1.0 而非 @v4),防供应链投毒
  • [ ] 部署账号禁止交互登录(usermod -s /usr/sbin/nologin 思路)
  • [ ] 流水线日志不打印敏感信息(token、密码打码)

供应链安全背景见Git 仓库与代码泄露防范,服务器侧加固见服务器安全基线清单。

七、常见问题排查

症状原因解决
Action 卡在 SSH 那步私钥格式/known_hosts私钥整段含首尾行;首次连接加 -o StrictHostKeyChecking=no
Permission denied公钥没进 authorized_keys / 用户错核对 remote_user 和服务器上的 authorized_keys
rsync 删了不该删的--delete 参数确认 path/remote_path 别填错;测试用 --dry-run
部署成功但服务没更新缓存/构建产物没同步检查构建输出目录和 rsync path 一致
定时/回滚不可控没有版本管理上 tag 发布流程,回滚=重新部署旧 tag

部署后效果验证用网站速度测试,排错方法论见服务器故障排查完全手册。


结语

CI/CD 的本质是把"上线"从一门手艺变成一条流水线:可重复、可审计、可回滚。个人开发者用 GitHub Actions,企业内网用 Gitea + Woodpecker——工具不重要,养成"代码即部署"的习惯才重要。下次半夜改生产环境时,你会感谢现在搭流水线的自己。

需要跑 CI 和生产的服务器,可以看看财富云计算:NAT 型云服务器内存档位全、SSD 系统盘、支持热升级,价格比独立 IP 机型省一半以上——跑构建、测试、内部服务这类"吃配置不吃公网 IP"的场景,NAT 型刚好对症。

延伸阅读
  1. Homepage 个人仪表盘:把散落各处的服务收进一个首页
  2. SSL 证书完全指南:免费证书、自动续期与不过期监控
  3. 数据备份体系完全手册:从随手拷到 3-2-1,让数据永远丢不了
  4. 压测与容量规划:上线前就知道你的服务器能扛多少人
  5. AdGuard Home 内网 DNS:全家去广告、防劫持、内网解析一站解决
  6. Hugo 静态博客搭建:秒开的博客,部署只要一个目录
  7. 安全应急响应完全手册:服务器被入侵后,按这个流程走
  8. 游戏服运营完全手册:从开服到停服,把一台服务器运营成百年老店
更多阅读
  1. 上一篇:Memos 个人笔记自托管:flomo 的开源替代,把灵感存在自己服务器上
  2. 下一篇:Loki 日志体系搭建:轻量级日志聚合,Grafana 里查遍所有服务器
发表评论 3

取消回复