1

前文我们搭建了3Master节点4Node节点的高可用集群,本文讨论一下过程中的总结。

高可用策略

前文说了Kubernetes集群高可用的关键在于Master节点的高可用,官方也给出了2种高可用拓扑结构

  • Stacked etcd topology (堆叠etcd)

    991a314a-k3s-0-1024x617

    该方案中,所有Master节点都部署kube-apiserver,kube-controller-manager,kube-scheduler,以及etcd等组件;kube-apiserver均与本地的etcd进行通信,etcd在三个节点间同步数据

  • External etcd topology (外部etcd)

    kubeadm-ha-topology-stacked-etcd-1

该方案中,所有Master节点都部署kube-apiserver,kube-controller-manager,kube-scheduler组件,而etcd 分布式数据存储集群独立运行。这种拓扑结构解耦了Master节点组件和 etcd 。

vcontrol plane node 即Master节点

高可用方案分析

通过官方给的拓扑图,有2个关键的地方:

  • etcd
  • load balancer

也就是说,只要完成上述两者的高可用,整个Kubernetes集群即达到了高可用的目的。

etcd高可用

Kubernetes选用etcd作为它的后端数据存储仓库也正是看重了其使用分布式架构,没有单点故障的特性。虽然单节点的etcd也可以正常运行,但是推荐的部署方案均是采用3个或者5个节点组成etcd集群,供Kubernetes使用。

etcd的高可用方案是官方2种拓扑结构中最明显的差异,区别就是etcd的位置不同。官方对于2种etcd的方案也做了比较,我总结如下:

  • 堆叠方式

    1. 所需硬件资源少
    2. 部署简单、利于管理
    3. 横向扩展容易
    4. 缺点在于一台宿主机挂了,Master节点和etcd就少了一套,会大大降低集群冗余度和健壮性。
  • 外部方式

    1. Master节点和etcd解耦,Master节点挂了后不影响etcd,集群健壮性增强
    2. 缺点是硬件资源几乎2倍于堆叠方案,并且运维复杂度会变高。

不管从我个人角度还是企业角度,我毫不犹豫选择堆叠方式,横向扩展的便捷性以及对于硬件的需求这2点理由就足够了。另外在搜集资料的时候还发现了一篇文章-Scaling Kubernetes to 2,500 Nodes,里面实践证明,etcd挂载本地ssd的方式会大幅提高超大规模(节点大于2000)集群性能。

除了上述2种方式之外,在网上冲浪时还发现了一篇文章提到另一种方式:

  • CoreOS提出的self-hosted etcd方案

    将本应在底层为Kubernetes提供服务的etcd运行在Kubernetes之上。实现Kubernetes对自身依赖组件的管理。在这一模式下的etcd集群可以直接使用etcd-operator来自动化运维,最符合Kubernetes的使用习惯。

当然,这种方式资料并不多,也没找到具体的实践方式,并且在官方文档中多带有实验性的前缀,并且官方文档如下说明:

  1. The self-hosted portion of the control plane does not include etcd, which still runs as a static Pod.

也不知道是曾经版本中支持过后来取消还是怎么的,在此不做深究吧,只是提一句题外话。一切还是以官方文档的两种方式为准。

kube-apiserver高可用

在官方的拓扑图中,除了etcd以外很容易忽略load balancer这个节点。kube-apiserver节点通过前置负载均衡器load balancer实现高可用,但是目前尚没有官方的推荐方案。常用的思路或者方案大致归纳有以下几种:

  • 外部负载均衡器

    不管是使用公有云提供的负载均衡器服务或是在私有云中使用LVS或者HaProxy自建负载均衡器都可以归到这一类。负载均衡器是非常成熟的方案,如何保证负载均衡器的高可用,则是选择这一方案需要考虑的新问题。

    目前网上大部分资料都是通过HAproxy实现kube-apiserver的高可用并且通过Keepalived方式确保HAproxy自身的高可用,前文我们使用的就是这种方式,刚入门随大流总没有错的。

  • 网络层做负载均衡

    比如在Master节点上用BGP做ECMP,或者在Node节点上用iptables做NAT都可以实现。采用这一方案不需要额外的外部服务,但是对网络配置有一定的要求。

  • 在Node节点上使用反向代理对多个Master做负载均衡

    这一方案同样不需要依赖外部的组件,但是当Master节点有增减时,如何动态配置Node节点上的负载均衡器成为了另外一个需要解决的问题。

