Docker 运维入门实战:改了代码不用手动部署——用 GitHub Actions 做一次最简单的 CI/CD
关键词:CI/CD、GitHub Actions、自动部署、SSH、镜像标签、回滚
上一篇我们把 Docker 运维这条线收了尾:服务能对外提供、出了问题能定位、改了代码能安全更新、单个服务失控也不会拖垮整台机器。但还有一个手动的尾巴没去掉:每次改代码,都要自己登录服务器、构建、重启、等健康、再验证。改得越勤,这个流程就越烦,也越容易在某一步手滑。今天把这一步也自动化:改了代码、推上去,服务器自己更新——也就是最简单的 CI/CD。
先说清范围:本文只做一条最简单的流水线,不碰多环境、不碰灰度、不碰 Kubernetes。目标是“推代码就自动部署,且每一步都能验证、能回滚”。本文还是接着前面的结构讲:gateway(Nginx 网关)、web(静态网页)、api(8000 端口的 Python 接口服务),三者都在一台服务器上跑,代码放在 GitHub 仓库里。
一、CI 和 CD 各自在做什么:别被缩写吓到
CI(持续集成)和 CD(持续部署)听起来很大,落到这条线其实就是两件事:
- CI:代码推上去之后,自动做一次“构建能不能过”。对
my-app来说,就是docker compose build api能不能成功,镜像能不能打出来。 - CD:构建通过之后,自动把新镜像部署到服务器上,跑前面讲过的那套更新流程:只动改了的服务、等健康、从网关侧验证。
区分这两步的意义在于:构建失败时,流水线应该停在 CI,不碰服务器——坏代码连上服务器的机会都没有。只有构建通过了,才轮到部署。
二、流水线长什么样:一次推送触发的三步
GitHub Actions 的流水线写在一个 YAML 文件里,放在仓库的 .github/workflows/deploy.yml。一次最简单的版本只有三步:
name: deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 构建镜像
run: docker compose build api
- name: 部署到服务器
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /opt/my-app
git pull
docker compose up -d --no-deps api
docker compose ps api
读这段配置时抓住三个点就够了:第一,on.push.branches: [main] 表示只有推到 main 分支才触发,平时在别的分支上折腾不会误部署;第二,构建和部署是分开的两步,构建失败就不会走到部署;第三,部署那一步沿用的是前面讲过的更新办法——up -d --no-deps api 只动 api,gateway 和 web 不动。
三、密钥怎么放:千万别写进代码里
上面配置里 ${{ secrets.SERVER_HOST }} 这种写法不是占位符,是 GitHub Actions 的 Secrets:先在仓库的 Settings → Secrets and variables → Actions 里填好服务器地址、用户名和 SSH 私钥,流水线运行时再取出来用。日志里会自动把它们打码,不会明文出现在构建记录里。
这一条是 CI/CD 里最容易出事、也最不能省的规矩:服务器地址、账号、密钥、密码,永远不要直接写进仓库文件里。写进了 Git 历史,就算后来删掉,历史记录里也还留着,等于公开了。判断标准很简单:仓库公开之后,你会不会心慌——会,就说明有东西不该在里面。
四、部署完怎么验收:流水线里也要等健康
前面讲过,更新的验收标准是三条:健康状态 (healthy)、服务日志无报错、从网关侧真实请求一次。这三条在流水线里同样不能省,只是从“人手动敲”变成“流水线自动跑”:
# 部署脚本里,重启之后加上这两步再结束
docker compose ps api # STATUS 一列应出现 (healthy)
curl http://localhost/api/health
如果健康检查一直没变 (healthy),或者网关侧请求失败,流水线应该直接报错停下,而不是悄悄算成功——自动部署的意义是“不用人守着”,不是“出问题也不告诉人”。GitHub Actions 里这一步失败,仓库的 Actions 页面会直接标红,推代码的人一眼就能看到。
五、出问题了怎么回滚:自动部署也要留后路
自动部署最怕的不是部署失败,而是“部署成功了,但新版本有问题”。这时回滚的办法和前面讲过的一样,只是触发方式从手动变成“再推一次”:
- 如果是代码问题:把代码回退到上一个能用的提交,再推到
main,流水线会重新部署旧版本。 - 如果是镜像问题:服务器上旧版本的镜像还在(前面讲过,别只用
latest,要打版本标签),直接在服务器上把镜像标签改回上一版,再docker compose up -d --no-deps api,同样等健康、同样从网关侧验证。
回滚的前提还是那句话:旧镜像还在、旧代码能找到。自动部署没有改变这个前提,只是让“重新部署一次”的成本从手动登录服务器变成一次推送。
六、三个新手最容易踩的坑
1. 任何分支一推就部署
流水线只应该由 main 分支触发。开发分支、实验分支一推就上服务器,等于把没验证过的代码直接推给了用户。分支策略就是部署策略的一部分。
2. 构建和部署混成一步
构建失败时还继续往服务器上推,等于把坏代码的部署也自动化了。先构建、构建通过再部署,这个顺序别省。
3. 部署完只看流水线变绿
流水线变绿只说明“命令跑完了”,不说明“服务真的能用”。健康状态、日志、网关侧请求这三条验收,流水线里也要跑,缺一都不能算部署完成。
结语
从单容器、Compose 编排、数据卷、反向代理和 HTTPS、日志与健康检查、滚动更新、资源限制和重启策略,到今天的 CI/CD,这条 Docker 运维的线已经从“手动把服务跑起来”走到了“改了代码、推上去,服务器自己更新、自己验收”。下一篇可以往“服务器不止一台”走一步:多个服务、多个副本时,怎么让它们互相发现、怎么分摊流量——也就是从单机 Compose 往集群的方向看一眼。
- 点赞
- 收藏
- 关注作者
评论(0)