CI/CD从零到实战:3天自动部署,节省90%重复劳动
刚开始我也以为CI/CD是那种高大上的东西,得资深DevOps才能搞。结果被线上bug逼疯之后,花了一个周末硬啃下来,发现这玩意儿其实没那么玄乎——关键是你得知道哪些坑别踩,哪些配置是白送的。
本文纯实战,不讲那些虚头巴脑的理论。我直接拿我们项目组从手动部署到自动流水线的过程说事儿,你跟着走一遍,3天之内肯定能跑起来。
(开篇配图:一张对比图,左边是手动部署的混乱流程图——打码、上传、查日志、反复崩溃;右边是CI/CD流水线的清晰箭头——Push代码→自动测试→自动部署→上线成功。配文:“手动部署 VS CI/CD,你选哪个?”)
先搞清楚CI/CD到底解决了什么
0px rgba(0,0,0,.08);”
loading=”lazy” width=”800″ height=”500″>
rgba(0,0,0,.08);”
loading=”lazy” width=”800″ height=”500″>
别被那些专业名词唬住。CI/CD说白了就是两件事:
- CI(持续集成):你每次提交代码后,自动跑测试、自动检查代码质量。防止你写的垃圾代码污染主分支。
- CD(持续交付/部署)):测试通过后,自动把代码部署到服务器上。省掉你手动打jar包、传服务器、重启的那些破事儿。
我以前团队有个哥们儿,每天下午4点准时手动部署,然后盯着屏幕等5分钟,再查日志。一天至少重复5次,每次提心吊胆。后来上了流水线,他每天多出两小时喝茶摸鱼。
工具选型:别纠结,直接GitHub Actions
市面上工具一大堆:Jenkins、GitLab CI、CircleCI、Travis CI……但我建议新手直接上GitHub Actions。原因就仨:
你要是用GitLab,也有类似的CI,但GitHub用户量大,社区模板多,抄作业方便。
第一步:搭一个最简单的CI——代码检查+单元测试
先别想部署那一步,先让代码提交后自动跑检查。这是CI的核心。
我拿一个前端React项目举例。在项目根目录建个.github/workflows/ci.yml:
“yaml
name: CI - Code Check
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- name: 拉取代码
uses: actions/checkout@v3
- name: 搭建Node环境
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: 安装依赖
run: npm ci
- name: 代码风格检查
run: npm run lint
- name: 运行单元测试
run: npm test -- --coverage
- name: 上传测试报告
uses: actions/upload-artifact@v3
if: always()
with:
name: coverage-report
path: coverage/
`
为什么要这么写?
- on.push
和on.pull_request:只在主分支和拉取请求时触发,避免每次提交都跑一遍浪费时间 - npm ci
:比npm install更快更严格,会根据package-lock.json安装,避免团队间环境不一致 - upload-artifact
:这个很有用,测试报告会上传到GitHub,你可以直接在Action页面下载查看覆盖率
我第一次写完这个配置,push上去,看到那个绿色勾勾亮起来的时候,真有种“老子终于自动化了”的爽感。
第二步:加上Docker构建和镜像推送
这里有个坑:千万别在CI里现编译代码。太慢了。正确的做法是:先把代码编译成可部署的产物(比如Docker镜像),推到镜像仓库,然后部署时直接拉镜像。
我用的阿里云容器镜像服务(ACR),国内速度快,价格便宜。你也可以用Docker Hub,但国内访问慢得想砸电脑。
`yaml
name: Build and Push Docker Image
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 登录到阿里云镜像仓库
uses: aliyun/acr-login@v1
with:
region-id: cn-hangzhou
access-key-id: ${{ secrets.ALIYUN_ACCESS_KEY_ID }}
access-key-secret: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }}
instance-id: your-instance-id
- name: 构建并推送镜像
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
registry.cn-hangzhou.aliyuncs.com/my-project/my-app:latest
registry.cn-hangzhou.aliyuncs.com/my-project/my-app:${{ github.ref_name }}
`
为什么用tag触发而不是branch?
因为生产环境部署应该慎重。只有打了v1.0.0这种版本标签的提交,才构建生产镜像。main分支的提交只跑CI,不构建镜像,这样可以防止手滑。
第三步:自动部署到服务器——最爽的一步
这里我用的方案是:阿里云ECS + Docker Compose。GitHub Actions通过SSH连接到服务器,执行部署脚本。
`yaml
name: Deploy to Production
on:
workflow_run:
workflows: ["Build and Push Docker Image"]
types:
- completed
jobs:
deploy:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: SSH到服务器并部署
uses: appleboy/ssh-action@v0.1.5
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /opt/my-app
docker-compose pull
docker-compose up -d --remove-orphans
docker image prune -f
`
这个设计真的反人类吗? 第一次看这个配置时我也觉得“怎么这么绕”,但实际跑通了就真香。它解决了三个痛点:
会先启动新容器,再停掉旧的防止服务器磁盘爆满(中间配图:一张CI/CD流水线的可视化截图,展示GitHub Actions界面上三个工作流的运行状态——CI检查绿勾、构建镜像绿勾、部署成功绿勾。配文:“三个工作流串联起来,就是一条完整的CI/CD流水线”)
避坑指南:我连踩的三个坑
坑1:Secrets管理不当
一开始我把服务器密码直接写在YAML文件里,push上去后GitHub报警说检测到敏感信息。吓得我赶紧改了。
正确做法:所有敏感信息(密码、密钥、Token)都存到GitHub仓库的Settings → Secrets and variables → Actions里。然后在YAML中用${{ secrets.XXX }}引用。
坑2:测试环境和生产环境配置混在一起
有次我改了数据库配置,结果CI跑测试时连接了生产数据库,差点酿成大祸。
解决方案:用环境变量区分。process.env.NODE_ENV设为test、development、production。不同环境加载不同配置文件。
坑3:deploy脚本没有幂等性
我写的第一个部署脚本是“先停掉所有容器,再启动新的”。结果有一次部署到一半,网络断了,容器全停了但新的没起来,服务挂了10分钟。
改进后:用docker-compose up -d –no-deps先启动新容器,等新容器运行正常后,再停掉旧容器。整个过程服务不间断。
还有个技巧:自动回滚
这个是我后来加的,但强烈推荐。如果部署后监控到错误率飙升,自动触发回滚。
思路是在服务器上保留上一次成功的镜像版本,然后写一个健康检查脚本:
`bash
#!/bin/bash
健康检查脚本,部署后运行
sleep 30
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/health)
if [ "$HTTP_STATUS" -ne 200 ]; then
echo "Health check failed! Rolling back..."
docker-compose down
docker-compose -f docker-compose.rollback.yml up -d
exit 1
fi
echo "Deployment successful!"
`
然后在GitHub Actions里加一个步骤:
`yaml
- name: 健康检查
run: |
sleep 20
if ! curl -f http://localhost:8080/health; then
echo "Deploy failed, rolling back"
# 触发回滚工作流
fi
`
(结尾配图:一张时间线对比图,展示手动部署流程耗时(平均8分钟/次,含等待和排查)vs CI/CD自动化流程耗时(平均45秒/次)。配文:“从8分钟到45秒的差距”)
总结一下,你可以立刻用的三个点
最后说一句:CI/CD不是大公司的专利。哪怕你只有一个个人项目、一台低配服务器,也值得花半天时间搞一套。当你习惯了“push代码就完事儿”的爽感,就再也回不去了。