Kubernetes 在网易云中的落地优化实践
技术
作者:张亮
译者:张亮
2018-08-22 11:13

Kubernetes 在网易云应用已经有几年了,随着 Kubernetes 的快速发展,早期使用的版本也面临升级问题。由于网易云的 Kubernetes 服务与网易云的其他服务有一定的依赖关系,因此比普通的 Kubernetes 升级所要考虑的问题更复杂。本次内容主要介绍网易云如何进行这个升级以及如何处理中间碰到的问题。 

今天我跟大家讲的是 Kubernetes 在网易的一些实践,目的是抛砖引玉,看看大家在这个方向有没有更好的实践方法。简单介绍一下网易云。网易云是从最早 Kubernetes 1.0 开始做起,后面 1.3 版本一直用了很长的时间。最近,对内部业务提供了 1.9 的版本。

容器云平台架构  

这是网易公有云的一个容器平台架构,可以简单的分为几个部分。最底下的是基础设施层。这个是由 laaS 来负责,在 laaS 以上,是容器和 Kubernetes 的调度层。因为 laaS 会去做其它的公共系统(比如认证、计费)。NCE(Netease Container Engine,容器引擎)这层服务,是容器和 Kubernetes 调度上面的一个抽象层,它上面是为用户提供前端的 Web 和 API 服务。 

我们也是 Kubernetes 用户,在为用户提供 Kubernetes 服务的同时,也在解决我们的问题。在容器服务上,2016 年就已有几十个业界知名的服务微服务的工具,一个是 Spring Cloud,一个是 Kubernetes。在我们开发团队中,也会去想用什么样的技术更适合我们。Spring Cloud 和 Kubernetes 都是一个利器。它们面向的用户和解决思路是不一样的。Spring Cloud 更多的是面向开发者们,尤其是 Java 开发者,他们是从代码级别去考虑微服务是怎么去设计的。开发者们需要去处理,比如注册、环境配置怎么拿到?对于他们来说,除了做业务开发,他还要关心一些业务之外的东西。 

我们也是 Kubernetes 用户,在为用户提供 Kubernetes 服务的同时,也在解决我们的问题。在容器服务上,2016 年就已有几十个业界知名的服务微服务的工具,一个是 Spring Cloud,一个是 Kubernetes。在我们开发团队中,也会去想用什么样的技术更适合我们。Spring Cloud 和 Kubernetes 都是一个利器。它们面向的用户和解决思路是不一样的。Spring Cloud 更多的是面向开发者们,尤其是 Java 开发者,他们是从代码级别去考虑微服务是怎么去设计的。开发者们需要去处理,比如注册、环境配置怎么拿到?对于他们来说,除了做业务开发,他还要关心一些业务之外的东西。 

但是在 Kubernetes 里,你只需要去创建一个服务,然后访问这个 Kubernetes 服务,你不用去关心后台策略是怎么做、服务是怎么发现、请求是怎么传到后面的 Pod。所以它是从不同的层面去解决这个问题的。因为 Spring Cloud 和 Kubernetes,不是一个完全对应的产品。作为微服务的一些基础要点,它们是有自己不同的解决方案。这里是做了一个相对的比较。你会发现无论是 Spring Cloud,还是 Kubernetes,其实都有他自己的方式去解决问题。 

Spring Cloud or Kubernetes? 

我们开发团队做了一些调研,Kubernetes 加 Docker,我们可以比较好的解决部分问题。有一些问题 Kubernetes 自己可以解决,但用起来不是很方便。还有一些只能用第三方工具,或者自己的方式来解决。比如说像监控,我们就是用第三方工具。比如说 ELK 是用 Grafana 去做监控。分步式追踪,我们做了一个 APM 分布步式追踪。 

比较来看,如果做微服务,并不是说我只选 Kubernetes,或是 Spring Cloud,而是把它们结合起来用,更能发挥他们各自的优势。再比如服务发现、负载均衡、高可用、调度、部署这部分,我们是用 Kubernetes。比如故障隔离,我们用 Hystrix 去做进程内隔离,但是做进程间隔离,是通过 Kubernetes 的资源配额去做。比如像配置问题,Docker 能解决应用和运行环境等问题。 

一般情况,我们会分为开发环境、测试环境、预发布和线上环境。不同的环境,它的配置是不一样的。如果是用 Kubernetes ConfigMap,它不能实时生效,所以我们用的 Disconf 配置中心。要把微服务做好,你要根据自己的实际情况,去选择适合的产品和技术,还要做一些基于第三方的开源工具和开发。

