把工具换到文化时踩过的坑

那阵子我见过的项目, README 第一行基本都是 “开箱即用,一条命令启动”,然后第二条就是 “需要先安装 Docker、Docker Compose、Redis、MySQL、RabbitMQ,还要注意端口冲突”。

后来 Kubernetes 火起来,HPA 配置写错一次,生产自动扩容把账单干爆,老板脸色比运维代码里的 console.log 还难看。DevOps 圈子里有个常见现象:我们总是先搞定工具,再想文化——工具看得见摸得着,文化太虚。

这篇不写「DevOps 实施标准手册」,只聊我们团队转型里踩过的坑,以及那些比工具难啃得多的东西。

工具不是文化,但工具能暴露问题

我们团队刚开始搞 DevOps 的时候,思路挺简单:先搞套 CI/CD,能自动构建部署,就算 DevOps 落地了。

当时选的方案是 GitLab CI,原因无他:公司已经在用 GitLab 代码托管,不用再折腾权限。写了几个 .gitlab-ci.yml 配置文件,跑了几次 pipeline,算是完成了"从 0 到 1"。

stages:
  - build
  - test
  - deploy

build:
  stage: build
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - dist/

test:
  stage: test
  script:
    - npm run test
  dependencies:
    - build

deploy:
  stage: deploy
  script:
    - scp -r dist/ user@server:/var/www/html/
  only:
    - main

这套配置看着挺标准,但跑了几周就发现问题了。

pipeline 失败率偏高:本地 node 版本和 CI 不一致、依赖安装超时、部署脚本里硬编码的密码失效——每次失败都得有人盯着修,然后重新跑。

然后是"谁负责"的问题。代码提交后 pipeline 红了,前端说后端接口改了没打招呼,后端说前端依赖升级导致测试失败,运维说脚本没问题是环境问题。甩锅链路比代码调用链还长。

这时候我们才意识到一个挺残酷的现实:工具自动化了流程,但没解决协作的问题。相反,正因为自动化了,原来手动操作时还能私下商量的事,现在全变成了 pipeline 里的报错,谁的责任一目了然,矛盾反而更暴露了。

这里有个挺有意思的矛盾:你越自动化,原来靠人情和口头沟通能模糊过去的问题,越变成必须要解决的硬约束。这就像家里没人的时候,垃圾多堆几天也行;但住了一对爱干净的室友,每天都要倒垃圾,谁的垃圾没倒就很清楚。

graph TD A[手动部署] -->|问题模糊| B[互相迁就] B --> C[能跑就行] D[CI/CD自动化] -->|问题显形| E[责任明确] E --> F[必须解决] G[工具暴露问题] --> H[文化倒逼改变] H --> I[真正的 DevOps]

第一个坑:我们以为只要工具对,流程就会顺

我们团队有个很典型的坑:把 DevOps 当成了工具选型,而不是协作模式。

当时花了两个月搞了一套"完整方案":代码管理用 GitLab,CI 用 GitLab CI,CD 用 Ansible,监控用 Prometheus + Grafana,日志用 ELK。技术栈挺标准,POC 环境也跑通了,但真的推广到业务团队时,卡住了。

卡在什么地方?很多问题想起来挺荒谬:

  • 开发人员不愿意写单元测试,说"项目太急,没时间",结果 CI pipeline 里的测试环节形同虚设
  • 测试人员觉得自动化测试不够可靠,还是要手动回归,浪费了做测试自动化的时间
  • 产品经理想快速迭代,但开发说"现在发版要走 CI/CD,要等 pipeline",就把锅甩给工具
  • 运维嫌 Ansible 的 playbook 太复杂,有时候还是手动 SSH 上去改配置

最后结果是:工具都上了,但真正使用的是其中 30%。剩下的 70% 还是靠手动、靠口头、靠人情、靠"这次特殊情况先这样"。

后来复盘时我们才明白,问题不在工具本身,而在于我们根本没有解决"为什么要自动化"这个问题。自动化是为了什么?是为了更快的交付?还是为了更少的错误?还是为了降低依赖某个人的风险?

不同的目标,对应不同的自动化策略。如果你是为了更快交付,那应该优先做那些重复性高、标准化程度高的工作;如果你是为了减少错误,那应该优先做质量门禁和环境一致性。