目前除了自己用的方式外,对于其他方式还需留待后续研究和尝试。

kube-controller-manager与kube-scheduler 高可用

这两项服务是Master节点的组件,他们的高可用相对容易,仅需要运行多份实例即可。Kubernetes自身通过leader election机制保障当前只有一个副本是处于运行状态。

至于leader选举的机制,简单来说是:多个kube-controller-manager与kube-scheduler的副本对 endpoint 锁资源做抢占,谁能抢到并将自己的信息写入 endpoint的 annotation 中,谁就成为了leader,leader需要在可配置的一个间隔时间内更新消息,其他节点也能看见当前的leader和过期时间;当过期时间到并且leader没有更新后,其他副本会尝试去获取锁,去竞争leader,直到自己成为leader。

关于Kubernetes的高可用方案就总结这些。

ipvs VS iptables

上文部署过程中,我使用了ipvs的方式代替了默认的iptables,起初呢只是抱着不明觉厉,自定义的肯定要比默认的强哈的想法,事实上呢还是要自己调研对比下才行。

从前文集群配置文件kubeadm.yaml中可以看到ipvs配置是针对kube-proxy组件的。kube-proxy 是 Kubernetes 中的关键组件。他的角色就是在服务(仅限ClusterIP 和 NodePort方式)和其后端 Pod 之间进行负载均衡。kube-proxy 有三种运行模式,每种都有不同的实现技术:userspaceiptables 或者 IPVS。userspace 模式非常陈旧、缓慢,已经不推荐使用。

  • iptables

    iptables 是一个 Linux 内核功能,是一个高效的防火墙,并提供了大量的数据包处理和过滤方面的能力。它可以在核心数据包处理管线上用 Hook 挂接一系列的规则。iptables 模式中 kube-proxy 在 NAT pre-routing Hook 中实现它的 NAT 和负载均衡功能。这种方法简单有效,依赖于成熟的内核功能,并且能够和其它跟 iptables 协作的应用(例如 Calico)融洽相处。

    然而 kube-proxy 的用法是一种 O(n) 算法,其中的 n 随集群规模同步增长,这里的集群规模,更明确的说就是服务和后端 Pod 的数量。

  • ipvs

    IPVS 是一个用于负载均衡的 Linux 内核功能。IPVS 模式下,kube-proxy 使用 IPVS 负载均衡代替了 iptable。这种模式同样有效,IPVS 的设计就是用来为大量服务进行负载均衡的,它有一套优化过的 API,使用优化的查找算法,而不是简单的从列表中查找规则。

    这样一来,kube-proxy 在 IPVS 模式下,其连接过程的复杂度为 O(1)。换句话说,多数情况下,他的连接处理效率是和集群规模无关的。

    另外作为一个独立的负载均衡器,IPVS 包含了多种不同的负载均衡算法,例如轮询、最短期望延迟、最少连接以及各种哈希方法等。而 iptables 就只有一种随机平等的选择算法。

    IPVS 的一个潜在缺点就是,IPVS 处理数据包的路径和通常情况下 iptables 过滤器的路径是不同的。如果计划在有其他程序使用 iptables 的环境中使用 IPVS,需要进行一些研究,看看他们是否能够协调工作。(Calico 已经和 IPVS kube-proxy 兼容)

看完概念还是不明觉厉,这里我贴出一篇文章供大家了解一下kube-proxy 模式对比:iptables 还是 IPVS?这是一篇译文,原文地址Comparing kube-proxy modes: iptables or IPVS?,感谢二位作者,这篇文章详细对比了两种模式的优劣。我直接贴出结论:

在超过 1000 服务的规模下,kube-proxy 的 IPVS 模式会有更好的性能表现。虽然可能有多种不同情况,但是通常来说,让微服务使用持久连接、运行现代内核,也能取得较好的效果。如果运行的内核较旧,或者无法使用持久连接,那么 IPVS 模式可能是个更好的选择。

抛开性能问题不谈,IPVS 模式还有个好处就是具有更多的负载均衡算法可供选择。

如果你还不确定 IPVS 是否合适,那就继续使用 iptables 模式好了。这种传统模式有大量的生产案例支撑,他是一个不完美的缺省选项。