除了微服务这块,在我们自己的生产测试环境中,我们也用到了亲和性和反亲和性的特性,比如说一个很常见的需求,当我有些关键的应用模块需要去做储备部署,我需要去测试。当主模块坏掉后,我们通过它的亲和性去做,比如我们用 Node 和 Pod 的亲和性和反亲和性去做,中间会给你一些表达式去匹配规则。  

这里面有两种模式,一种是 Hard 模式,一种是 Soft 模式。Hard 模式是你必须满足我的要求,它还有一个特性,在调度期间,我必须要满足它的要求。如果我已经调度成功了,后面的标签我没有改动,还是要让 Pod 正常运行。Soft 模式,我如果满足你表达式匹配的资源,那我就优先满足你。如果没有,那你按照调度器的分配继续进行下去。 

在我们的环境里,对 Node 打标签。比如说有两台,一台是 ci1,一台是 ci2,在文件中进行配置。比如一个节点,它可以是 ci1 或者 ci2,它把 Pod 调度到 Node 上,但是这两个应用跟储备又不能在一起,它们必须要分开。我们在应用名字设置了一个 Pod 反亲和性。如果发现这个 Pod 已经在一台上,那它只能被调动到另一台上。我们可以用同样的一个配置去部署这样一个应用,不需要去分两个配置。而且我们不需要去管底层的 Node 资源是怎么样的,或者说我们要靠其它的一些灵活方式去做反亲和性配置。 

网易云有一个现存的监控系统。它对于数据的要求,有一些规范。我们专门做了一个 Agent。这个 Agent,就是为了它从 cAdvisor 上挖数据,上报给监控系统。一方面,社区对模块是有更新的,我们不想对它有新动作。如果你做了自己的逻辑在里面,而社区更新了,在此之后就很难处理这件事。我们想把社区和我们制定一些东西,或者说我们跟内部其它系统去做的相关适配,可以去解耦。 

通过 cAdvisor,做一个 DaemonSet。我们在每一个节点上,都会部署一个 Agent,它会去拉 cAdvisor 的数据。如果 Agent 在后面社区版本里进行了数据更新。我们只需要做一些相对应的适配,重新更新与部署。对我们来说,维护会非常简单。另外一个,我们的外部客户可能会有些非社区原生的需求。

比如说他要在界面上做保存镜像的操作。这些非原生的需求,我们会在 Node 端有一个容器服务的 Agent,它去监控这些命令。如果是这些非原生的需求,是由 Agent 来处理。我们就可以看到,DaemonSet 可以简化一些部署的难度。在特定的场景下,它可以在我的集群里,对所有的 Node,或是部分的 Node 部署一个 Agent。 

如果你更新且把镜像重新做好,重新拉一个镜像就可以。所以说非常适合这种类似于获取日志,长期在 Node 上做工作的事情。而且,它可以把一些定制化需求和社区原生需求解耦,这样既能跟上社区更新,又可以根据自己的系统和业务产品,去做一些特别的适配。 

因为网易云本身提供有关 Node/PV 的服务,所以在这一块又和私有云有点不一样。因为像社区原生的 Kubernetes,它更多的是面向私有云市场。像很多公有云厂商,提供的 Kubernetes 服务,也是用户申请后,它给你一套完整的 Kubernetes 集群。这个集群是由用户自己来维护的。 

但是网易云目前所提供的 Kubernetes 服务,有点像 AWS 的 Fargate 的模式,所有租户都在这整个公有云中,都是在同一个 Kubernetes 集群下管理。租户是对 Kubernetes 没有访问权限的,他只能访问平台暴露出来的,比如 Deployment、Service、Ingress、Statefulset、Endpoint 等对象,用户只能使用这些资源。 

K8S 公有云实践 

公有云和私有云的场景存在不同。私有云没有足够的概念。在企业内部,只是不同部门或者不同项目,但是资源可能是全局可见的。但在公有云上就不一样。因为公有云是有租户的,我匹配的这些资源不是全局共享的,因为你不用关心这种安全性问题。但是在公有云上,用户很多,租户很多,租户之间不需要去做隔离。 

我们的容器平台和底层的 laaS 层是做耦合。它不是原生的 Kubernetes 形态,像主机,我们是由 laaS 提供一个虚拟型或尾机给容器做一个 Node。硬盘,我们是由 laaS 层存储一个后端硬盘,网络是通过 VPC 网络,所以说租户与租户之间是隔离的。 

我们以前的版本是一对一的,到了新的版本后每个租户可以去创建多个 Namespace。像认证、授权、也是在租户这边安排。还有一个像 Kubernetes ,它的 Node 资源,是预分配的,你在创建集群时,就要提前把这些 Node 资源准备好,它才可以调度。但是这个场景在公有云就不合适。因为你无法预估用户需要多少资源,没办法根据这个去部署。 