但我们的问题在于:我们搞自动化,是因为"别的团队都在搞"和"DevOps 就是要自动化"。这就像健身,你买了健身卡、买了装备、找了私教,但你不知道为什么要健身,是为了健康还是为了好看,还是为了发朋友圈。结果就是,健身去了几次,然后不了了之。

文化不是宣讲出来的,是实践出来的

我们团队后来意识到,文化这东西,靠宣讲、靠培训、靠墙上的标语,基本没用。文化是实践出来的,是日常工作中一次又一次的小选择累积起来的。

有一次,我们团队遇到了一个生产环境事故。某个后端服务突然内存泄漏,导致整个服务不可用。

按照以前的流程,这个事情会这样处理:运维发现服务挂了,重启服务;开发接到通知,查日志,定位问题;测试人员检查回归;最后写个事故报告,开个复盘会。

但在我们的 DevOps 转型过程中,这个事情暴露了很多问题:

  • 服务监控告警延迟了 10 分钟才发出来,运维说"告警阈值设置得比较保守,怕误报"
  • 开发查日志时发现日志级别不对,关键信息没打出来,开发说"当时赶时间,没来得及调"
  • 测试说"我们测试环境没测出内存问题,生产环境数据和测试环境不一样"
  • 最后复盘会上,大家都说"以后要更注意",但没人具体说"明天要做什么改变"

这就是典型的"文化断层":我们嘴上说 DevOps,但实际行动还是老一套。

后来我们做了几个改变,没开大会拍板,是在具体事故里被迫调整的:

第一,把告警阈值调得更敏感,哪怕有误报也要优先发现。

原来运维怕误报,把 CPU 使用率阈值设到 90%,内存使用率设到 85%。改成 70% 和 60% 后,误报确实多了,但我们也早发现了几次潜在问题。后来慢慢优化了告警规则,找到平衡点。

第二,把日志级别调到 debug,在预发环境复现问题。

原来开发怕日志影响性能,在生产环境只打 info 级别。后来发现,出了问题还是要调日志再重启才能复现,还不如平时就打详细日志,反正磁盘够用,日志轮转也配好了。

第三,测试环境尽量贴近生产环境的数据和配置。

原来测试环境用的是假数据,后来想办法脱敏了一部分生产数据放进去,虽然增加了成本,但问题复现率明显提高了。

这些改变谈不上什么「最佳实践」,就是我们在具体痛点上摸出来的。靠一次培训学不会,得在一次次「为什么又出问题了」的追问里慢慢形成共识。

graph LR A[事故发生] --> B[原来的处理方式] B --> C[发现问题] C --> D[被迫调整] D --> E[形成共识] E --> F[变成习惯] F --> G[成为文化] H[培训/宣讲] -->|影响有限| I[短期记忆] I -->|容易遗忘| J[回到老路]

第二个坑:我们以为 DevOps 是开发的事,跟运维关系不大

我们团队刚开始搞 DevOps 的时候,参与的主要是开发人员。运维偶尔参与一下,比如配置服务器权限,或者帮忙看一下部署脚本。

后来我们意识到这个问题的严重性,是因为一次"部署事故"。

某个新功能上线后,生产环境的数据库连接数突然暴增,导致数据库响应变慢,整个系统的性能都受到了影响。开发说"我们测试环境没测出来",运维说"你们代码里开了太多连接,数据库配置没问题",测试说"我们没测高并发场景"。

甩了半天锅,才发现问题的根源:开发在测试环境使用的数据库连接池配置,和生产环境不一样。因为运维和生产环境的配置是分开管理的,开发也不知道生产环境的实际配置是啥。

这暴露了 DevOps 的一个关键点:开发、测试、运维的边界不能太硬。开发要懂一点运维,要能看懂日志、能理解配置、能意识到某个代码修改对生产环境的影响;运维要懂一点开发,要能看懂代码、能理解业务逻辑、能参与到架构设计里。

但现实是:很多团队的边界还是很硬。开发只管写代码,不管部署;运维只管部署,不管代码;测试只管测试,不管上线。这种分工在瀑布时代可能还行,但在快速迭代的环境里,问题就暴露出来了。

我们后来做了几个调整:

第一,让开发也参与值班。

原来运维负责 7×24 小时值班,出了问题找运维。现在改成开发轮流值班,每人负责一周。值班期间要负责处理线上问题,哪怕半夜也得起来看日志。这个调整一开始很痛苦,开发抱怨"这不是我的工作",但慢慢地,开发开始意识到"我的代码在生产环境到底是怎么跑的"。

