老专家详解 DevOps “八荣八耻”

  • 邵海杨

2016 年 11 月 15 日

话题:DevOps文化 & 方法

又拍云运维总监邵海杨在 Tech Minds No.5 中根据自己 10 余年的研发、运维经验并结合又拍云的实际情况,总结并分享了 DevOps 的“八荣八耻”、Herku PaaS 的 12 要素宣言。

以下是邵海杨分享的全文:

今天我讲的是 DevOps 的八荣八耻,和后面 Herku PaaS 的 12 要素宣言相呼应,大家可以一起对照的来看,这其实是一个互相印证的过程。

DevOps 这个思想提出来已经五六年了,一直都是呼声很高,落地很难,为什么呢?这可能与各个公司的业务情况和技术发展路线有或多或少的关系,比如说创业的最早技术合伙人是运维出身或者技术出身,但是水平不高,为了公司持续发展,引入新鲜血液时,就会存在技术的先进性跟解决遗留烂摊子的矛盾。又或者业务本身偏向于用户,导致技术被边缘化,产品又没有好的架构,限制了快速发展等;所以,DevOps 的推进一定要自上而下,凭借挑战自我,颠覆传统的勇气才能去落实。

DevOps 的八荣八耻

一、以可配置为荣,以硬编码为耻

做过开发的朋友都知道,程序跟变量分离会出现单独的配置文件,所以运维要像开发学习,学会把软件和配置分离。

又拍云第一个版本自动化运维时,我们把所有的硬参数配置到 upyun.cfg,如上图。它的好处在于,只要编写一个 config 函数,就可以把这种文本的运行方式,变成右边 nginx 所能运行的格式。并且可以用同样一份配置去适用不同的后端软件,如用 haproxy 替换 nginx,只要重新写一下 config 函数即可,运维和开发仍然可以使用同一份参数。

本地配置的文本格式有 txt、ini、cfg,但是这些格式都没有没有硬性要求,如 ini 在 php 里用的多一点,cfg 在 mysql 里用的多一些。一般配置的文件里面会包括开关变量,调试参数、可变参数、权重,以及关联上下游的参数,这些都可以放在配置里面,通过程序生成程序的方式完成。

第二代就不再使用它了。我们现在使用 Yaml 来定义配置文件,通过它动态生成配置文件,这两者的思路是相同的,都是通过程序生成程序。除此之外,现在也有一些更加流行的技术,比如通过 etcd 或者是 consul 来实现服务的自动发现、自动注册。

二、以互备为荣,以单点为耻

互容互备一直是优良架构的设计重点。

又拍云早期做架构设计,使用了 LVS+Keeplived+VRRP 做转换,这样可以方便负载均衡,动态升级,隔离故障。现在的又拍云第二代,已经在部分大节点使用 OSPF 和 Quagga 做等价路由的负载均衡和冗余保障。

Nginx 可以加 Haproxy 或 LVS 做负载均衡。MySQL 可以做主从切换,或者是 MMM 的高可用成熟解决方案。我们的消息队列之前用 rabbitmq 做,现在主要是 redis 和 kafka 集群化,其中 kafka 已经迁到了 Mesos 容器平台里。

服务的自动发现、注册,我们可以使用 consul、etcd、doozer(Heroku 公司产品),还有 zookeeper。主要区别是算法不一样,zookeeper 用的是 paxos 算法,而 consul 用的是 raft 算法。目前看来 consul 比较流行,因为 consul 的自动发现和自动注册更加容易使用。etcd 主要是 CoreOS 在主推,CoreOS 本身就是一个滚动发布的针对分布式部署的操作系统,大家可以去关注一下它。还有一个是 hadoop 和 elk,大数据平台的可扩展性是标配,很容易互备。

上面是举了一些常见互备的软件组件的造型,那我们如何是设计一个无单点的架构呢?主要掌握以下几点:

1. 无状态

