很多技术负责人或者CTO在接手一个十来人的中小团队时,往往会面临一个尴尬的局面:代码上线全靠吼,回滚全靠手,环境配置在每个人电脑上都不一样。
为了解决这个问题,我曾见过不少团队陷入“工具崇拜”的怪圈。他们一上来就搭建庞大的Jenkins集群,甚至在只有单体应用的时候就强上K8s。结果呢?因为没有专职运维,开发人员每天要花20%的时间去维护这些“自动化工具”,效率反倒不如以前手动传包。
我曾在一家B轮创业公司带过两年的技术团队,接手时只有8个开发,没有运维。那时候每逢周五下午发版,我心跳都会加速,生怕那个手动打包的JAR文件少传了配置文件。
经过几轮血泪教训,我摸索出了一套适合中小团队(特别是无专职运维团队)的DevOps落地路径。这里的核心逻辑不是“引入大厂工具”,而是**“将重复动作标准化”**。
DevOps不应该是负担,而应该是像呼吸一样自然的基础设施。以下是三个具体的落地阶段。
阶段一:脚本化——把“人工操作”翻译成代码
很多团队所谓的“部署”,其实是依赖某个核心开发的“肌肉记忆”。比如,“先连VPN,再SSH到服务器,杀掉进程,上传包,重启”。这种流程极度脆弱。
DevOps落地的第一步,绝不是搭建CI/CD平台,而是写脚本。
真实案例
我之前带过一个电商SaaS团队,最开始发布流程非常原始:后端把WAR包发给前端,前端用FTP上传。有一次,因为负责上传的同事感冒头晕,把测试环境的数据库配置带到了生产环境,导致所有支付接口报错,直接损失了几万块的流水。
落地方法
在那次事故后,我没有引入任何复杂的平台,只是强制推行了一个规则:“所有操作,必须通过脚本执行”。
我们在项目根目录下建立了一个 ops/ 文件夹,里面只有三个Shell脚本:
build.sh:负责编译、打包;deploy_test.sh:一键部署到测试机;deploy_prod.sh:一键部署到生产机(包含备份逻辑)。
我们约定,任何人不得手动SSH上去敲命令,必须在本地运行这些脚本。
硬核建议:如果你连一个能在本地稳定运行的
deploy.sh都写不出来,千万别去碰Jenkins。自动化只是把脚本搬到了服务器上跑,脚本本身的质量决定了自动化的成败。
阶段二:容器化与标准化——消灭“在我电脑上是好的”
脚本化解决的是操作流程的一致性,但解决不了环境的一致性。中小团队最痛苦的莫过于:开发环境用Java 17,服务器上却是Java 8;本地装了ImageMagick,服务器上没装。
真实案例
这是一个发生在我朋友公司的惨案。他们为了省钱,生产服务器用了几台配置不一的云主机。某次上线,代码在A服务器运行正常,在B服务器疯狂报错。排查了一整晚,发现是B服务器的系统时区设置错了,导致定时任务触发时间混乱。
落地方法
这时候,Docker就是你的救命稻草。对于中小团队,不要去搞复杂的K8s编排,Docker Compose足矣。
我们的做法是:
- 强制Dockerfile交付:代码库里必须包含Dockerfile,开发交付的不再是代码,而是镜像。
- 环境配置代码化:利用
docker-compose.yml来定义运行环境。数据库、Redis、应用服务,全部写在文件里。
这样做的最大好处是,新来的实习生,只需要一句 docker-compose up,就能在本地拉起一套和生产环境一模一样的系统。
这里分享一段我用了很久的 Makefile 片段,我习惯把它放在项目根目录,通过简单的命令屏蔽复杂的Docker指令:
# Makefile 示例
.PHONY: build deploy
IMAGE_NAME = my-app
VERSION = $(shell git rev-parse --short HEAD)
build:
@echo "Building Docker image..."
docker build -t $(IMAGE_NAME):$(VERSION) .

deploy:
@echo "Deploying to remote server..."
# 这里的逻辑是:构建镜像 -> 保存镜像 -> 传输到服务器 -> 加载镜像 -> 重启容器
docker save $(IMAGE_NAME):$(VERSION) | ssh user@remote-server 'docker load && docker-compose up -d --no-deps --build app'
阶段三:轻量级流水线——让机器代替人熬夜
当你有了脚本,也有了标准化的容器环境,这时候才轮到CI/CD工具登场。
但我强烈建议:除非由于安全合规要求必须内网部署,否则中小团队请直接使用SaaS版的CI工具(如GitHub Actions, GitLab SaaS, 阿里云效)。 自己维护一套Jenkins或者GitLab Runner,光是磁盘清理、插件升级、节点掉线这些破事,就足够消耗掉你一个高级开发的半个人力。
真实案例
我在2021年接手的一个项目,前任负责人搭了一套非常复杂的Jenkins,配置了十几个插件。结果由于插件版本冲突,导致整个构建服务瘫痪了两天,全员无法合并代码。
后来我们花了一周时间,把所有流程迁移到了GitHub Actions。没有运维成本,不仅免费额度够用,而且配置文件直接放在代码库里(.github/workflows),开发人员自己就能看懂改懂。
落地方法
对于中小团队,一个合格的流水线只需要做三件事:
- 代码提交自动跑单元测试(哪怕只有核心业务的几个测试用例);
- 合并到主分支自动构建Docker镜像;
- 调用SSH推送到服务器(或者触发Webhook让服务器自己拉取)。
这种“极简主义”的流水线,稳定性远高于那些花哨的仪表盘。
拿来即用的落地工具箱
DevOps的本质不是工具,而是文化和纪律。对于中小团队,能跑通流程的方案就是好方案。
最后,分享一个我个人常用的 “GitLab CI 极简部署模板”。如果你正在用GitLab,直接把这个文件复制到项目根目录命名为 .gitlab-ci.yml,配合上面的Shell脚本思路,半天就能搭起一套自动化发布系统。
stages:
- build
- deploy
variables:
# 建议在GitLab后台设置变量,不要明文写在代码里
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
build_image:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
only:
- master
deploy_prod:
stage: deploy
image: alpine:latest
before_script:
# 配置SSH免密登录
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
script:
# 远程执行部署命令
- ssh $SERVER_USER@$SERVER_IP "cd /opt/app && export IMAGE_TAG=$IMAGE_TAG && docker-compose up -d"
only:
- master
接下来你可以做的3个具体行动:
- 盘点现状:把你现在的发布流程写在白板上,圈出所有需要“手动输入命令”或“手动修改配置”的环节。
- 脚本化改造:挑选最痛的一个环节(通常是打包或上传),在本周内用Shell脚本或Python脚本替代,并提交到代码库。
- 容器化尝试:找一个非核心边缘服务,尝试为其编写Dockerfile,并确保能用Docker运行起来。
DevOps是一场长跑,不要试图一步登天。从写好第一个脚本开始,你就已经在路上了。