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在所有节点上都存在,主要有以下几个任务

  1. 定期从kube-apiserver接收新的或修改的pod声明,确保pod在期望下运行
  2. 定期扫描static pod声明(无论本地还是网络),确保static pod在期望下运行
  3. 同时作为工作节点的监控组件,向kube-apiserver上报节点运行状况

一旦kubelet无法和kube-apiserver通信,如果此时集群正常运行,那么这个节点会处于NotReady状态而无法正常使用

请注意不只是kubelet,大部分服务调用apiserver时都只能有一个唯一的地址,这里要自己解决lb问题

lb和vip

自建机房

如果可以进行ARP广播,那么就可以使用keepalived方案

  1. 在每个cp上运行一个haproxy,代理所有cp的6443接口
  2. 通过keepalived实现多机热备,确保vrrp协议正常,确保vip可以正常漂移

此方案需要用到haproxykeepalived,安装配置可能有点麻烦,但所增加的成本不会很高

当然如果worker成千上万,最好还是把haproxy放在单独机器上

但是对网络环境有要求,公有云通常是不可能的

公有云

公有云通常是不可能进行ARP广播的,大概只有如下几个选择

  1. 直接用公有云提供的高可用cp方案
  2. 使用公有云的lb内网代理所有cp的6443接口
  3. 如果公有云提供获取固定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呢?实际会出现以下两个大问题

  1. 端口问题:kubernetes这个svc的port是443,改动太大兼容性很是问题,这个端口最好就是6443
  2. 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的修改了

  1. 副本数为2,并通过k8s内建的resourcelock和选举机制实现apiserver-updater的主备
  2. 监听kubernetes这个ep,首次创建ep,之后一旦变化就去修改apiserver的ep

启动引导

对于启动时需要apiserver的问题,我们可以在worker的网络可用并且kubelet服务启动之前执行一段bash脚本,满足以下条件即可

  1. 所有worker应该知道所有的cp的真实ip,并且知道apiserver这个svc的vip
  2. 从cp的真实ip列表中选择一个可用的ip(可以使用curl、nc等工具去检测是否可以建立连接),并更新本机host将apiserver地址指向这个ip
  3. 等待,直到vip可用,可用后更新本机host将apiserver地址指向vip

总结

此方案看上去很美好:实现起来难度不算太大,做好之后也不会有太多配置,也不会增加什么成本(只多了两个pod)。但是最大的问题是:这玩意是我原创的貌似可行但本人目前懒得去实现;也就是说并没有经过生产的验证


Kubernetes常见高可用方案
https://back.pub/post/kubernetes-ha-scheme/
作者
Dash
发布于
2019年7月19日
许可协议