无状态意味着没有竞争,很容易做负载均衡,负载均衡的方式有很多种,F5,LVS,Haproxy,总能找到一种适合你的方式。

2. 无共享

以前我们很喜欢用内存来保持临时信息,如进程间的交换,这种方式虽然效率很高,但是对程序的扩展性没什么好处,尤其是现在的互联网体量,光靠单机或者高性能机器是明显玩不转的。所以我们现在就需要使用类似消息队列的组件,把数据共享出去,利用多台机器把负载给承担下来。

3. 松耦合 / 异步处理

以前我们用 Gearman 这样的任务框架。大家可以把任务丢进任务池里,生成多个消费者去取任务。当我的消费不够用时,可以平滑增加我的 work 资源,让他从更快的去拿任务。运维平台这边以 python/celery 的组合使用更多。 

4. 分布式 / 集群协作

像 Hadoop 这样的天生大数据 / 数据仓库解决方案,由于先前设计比较成熟,一般都是通过很多台机器扩容来实现 map/reduce 的扩展计算能力。

三、以随时重启为荣,以不能迁移为耻

关于这个点,我们讲三个方面:

1.Pet 到 Cow 观念的转变

以前我们说机器是 pet,也就是宠物模式,然后花了几万块钱去买的服务器,当宝一般供奉。但事实上却并不是这样,任何电子设备、服务器只要一上线,便开始了一个衰老的过程,你根本不知道在运行过程中会发生什么事,比如说质量差的电容会老化爆浆,电子元器件在机房的恶劣环境里会加速损坏,这些变化都是我们无法参与控制的,所以无论我们怎么努力,都无法保障机器有多么的牢靠。

谷歌指出的 Cow 模式就是指农场模式。就是要把机器发生故障当做常态,打个比方,比如说这头牛死了,那我就不要了,因为我有很多这样的牛,或者是再拉一头新的牛。这就是我们软件开发和运维需要做的转变,去适应这种变化。

2.OpenStack 虚拟机的编排

虚拟化是个好东西,通过 OpenStack 我们很容易就可以做出一些存储或者迁移的操作,但是在实施的过程中,也是一波三折的。

又拍云从 2014 年开始在内部推动 OpenStack,当然我们也踩过 OpenStack 网络的坑,那时候我们用双千兆的卡做内网通讯,因为使用 OpenStack 实现虚拟化后,一切都变成了文件,在网络上传输的话,对网络的压力会非常大,结果就导致部分服务响应缓慢(因为本身就是实验性质,所以在硬件上没有足够投入,内测时也没有推广,所以影响不大)。

2015 年又拍云再上的 OpenStack,全部都用双万兆的网卡做 bonding,交换机也是做了端口聚合和堆叠。目前来说,只有云存储没有上线,其它云处理,云网络的使用还是能够满足要求。

3.Docker 的导入导出

Docker 是更轻量级的资源隔离和复用技术,从 2016 年开始,又拍云同时也在尝试使用 Mesos/Docker 来实现云处理的业务迁移。

四、以整体交付为荣,以部分交付为耻

以往开发运维要安装一个机器,首先要去申请采购,购买完了还要等待运输,在运输中要花去一天的时间,之后还需要配交换机和网络。在这个过程中你会发现,简单的给开发配台机器,光上架就涉及到运维的很多环节,更不要说系统安装,优化,软件配置等剩余工作了,所以大多数情况下你只能做到部分交付。

要如何解决这些问题?通过 OpenStack 可以做到云计算、云网络、云存储这三块搭建完成之后,进行整体交付。

