高可用
在本节,将针对 Kubernetes 实现高可用进行介绍,主要包括两个方面的高可用:
客户应用的高可用;
集群自身高可用;
应用高可用
微服务应用在 Kubernetes 集群中运行可以充分的发挥优势,例如 Kubernetes 提供的 Deployment
应用可以维护应用的多个实例,并且若有实例挂掉,Deployment
控制器也会立刻进行恢复,以保证服务不间断工作。
针对无服务的应用,使用这种水平扩展的方式比较简单,因为它们可以同时运行,然而对于不能直接水平扩展的应用则需要采取 领导选举机制。
领导选举机制
针对有状态应用,不可以直接水平扩展,因为这有可能导致数据冲突等不可预测的问题。
领导选举机制 是指在多应用实例情况下,对谁是领导者达成一致的机制。例如,领导者要么是唯一执行任务的那个,其他所有节点都在等待领导者宕机,然后成为新的领导者;或者都是活跃的节点,只是领导者负责写操作,其他节点负责读操作(数据库的读写分离)。
应用中领导选举机制的实现
幸运的是,在 Kubernetes 集群中,不需要我们自己的应用实现该机制,官方已经为我们提供了相关的容器可以替我们完成领导选举操作,镜像名为 k8s.gcr.io/leader-elector
,由于 GFW 的原因,国内的用户可以下载 fredrikjanssonse/leader-elector:0.6
并重命名即可,同时加上参数 --election={app_name}
。
该容器完成的功能就是竞争领导者,当创建多个该容器时,这些容器会循环竞争,并选择领导者。我们先不考虑该容器是如何竞选领导的,我们只要知道通过向其发送 HTTP 请求的方式即能知道当前的领导者是谁即可:
从上面的输出中可以很容易的知道当前的领导者是名为
leader-elector-6f5c59d88d-ttkbl
的这个 Pod
由于一个 Pod 中可以包含多个容器,因此我们可以将我们的应用容器与该容器整合起来,将该容器以 sidecar 的形式与应用容器整合成一个 Pod,由于同一个 Pod 中的所有容器共享网络命名空间,因此在应用容器中同样可以使用上述方法知道哪个应用容器所在 Pod 是领导者。
领导选举机制原理
虽然可以直接使用官方提供的容器达到该功能,但是还是需要了解一下其中的原理的。
在 Kubernetes 中竞选领导需要用到锁机制,在集群中直接使用 Endpoint
资源或 ConfigMap
资源作为资源锁,因为这些资源是 Pod 通过 API 服务器可以共同接触到的,默认情况下使用 Endpoint
资源。
在这两个资源(或者说所有资源)中,竞选过程中需要使用到的有两个字段:
metadata.annotations
: 以键值对的形式存在,所有竞争者会尝试向control-plane.alpha.kubernetes.io/leader
键中填入值,若填入成功,则称为领导;metadata.resourceVersion
: 用于实现乐观锁,提高效率;
当使用 k8s.gcr.io/leader-elector
容器时,需要向其中传递参数,即上述的 --election={app_name}
,在容器运行后,便会在当前 namespace 下创建一个名为 {app_name}
的 Endpoint 资源,并填充上面说的两个字段。
案例如下:
annotation
中的 control-plane.alpha.kubernetes.io/leader
键对应的值中的个字段含义如下:
holderIdentity
: 当前资源锁的所有者leaseDurationSeconds
: 资源锁租约时间是多长(资源锁的有效时长,所有者霸住该资源锁的时间超过这个限制,则会被其他竞争者抢占)acquireTime
: 锁获得的时间renewTime
: 续租的时间leaderTransitions
: leader 进行切换的次数
resourceVersion
用于乐观锁,即当竞争者 A 尝试竞争时,首先读取版本号,假设为 1,在本地将信息更新后准备填充至 annotation
中之前,先再次读取版本号,若仍然为 1,则可以填充;若大于 1,则表示资源被更新了,因此需要重新读取版本号,并重新更新信息,再尝试抢占。
Kubernetes 集群高可用
K8S 集群的高可用主要是指其控制平面的高可用,即下面几个组件:
etcd
API 服务器
ControllerManager
Scheduler
在上面这个高可用集群中,控制平面的每一个部分都做了高可用,包括用于接受请求的负载均衡器。
负载均衡器
请求的负载均衡器可以使用 硬件 或 软件 实现,在通常情况下使用 nginx, haproxy 等软件实现,其可以将请求分发至后端不同的 API 服务器中。
而负载均衡的高可用需要搭配虚拟 IP (vip) 以及 Keep-alive 软件实现,即所有来自客户端的请求都发送至虚拟 IP 中,即对用户而言可见的只有虚拟 IP,由软件决定将其交付给哪一个负载均衡器。
实际上不一定需要使用 keep-alive,直接使用 iptables 设定规则,将请求的目的 IP 进行更改同样能达到该效果。
etcd
etcd 会跨实例复制数据以保证彼此之间的数据一致性。
API 服务器
API 服务器直接通过运行多实例的方式就可以实现高可用,因为它是无状态应用(所有数据均存放在 etcd 中,其本身不做缓存),它们彼此之间不会感知对方存在。
API 服务器 只会与本地的 etcd 交互,这样读写效率更高,因此在高可用的情况下,API 服务器和 etcd 实例是成对出现的。
ControllerManager 与 Scheduler
ControllerManager 和 Scheduler 都不是无状态应用,它们都需要接收来自 API 服务器的通知,并执行操作。在多实例的情况下,若多个实例执行了同一个操作,将会造成不可预测的问题。
因此这两个组件的高可用不能仅仅通过水平扩展的方式解决,需要使用 领导选举机制,即同一时间内只能有一个实例执行任务,其他实例都等待成为领导者。
这两个组件原生支持 领导者选举机制,该机制的实现与上述的 k8s.gcr.io/leader-elector
容器一致,都是通过 Endpoint 资源或 ConfigMap 资源实现的,可以通过 --leader-elect
开启组件的选举机制,该选项默认为 true
。
ControllerManager 与 Scheduler 可以与 API 服务器 和 etcd 在同一个节点上,此时直接与本地 API 服务器通信;也可以不在同一个节点上,此时需要通过负载均衡器与 API 服务器通信。
Last updated
Was this helpful?