讲师 | 李志伟
编辑 | 小百科
K8S 2 周岁 Meetup 已圆满落幕。从今天开始,本公众号将会陆续发送北京、上海两地讲师们的课程图文直播及 PPT 下载,敬请关注!
今天,为大家带来的是当当网数字业务事业部技术总监李志伟的演讲文字版 + PPT。下面是其 《 Kubernetes 容器云平台迁移实践 》的演讲实录(全程文字速记由 IT 大咖说整理提供),感谢李志伟老师校稿。
在公众号对话框输入:当当网,获取 PPT 下载。
大家下午好,非常荣幸今天能够在这个周末跟大家一起分享关于我们在容器云迁移过程当中一些实践的经验。当然,这些经验也许不是普适的,也许它只适合当时我们的一些应用场景,但是我想这些在业务迁移过程中,大部分人都会遇到的一些问题。因为时间有限,今天主要挑一些主要的部分跟大家一起分享。
我们团队最早选择容器这个平台这个决定最早从 2015 年下半年开始,当时其实 Kubernetes 在 1.0 版本左右的阶段,还存在着非常多的问题。
对于当时的情况来讲,我们希望往容器方面转型,是因为当时我们大部分业务还是在基于物理机平台在运行。我们想是不是应该先构建自己的私有云平台然后再考虑其他的,但是当我们接触到 Kubernetes 平台以后,我们觉得可以跳过这个过程,直接进入到容器云平台上来。
我们当时的情况是:大多数业务运行在物理机上,运维自动化水平不高,系统架构也是各种各样错综复杂,计算资源的利用率不足 20%,众多的物理机资源利用率非常低。但也没有什么办法,基于物理机部署的问题是如果你要把很多业务部署在一个物理机上,会导致很多系统非常复杂,并且管理起来非常麻烦。当有突发流量的时候,水平扩展和迁移都会非常困难。还有因为历史原因,企业的老旧服务非常多,这些部分都需要有一个新的平台平滑迁移过去。
K8S 是微服务架构最佳的运行平台之一,我们如果把现有业务都按照微服务架构思想来改造一遍,在当时的情况下,也许需要几年时间。
最终我们的决定基于我们现有的应用的基础上来做一个平滑迁移,我们的时间只有半年左右,我们要把所有业务都迁移过来。这个挑战在于现有业务错综复杂的情况,如何能够快速迁移过来。
我们当时的目标定的比较简单, 我们既然还没有一个自动化的运维平台的情况下,我们就先把第一阶段的目标着眼于用 Kubernetes 来实现一个高可用的运维平台,一个高效的服务编排平台,还有高度自动化的运维平台。
至于说当时为什么选择 Kubernetes 呢,当时可选择的方案也不够多。Kubernetes 当时已经在 1.0 版本,已经可以考虑应用在生产环境。所以我们先在部分业务先开始进行尝试。
Kubernetes 的这些优势我想就不用多说了,因为这些已经是大家都熟知的。我想强调的是这个平台真正是改造了我们研发流程,传统的研发流程里面要做的很多事情,在不同的运行环境当中要改很多东西才能跑起来,容器平台真正实现一次构建,就可以运行在多个运行环境中。
还有一个原因,我们当时考虑过未来有可能使用公有云的解决方案,如果使用 Kubernetes 可以让我们在不同的服务商之间快速迁移,解决了服务商锁定的问题。 还有就是因为 Kubernetes 遵循 Borg 的架构思想,使用它可以规范我们现有的系统架构设计。同时,自动化运维基本上就可以实现的。
我们先从迁移前基本的准备工作开始,基本上就这些。
首先需要搭建一个 Kubernetes 集群,这是一个前提,但是这里面临困难,应该主要是需要通过 vpn 链接到 gcr 的问题,这需要自己想办法来解决,就不多说了。
接下来就是搭建自己的镜像仓库,企业需要把自己的业务容器镜像放在自己本地的仓库中来,我们初期用的是 registry,后来我们更换成了更好用的 harbor。
集群监控我们用的是 Heapster ; CI、CD 采用的是 jenkins ;分布式存储解决方案用的是 Glusterfs。
这是容器云组件的选型情况,我们当前已经在测试期 1.6 版本。
在整个业务迁移过程当中,我们首先要制定很多规范,最早我们没有规范的时候,发现迁移的困难非常之大,所以初期需要制定一个规范出来。
这里规范有容器镜像的封装需要有一些基本原则、Name Space 的使用规范、Serivcer name 的命名规范、健康检查规范、还有 Image 而 tog 配置规范、还有 Config Map 的使用。
首先,从容器镜像封装的一些基本原则来说,就是在容器当中,所以尽可能设计成无状态服务。为什么说尽可能的呢?如果新设计的话,那肯定是强制要求必须要设计无本地状态的服务,因为老的应用部分还是有一些有状态的,这个时候需要一些其他的办法来解决。
还有尽可能消除不必要的运行环境依赖。需要持久化的数据写入共享存储当中,尽可能保持业务的单一性,如果比较复杂的业务集中在一个容器中,故障率也会很高,调试起来非常复杂,所以尽量保持它的单一性。
还有日志输出到标准措施当中,日志的输入量要进行控制不能过大,过大的话有可能会对服务性能有一定影响。
还有是配置与镜像分离,这个是必须要做到的。
容器中使用 K8S 内部的 dns 代替 ip 地址配置形式,不同的容器之间使用 service 调用的时候,用 dns 比较好。
日志采用集中化处理方式,现在基本上是 EFK,还有关于定时任务,定时任务最早是自己在做的,做一个独立的容器专门来跑定时任务。现在还可以考虑用 cron job 的解决方案。
NameSpace 应该有很多的用法,我们用于它作为隔离运行环境的一种方法。我们的开发环境、测试环境、预发环境、生产环境实际上是用 NameSpace 进行隔离的,这种方式比较方便的。
初期担心可能会相互之间影响,实际上隔离效果还不错没有太大的影响,毕竟生态环境的配置和测试环境还是有差异的。
NameSpace 还能实现资源隔离,这个在一些中等规模的业务场景下还是比较好的。
还有 Service name 的命名规范。很多人觉得不值得一提,其实很多服务在做业务定义的时候发现受制于字符数限制,所以需要做一些运营规范。我们当时定义各种简写的组合这样的方式,但是 1.5 以后这个问题已经不大了,1.5 以后基本上都是 63 个字符了,这个问题基本上可以解决了,但是有些首字母不能是数字。
关于应用健康检查这部分,我认为健康检查这部分在整个迁移过程当中是至关重要的一个环节,这个环节是你想要达成一个自动化运维的一个最基本的手段。
这个手段现在似乎还是没有什么其他的可替代的方法,所以这里面要解决的就是说多你服务众多的时候,你的服务的管理和故障自我恢复要靠传统的方式实现就变得非常困难,用健康检查的方式还是非常不错的,这种方式其实绝大部分的故障都可以实现自动恢复。
Kubernetes 健康检查有两种,一种是进程级的健康检查,进程级的健康检查是系统自带的;一种就是业务级的健康检查,这是我们必须要针对每一个业务都要专门去做的,这个部分不是配一个检查端口检查就可以的,而是要针对业务对每一个核心业务流程全都检查了一遍。
如果订单服务出现错误的话,用传统的方式发现问题很麻烦,但是在你做好的健康检查的程序以后,这个问题就会变得比较简单。出现问题的时候,非常容易发现到底出现在哪一个容器里面的问题。
这里面要注意的一些部分,就是健康检查程序的执行时间要小于健康检查周期,这个部分似乎不是问题,但是如果你的业务很复杂,非常容易超过它的默认时间,你就会发现有众多的问题出现了,这个时候可能就会把你主机的资源耗光。
健康检查的资源占用也要合理控制,这一个也是有教训的。我们提出来健康检查的规范和执行的时候,很多人不太考虑资源占用的问题,经常会发现说有一些健康检查程序消耗非常过大,每次调度的时候,对 CPU 的资源异常增减,完全不知道什么原因,最终从健康检查程序中找到原因。
这些基本的规范是要检查的,这个是健康检查基本实现的,其他的网络服务只要探测端口就可以验证。还有如果不是网络服务的话,就是在选择程序来检查、自己来检查健康状态。
关于 Yaml 文件当中 Image tag 配置规范,这个问题也困扰我们一段时间,发现容器升级了以后不更新,这个问题其实实际上就 Image tag 使用形式问题造成的。
使用 Config Map 实现应用平滑迁移,配置应该与镜像内容分离以保持容器化应用程序的可移植性。Config Map 是非常推荐一定要用的,配置里面使用场景,填充环境变量的值,设置容器内的命令行参数,填充卷的配置文件,不需要更改配置的方法。
迁移中遇到其他的一些问题,关于 CI / CD 的问题,其实我们采用 Jenkins 实现 CI / CD 的时候,很多人会觉得 Kubernetes 很复杂,不知道该怎么做。
后来我们是说一定要让它 CI / CD 自动化实现,就把这个门槛降低了很多,所以 CI / CD 基本上都是通过 Jenkins 它来实现全自动化的过程。开发人员不用太了解它的内部机制基本上都实现了。
时区的问题,我们最早是从 Docker hub 上下载的容器镜像,容器镜像的时区配置都是默认值,所以我们希望用比较简单的方式来实现时区的切换,不想改基础镜像的直接通过配置就可以实现。
时区的问题,我们最早是从 Docker hub 上下载的容器镜像,容器镜像的时区配置都是默认值,所以我们希望用比较简单的方式来实现时区的切换,不想改基础镜像的直接通过配置就可以实现。
关于网络上的一些问题,这个问题在我们之前物理机当中,或者说在负载不是很高的情况下,这个问题很难会出现。
但因为当时我们千兆网络基本上快跑到 500 兆左右的情况下,经常会出现一些问题,最后找到的原因,就是这个问题造成的。K8S 集群要考虑网络层面的各种优化,这个部分的优化社区中的最佳实践比较少。
我们最终的目标并不是把业务迁移过来就好,我们更长的目标是说把我们业务做微服务化的改造,这是我们最终的目标,我们当前也正在进行中,只有这样才能真正发挥 K8S 的优势。
我今天的分享就这些,谢谢大家!
答疑环节
Q1:您好,我有一个问题就是关于负载均衡的问题,就是你们迁移完之后用的是 Service,Service 做负载均衡是一个假的负载均衡,有性能问题,提高有没有接入一些外部的负载均衡器?因为有很多的功能可以用的。
A:刚才没有说清,我们在最外一层是在集群之外还有负载均衡器,它的负载均衡器是在最外层,最外层它是通过我们的负载均衡器对外提供服务。
Q1 追问:它还是会过 Service 是吗?
A:对,会过 Service,这个我们并没有改,这个部分早期版本说有性能问题,但是我觉得现在的版本性能问题并不大。
Q2:在 CI 的时候我们会不停的生成测试版,这样会导致占用大量的资源,我们怎么考虑做自动的清理?
A:我们在这方面其实也没有做太多的事情,因为本来 Docker 存储占用并不是太大,如果真的需要清理的话,我觉得应该也没有问题。这个可以写个脚本直接通过 API 把它清除掉。
Q3:我想咨询一下,咱们输出都是标准输出吗?如果通过标准输出有一个界限是多少?
A:业务级的日志,我们并不往标准输出里面写。搜集日志有几种方式,可以通过共享存储的方式,也可以通过 EFK 这种方式。
Q3 追问:你说在应用层内部直接通过 Nginx 进行发布是吗?
A:对,我可能理解的不全面,我说一定是要把应用本身的各种日志,比如调试细节或者错误信息,那个是标准错误输出或者标准输出上面,直接采集出来。如果业务级的日志的话,我觉得应该用独立的方式处理,就是容器内部自己来去解决这个问题。
Q3 追问:刚才说到标准输出的时候有一个阀值,这个阀值是多少?输出多少量导致容器崩溃?
A:在 2016 年初的一个版本 Docker 版本当时有这么一个 bug,当标准输出缓冲区填满时会假死,但是后来版本已经修复了这个问题。当时我们认为大量业务日志输出到标准输出并不是特别合适,所以既使没有了限制我们也没有把所有日志都放进去。