第二,让运维参与代码评审。

原来代码评审只有开发参与,现在也邀请运维来看。运维主要看配置、资源占用和潜在风险,不评审业务逻辑。比如看到某个定时任务没配并发控制,运维就会提醒「这个可能会把数据库打挂」。

第三,建立"共同目标"而不是"各自KPI"。

原来开发的 KPI 是"功能按时上线",运维的 KPI 是"服务稳定性",测试的 KPI 是"Bug 数量"。这三个目标有时候是冲突的:开发为了赶时间可能不充分测试,运维为了稳定性可能不愿意频繁发布。

后来我们把目标改成"交付价值的速度和质量",让大家有共同的利益绑定。功能上线了但很快出了事故,不算成功;服务很稳但功能总是推不出来,也不算成功。

graph TD A[原来的分工] --> B[开发只写代码] B --> C[运维只管部署] C --> D[测试只管测试] D --> E[边界太硬] E --> F[问题暴露] G[DevOps 转型] --> H[开发参与值班] G --> I[运维参与代码评审] G --> J[共同目标] H --> K[理解生产环境] I --> L[理解代码逻辑] J --> M[利益绑定] K --> N[边界变软] L --> N M --> N

实践中的一些具体经验

上面说的这些,听起来有点抽象。我想分享几个具体的小经验,都是我们在实践中摸出来的。

经验一:工具选型要看团队的实际情况

我们团队一开始选的监控方案是 Prometheus + Grafana,这套方案在技术圈子里很火,也有很多成功案例。但我们用了几个月后发现,这套方案对我们来说太重了。

Prometheus 的配置有一定复杂度,Grafana 的仪表盘要自己去设计。我们团队里真正熟悉这套方案的只有两个人,其他人都不会。这导致每次要新增监控项,都得依赖这两个人,形成了新的瓶颈。

后来我们换了个轻量级的方案:Uptime Kuma。界面简单,配置也直观,基本 5 分钟就能上手。虽然功能没有 Prometheus 那么强大,但对我们团队来说够用了,而且大家都能自己配。

这个教训很简单:工具是为人服务的,不是人迁就工具。你的团队是什么水平、什么规模、什么需求,就选什么样的工具。不要因为某个方案在技术圈子里火,就非要上。别人的答案不一定是你的答案。

经验二:文档比代码还重要,但文档不是越多越好

我们团队刚开始搞 DevOps 的时候,写了不少文档:开发流程、部署手册、故障处理指南。但后来发现,这些文档要么没人看,要么过时了没人更新,要么写得不清不楚。

比如部署手册,里面写了很多命令、很多配置项,但没有说明"为什么要这样配置"。等到要改的时候,没人敢动,怕改出问题。

后来我们调整了文档策略:只写那些"必须记录下来才能避免重复踩坑"的东西,而且尽量写得简洁、实用。

比如我们写了个「故障处理 checklist」,不写逐步命令,只写「遇到这类问题,先看什么、再看什么、要注意什么」。细节留给当时处理的人去查。

## 服务突然变慢 checklist

- 先看 Grafana 的系统监控,CPU、内存、磁盘、网络有没有异常
- 再看应用日志,有没有大量错误日志或者慢日志
- 检查最近有没有代码发布,回滚一下看看是否恢复
- 检查数据库连接数、慢查询日志
- 如果以上都正常,联系业务方,问下最近流量有没有异常

这个 checklist 没写具体命令,没有写每个工具怎么用,只是给了个思路。但实际用起来,比写了几十页的详细手册更有效。

经验三:文化转型要有耐心,不要指望一次就成

我们团队搞 DevOps 转型,前后用了差不多两年。没一直在开会培训,是在日常工作里一点点调整。

比如让开发参与值班,刚开始大家都很抵触,说"这不是我的工作"。但坚持了几个月后,开始有人说"原来我的代码在生产环境是这样跑的",开始有人主动在代码里加日志、优化配置。

再比如代码评审邀请运维参与,刚开始运维说"我看不懂你们的代码",开发也说"运维评审啥"。但坚持了几个月后,开始有人说"运维提的这个配置问题确实要考虑",开发也开始主动在代码里写注释说明配置影响。

