Kubernetes常见高可用方案
首先看下一个高可用集群需要哪些先决条件
- etcd集群要高可用:节点在3个或以上
- cp上的服务:kube-apiserver、kube-controller-mansger和kube-scheduler有多个实例
以上两个条件是一个HA集群的基础,无论是通过kubeadm还是其它手段都很容易做到。这里不做过多讨论,官方文档已经很详细了,目前主推Stacked
我们知道在集群内部会生成一个kubernetes
服务,集群内部对apiserver
的调用通过这个svc实现了高可用,那么是否还有哪里不满足高可用呢?
不仅有,还是一个很重要的、每个节点都需要安装的组件:kubelet
堆叠etcd:每个控制平面节点创建一个本地 etcd 成员(member),这个 etcd 成员只与该节点的 kube-apiserver 通信。 这同样适用于本地 kube-controller-manager 和 kube-scheduler 实例。
外部etcd:但是 etcd 成员在不同的主机上运行, 每个 etcd 主机与每个控制平面节点的 kube-apiserver 通信
请注意堆叠下是本地etcd只和本地apiserver通信,而不是说etcd集群内部不通信了,毕竟有小朋友问我etcd只在本地通信如何实现高可用
kubelet
kubelet
在所有节点上都存在,主要有以下几个任务
- 定期从
kube-apiserver
接收新的或修改的pod
声明,确保pod
在期望下运行 - 定期扫描
static pod
声明(无论本地还是网络),确保static pod
在期望下运行 - 同时作为工作节点的监控组件,向
kube-apiserver
上报节点运行状况
一旦kubelet
无法和kube-apiserver
通信,如果此时集群正常运行,那么这个节点会处于NotReady状态而无法正常使用
请注意不只是kubelet
,大部分服务调用apiserver
时都只能有一个唯一的地址,这里要自己解决lb问题
lb和vip
自建机房
如果可以进行ARP广播,那么就可以使用keepalived
方案
- 在每个cp上运行一个
haproxy
,代理所有cp的6443接口 - 通过
keepalived
实现多机热备,确保vrrp协议正常,确保vip可以正常漂移
此方案需要用到haproxy
和keepalived
,安装配置可能有点麻烦,但所增加的成本不会很高
当然如果worker成千上万,最好还是把haproxy
放在单独机器上
但是对网络环境有要求,公有云通常是不可能的
公有云
公有云通常是不可能进行ARP广播的,大概只有如下几个选择
- 直接用公有云提供的高可用cp方案
- 使用公有云的lb内网代理所有cp的6443接口
- 如果公有云提供获取固定vip的手段,也可以尝试下
keepalived
但不推荐
本地lb
通常我们不会将apiserver
的地址配置成ip,而是配置为一个类似https://apiserver.cluster.local:6443
的url,然后将host解析为lb的地址
对于cp节点,所有组件都只用和本地的apiserver
通信,只用将本机host解析127.0.0.1
即可;那么对于worker节点能否也采用类似的方案呢?
这就是本地lb:每个worker节点上创建一个lb(通过ipvs或者haproxy都可),这个lb代理所有cp的6443端口,本机host同样解析为127.0.0.1
即可
本地lb最大的优点就是简单:编写简单、不像keepalived
配置复杂、可以通过static pod
或者systemd
保活;其次对网络环境没有要求,公有云也可以简单实用
本地lb的缺点也很明显:每个worker上都要有一个lb进程;每一个lb都要对所有cp进行健康检查,如果worker数量过大,会对cp造成较大压力
使用svc
这是本篇的重点,有没有一种即简单、对网络环境没有要求、且不需要在每个worker上启动lb的方案呢?
首先K8S集群内部就有一套自己的lb,同时kubernetes
这个svc实现了集群内的apiserver
高可用。那我们是不是可以复用这个svc呢?实际会出现以下两个大问题
- 端口问题:
kubernetes
这个svc的port是443,改动太大兼容性很是问题,这个端口最好就是6443 - worker重启后ipvs规则会清空,
kube-proxy
需要从apiserver
拿到所有svc,之后才能配置ipvs规则。这是一个鸡生蛋、蛋生鸡的问题
但是办法总比问题多,下面我们来解决这两个问题
新建svc
首先新建一个叫apiserver
的svc,必须设置clusterIP,同时port是6443,然后设置label希望能选择到kube-apiserver
这一组pod?
这里又出现了另一个问题:static pod
不归k8s集群管,它们只是单单运行在集群上而已!所以这个label只能设置上去看看??
那么kubernetes
这个服务是如何做的呢?通过svc的文档可以发现,还有without selectors
这种神奇的svc,它需要一个同名的ep。那我们大概也要采用一样的手段来解决问题
新建deployment
既然不能使用label机制来自动发现pod,那就只能自己实现一个服务apiserver-updater
来完成对ep的修改了
- 副本数为2,并通过k8s内建的resourcelock和选举机制实现
apiserver-updater
的主备 - 监听
kubernetes
这个ep,首次创建ep,之后一旦变化就去修改apiserver
的ep
启动引导
对于启动时需要apiserver
的问题,我们可以在worker的网络可用并且kubelet
服务启动之前执行一段bash脚本,满足以下条件即可
- 所有worker应该知道所有的cp的真实ip,并且知道
apiserver
这个svc的vip - 从cp的真实ip列表中选择一个可用的ip(可以使用curl、nc等工具去检测是否可以建立连接),并更新本机host将
apiserver
地址指向这个ip - 等待,直到vip可用,可用后更新本机host将
apiserver
地址指向vip
总结
此方案看上去很美好:实现起来难度不算太大,做好之后也不会有太多配置,也不会增加什么成本(只多了两个pod)。但是最大的问题是:这玩意是我原创的貌似可行但本人目前懒得去实现;也就是说并没有经过生产的验证