根据一些经验总结,在整个云平台当中,云存储的坑最多,云计算、云网络相对来说比较成熟。现在云计算的硬件基本上是基于英特尔 CPU 的虚拟化技术来硬件指令穿透的,损耗大概 2%~5%,这是可以接受的。至于云网络,刚才胡凯(B 站运维总监)提到内网包转发效率,我做过一个测试,在 OpenStack 的内网中,如果 MTU 默认是 1500,万兆网卡的转发率大概为 6.7xxGbps。后来我在优化的过程中,也翻查一些文档,看到的数据是可以达到 9.5xxGbps,通过不断的摸索,对比测试后发现,如果把内网的 MTU 搞成大包,如 9000 时,万兆网卡的存储量直接达到了 9.72Gbps 左右的。不过,这个 MTU 需要提前在宿主机上调整好,需要重启生效。所以,这个问题发现得越早越好,这样就可以做到统一调度,分配资源。

Docker 的好处是可以做到 Build、Shipand Run,一气呵成。无论是对开发,测试,还是运维来说,Docker 都是同一份 Dockerfile 清单,所以使用 Docker 在公司里的推动就很顺畅。虽然 OpenStack 也可以一站式交付,整体交付,使用时非常方便。但是对开发来说,他还是拿到一台机器,还是需要去安装软件环境,配置,上线,运行,除了得到机器快一些,对上线服务没有什么大的帮助,所以又拍云现在的 Openstack 集群一般对内申请开发测试用,外网生产环境还是以 Docker 容器化部署为主,这也是大家都喜闻乐见的方式,但前提是开发那边能够适应编写 Dockerfile(目前是我在内部推动这种变革,如新的项目就强制要求用 docker)。

五、以无状态为荣,以有状态为耻

有状态的服务真的很麻烦,无论是存在数据库、磁盘开销,还有各种锁等资源的竞争,横向扩展也很差,不能重启,也不能互备。所以,有姿态的服务对于扩展原则来说,就是一场恶梦。如果是说我们解决这个问题,那就要使用解耦和负载均衡的方法去解决问题。

1. 使用可靠的中间件

中间件其实最早出现在金融公司、证券公司,后来随着互联网行业不断壮大以后,就用一些高可靠性的号称工业级的消息队列出现,如 RabbitMQ,一出来以后,就把中间件拉下神坛。随着中间件民用化,互联网蓬勃发展,是可以把一些服务变成无状态,方便扩展。

2. 公共资源池

我们可以通过各种云,容器云、弹性云,做计算单元的弹性扩展。

3. 能够被计算

如果你不想存状态,那也可以被计算,比如说 Ceph 存储,它的创新在于每个数据块都是可计算出来的,这就类似无状态的,每次都算,反正现在的 cpu 都这么强悍了,所以,无状态是一个命题,在做架构的时候,你脑海里一定要有这个意念,然后再看你用什么样的方式开动脑筋,预先的跟开发,运维沟通好,把应用拆分成一种无状态的最佳组合。

六、以标准化为荣,以特殊化为耻

在标准化方面,我们在这几个方面改良:

1. 统一输入输出

统一入口是我加入又拍云后做的第一件事情,我们用一个统一的文本,到现在也在用,然后推送到所有的边缘,服务器上面的组件,要用到的参数,都能从配置里读出来。代码管理方面我们也使用 git,git wiki,批量部署我们用 ansible(早在 2012 年,我做了一些比较后,就在公司里推行 ansible,看来还是很明智的决定)。

2. 统一的流程管理

运维中使用 python 最多,所以我们使用了 yaml 和 playbook。又拍云有自己的跳板机,通过 VPN 登陆,目前我们也在试用一个带有审计功能的堡垒机,可以把每个人的操作录制下来,然后再去回放观察,改进我们的工作流程。

3. 抽象底层设计和复用组件

如果是开发者的话,就会写很多的复用函数,对于优秀的运维人员来说,也要有优秀的抽象业务的能力,也要去做一些重复工作的复用准备,如频繁的,繁琐易出错的手工操作抽象成若干运维的脚本化。