文化转型不像工具升级,装个新版本就完成了。它更像改掉一个坏习惯,需要时间、需要耐心、需要一点压力。不要指望一次培训就能让团队突然焕然一新,也不要指望某个人突然变成 DevOps 专家。

graph LR A[开始] --> B[抵触] B --> C[坚持] C --> D[尝试] D --> E[尝到甜头] E --> F[形成习惯] F --> G[变成文化]

实践中踩过的一些坑

我们团队在 DevOps 转型中踩过不少坑,挑几个比较典型的说下。

坑一:过度自动化,反而降低了效率

有段时间我们团队特别迷恋自动化,恨不得把所有手工操作都自动化掉。

比如代码发布,我们写了个很复杂的 Ansible playbook,支持灰度发布、支持回滚、支持配置热更新。看起来很牛,但实际用起来问题很多:

  • playbook 写得很长,每次要发布都要先看半天配置
  • 出了问题很难调试,playbook 的报错信息不太友好
  • 每次有新需求,比如要支持某个特殊的部署场景,都要改 playbook

后来我们才意识到,自动化是为了提高效率,不是为了自动化而自动化。如果自动化本身变成了负担,那就得不偿失了。

我们现在更倾向于"自动化那些重复性高、标准化程度高的工作",剩下的保持灵活。比如代码发布,我们用了个相对简单的脚本,能做基本的部署和回滚就够了。一些特殊的部署场景,还是手动操作更靠谱。

坑二:追求"完美工具",忽略"团队实际"

我们团队在工具选型上,有个比较典型的毛病:总想找"最佳方案"。

比如 CI/CD 工具,我们试过 Jenkins、试过 GitLab CI、试过 GitHub Actions,每个都用了一段时间,然后总觉得"还有更好的"。结果团队里熟悉各个工具的人不多,配置也不统一,最后还是要花时间去统一。

后来我们明白,“最佳方案"不一定适合你的团队。你的团队用什么、熟悉什么、业务是什么样子,这些因素比"哪个方案更火"更重要。

我们现在选工具时,会先问几个问题:

  • 团队里有没有人熟悉这个工具?
  • 学习成本有多高?
  • 维护成本有多高?
  • 这个工具的社区和生态怎么样?
  • 它能解决我们当前的主要问题吗?

这些问题比"这个工具在技术圈子里有多火"更有意义。

坑三:只关注工具,忽略沟通

我们团队刚开始搞 DevOps 时,把精力都放在工具上:CI/CD、监控、日志、自动化,但忽略了团队沟通。

后来发现,很多问题的根源在沟通:开发不知道运维的痛点,运维不理解开发的压力,产品经理不清楚技术风险。

举个具体的例子:有一次产品经理想快速上一个新功能,开发说"这个功能有点复杂,需要两周”,产品经理说"一周能不能搞定",开发最后答应了,但为了赶时间没做充分的测试,上线后出了问题。

事后复盘时,大家才意识到,问题不在于开发写代码的能力,而在于沟通不到位。产品经理不知道这个功能的复杂度,开发也不知道为什么产品经理急着要上。

后来我们做了一个调整:每次开发前,先开个短会,产品经理讲清楚为什么需要这个功能、什么时候要上,开发讲清楚实现难度、需要多久、有什么风险。这样大家都心里有数,避免了"为了赶时间牺牲质量"的情况。

结语

DevOps 说简单也简单,说复杂也复杂。核心就几条:打破边界、持续交付、快速反馈、持续改进。这些东西靠工具搞不定,得靠时间、耐心,还有一次次实践。

我们团队搞了两年,现在也不敢说「成功了」。但确实比以前更了解自己的系统,更能应对突发问题,更能在快速迭代里保持质量。

如果你也在搞 DevOps,我有几条亲身体会:

  • 工具是手段,别为了上工具而上工具
  • 文化靠日常小选择堆出来,比搞一次大活动管用
  • 要有耐心,文化转型比工具升级难得多
  • 关注人,比关注某个工具的版本号重要

DevOps 没有终点,只有下一个版本。这篇文章写的是我们的实践,不一定适合你的团队;能捞到一两个有用的点,就不算白写。

参考

版权声明: 本文首发于 指尖魔法屋-把工具换到文化时踩过的坑https://blog.thinkmoon.cn/post/128-devops-culture-practice-tool-to-culture/) 转载或引用必须申明原指尖魔法屋来源及源地址!