传统解决运维的问题有几种,一种是用自动化的部署工具去做软件的部署,用一些配置管理工具,比如Puppet等,某个软件需要在哪些服务器里是有的,或者直接给他一个版本或者某个配置文件,需要它存在某个路径里,现在可以做更多的事情。
我今天想跟大家分享一下基于网易的一些互联网产品运维的经验,跟大家聊一聊容器在运维这块的一些实践和经验。
网易云的历程,大家现在知道比较多的网易云音乐、考拉、网易邮箱。最早网易云是给这些部门提供支撑的,自己在运维上积累了很多经验。这是我们运维部门统计出来从2015年-2016年之间服务器数量提交的工单增长,主要是因为这些业务部门产品发展非常快,所以它的规模发展也非常快,很多问题都会凸显出来,比如以前需要手动做的东西,当大到一定规模的时候,会发现这个跟不上业务的发展。
传统解决运维的问题有几种,另一些是用配置管理工具,比如Puppet等,某个软件需要在哪些服务器里是有的,或者直接给他一个版本或者某个配置文件,需要它存在某个路径里,现在可以做更多的事情。但是传统的这些解决办法也有一些它自己的不足,比如自动化工具在做的过程中,可能会碰到这个节点在操作、更新或者部署的过程中,节点连接断了,或者说操作过程中导致操作系统或者应用出了一些问题,你没办法回滚,也没办法继续进行下去,你只能说登录上去看一下怎么回事。配置管理工具也有一些问题,你要写一个规则或者模板去定义你希望这些节点是处于一个什么样的状态,比如说你更新软件,它是对于你正在提供服务的节点进行更改,比如你提供一个工具的更新,你更新过程中这个工具是用不了的。如果是操作系统级别的更新,可能还是需要在整个虚拟机的生命周期做这些操作,可能还是要有些重启的操作。这些传统的方法我们自己也用过,他们是解决了以前的一些问题,但是仍然还有些问题。另外,不可变基础架构,当我需要去做一个更新的时候,我不是对现在正在工作的节点去做更改,去更新、打补丁、改配置,而是说去重新创建一个节点,用新的文件,用新的应用版本去发布新的节点,然后去替换现有的节点。这是它和传统方式非常不同的一点,如果我想从一个1.0的版本升级到1.1,我传统的方式可能是不管手动也好,用自动化的工具也好,把这个旧的版本卸载掉,或者直接升级到1.1版本。不可变架构是旧的节点不要了,重新创建一个跟它一模一样的节点,但是这个节点装的是1.1的版本。我的基础架构怎么可能不变呢,总会有些配置是要改的,总会有些补丁要打的,包括一些安全的漏洞,可能还是要去做的。
并不是说整个系统不变,这个系统指的由多个组件、多个应用组成的系统,而是说系统中的一部分可能是无状态的应用,这些无状态的应用,尤其现在大家说得比较多的分布式的应用,总会有一些是无状态的应用,部分采用不可变基础架构是比较合适的,而那些有状态的部分,可能还是要通过传统的一些。我们也看到有些公司已经在用类似的基础架构去管状态的服务,但是挑战会更大。不可变基础架构和传统模式的变化,右边这个图大家都很熟悉,典型的软件层代码发布到管理工具,CI/CD这个流程,部署到测试环境最后上线的典型过程。其实有很多地方是没有变的,它相比传统模式怎么变化,以前我的运维,尤其是修补的那部分,我系统上线以后我发现要更改的那部分,我是在系统上线以后做的,不可变基础架构的理念是说把这部分工作移到你前面在提交代码和构建镜像的那部分,把这部分工作放到这边去做,包括你环境配置的更改都是提前去做。它不影响你现有的已经上线的环境,不会影响你现在的服务,你仍然可以继续提供服务,我会去发布一个新的环境,去把流量导过来。原来做的那些更改,有可能产生风险的那些操作提前了。这么带来一个好处,你能提前发现你的错误,因为有时候你做一些更改,理论上做任何更改都是有风险的,你打一个补丁,你不知道可能会影响某个依赖或者操作系统哪块出问题了,就会导致一些不可预计的问题。但是你如果是提前去,在预生产环境就能发现这个问题,你能很快修复这个问题,最重要是你的服务并没有中断,这是它和以前很不一样的理念的地方。
我们的变化就是在不可变基础架构里,我们做的变更修改是可以做版本控制,做版本控制带来的好处是,可以做自动化,可以像管理代码一样去管理你的这些基础设施。我们以前在做传统运维的时候,我们上线的那些节点,很可能很多操作你是没有记录下来的,没有书面记录或者有一个系统记录你做了什么东西,你改了某个配置,你改一个host文件,你去打一个补丁升级的库,没有这种记录。而且你的记录是很难有版本化的,你可能是在你团队的某个文档里去写,而这个文档可能只有做这个操作的人才知道,其他维护的人有可能根本不知道有这么一个文档。很难把做下来的那些办法记录下来,以后如果其他的系统或者你要把这个业务节点迁到另外一个平台上,你其实不敢迁,因为你都忘了你自己做了哪些操作了,时间越来越长以后,其实你在系统上做了很多事情。
举个例子,这是一个外国人在另外一个大会上分享的例子,比如说用配置管理工具去装一个openssl的管理工具,写一行确定这个openssl在这个节点上是安装状态就可以了,不用去管它,它每次会去检查,如果检查没有openssl,会自动去。在真正的产品下,有可能会碰到,比如说发布了一个公告说openssl工具有一个漏洞,请大家打上某一个版本的补丁,这是一个非常常见的现象。它可能需要你打1.0.1g这么一个版本,你现在写这么一个脚本,然后说我要打上这个版本,配置管理工具开始推送新的版本。但是有可能你会发现这个新版本可能会有些问题,比如说影响了你现在的某个业务的一个东西,或者说它就是装的时候把有些东西搞坏了,你的工具就不工作了。你发现这个问题了,你想回滚,你重新改成installed的状态,因为你这个版本已经是installed的状态,所以你再说installed,它是什么都不会做的,就保持这个新的版本,因为已经是installed状态了。怎么办,你说换一个版本,换一个ensure版本,你觉得这会能解决这个问题。结果你可能发现你的仓库里面根本没有这个包,这个包可能是比较旧的。你说我装个更久的版本,0.9.8,旧的版本有更大的可能性是找不到的。你可能会发现你装了一个新的版本以后,你可能把相关依赖的都给装上去了,这时候再回到以前的旧版本是可以做的,但是会发现比较麻烦,你要手动去做很多事情,这就是配置管理工具的一个问题。
同样是这个问题,我们用不可变基础设施的方法去做会是什么样,你会发现首先旧的那个节点还是在的,并没有被删除也并没有被更改,如果你避嫌有问题以后,一个是你在更新的时候,你这个服务仍然可以对外提供服务,如果你一旦发现有问题,可以马上回滚到原来的状态,也就是说把新的实例删掉,这是用旧版本去提供服务。从这个例子可以看到,这种运维产品它是会带来一些好处的。你可以在旧版本继续提供服务的情况下做一些排错,做一些新的版本,把安全补丁打上,让服务恢复正常。这里是采用不可变基础架构的一些好处,我的系统基本上是刚配置好的时候是最正常的工作状态,什么问题都不会有,就像大家一般碰到问题,比较快的是重启一下试试。你从开发、代码提交到测试完,测试正常以后,做出来以后肯定是正常的状态,但是你跑的时间久了就不知道这个节点会出现各种各样奇怪的问题。你永远是跑的一个新的实例,而且这个实例基本上是在你刚装好的打包好的状态。
开发和生产环境一致,如果大家熟悉十二要素,里面有一条环境等价,我的开发、生产环境是一致的,开发环境里面工作得好好的东西,可能到运维的环境或者测试的环境就不一样,以前是开发自己搭自己开发的环境,测试有测试的环境,运维有运维的生产环境,这些环境你可能是靠文档去保证它的一致性,但是靠文档就会有一个问题,人是有可能出错的,你才用不可变基础架构,你的开发生产环境,只要在开发中能正常应用,在生产中也会正常,不会因为环境问题而导致问题。再一个是容易回滚,刚才说把排错和发现问题的时间已经提前到你的代码提交和环境配置的阶段,比如说到预发布环节的时候,你就可以看你这个生产会不会对生产环境产生实际影响。你的变更管理会做得比较简单,以前像金融这种客户,他们有些要求比较严的,他们要求录屏,做什么操作首先要申请,要审批,才能登到某个机器上用某个账号做操作。刚才说到的ITIL,你可能还要提变更管理,在ITIL里提变更管理,要告诉审核的人我要做什么变更,目的是什么,预计多久完成,有没有提前做测试,如果失败的话回滚计划是什么,这是非常花时间的,现在有了这个,你的操作都可以在版本控制的工具里做了,比如我们常用的Git或者SVN这些工具,它是可以支持你的版本管理的,那你的这些运维的操作也可以在那里面被记录下来,你以后去查我什么时候做了个什么事情,你其实也是很容易查的。
做运维的同学可能都知道,其实很多时间是花在排错上,你出现一个问题,然后可以抓包,你最终花很长时间找一个原因,但实际上你想要的并不是说找的这个原因,而是希望这个服务内容能够持续可用。当然有一些原因你总是还要解决,如果这个问题是可重现的,并且确实是你产品里的bug,但是在线上你第一目标是保证系统可用。你是希望我的宕机时间越少越好,很多的拿一个新的工作的节点去替换你现在旧的节点,让这个入伍继续可有,对你来说更重要。排错可以把这些有问题的节点拿到线下环境,你再去慢慢排错,这种方式比较好。运维现在很大一部分时间,除了排错,部署新的应用,那些互联网的应用迭代都很快,所以可能每天有好几个版本发布。如果你的部署总是像以前传统的流程比较长手动,或者有些流程手动、有些流程自动,其实是会比较慢的。现在这样,你除了前面去配置,比如你改一些代码或者改一个环境的配置,除了这部分是放到前面以外,后面改了以后,把这个新的代码提交出来打包编译,测试完以后提交镜像,再推到线上环境,所有的流程是一个自动化的,你后面的东西不会再被更改。
为什么选择容器,了解容器的同学都知道,容器本身就是,打包以后,底下都是一层一层的操作系统,只有上面一层是可写的。你做了一些更改,你一重启就没有了,它又回到了它原来的样子。所以容器这个特性很适合不可变基础设施这种理念。再有容器里有一个参数,read only,比如一个黑客黑到你Web Server里,要改你的网站,要改你Index的文件,以前他黑进去了就可以改,但是如果你加入read only,即便他进去了,但是这个他改不了,它是只读的。容器本身它的镜像是支持这种,冒号后面是加版本的,所以容器本身支持版本管理,都是可以编程的,可以像代码一样管理,你就能自动化,你就可以用一些工具去调用,把容器这种嵌入到工具链里,而不是为这个技术重新去写。再一个,Docker跨平台,当然现在也有一些ansible,一些做配置管理的工具也支持做跨平台的镜像。容器来讲,从你自己的私有云也好,或者从公有云,亚马逊也好到其他的厂商,或者从一个虚拟化平台到另一个虚拟化平台,你只是拿了一个Docker File,只是个文本,没有平台依赖性。再一个,Docker本身支持环境变量的注入,你的很多配置可以通过环境变量的方式注进去。这样会减少你对以前传统配置管理工具去配置文件的依赖。其他的像轻量、快速,大家听得很多了,这里不讲了。
这是一个例子,从版本1发布到版本2,还是要人工去参与,当然你自己写一些工具链,其实也可以做到自动化比较好,但是你可能还是要关注其中的一些东西。这里面比如一个前端的系统、服务,从版本1发布到版本2,从新版本的容器的创建生成到旧版本的删除,到负载均衡流量从旧版本切换到新版本,这个都是自动的。如果用命令行只需要敲这么一行命令,从前端的版本1,用一个Json的文件,只需要在文件里把版本号和名字改一下。它首先会创建一个新的版本2的示例,它会删除一个旧的版本,再起来一个新的版本,再删除一个旧的版本,一直到旧的版本都变成0,新的版本变成你想变的数量,它的滚动升级就完成了。如果中间你发现有问题,你可以再很快去回滚,也是一行命令就可以让它从版本2再回滚到版本1,因为这个镜像都是存在的。我们现在做滚动升级就很方便了,我只需要把我的新版本写好以后提交,剩下的事情,我新版本怎么部署出来,交给容器来做。
没有一个工具或者理念是完美的,不可变基础设施也有用得不太好的地方,比如以前我有一个小的修改,要去修改一行css代码,或者就是简单在后面加几条记录,可能以前我们就ssh上去改一下就好了,1分钟不到就做完了。但是你换成不可变基础设施这种方式,你会发现你的流程就比较长,首先你要改你基础的镜像或者改你的Docker File,改完以后验证,验证好以后把这个提交,然后重新打镜像,再把这个镜像拉下来,再推到生产环境,会发现比以前的传统方式要更花时间。对这种非常微笑的修改,它其实没什么优势,它其实还是挺花时间的。但是一个解决办法,你把验证以后的那些步骤全部自动化,这样对运维来说,我自己花的时间还是一样的,我以前手动的方式也是去改这个配置,改完以后验证一下,没问题。
现在也是做这些事情,只不过你做完以后系统会帮你自动化打包、镜像推送,对于你运维人员来说,花的时间是一样的,但是对系统上线来说可能会长一点。有时候你会采用一种理念,比如不可变基础设施这种理念的代价和你带来的艺术,哪个更大一点,并不是所有的公司或者所有的系统都适合用不可变基础设施。不可变基础设施其实是有前提的,我们右边是CI/CD用得比较多的工具,首先要自动化,不可变基础设施好处就是我自动化了很多东西,我把我部署的流程,从代码端到部署的流程给自动化了,这里面你创建销毁你的节点,编排调度,你的服务发现、服务注册、监控报警,配置管理、流量切换,日志收集这些东西,你其实都要自动化,你才能比较好的去享受它的优势。但是现在很多公司会发现,他觉得这个理念很好,想去做这个事情,会发现有很多工具,但是每个工具都只管了一块,这是很长一部分的链条或者工作流,Jenkins也好,Git也好,这些工具都只是自动化其中一部分,但是你想把这些工具全部都串起来,你自己需要去拼接它们,你需要自己有一些开发工作量的,这个也是有一些门槛的。再一个,你的流程和组织也有些变化,这个其实跟DevOps那些都是类似的,你有些新的完全和以前不同的生产方式,那你一定会对组织和流程有影响的。
除了一些我们常见的无状态的应用,企业还有很多是有状态的应用,包括重型的数据库,大家会想,这些系统怎么处理。我们也有一些方法不是完全不可以做,比如有一些是存用户session的系统,要么通过在负载均衡上做session affinity,或者直接把你系统里面写代码,使用外部的,比如说Redis缓存,把session写到Redis上,这样不管哪个节点起来,这些session都能从Redis得到。把应用做容器化经常碰到的问题,一个是大家要习惯使用环境变量,因为容器支持环境变量,也比较方便,环境中有一些东西,IP也好,或者数据库的用户名密码或者一些东西。再有一个,因为容器是用后即销毁,如果你想去看log,它的log默认是存在本地的,这个节点一旦没了,你的gog就看不到了。你可以把logo输出到STDOUT/STDERR,对你排错也比较方便。再一个是数据库,我们看了一些其他厂商的经验,他们的建议是把这些脏活和累活交给那些云厂商,比如说亚马逊,或者放到其他的公有云厂商,因为大部分自己的开发其实是面向业务的,他们的开发部想去管他的数据库,数据库我要做一个集群,我要什么东西,包括数据怎么同步,跨可用区的做数据同步,这种其实交给云厂商你会发现你可以节省很多时间。如果你确实希望自己去管理数据库,如果你想用容器也是可以的。现在容器也支持挂外部持久化存储,有些公司已经把MySQL等数据库放到容器里去跑。
最后在用容器的中间有一些经验,一个系统你会经常去改,如果说你总是一个base image,一个基础镜像,然后加很多修改,你会发现你打这个镜像的时间会比较长,因为中间你积累了很多更改。我们的建议是,你不经常更改的那部分,公司的一些安全加固和你们自己的一些东西,就放到base image,作为中间镜像的状态,当你想去更新的时候只需要更新那部分就可以了,因为容器本身是有缓存的,你在一定时间里挖过来的镜像,你有的那部分就不会再去拉了。这对大家来说都是减少时间。再一个是收集日志,你采用了微服务或者分布式系统以后,一个挑战是你的排错能力比以前更困难了,因为你要去case很多不同的系统,可能他们之间有的是用接口调用的,这时候你的包本来以前可能是在一个系统里就能把这个全都拿到,但是可能要加一些应用的ID,去检查这个ID,你才能发现这个调用链是怎么回事,用序列化把这个log分析出来。像ELK这个你可以集中存储,到时候去做排错或者去做分析。再一个,如果你们是很小的团队,或者说你们希望把你们的开放时间更多集中在业务上,能尽量多的用现成的工具和云服务厂商提供的工具是比较好的,对开发者来说是更节省时间的。
我就跟大家分享这些,谢谢大家。
浏览4110次
浏览4616次
浏览4041次
浏览11235次
浏览10495次
浏览5677次
2025-01-08 昆明
2025-04-19 南京
2024-12-27 上海
2025-10-23 上海
打开微信扫一扫,分享到朋友圈