最后是巧妙的利用虚拟化、容器服务、server-less 微服务,这些服务是可以被备份,还原的,可以保持一个相对稳定的状态,我们要拒绝多的特殊管理操作。香农 - 信息熵理论里说,变量的不确定性越大,熵就越大,把它搞清楚所需要的信息量也就越大。理论上来说,如果是一个孤立的系统,他就会变得越来越乱。

七、以自动化工具为荣,以手动和人肉为耻

又拍云早期,用的是 bash、sed、awk,因为我之前有搞嵌入式的背景和经验,对一个十几兆的嵌入式系统来说,上面是不可能有 python/perl/nodejs 等环境。所以我们把服务器批量安装,部署,上线,做成了嵌入式的系统后,只要点亮以后,运行一个硬件检测的程序,会把机器的 CPU、内存、硬盘大小等都打印出来,供货商截图给我看,这个机器是否合格。合格的机器可以直接发到机房去,在机器到了机房通上网线以后会有一个 ansibleplaybook 的推动。

自从用了这种方法以后,我们在公司里面基本上没有见到服务器,一般直接产线上检测通过后发到机房。然后又拍云的运维人员就可以连上去远程管理,在过去的三年里我们服务器平均每年翻了三倍,节点翻了六倍多,但是人手并没有增加。

关于 tgz、rpm、pkg 的打包部署,我们用的是 tgz 的打包及 docker 镜像。优势在于,又拍云自有 CDN 网络,软件通过推动到 CDN 网络下可以加速下发。

关于集成测试、自动测试的发布,像 ELK 集中日志的分析、大数据的分析,我们现在使用 ELK 以后,只要有基础的运维技术知识便可看懂,不需要高深的运维知识和脚本编辑知识,大多数人都可以完成这份工作,好处就是你多了好多眼睛帮你一起来发现问题,定位问题。

最后是不要图形,不要交互,不要终端。一旦有了图形以后,很难实现自动化。原则就是,不要手工 hack,最好是用程序生成程序的方式去完成这个步骤。

八、以无人值守为荣,以人工介入为耻

运维部门要做的事情有三件:

1. 运维自动化

要有一定的业务抽象能力,要有标准化的流程。没有好的自动化,就很难把运维的工作效率提升了,只要做好这些,就可以节省时间,从容应对业务增长。而且运维自动化的另一个好处就是运维不会因为人的喜怒哀乐而受到影响稳定性,比如说我今天心情不好,你让我装一台机器我还可以忍,你让我装十台一百台就不行了。但如果公司有了运维自动化的流程,这个事情就可以避免,因为谁做都一样。

2. 监控要常态

2016 年年初,又拍云特别成立大数据分析部门,我们把日志做了采样收集和过滤,通过大数据平台做日志的同构数据分析,重点关注 4xx/5xx/2xx 比例,响应时间分析如 100 毫秒、200 毫秒、500 毫秒,还有区域性的速率分布,讲真,这真是一个好东西。

3. 性能可视化

数据的有效展示。现在 ELK 对我们的帮助很大,从监控图上来看相关的数据指标,一目了然。这里就不反复赘述了。

DevOps 的本质

最后,我们谈一谈 DevOps 的本质。

1. 弹性

像亚马逊推云时,那个单词叫 elastic,意思是,你要能够扩展,如横向扩展;你要能负载均衡,如果你是基于 openstack/docker 资源池,你的资源就可以复用,可以编排回滚。比如说 OpenStack 有模板,我打一个镜像包,稍微重了一点,Docker 的就轻一点,Docker 可以做一个滚动发布,可以保留原来的程序、原来的容器,你可以做快速切换,这也是一种变化的弹性。

2. 无关性

如果是虚拟化资源,一切都可以在模板里面设置,可以把底层的硬件、系统、网络抚平差异,比如说不管物理磁盘是 1T(市面上缺货)/4T/6T 的盘,都可以划分 100G 容量,所以当把一切变成按需申请的服务,无论是开发还是运维,工作都会比较简单,因为它的无关性。

3. 不可变的基础设施

