别盲目上Jenkins:中小团队DevOps落地的三步法

很多技术负责人或者CTO在接手一个十来人的中小团队时,往往会面临一个尴尬的局面:代码上线全靠吼,回滚全靠手,环境配置在每个人电脑上都不一样。

为了解决这个问题,我曾见过不少团队陷入“工具崇拜”的怪圈。他们一上来就搭建庞大的Jenkins集群,甚至在只有单体应用的时候就强上K8s。结果呢?因为没有专职运维,开发人员每天要花20%的时间去维护这些“自动化工具”,效率反倒不如以前手动传包。

我曾在一家B轮创业公司带过两年的技术团队,接手时只有8个开发,没有运维。那时候每逢周五下午发版,我心跳都会加速,生怕那个手动打包的JAR文件少传了配置文件。

经过几轮血泪教训,我摸索出了一套适合中小团队(特别是无专职运维团队)的DevOps落地路径。这里的核心逻辑不是“引入大厂工具”,而是**“将重复动作标准化”**。

DevOps不应该是负担,而应该是像呼吸一样自然的基础设施。以下是三个具体的落地阶段。

阶段一:脚本化——把“人工操作”翻译成代码

很多团队所谓的“部署”,其实是依赖某个核心开发的“肌肉记忆”。比如,“先连VPN,再SSH到服务器,杀掉进程,上传包,重启”。这种流程极度脆弱。

配图

DevOps落地的第一步,绝不是搭建CI/CD平台,而是写脚本。

真实案例

我之前带过一个电商SaaS团队,最开始发布流程非常原始:后端把WAR包发给前端,前端用FTP上传。有一次,因为负责上传的同事感冒头晕,把测试环境的数据库配置带到了生产环境,导致所有支付接口报错,直接损失了几万块的流水。

落地方法

在那次事故后,我没有引入任何复杂的平台,只是强制推行了一个规则:“所有操作,必须通过脚本执行”

我们在项目根目录下建立了一个 ops/ 文件夹,里面只有三个Shell脚本:

  1. build.sh:负责编译、打包;
  2. deploy_test.sh:一键部署到测试机;
  3. deploy_prod.sh:一键部署到生产机(包含备份逻辑)。

配图

我们约定,任何人不得手动SSH上去敲命令,必须在本地运行这些脚本。

硬核建议:如果你连一个能在本地稳定运行的 deploy.sh 都写不出来,千万别去碰Jenkins。自动化只是把脚本搬到了服务器上跑,脚本本身的质量决定了自动化的成败。

阶段二:容器化与标准化——消灭“在我电脑上是好的”

脚本化解决的是操作流程的一致性,但解决不了环境的一致性。中小团队最痛苦的莫过于:开发环境用Java 17,服务器上却是Java 8;本地装了ImageMagick,服务器上没装。

真实案例

这是一个发生在我朋友公司的惨案。他们为了省钱,生产服务器用了几台配置不一的云主机。某次上线,代码在A服务器运行正常,在B服务器疯狂报错。排查了一整晚,发现是B服务器的系统时区设置错了,导致定时任务触发时间混乱。

落地方法

这时候,Docker就是你的救命稻草。对于中小团队,不要去搞复杂的K8s编排,Docker Compose足矣。

我们的做法是:

  1. 强制Dockerfile交付:代码库里必须包含Dockerfile,开发交付的不再是代码,而是镜像。
  2. 环境配置代码化:利用 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) .

![配图](https://picsum.photos/800/450?random=1768389901005)

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),开发人员自己就能看懂改懂。

落地方法

对于中小团队,一个合格的流水线只需要做三件事:

  1. 代码提交自动跑单元测试(哪怕只有核心业务的几个测试用例);
  2. 合并到主分支自动构建Docker镜像
  3. 调用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个具体行动:

  1. 盘点现状:把你现在的发布流程写在白板上,圈出所有需要“手动输入命令”或“手动修改配置”的环节。
  2. 脚本化改造:挑选最痛的一个环节(通常是打包或上传),在本周内用Shell脚本或Python脚本替代,并提交到代码库。
  3. 容器化尝试:找一个非核心边缘服务,尝试为其编写Dockerfile,并确保能用Docker运行起来。

DevOps是一场长跑,不要试图一步登天。从写好第一个脚本开始,你就已经在路上了。