不出事故,没有人知道你重要

有同学在知乎上提问:“线上无事故,运维还重要吗?”,描述如下: 本人运维行业,本部门在近几年一直保持效率增长且极少出现重大saas生产事故,并且为其他部门输出提升方法以及友好协同提升,但是最近从各层面接到反馈说对运维的投入减少,着实想不通,线上出了事故要运维背锅,产品出了bug要运维陪着到最晚,为什么把线上环境搞得稳定了,却不重视运维岗了? 这是原贴:https://www.zhihu.com/question/497361582 以上提问的是一个运维的同学。言下之义是不出事故,没有人知道运维重要。 这位同学的的感受,过去几年,我感同深受。我相信因为这个标题而点进这篇博客的同学,也有同样的感受。 但是,为什么出事故后,是运维重要呢?而不是测试、开发或者手机端开发呢? 通常是因为运维这个角色: 线上环境,他们最清楚,通常也只有他们有权限操作线上环境,可以紧急加一个数据库索引; 他们掌握了部署能力,可以发起回滚操作; 有权限查看各个组件的情况,并诊断根因; 为团队准备基础设施能力,如金丝雀发布能力; 搭建告警监控系统、CMDB、DevOps平台等。 等等 但是,这些与是否出事故,有多大的关联性呢?我们应该统计各种事故的根因的类型的比例,才有答案。 就目前而言,我们并不能说因为我们看重运维,就不出事故。 以上的问题是从个人感受出发的提问。只是更深层次问题的表象。 从企业层面上,我的疑问是:为什么在企业里,稳定性建设通常都是一阵阵的。即出一次事故,就立个项,就加班加点去完成“稳定性”项目。 比起讨论个人感受,从企业层面讨论这个问题,似乎更有趣。 其实,除了稳定性,软件的质量建设也是一阵阵的。想想,不是吗?不出Bug,没有人知道测试重要。 也许这是所有企业的正常表现。就像人的身体,痛风(一种慢性病)不发作时,你是不会感受它的存在,也自然就不会想到要去治疗或者预防它。然而,如果平时不注意饮食和锻炼,痛风经常复发。 线上事故就如同企业的痛风。企业应对“痛风”,容易好了伤疤忘了痛。 虽说可能是所有企业的正常表现,但不是一种健康的表现。 预防痛风,只能通过健康的生活方式如: 限制或避免饮酒,尤其是啤酒。 限制或者避免饮用含糖饮料,尤其是含高果糖玉米糖浆的饮料。 限制肉类摄入量,尤其是红肉、内脏和海鲜。 保持健康的体重。如果您需要减肥,请避免断食或过快地减肥,因为这可能会暂时增加尿酸水平。 增加水和低脂乳制品的摄入量。这些有预防痛风的作用。 一个人应对痛风的健康表现应该是采用健康的生活方式。 说回企业的稳定性建设,也是一样的道理。 稳定性不是通过“一阵阵的运动”或者“一阵阵的表演”来建设的,而是通过平时健康的企业活动来实现(我无意指导别人的企业,这只是我个人的思考)。 当然,现实中,对于有些人,要维持健康的生活方式是一件很难的事情(想想有身边有多少人做到早睡早起),而另一些人是一件很自然的事。为什么呢? 相同的,一家企业为什么无法自然地做到健康的企业活动?一定要出事故,才知道X的重要性呢?(X代表任何东西) 这个问题就很大了。希望对各位读者有启发。

2023-11-21 · 1 min · 34 words · 翟志军 Jack Zhai

听说最近的事故都是循环依赖导致的?

​年底了,事故频发。但是都听说是因为循环依赖导致。所以,我决定来写写依赖管理领域中,通常不被重视的循环依赖问题。 循环依赖(circular dependencies)的定义 来自维基百科的定义: 在软件工程中,循环依赖是两个或多个模块之间的关系,这些模块直接或间接地相互依赖才能正常运行。此类模块也称为相互递归。 循环依赖是依赖管理领域中经常出现的一种现象,如下图: 循环依赖的不同层次以及后果 循环依赖可以发生在两个层次: 源码之间相互引用依赖; 服务之间相互调用依赖。 源码之间产生循环依赖带来的问题是:构建工具不知道应该从何处开始构建。因为构建工具无从下手。 构建工具底线这时就体现出来了,如果它可以忽略其中一个节点,“勉强”构建出一个制品,那么这个制品估计你也不敢用,因为该制品存在不确定性。这是源码层次中,循环依赖的后果。 而服务之间调用的循环依赖就更麻烦了。平时非常难发现,是不会出事故的,但一出事故就会雪崩。 因为你无法单独启动循环依赖中的任何一个服务,而循环依赖中的任何一个服务挂了,其它所有的节点都会同时挂。 循环依赖的环越大,影响面越大。 为什么会出现循环依赖 既然循环依赖从任何一个节点都法进行构建或者启动,那么它又为什么会产生?难道定义依赖的人不知道吗?Code Review的人不知道吗? 因为循环依赖是软件系统在长时间发展中,不加以合理的依赖管理所导致的。如下图。一开始只是B的V1版本依赖A的V1版本,最后变成B的V2版本与A的V3版本相互依赖。 软件系统发展的时间足够长,交接的人数足够多,软件依赖关系的规模早就超出了人类能处理的限度。 如何从根上就避免循环依赖 用人力的办法解决循环依赖,是不现实的。依赖管理这种吃力又不讨好,还拿不上台面的术语,没有人会去做。更不会得到KPI的“赏识”。 可以预料得到,如果对依赖管理治理不当,那么每几年,就可能出现一次循环依赖的事故。不发生事故的原因可能只有一个:软件的规模还不够大。 所以,我们必须想办法从根上避免循环依赖,即从依赖的定义的地方开始治理。 源代码层次,构建工具就可以帮我们依赖。只要出现循环依赖,构建就不通过。例如Make工具: make: Circular main.asm.o <- main.asm dependency dropped. 但是,如果是多仓库管理源代码的模式下,你可能还是很难避免循环依赖的情况。如果是单仓库,就不会出现这样的情况。 而服务之间的调用关系,在不同的公司定义的位置不一样。这也决定了查找循环依赖的成本。 服务之间的调用关系的定义,本质上属于配置管理的范畴。因为你总要在某个地方定义这些依赖。 服务之间的循环依赖查找,本质上就是配置管理领域中,查找配置之间的相互引用关系。 这说明配置管理的方式决定了查找服务之间循环依赖的成本。 什么样的配置管理方式才能低成本的实现查找循环依赖呢? 在这里,我提出我的方案:使用Jsonnet定义所有的配置(某些case无法覆盖),然后通过Bazel进行构建。 Jsonnet是一门专为配置定义而设计语言,语言只有一页A4纸; Bazel是一款支持增量构建、分布构建、构建缓存、支持多语言的构建工具。 通过这个方案,Bazel会自动构建一个软件依赖关系图,同时检测其中是否存在循环关系。只要循环关系存在,构建就不通过,当然就无法上线了。这样就从根上就避免了循环依赖。 如果想了解更多具体的落地方式,请关注我。并转发本文,让更多人看到这种神奇的配置管理方式。

