为什么我们小厂后端要搞部署工具?从线上炸锅到简历镀金的血泪史
上周五晚上十点,我正窝在出租屋里啃着冷掉的黄焖鸡,突然钉钉连环炸——"用户登录不上了!"、"支付接口502!"、"快救火!!!"。我手一抖,汤汁溅到了 MacBook 的触控板上。
又双叒叕是部署问题。
这已经是我们这个月第三次因为手动部署翻车了。作为北京一家不到50人的创业公司里,独立负责用户中心这条业务线的后端开发,我一度以为自己是个写代码的,结果发现80%的时间都在当“人肉运维”。
被逼上梁山:从“改一行代码上线”到“不敢动生产环境”
说来你可能不信,半年前我们公司的部署流程是这样的:
- 我在本地
git commit -m "fix login bug" - 登录生产服务器(对,就一台)
git pull origin main- 手动重启 Java 进程(有时候忘了 kill,两个进程跑着,内存爆了)
- 祈祷没出问题
听起来像段子?但这就是现实。我们团队没有专职运维,产品经理天天催“这个需求很简单,改个字段就行,今晚能上线吗?”,测试同学一边测一边吐槽“你们后端是不是又把数据库连错了?”
最离谱的是去年双11前夕,我为了修复一个紧急订单状态问题,在凌晨三点直接在生产机器上 vim 修改了 .properties 配置文件,结果手滑多打了个冒号,服务直接起不来。那晚,老板站在工位后面沉默了十分钟,然后说:“要不……咱们搞个部署工具?”
我当时心里想:早干嘛去了?
面试题挑战:面试官问我“你们怎么部署的”,我差点社死
其实推动我认真搞部署工具的,还有一个“私心”——跳槽。
上个月我去面了一家大厂,终面时技术总监随口问:“你们团队是怎么做 CI/CD 的?”
我支支吾吾说了半天“就是 git pull + 手动重启”,对方眼神明显变了,最后委婉地说:“嗯……经验很‘原始’啊。”
那一刻我意识到:不会部署自动化,简历上就少了一块硬通货。现在随便看个中级后端岗 JD,不写着“熟悉 Jenkins/GitLab CI/ArgoCD”都不好意思发。更别说 DevOps、SRE 这些词早就不是大厂专属了——连我们这种小破厂,投资人尽调时都会问“你们有自动化部署能力吗?”
于是,我下定决心:哪怕是为了下次面试不被问住,也得把这套东西搞起来。
选型纠结症:Jenkins?GitLab CI?还是直接上云原生?
一开始我想直接上 Jenkins ——毕竟老牌、文档多、插件全。但一想到要在那台已经跑了 MySQL、Redis、Nginx 和我们三个微服务的 4C8G 小破服务器上再塞个 Jenkins master + agent,我就头皮发麻。
而且 Jenkins 的 Groovy 脚本写起来真的反人类,光是配置 Webhook 触发就折腾了两天,还老是权限报错:
ERROR: Failed to push image: denied: requested access to the resource is denied
我一度想放弃,直到隔壁组前端小哥(对,就是那个喜欢研究 Lottie 动画的)说:“你们后端还在玩 Jenkins?我们前端都用 GitHub Actions 了,YAML 写起来贼清爽。”
一句话点醒梦中人。
考虑到我们代码托管在 GitLab(公司为了省钱自建的),最终决定用 GitLab CI + Docker + 自建 Runner 的组合。理由很现实:
- 不用额外买 SaaS 服务(省💰)
- 和现有 Git 仓库无缝集成
- YAML 配置比 Groovy 友好多了
- 我还能顺手学点 Docker,给简历加个“容器化”标签
实战:从零搭建自动部署流水线
说干就干。第一步是申请资源——其实就是跟老板要了另一台 4C8G 的云服务器专门跑 CI Runner。老板听说能减少线上事故,爽快批了(看来上次双11的锅他记忆犹新)。
1. 安装 GitLab Runner
在新机器上跑几条命令就行:
# 安装 runner
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
sudo apt-get install gitlab-runner
# 注册 runner(注意执行器选 docker)
sudo gitlab-runner register \
--url "https://your-gitlab.company.com/" \
--registration-token "YOUR_TOKEN" \
--executor "docker" \
--docker-image alpine:latest
这里踩了个坑:一开始用了 shell 执行器,结果每次构建都污染宿主机环境。换成 docker 后,每个 job 都在干净容器里跑,再也不怕“在我机器上能跑”。
2. 编写 .gitlab-ci.yml
这才是重头戏。我们的目标很简单:提交代码 → 自动构建镜像 → 推送到私有仓库 → 滚动更新生产服务。
stages:
- build
- deploy
variables:
IMAGE_NAME: registry.company.com/user-service
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_VERIFY: "0"
# 构建阶段
build_image:
stage: build
image: docker:20.10.16
services:
- docker:20.10.16-dind
script:
- echo "Building image with tag: $CI_COMMIT_SHORT_SHA"
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHORT_SHA .
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker push $IMAGE_NAME:$CI_COMMIT_SHORT_SHA
only:
- main
# 部署阶段
deploy_to_prod:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client curl
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- ssh-keyscan -H $PROD_HOST >> ~/.ssh/known_hosts
script:
- |
ssh $PROD_USER@$PROD_HOST "
docker pull $IMAGE_NAME:$CI_COMMIT_SHORT_SHA &&
docker stop user-service || true &&
docker rm user-service || true &&
docker run -d --name user-service -p 8080:8080 $IMAGE_NAME:$CI_COMMIT_SHORT_SHA
"
only:
- main
when: manual # 手动确认才部署,防误触
几个关键点:
when: manual:必须手动点击“部署”按钮才会执行,避免一合并就上线(吃过亏)- SSH 密钥通过 GitLab Variables 注入:不用把私钥写进代码
- Docker-in-Docker (dind):让构建过程完全隔离
3. 配合前端动画?其实是配合前端部署!
说到前端,我们前端小哥之前用 Vercel 一键部署,羡慕死我了。现在我们也给他搞了个类似的流程:
# 前端项目的 .gitlab-ci.yml 片段
deploy_frontend:
stage: deploy
image: node:18
script:
- npm ci
- npm run build
- rsync -avz dist/ $FRONTEND_USER@$FRONTEND_HOST:/var/www/html/
only:
- main
现在他每次提 PR,CI 会自动构建预览版;合并到 main 后,点一下“deploy”,5 秒上线。上周他甚至用 Lottie 做了个部署成功动画嵌在管理后台里,美其名曰“提升运营幸福感”——产品经理居然真吃这套!
效果对比:从“提心吊胆”到“躺平发布”
搞完这套工具后,变化立竿见影。我整理了个小表格:
| 指标 | 手动部署时代 | 自动化部署后 |
|---|---|---|
| 单次部署耗时 | 15~30 分钟 | < 3 分钟 |
| 回滚速度 | 手动切分支 + 重启(10分钟+) | 重新运行上一个 job(1分钟) |
| 人为失误率 | 每月 2~3 次事故 | 0(近3个月) |
| 发布心理压力 | “上线如上坟” | “点完按钮去喝咖啡” |
| 简历关键词 | “熟悉 Spring Boot” | “主导 CI/CD 流水线建设” |
最爽的是上周三,产品临时要改个文案,我改完代码推上去,CI 自动跑完,点一下 deploy,两分钟后他说“好了!”。他惊讶地问:“这就完了?不用等你操作?” 我淡淡一笑:“时代变了,兄弟。”
血泪教训:别踩这些坑!
当然,过程不是一帆风顺。分享几个让我半夜惊醒的坑:
Runner 权限过大
一开始 Runner 用 root 跑,结果某个 job 里的rm -rf /(虽然是误写)差点清空服务器。后来严格限制容器权限,并用非 root 用户运行服务。镜像标签混乱
最初用latest标签,导致回滚时不知道该拉哪个版本。现在强制用git commit short hash作为标签,可追溯性拉满。没做健康检查
有次服务启动了但数据库连不上,CI 显示“部署成功”,实际用户用不了。现在 deploy 脚本加了curl -f http://localhost:8080/health,不健康就自动回滚。忽略日志收集
刚开始容器日志没挂载,出问题只能docker logs。现在统一用 Filebeat + ELK,查日志快多了。
不只是为了“不出事”,更是为了“能睡觉”
写这篇文章的时候,我刚下班,通勤地铁上刷着手机。以前这时候我总在担心:“今天下午上线的那个功能,半夜会不会挂?” 现在?CI 流水线跑完绿色 ✅,我就敢关电脑走人。
部署工具的本质,不是炫技,而是把人从重复、高危、低效的操作中解放出来。
对我们这种小厂来说,没有运维团队兜底,后端就得自己扛起稳定性大旗。搞自动化部署,短期看是花时间,长期看是救命。而且——别忘了——它还能让你的简历在 HR 筛选时多亮那么一丢丢。
下次面试官再问“你们怎么部署的”,我可以淡定掏出手机,给他看 GitLab CI 的 pipeline 页面,然后说:“喏,点一下,就上线了。”
最后说点人话
我知道很多小厂同学会说:“我们人少、项目急、老板不给资源,搞什么 CI/CD?”
但我想说:恰恰是因为人少、项目急,才更需要自动化。你省下的每一分钟救火时间,都是在延长自己的职业生涯寿命。更何况,现在开源工具这么成熟,一套基础流水线,周末两天就能搭起来。
别等到线上炸了、背了锅、面试被问住,才后悔没早点动手。
对了,如果你也在小厂挣扎,欢迎评论区交流。顺便,有没有内推机会?简历已更新,关键词加满了 😏

评论 0