高可用背后的原理

这两个月和博云合作的项目是要用于客户生产环境的,这个和我以前做的东西有很大的不同,所有基础架构必须给出高可用的解决方案。在这之前我只做过一些流量较小的用户产品或者一些原型项目,一开始基础架构都只给出了单节点的解决方案,结果被大师兄喷这个在生产环境根本不可用。不过事实确实是在一个真实的分布式系统中硬件损坏、进程崩溃、网络不通都可能直接导致系统不可用,所以后来花了很大的精力将项目的基础架构层面改造成完全高可用的,这个过程对一个之前完全没做过这方面东西的人来说确实会感觉比较困难,不过在这个过程中也确实实践了不少分布式系统的理论,了解了各种各样的高可用解决方案,也对Kubernetes的架构运行原理有了更加深刻的认识。下面我会从我调研的高可用解决方案的各个细节来给大家介绍一下高可用的Kubernetes集群背后的原理。

ETCD高可用

Kubernetes使用ETCD持久化存储集群状态信息。ETCD是CoreOS开源的一个强一致性的分布式键值存储服务,ETCD使用raft算法将一组主机组成集群,集群中的每个节点都可以根据集群运行的情况在三种状态间切换:follower, candidate 与 leader。leader 和 follower 之间保持心跳。如果follower在一段时间内没有收到来自leader的心跳,就会转为candidate,发出新的选主请求。

集群初始化的时候内部的节点都是follower节点,之后会有一个节点因为没有收到leader的心跳转为candidate节点,发起选主请求。当这个节点获得了大于一半节点的投票后会转为leader节点。

当leader节点服务异常后,其中的某个follower节点因为没有收到leader的心跳转为candidate节点,发起选主请求。只要集群中剩余的正常节点数目大于集群内主机数目的一半,ETCD集群就可以正常对外提供服务。

当集群内部的网络出现故障集群可能会出现“脑裂”问题,这个时候集群会分为一大一小两个集群(奇数节点的集群),较小的集群会处于异常状态,较大的集群可以正常对外提供服务。

ETCD的作用是用来存储,其高可用的很大一个作用是用来备份存储的数据,因此它基于Raft的高可用解决方案会使每个节点都在工作。还有一个值得注意的一点,ETCD 2个节点是不能实现高可用的,最少需要3个节点,这个是Raft协议的特性使之的,大家感兴趣可以去读一下Raft的论文。

controller manager和scheduler的高可用

Kubernetes使用scheduler、controller manager来实现pod的调度和rc、deployment等的副本控制。Kubernetes也是通过controller manager和应用健康检查机制实现了它上面部署应用的高可用。而scheduler、controller manager的高可用原理和上面又是不一样的,为了保证集群运行正常,scheduler、controller manager同一时刻只允许一个服务处以具体的任务。Kubernetes中实现了一套简单的类似于分布式锁的方案,依赖ETCD实现scheduler和controller-manager的选主功能。

如果scheduler和controller manager在启动的时候设置了 --leader-elect 参数,它们在启动后会先尝试获取leader节点身份,只有在获取leader节点身份后才可以执行具体的业务逻辑。它们分别会在ETCD中创建scheduler和controller manager的endpoint,endpoint的信息中记录了当前的leader节点信息,以及记录的上次更新时间。leader节点会定期更新endpoint的信息,维护自己的leader身份。每个从节点的服务都会定期检查endpoint的信息,如果endpoint的信息在时间范围内没有更新,它们会尝试更新自己为leader节点。scheduler服务以及controller manager服务之间不会进行通信,利用ETCD的强一致性,能够保证在分布式高并发情况下leader节点的全局唯一性。

当集群中的leader节点服务异常后,其它节点的服务会尝试更新自身为leader节点,当有多个节点同时更新endpoint时,由Etcd保证只有一个服务的更新请求能够成功。通过这种机制sheduler和controller manager可以保证在leader节点宕机后其它的节点可以顺利选主,保证服务故障后快速恢复。

apiserver的高可用

Kubernetes的接入层服务主要是kube-apiserver,官方文档上说apiserver是Kubernetes资源的唯一入口。其实后来根据我对Kubernetes的使用和对其源码的阅读,可以看作apiserver实现了Kubernetes对象的model存到ETCD里面,然后自己作为一个RESTful的HTTP server和外界交互作CRUD操作。apiserver是无状态的,无状态的意思是在哪个apiserver上面接收请求都会得到相同的结果,我们可以同时开多个apiserver处理外部请求,这一点是和scheduler和controller manager不同的。因此apiserver的高可用没必要像scheduler、controller manager一样通过分布式锁强一致性实现同一时间只有一个leader在工作。

而使用多活apiserver的最大难点是如何实现能保证高可用的负载均衡,为了解决这个问题主要使用了keepalived和Nginx。可以使用keepalived配置节点的优先级和检测失活脚本,然后可以给外部一个同一的VIP,并且保证机器肯定能接收到请求,然后配置Nginx可以将请求负载到可用的apiserver上。

使用keepalived、Nginx这种IP漂移、负载均衡的方式比分布式锁服务保持单个leader这种方式可以保持多个实例、性能更高。

除了这个三个主要的之外,我还做了一些例如kube-DNS、CNI插件Calico、Docker镜像仓库的高可用。但是这些都可以是可以基于Kubernetes的副本控制去做的,这里我就只说明一下Kubernetes自身高可用的解决方案,其他就不赘述了。

坚持原创技术分享,您的支持将鼓励我继续创作!
显示 Gitment 评论