2023-11-11 · 1 min · 43 words · 翟志军 Jack Zhai

出现运维事故后,你会怎么办?

从聊天说起 有一次和朋友聊天,他说他们有一次部署出事了,影响还挺大,那次事故后,他们公司对于部署流程增加了更多的审批。 当朋友说完前半句时,我已经猜到下半句,那是很多公司或个人会做出的反应。至于为什么会做出这样的反应,我也不知道。 我问:为什么那次部署会“出事”? 他说:当时部署的人忘记了那台机器上有一条 Iptable 规则,导致了事故。 我就在想,如果有人审批,那次事故就不会发生吗?审批的人就知道那台机器上有一条规则导致事故的发生?然后驳回这次部署吗?连一线的开发和运维都忘记了的 Iptable 规则,“高高在上的审批领导”就更不知道了。 题外话:增加审批流程并不能避免这次事故,只不过当出现事故时,可以更好的定责。然而我又好奇了,这种“审批”是为了解决问题,解决什么问题?,还是为了逃避责任?谁逃避了责任?谁又有责任? 对于这类问题,我心里已经有数了,但想知道这位朋友的回答,就接着问:那么怎么杜绝这类问题呢? 他说:因为那条 Iptable 规则的设置太久远了,是谁都记不起。如果能把每次部署的步骤记录下来,这样下次部署的时候,过一下以前的部署记录,就会知道那个 Iptable 规则了。(作者:大概原意,已经记不清原话) 这位朋友说的做法,我之前待的一个团队的做法也差不多:会有一个页面专门记录下每次部署的步骤,步骤由开发人员写,然后由运维人员执行。只是我不知道他们会不会回顾之前所有针对这台机器的部署步骤。 这个团队里有某某大型互联网公司来的架构师和某财务软件公司来的运维,所以,我不负责地推测,我们这个行业很多公司对于配置的管理还没有达到足够的重视,也没有正确的看待。 我笑了,接着问朋友:那我要知道当前机器的“最终状态”,是不是要找出所有部署记录,还要过滤出对这次部署有影响的每一个细节?比如那条 Iptable 规则。 接下来的对话细节已经记不清,也不重要了。重要的是找出针对这类运维事故根本原因及解决办法。 我个人认为这类问题的根本原因在于: 配置管理的失控: 已经没有人完整知道线上环境配置是什么了?要了解时,只能一个个查。 测试环境与生产环境的配置不一致: 如果那位倒霉的同学在测试环境部署出现这样的问题,到生产环境部署时,自然就会注意相关配置项了。 以上只是我个人认为的,不一定正确,欢迎各位读者讨论。 那如何杜绝这类问题呢? 这两个原因可以看作一个,也可以看作两个。但方法都是一样的: 使用声明式的配置管理方法,而不是脚本式 版本化这些声明的配置 所有环境使用同一套装配置管理方法 使用声明式的配置管理方法,而不是脚本式的 脚本式的配置管理是这样的: apt-get install build-essential apt-get install libtool cd /usr/local/src wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.37.tar.gz tar -zxvf pcre-8.37.tar.gz cd pcre-8.34 ./configure make make install 而声明式的配置管理是这样的: # ./ansible-nginx/tasks/install_nginx.yml # 使用这个7-0.el7版本的yum包 - name: NGINX | Installing NGINX repo rpm yum: name: http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm # 当前机器的nginx的状态应该是最新版本 - name: NGINX | Installing NGINX yum: name: nginx state: latest # 当前机器的 nginx service 的状态应该是已经启动的。至于如何确保 nginx 这个 service,当前是什么状态的,又是如何启动的,我们不需要关心。 - name: NGINX | Starting NGINX service: name: nginx state: started 声明式的配置里写的是当前环境的“状态”,语意上,声明式的配置不论你执行多少次,你得到最终的“状态”就是你所声明的,这也就实现了《持续交付》里说的: ...

2018-03-30 · 1 min · 135 words · 翟志军 Jack Zhai