你还在用 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 mainGitHub 仓库 → 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-oldNginx 切换配置见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 型刚好对症。
延伸阅读
- Homepage 个人仪表盘:把散落各处的服务收进一个首页
- SSL 证书完全指南:免费证书、自动续期与不过期监控
- 数据备份体系完全手册:从随手拷到 3-2-1,让数据永远丢不了
- 压测与容量规划:上线前就知道你的服务器能扛多少人
- AdGuard Home 内网 DNS:全家去广告、防劫持、内网解析一站解决
- Hugo 静态博客搭建:秒开的博客,部署只要一个目录
- 安全应急响应完全手册:服务器被入侵后,按这个流程走
- 游戏服运营完全手册:从开服到停服,把一台服务器运营成百年老店

[...]}Nginx 基础见Nginx 安装与配置,HTTPS 证书见SSL 证书完全指南。成本彩蛋:静态站资源消耗约等于零,2核2G 轻量服务器能挂十几个静态站——每个站一个 Nginx server 块 + 一个目录的事。想更省,直接上 CDN/对象存储,源站钱都省了,见静态网站 CDN 部署。方式 2:GitHub Pages / Vercel / Netlify代码推 GitHub,平台自动构建发[...]
2026-10-19 回复
[...]Requests/sec: 2804.26Latency Avg 145ms:平均响应,还行Max 1.99s:长尾已经出现,P99 会更差Req/Sec 2804:这台配置的实测 QPS 上限结合系统监控:QPS 上不去时,看是 CPU 100%(应用瓶颈)、IO 100%(数据库/磁盘)、还是带宽打满先崩的组件决定优化方向:瓶颈现象根因优化CPU 100%,QPS 到顶应用计算密集加核 [...]
2026-10-19 回复
[...]netsh advfirewall firewall add rule name="GameRange" dir=in action=allow protocol=UDP localport=26901-26903完整的 Windows 环境准备(远程桌面连接、运行库安装、SteamCMD 用法、开机自启、8 个常见坑)见:《Windows 云服务器开服通用前置教程》→ htt[...]
2026-10-19 回复