这个对传统运维可能是一种打击,因为基础镜像可能已经做的足够安全,足够完美,足够精干,不需要基础运维过多的人工参与。但我认为恰恰能帮助传统运维减轻工作量,反而有更多的精力去迎接虚拟化、容器化,SDN 的挑战,掌握了新技能后,就可以随取随用。

我现在是用比较开放的心态去接受这些变革,比如说像 CoreOS 这个操作系统,你到里面去看一下,真的没什么各种组件,除了最基础的就只有 docker/RKT 运行环境。当然,这个是很极端的例子,但是我觉得当底层的操作系统,他可以做到自我更新,足够安全的时候,确实不需要你去操心这个事情。这跟我们用小 U 盘做整体升级的思路是一样的,但是它那个更先进,更彻底,更值得我们去学习。

又小拍:除了分享“DevOps 的八荣八耻”,海杨哥哥还在 TechMinds No.5 上逐一解读了“Heroku PaaS 的 12 要素宣言”和“OpenStackIaaS 的设计指导思想”。为了避免篇幅过程,小拍把这两块内容的精典都概括出来啦。

Heroku PaaS 的 12 要素宣言

Heroku 是业内知名的云应用平台,从对外提供服务以来,他们已经有上百万应用的托管和运营经验。前不久,创始人 AdamWiggins 根据这些经验,发布了一个“十二要素应用宣言(TheTwelve-Factor App)”

1. 基准代码

一份基准代码多处部署,这个就是刚才讲的标准化,还有复用组件,这些是吻合的。 

2. 显式声明依赖关系

比如说像 nodejs、go、maven,在提供的配置文件上,要标明上下游的关系,标明可配参数。

3. 必须要在环境中存储配置

这一点我觉得可以保留,因为完全变量有它的好处,一是语言无侵害性,二是代码无关性。比如说 OpenStack 里也有环境变量,它是会导入环境变量的那份文件。

4. 要把后端服务当做一个附加资源

这就是我们一直在强调的松耦合,要把后端服务变成一个 url 去调用,像 Mysql,或者是通过消息队列或任务框架。或者更加智能一点,做自动的服务发现、服务注册,把后端服务剥离开去。这样做的还有一个好处,就是能够区分运维工作中哪些是重点资源,哪些是需要更多的人去监控后端的服务,当知道这是重中之重的时候,就可以把有限的运维力量投到上面去。

5. 构建、发布和运行

用了 Docker 以后就很容易实现这个标准,关键是统一性、标准化和模板或容器格式。

6. 进程必须是无状态而且无共享

任何需要持久化的数据都要存储在后端服务里。

7. 要做端口绑定

要通过端口绑定来服务,你可以基于 Restful,也可以基于 rpc 或者是 http,这样的好处是松耦合和方便负载均衡,做水平扩展。

8. 并发

要通过进程模型进行扩展,方便扩展。

9. 易处理

快速启动和优雅终止可最大化健壮性。参考雅虎原则。对雅虎来说,如果开发需要上线服务,就必须向运维提供 start、stop、status、reload/restart 这几个开关,如果你不提供这些开关的话,雅虎的运维是不允许这个服务上线的。因为能提供 stop,那就说明服务是能被随时重启的最好证明。

10. 开发环境与线上环境等价

尽可能保持开发、预发布和线上环境相同。用了 Docker 以后,这个事情就不是个事情。

11. 把日志当做一个事件流

我们现在用 logstash 和 heka 去做一些处理。对云服务来说,我们现在发现,日志越来越重要,日志收集对我们来说价值非常大,通过大数据分析平台,可以做到秒级分析,秒级监控报警,秒级定位问题,甚至我们可以比客户更早发现问题,我觉得这是一件非常有意义,值得我们投入精力去做的事情。

12. 管理进程

后台管理任务可以当做一次性的进程运行,相当于微服务。

DevOps文化 & 方法