如果你部署多了会浪费。你部署少了,资源可能就不够。我们想的一种办法就是用类似于半实时的预备资源方式。它不完全是你申请时才给你创建的一个 Node。如果是这样,对用户来说,体验会不好。所以我们的办法就是提前准备一个资源池,把临时的 Node 创建出来后不给它联网。它在临时资源池里,我们会维持一个水平。 

此时如果一个用户,他申请了资源,我们就把创建好的 Node 做初始化,放在这个租户的 VPC 网络里。把信息给到他,他就可以去做第二步。一方面,我们不需要去预备更强大的资源;另一方面,我们在用户申请时,时间响应会少一些。

当用户把资源池里的 Node 请求消费了一部分,我们会根据消耗水平在后台补一些新的资源,它一般会维持这么一个规模。在一般情况下,没有特别多的高并发,或者说在很多大规模部署情况下,我们是能减少用户的等待时间又不会占用太多的底层资源,这是我们对这块的一个处理方式。  

为什么要做系统优化呢?有这几个方面的原因。最核心的是为了省钱,网易云需要支撑很多内部业务,每个业务都会有自己的一套资源群,而做高可用功能会占用很多资源。所以我们希望有能够管理所有租户的资源。面向用户的产品,也需要这样的需求。所以说我们面向多个团体,必须要保证当客户有具体的资源申请时,性能可以满足用户需求。 

社区从 1.0 的 100 个 Node 到 1.6 的 5000 个 Node。虽然数量已经增加了很多但可能还不够。我们是希望一个集群能做到 3 万个 Node,所以我们在这里做了很多东西。但在 Kubernetes 的架构里,它所有的请求都是通过 API Server,然后去做一些查询、访问,再到 etcd 做产品。etcd 就是一个瓶颈,而且 etcd 本身比较缺少水平扩展能力,所以需要做一些优化。 

当你规模加大后,在你做一些规则时就会碰到一些基本问题。我们是在 Controller 端,做一个自己的 Controller。它是和我们刚才说的 laaS 服务做一些交互,申请一个  Node,把网络打造在 Node 上,去创建 Pod。我们在前面又放了一层 HAProxy,做一些负载均衡。 

K8S 性能优化

 举个例子,如果你是社区原生方式,比如说你有 1 万个 Node,就可以创建 1 万个 Service,因为它是全局的可以互访,当你创建的规则是一万乘一万时,每个节点都会有这么多的规则。可想而知,你的性能就会很差。 

对于公有云来说,因为我们是做租户隔离,所以说它不需要把所有的规则在每个 Node 上都去设一遍。因为我的租户是一个 VPC,需要在这个 VPC 上设用户自己的规则。我不用去设置别的租户 Node 上的规则。在每个租户里,他的 Node 规则很少。即使你再次设置规则也没有关系。 

在后端存储,我们也是对 etcd 集群,就像 MySQL 一样,做类似分库的部署。我们会根据不同的对象、Node、RS、Pod,不同对象去分不同的多个 etcd 集群,减轻单个 etcd 集群的压力。其它做了一些调优,比如底层,我们用了 SSD,去做一些快照。在之前我们也考虑过是否有其它更好的 KV 存储方式,后来从 etcd 2 到 3 后,在性能上解决了不少问题,尤其是 etcd 3,它批量的消息推送效率比以前 2 版本要高了很多。 

比如说底层的网络,我们的 laaS 层网络是基于 OVS+vxlan 做的,而没有用 Calico 等业界的一些方案,网络上,我们要提供 VPC 的安全隔离;性能上,我们测试下来的网络优化,也比一些开源的方式好一些。所以我们把这部分放到云底层去做。我们的容器平台,不需要关心底层是什么样,它只需要对接这些网络层,相当于接口。它需要申请资源,申请下来就可以了。 

结语 

以上这些是当前网易云对 Kubernetes 的一些实践和体会,随着用户越来越多的了解 Kubernetes 和容器技术所带来的好处,以及这些技术和生态的更新和演进,未来 Kubernetes 和容器也会有越来越多的潜力被开发出来。 


从事网易云基础服务解决方案工作。多年企业基础架构、虚拟化项目设计、部署和运维经验,了解企业 IT 面对的问题。目前主要关注如何通过容器、IaaS、PaaS 等技术和解决方案解决实际问题,帮助企业从传统 IT 架构向云演进、并更好的利用云带来的益处来促进核心业务发展。 


 

193 comCount 0