我自己含蓄的总结就是:以普通企业的业务规模,两者没有差异。

关于初始化集群时ipvs配置问题

在使用kubeadm初始化集群时,开启ipvs配置如下,这里我着实遇到一个小坑:

# 错误配置
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
featureGates:
  SupportIPVSProxyMode: true  # 这个配置错误错误错误!!!按照网上资料这样配,kubeproxy组件会启动失败并一直重试!!
mode: ipvs

# 正确配置
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs

已经搭建好的集群中开启ipvs

修改kube-proxy配置

kubectl edit configmap kube-proxy -n kube-system
   minSyncPeriod: 0s
      scheduler: ""
      syncPeriod: 30s
    kind: KubeProxyConfiguration
    metricsBindAddress: 127.0.0.1:10249
    mode: "ipvs"                          # 修改此处
    nodePortAddresses: null

删除所有kube-proxy的pod

kubectl delete pod xxx -n kube-system

校验

kubectl logs kube-proxy-xxx -n kube-system日志出现Using ipvs Proxier即可

检查ipvs代理规则

kubectl get svc --all-namespaces

#可以看到service对应的很多规则
ipvsadm -ln 

有趣的现象-kubelet的默认cgroup driver

在初始化过程中出现错误:

failed to create kubelet: misconfiguration: kubelet cgroup driver: "cgroupfs" is different from docker cgroup driver: "systemd"

前面我们在部署Docker时设置了cgroupfs driver为systemd,因为Kubernetes官方这么建议,你这么建议就建议吧,为什么自己还默认cngroupfs?所以在实际部署时,Docker和kubelet都需要设置为systemd

Keepalived配置文件须知

检测脚本

vrrp_script chk_haproxy {
    script "/bin/bash -c 'if [[ $(netstat -nlp | grep 9443) ]]; then exit 0; else exit 1; fi'"  # haproxy 检测脚本,这里需要根据自己实际情况判断
    interval 2  # 每2秒执行一次检测
    weight 11 # 权重变化
}

检测健康的脚本需要根据实际情况判断,如果Haproxy是二进制方式安装,需要判断进程。如果是docker方式安装,光判断进程就无效了,需要判断Haproxy的端口号是否有监听存在。

当然,我觉得最完美的方式应该是判断apiserver的状态,这点还需要后续再完善,先留个问题待处理。

配置文件注意点

  • interface

    默认的Keepalived文件interface是eth0

    interface eth0

此处要根据自己的实际情况,否则Keepalived会失败

  • unicast_peer

    首先我的三台Master节点是跨网段的,keepalived默认的方式是组播,并不支持跨网段,所以我刚开始采用默认配置启动时出现了不同网段的2个Master,解决方法就是改成单播模式:

    unicast_peer {
            10.128.2.53
            10.128.2.52
      }
  • nopreempt 抢占/非抢占

    抢占模式是当master从故障中恢复后,会将VIP从BACKUP中抢过来,非抢占模式是master恢复后不抢占backup升级为master后的vip。

    vrrp_instance myland_slb {
        ...
        state MASTER  # 状态
        nopreempt    #不抢占表示
        priority 90  # 优先级(0~255)
        ...
    }

Kubectl get cs 废弃

截屏2021-02-26 下午5.13.34

在第一篇快速搭建集群时候遇到以上问题,并也给出了解决办法:检查配置文件/etc/kubernetes/manifests/kube-scheduler.yaml/etc/kubernetes/manifests/kube-controller-manager.yaml并将两个配置文件的配置项- --port=0均注释掉。

本次部署也按照此方法消除了错误,但是版本升级过后配置文件又恢复原状,这明显不符合常理,说明官方并不认同修改配置文件。于是乎查找资料,发现自1.19版本后kubectl get cs 已弃用,增加port=0是为了符合 CIS kubernetes 安全白皮书要求而添加。不推荐开启此http端口。

参考文章:

#553

#1998

直接查看相关组件pod运行状态即可

kubectl get po -n kube-system

配置自动补全命令

所有master节点执行
#安装bash自动补全插件
yum install bash-completion -y

设置kubectl与kubeadm命令补全,下次login生效

kubectl completion bash >/etc/bash_completion.d/kubectl
kubeadm completion bash > /etc/bash_completion.d/kubeadm

朱世伟
7 声望7 粉丝