这一篇咱们来说网易为支撑大规模公有云对于Kubernetes的定制化。前端
1、整体架构node
网易的Kubernetes集群是基于网易云IaaS平台OpenStack上面进行部署的,在外面封装了一个容器平台的管理层,负责统一的帐号,计费等。算法
Kubernetes集群固然是要高可用的,于是会有多个Master节点。编程
其中APIServer前端有负载均衡器haproxy,须要有一个VIP,在两台haproxy之间进行漂移,保证一台啊挂了,另一台可以使用同一个VIP接上。后端
这种漂移使用的协议为VRRP。api
Scheduler和Controller也是有多份的,可是只能有一个Leader,须要经过etcd进行选举。缓存
另外网易自身实现的部分放在单独的进程里NeteaseController,用于处理和IaaS层联动的功能。安全
2、一个仍是多个Kubernetes集群?网络
通常有IaaS平台的公有云或者私有云部署Kubernetes采用的是左面的样子。也即经过调用IaaS层的API,自动化部署多个Kubernetes平台,每个Kubernetes平台仅仅属于一个租户。架构
这样的好处是不一样的租户的Kubernetes之间是彻底隔离的。
坏处也不少:
当租户很是多的时候,Kubernetes集群很是多,很是难以维护
每一个Kubernetes集群的规模很是小,彻底不能发挥容器平台的优点,容器平台相对于IaaS平台比较轻量级,按最佳实践是可以管理比IaaS更大的集群的,例如几万或者几十万个节点,然而这种方式会将Kubernetes集群限制在很小的规模。
虽然不少云平台提供Kubernetes集群的自动化部署工具,可是仅仅自动化的是安装过程,一旦这个集群交给客户了,云平台就无论了,这样Kubernetes的升级,修复,运维所有要客户本身来,大大提高了运维成本。
Kubernetes集群规模须要提早规划阶段数目,添加新的节点数目须要手动进行。
网易采用的是右面的方式,整个IaaS层上面只有一个Kubernetes平台,Kubernetes平台的运维,升级,修复所有由云平台运维人员进行,运维成本较低,并且不须要用户本身操心。
对于用户来说,看到的就只是容器,不用关心容器平台。当用户建立容器的时候,当Kubernetes集群资源不足的时候,Netease Controller会自动调用IaaS层API建立虚拟机,而后在虚拟机上部署容器,一切自动进行。
固然这种方式的一个缺点是一个Kubernetes的不一样租户之间的隔离问题。
采起的方式是不一样的租户不共享虚拟机,不一样的租户不共享虚拟网络,这样租户之间就隔离了,容器的内核也隔离了,二层网络也隔离了。
固然这个方式的另外一个问题是Kubernetes的集群数目很是大,这在下面会详细说这个事情。
还有一个问题是容器放在虚拟机里面,虚拟机的启动速度就成了很大的问题,若是启动速度很慢,则会拖慢容器的敏捷性。
3、APIServer认证模块接Keystone解决复杂的租户管理问题
Kubernetes有基于keystone的用户名和密码进行认证的默认机制,然而每次用用户名和密码进行认证是效率很低的。
网易经过定制化认证流程,使得apiserver也采用相似nova的认证方式。
APIServer在keystone里面注册一个service角色的帐号,并从keystone获取临时管理员的Token。
客户请求到来的时候,经过用户名密码到Keystone进行认证,获取一个token,而且带着这个token到APIServer进行认证,APIServer带着这个token,连同本身的临时管理员的Token进行联合认证,若是验证经过则获取用户信息,而且缓存到etcd里面。
当客户经过获取的token来请求的时候,先从etcd里面读取这个token进行验证,为了使用本地缓存,加一个listwatch实时同步etcd里面的内容到本地缓存。
若是token过时,则重新到keystone里面revoke。
4、从APIServer看集群的规模问题
随着集群规模的扩大,apiserver的压力愈来愈大。
由于全部的其余组件,例如Controller,Scheduler,客户端,Kubelet等都须要监听apiserver,来查看etcd里面的变化,从而执行必定的操做。
不少人都将容器和微服务联系起来,从Kubernetes的设计能够看出,Kubernetes的模块设计时很是的微服务化的,每一个进程都仅仅干本身的事情,而经过apiserver松耦合的关联起来。
而apiserver则很像微服务中的api网关,是一个无状态的服务,能够很好的弹性伸缩。
为了应对listwatch,apiserver用了watchcache来缓解压力,然而最终的瓶颈仍是在etcd上。
最初用的是etcd2,这个时候listwatch每次只能接受一个事件,因此压力很大。为了继续使用etcd2,则须要使用多个etcd2的集群来解决这个问题,经过不一样的租户分配到不一样的etcd2集群来分担压力。
未来会迁移到etcd3有了事件的批量推送,可是从etcd2到etcd3须要必定的迁移工做。
5、经过优化Scheduler解决并行调度的问题
对于大的资源池的调度是一个很大的问题,由于一样一个资源只能被一个任务使用,若是并行调度,则存在两个并行的调度器同时认为某个资源空闲,因而同时将两个任务调度到同一台机器,结果出现竞争的状况。
当OpenStack遇到这种问题,采起的方法是从新调度的方式进行。
而Mesos则采起了更为聪明的两层调度的算法。
详情见文章号称了解mesos双层调度的你,先来回答下面这五个问题!
Mesos首先经过第一层的调度Allocator,将不一样的节点分给不一样的Framework,则不一样的Framework就能看到不一样的节点了,不一样Framework里面的调度器是第二层调度,是可以并行调度的,而且即使并行调度,不一样的Framework也是不会调度到冲突的节点的。
Mesos的双层调度策略,使得Mesos可以管理大规模的集群,例如tweeter宣称的几十万的节点,由于对于某一个Framework来说,不会同时在几十万个节点中选择运行任务的节点,而是仅仅在其中mesos分配给他的一部分中进行调度,不一样的framework能够并行调度。
还记得前面咱们提到的,为了租户隔离,不一样的租户是不共享虚拟机的,这样不一样的租户是能够参考Mesos的机制进行并行调度的。由于不一样的租户即使进行并行调度,也不会出现冲突的现象,每一个租户不是在几万个节点中进行调度,而仅仅在属于这个租户的有限的节点中进行调度,大大提升了调度策略。
而且经过预过滤无空闲资源的Node,调整predicate算法进行预过滤,进一步减小调度规模。
6、经过优化Controller加快新任务的调度速度
Kubernetes采用的是微服务常使用的基于事件的编程模型。
当有增量事件产生的时候,则controller根据事件进行添加,删除,更新等操做。
可是基于事件的模型的一个缺点是,老是经过delta进行事件触发,过了一段时间,就不知道是否同步了,于是须要周期性的Resync一下,保证全量的同步以后,而后再进行增量的事件处理。
然而问题来了,当Resync的时候,正好遇到一个新容器的建立,则全部的事件在一个队列里面,拖慢了新建立容器的速度。
经过保持多个队列,而且队列的优先级ADD优于Update优于Delete优于Sync,保证相应的实时性。
7、经过优化虚拟机启动速度加速容器启动速度
前面说了,为了保证安全性,容器是启动在虚拟机里面的,然而若是虚拟机的启动速度慢了,容器的启动速度就会被拖慢。
那么虚拟机的启动速度为何会慢呢?
比较慢的一个是网卡的初始化速度比较慢,在OpenStack里面,每每须要经过访问DHCP Sever获取IP地址和路由信息,把IP静态化能够解决这个问题。
另一个比较慢的是Cloud-init,这个是在虚拟机启动以后作初始化的一个工具,是为了可以在虚拟机启动后作灵活的配置,好比配置key,或者执行一些脚本。
在AWS里面,cloudformation编排工具在虚拟机启动后的一些编排工做是经过cloud-init来作的,弹性伸缩机制经过虚拟机镜像复制多份,复制后作的少许的配置工做也是cloud-init来作的。
OpenStack继承了AWS的这个机制,是经过Metadata Server来对虚拟机进行灵活的配置,包括OpenStack编排机制Heat也是经过Cloud-init进行配置。
对于Metadata Server的机制,能够看这篇文章当发现你的OpenStack虚拟机网络有问题,不妨先试一下这16个步骤
然而在虚拟机里面跑容器的方式,是不须要cloud-init的,由于灵活定制化的事情由里面的容器来作,用户使用的也是里面的容器,而不是虚拟机,于是虚拟机能够作的十分的精简,将不须要的服务所有删除。
8、经过使用IaaS层的高性能网络提供给容器高速互联能力
上一节咱们讲过,使用Flannel和Calico都仅仅适用于裸机容器,并且仅仅用于容器之间的互通。
一旦有IaaS层,就会存在网络二次虚拟化的问题。
虚拟机之间的互联是须要经过一个虚拟网络的,例如vxlan的实现,而使用Flannel或者Calico至关于在虚拟机网络虚拟化的上面再作一次虚拟化,使得网络性能大幅度下降。
并且若是使用Flannel或者Calico,那容器内的应用和虚拟机上的应用相互通讯的时候,则须要出容器平台,多使用node port,经过NAT的方式访问,或者经过外部负载均衡器的方式进行访问。在现实应用中,不可能一会儿将全部的应用所有容器化,部分应用容器化,部分应用部署在虚拟机里面是常有的现象。然而经过NAT或者外部负载均衡器的方式,对应用的相互调用有侵入,是的应用不能像原来同样相互调用,尤为是当应用之间使用Dubbo或者SpringCloud这种服务发现机制的时候尤为如此。
网易开发了本身的NeteaseController,在监听到有新的Pod建立的时候,调用IaaS的API建立IaaS层的虚拟网卡,而后在虚拟机内部,经过调用Netease CNI插件将虚拟网卡添加到容器里面。添加的技术用的就是上一节提到的setns命令。
经过这个图咱们能够看出,容器的网卡是直接链接到虚拟私有网络的OVS上的,和虚拟机是一个平的二层网络,在OVS来看,容器和虚拟机是在同一个网络里面的。
这样一方面没有了二次虚拟化,只有OVS一层虚拟化。另外容器和虚拟机网络打平的好处是,当部分应用部署容器,部分应用部署虚拟机的时候,对应用没有侵入,应用原来如何相互访问,如今仍是如何访问,有利于应用逐步容器化。
容器还能够有一个网卡直接链接到公网,这一点可以保持公网IP在容器内部可见,容易使用,并且能够保证在容器的生命周期内,公网IP能够保持。
另外这个网卡在负载均衡可使用,下面咱们详细说。
使用OVS的二层网络,能够很好的实现多租户,高性能互联,QoS,安全过滤,放ARP和IP欺骗等。
9、经过优化kube-proxy优化内部相互访问
默认的容器之间的相互访问经过服务进行。
一旦建立一个服务,就会建立一个Endpoint,kube-proxy可以在api-server监听到这个服务的建立,则须要监听服务的端口,而且设置iptables将对这个服务对应的VIP的访问转发到kube-proxy的这个端口上。
当一个客户端要访问这个服务的时候,首先经过DNS,将服务名转化为VIP,而后VIP经过iptables规则转发到kube-proxy的监听端口,kube-proxy将这个包随机转发给后端的一个Pod。
当Pod不管IP如何变,如何弹性伸缩,只要有服务名,都可以访问到。
仅仅经过DNS,是可以作的服务名和IP的对应,也是能够基于DNS进行负载均衡的,这就是基本的基于DNS的服务发现。然而这种方式的缺点是不可以流控,因此最好中间有一个proxy,这就是kube-proxy。
就像Mesos中原来的服务发现是经过Mesos-DNS进行,后来改用了minuteman作这件事情,minuteman的实现机制和kube-proxy很像很像。
然而这种方式的问题是,当集群规模很大的时候,服务建立的不少的时候,kube-proxy会牵扯到大量的iptables和监听端口。
然而在咱们的设计中,租户之间的容器网络应该是彻底隔离的,在某个租户的虚拟机上的Kube-proxy是无需包含另外一个租户的转发规则的。
因此这里作的一个优化是只监听本租户的service,而且建立转发规则就好,由于每一个租户的节点数目有限,转发规则也不会不少。
10、经过LVS和haproxy进行外部负载均衡器优化
Kubernetes默认的外部负载均衡是用ingress作的,性能不是很高。
通常公有云要求负载均衡可以承载的吞吐量很是的大,不能用纯软的方式。
在网易的方案中,最外层有两个LVS,部署在物理机上,经过多个万兆上行口进行转发,能够承载很是大的吞吐量。
对于不一样的租户,能够建立不一样的软负载均衡,经过建立多个haproxy,构成一个集群,因为haproxy是基于虚拟机的,能够弹性伸缩,使得他也不会成为瓶颈。
haproxy是后端和容器进行二层互联,是经过虚拟网络进行的,然而haproxy链接LVS是须要经过物理网络的,这就须要haproxy经过上面的机制,经过两张网卡,一张卡链接到物理网络,做为前端,一张卡链接到虚拟网络,做为后端链接容器。
最后给一张优化总图。