还是那熟悉的黑色终端窗口,光标在不停闪烁。
那是几年前的一个周五下午 5 点 55 分,我正准备把一个紧急修复的 Bug 推上线。没有自动化流水线,我熟练地敲下 scp 命令上传文件,SSH 连上服务器,手动停掉 Tomcat,覆盖 JAR 包,重启…
报错了。
服务器环境的 JDK 版本和我在本地开发的不一样。那个周末,我并没有去陪家人,而是在工位上重装了一遍服务器环境。
如果你现在的团队只有不到 10 个人,没有专职运维,代码部署全靠“人工拷贝”或者简单的 Shell 脚本,那你大概率也经历过这种时刻。
很多技术负责人觉得 DevOps 很高大上,需要几十万的预算和专业团队。其实不然。对于中小团队,DevOps 不是要你建一个谷歌那样复杂的平台,而是为了让你能在周五下午 6 点准时下班。
我花了两年时间,在一个 8 人开发团队摸索出了一套“穷人版”DevOps 方案,今天把踩过的坑和验证过的方法分享给你。
拒绝“大炮打蚊子”:别一上来就上 K8s
很多开发同学接手运维工作时,容易陷入“技术崇拜”的陷阱。
真实案例: 我有个朋友阿强,他在一家电商创业公司做 Tech Lead。团队一共 6 个后端,他为了证明技术实力,从第一天起就决定全套上 Kubernetes (K8s)。他花了整整两周写 YAML 文件,搭建集群。
结果呢? 大促前一天,集群的 etcd 挂了。因为团队里没人真正懂 K8s 的底层原理,大家对着报错日志大眼瞪小眼,最后不得不临时买了几台云服务器,花了一通宵手动把应用部署上去才扛过大促。
避坑指南: 对于中小团队,复杂性是万恶之源。K8s 确实强大,但维护成本极高。
推荐方案:Docker Compose + 单机/轻量集群 如果你的服务器在 5 台以内,Docker Compose 足够了。它能让你用一个文件定义所有服务(数据库、缓存、应用),一键启动。
我现在的习惯是,在项目根目录放一个 docker-compose.yml,即使新来的实习生,只要装了 Docker,敲一行命令就能把整套环境跑起来。
# 一个简单的 docker-compose 示例,足够应付大多数场景
version: '3'
services:
web:
build: .
ports:
- "8080:8080"
restart: always # 关键:挂了自动重启,某种程度的"自愈"
redis:
image: "redis:alpine"
这比维护一套 K8s 集群要省心 90% 以上。记住,工具是为了服务业务,而不是为了炫技。
统一环境:消灭“在我电脑上是好的”
这是开发兼任运维时最大的痛点:本地跑得飞起,一上服务器就崩。原因千奇百怪:Python 库版本不对、CentOS 缺少某个底层依赖、或者是时区设置错了。
真实案例: 两年前,我们团队负责的一个爬虫项目,本地开发用的是 Mac,生产环境是 Ubuntu。有个解析库在 Mac 上默认安装了二进制包,在 Linux 上却需要从源码编译(需要 gcc)。上线时,服务器 CPU 直接飙满,然后进程卡死。
排查了 4 个小时,我们才发现是编译环境缺失导致的。
解决方法:不可变基础设施(Immutable Infrastructure) 这词听着玄乎,落地其实就一句话:交付镜像,而不是交付代码。
不要在服务器上运行 git pull,也不要在服务器上运行 npm install 或 pip install。这些操作都充满了不确定性(比如 npm 源偶尔抽风)。
你应该在构建阶段就把所有依赖、配置、代码打包成一个 Docker 镜像。
“Docker 镜像就像一个自带操作系统的集装箱,你去哪里,环境就带到哪里。”
我强烈建议你在项目中加入一个多阶段构建的 Dockerfile。这样,最终产出的镜像只有几十 MB,且不包含源代码和编译工具,既安全又轻量。
# 这是一个多阶段构建的例子,产物极小
# 第一阶段:编译
FROM golang:1.18 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp main.go
# 第二阶段:运行
FROM alpine:latest
WORKDIR /root/
# 只拷贝编译好的二进制文件
COPY --from=builder /app/myapp .
CMD ["./myapp"]
自从强制推行“只交付镜像”后,我们团队因为环境不一致导致的上线故障率直接降到了 0。
自动化流水线:把重复动作交给机器人
如果你现在还在手动 SSH 连服务器部署,请立刻停止。人是会犯错的,特别是疲惫的时候。
真实案例: 这是我自己的教训。有一次由于没有自动化流程,我手动去服务器改 Nginx 配置。因为手抖少写了一个分号,reload 之后整个网站 502。当时正值晚高峰,用户群里瞬间炸锅。虽然我只用了 2 分钟修复,但这 2 分钟对用户体验的伤害是不可逆的。
落地路径:GitHub Actions / GitLab CI 你不需要搭建 Jenkins(那玩意儿太吃内存,配置也麻烦)。现在的代码托管平台都自带免费的 CI/CD 工具。
我建议的极简 DevOps 路径如下:
- 代码提交:开发人员 Push 代码到
main分支。 - 自动测试:CI 自动运行单元测试(跑不通不许上线)。
- 自动构建:CI 构建 Docker 镜像,并推送到私有镜像仓库(阿里云/腾讯云都有免费额度)。
- 自动部署:CI 通过 SSH 远程执行一条命令,让服务器拉取最新镜像并重启。
这是一个我用了很久的 GitHub Actions 核心片段,供你参考:
# .github/workflows/deploy.yml
name: Deploy to Server
on:
push:
branches: [ "main" ]

jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: SSH Remote Commands
uses: appleboy/ssh-action@master
with:
host: $
username: $
key: $
script: |
# 登录私有仓库
docker login ...
# 拉取最新镜像
docker pull my-image:latest
# 重建服务(平滑更新)
docker-compose up -d --no-deps web
# 清理旧镜像
docker image prune -f
配置这一套大概只需要半天时间,但它能为你节省未来两年所有的手动部署时间。更重要的是,它给了你“喝着咖啡看代码上线”的从容。
回顾与行动
开发兼任 DevOps,并不是要你成为全能的 SRE 专家,而是要学会借力。借 Docker 的力解决环境问题,借 CI/CD 的力解决重复劳动问题。
让我们总结一下核心思路:
- 别碰复杂的 K8s,用 Docker Compose 解决单机编排。
- 别在服务器上装依赖,用 Docker 镜像保证环境一致。
- 别手动 SSH 操作,用 Git 仓库自带的 CI 工具做自动化。
最后,做个小调查: 在资源有限的情况下,你更倾向于花时间搭建完善的监控系统(Prometheus+Grafana),还是优先完善自动化部署流程? 欢迎在评论区告诉我你的选择(A:监控优先;B:部署优先)。
给你的 3 个立即行动建议:
- 本周任务:挑一个最核心的项目,写一个
Dockerfile,确保它能在本地 Docker 跑起来。 - 下周任务:注册一个阿里云/腾讯云的容器镜像服务(个人版免费),试着手动 Push 一个镜像上去。
- 月度目标:配置一条最简单的 CI 流水线,哪怕只是实现“代码提交自动运行测试”也好。
路虽远,行则将至。希望下个周五下午,你也能准时合上电脑,享受生活。