本文讲述了使用 kubeadm 设置一个高可用的 Kubernetes 集群的两种不同方式:

  • 使用堆控制平面节点。这种方法所需基础设施较少。etcd 成员和控制平面节点位于同一位置。
  • 使用外部集群。这种方法所需基础设施较多。控制平面的节点和 etcd 成员是分开的。

在下一步之前,您应该仔细考虑哪种方法更好的满足您的应用程序和环境的需求。 这是对比文档 讲述了每种方法的优缺点。

如果您在安装 HA 集群时遇到问题,请在 kubeadm 问题跟踪里向我们提供反馈。

您也可以阅读 升级文件

这篇文档没有讲述在云提供商上运行集群的问题。在云环境中,此处记录的方法不适用于类型为 LoadBalancer 的服务对象,或者具有动态的 PersistentVolumes。

准备开始

对于这两种方法,您都需要以下基础设施:

  • 配置三台机器kubeadm 的最低要求给主节点
  • 配置三台机器 kubeadm 的最低要求 给工作节点
  • 在集群中,所有计算机之间的完全网络连接(公网或私网)
  • 所有机器上的 sudo 权限
  • 每台设备对系统中所有节点的 SSH 访问
  • 在所有机器上安装 kubeadmkubeletkubectl 是可选的。

仅对于外部 etcd 集群来说,您还需要:

  • 给 etcd 成员使用的另外三台机器

这两种方法的第一步

为 kube-apiserver 创建负载均衡器

使用负载均衡器需要许多配置。您的集群搭建可能需要不同的配置。下面的例子只是其中的一方面配置。

  1. 创建一个名为 kube-apiserver 的负载均衡器解析 DNS。

    • 在云环境中,应该将控制平面节点放置在 TCP 后面转发负载平衡。 该负载均衡器将流量分配给目标列表中所有运行状况良好的控制平面节点。健康检查 apiserver 是在 kube-apiserver 监听端口(默认值 :6443)上的一个 TCP 检查。

    • 不建议在云环境中直接使用 IP 地址。

    • 负载均衡器必须能够在 apiserver 端口上与所有控制平面节点通信。它还必须允许其监听端口的传入流量。

    • HA代理可以被用来做一个负载均衡器。

    • 确保负载均衡器的地址始终匹配 kubeadm 的 ControlPlaneEndpoint 地址。

  1. 添加第一个控制平面节点到负载均衡器并测试连接:

    1
    
    nc -v LOAD_BALANCER_IP PORT
    
    • 由于 apiserver 尚未运行,预期会出现一个连接拒绝错误。然而超时意味着负载均衡器不能和控制平面节点通信。 如果发生超时,请重新配置负载均衡器与控制平面节点进行通信。
  2. 将其余控制平面节点添加到负载均衡器目标组。

使用堆控制平面和 etcd 节点

控制平面节点的第一步

  1. 初始化控制平面:

    1
    
    sudo kubeadm init --control-plane-endpoint "LOAD_BALANCER_DNS:LOAD_BALANCER_PORT" --upload-certs
    
    • 您可以使用 --kubernetes-version 标志来设置要使用的 Kubernetes 版本。建议将 kubeadm、kebelet、kubectl 和 Kubernetes 的版本匹配。
    • 这个 --control-plane-endpoint 标志应该被设置成负载均衡器的地址或 DNS 和端口。
    • 这个 --upload-certs 标志用来将在所有控制平面实例之间的共享证书上传到集群。如果正好相反,你更喜欢手动地通过控制平面节点或者使用自动化 工具复制证书,请删除此标志并参考如下部分证书分配手册

标志 kubeadm init--config--certificate-key 不能混合使用,因此如果您要使用kubeadm 配置,您必须在相应的配置文件(位于 InitConfigurationJoinConfiguration: controlPlane)添加 certificateKey 字段。

一些 CNI 网络插件如 Calico 需要 CIDR 例如 192.168.0.0/16 和一些像 Weave 没有。参考 CNI 网络文档。 通过传递 --pod-network-cidr 标志添加 pod CIDR,或者您可以使用 kubeadm 配置文件,在 ClusterConfigurationnetworking 对象下设置 podSubnet 字段。

  • 命令完成后,您应该会看到类似以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    ...
    现在,您可以通过在根目录上运行以下命令来加入任意数量的控制平面节点:
    kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07
    
    请注意,证书密钥可以访问集群内敏感数据,请保密!
    为了安全起见,将在两个小时内删除上传的证书; 如有必要,您可以使用 kubeadm 初始化上传证书阶段,之后重新加载证书。
    
    然后,您可以通过在根目录上运行以下命令来加入任意数量的工作节点:
    kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866
    
    • 将此输出复制到文本文件。 稍后您将需要它来将控制平面节点和辅助节点加入集群。

    • --upload-certskubeadm init 一起使用时,主控制平面的证书被加密并上传到 kubeadm-certs 密钥中。

    • 要重新上传证书并生成新的解密密钥,请在已加入集群节点的控制平面上使用以下命令:

      1
      
      sudo kubeadm init phase upload-certs --upload-certs
      
    • 您还可以在 init 期间指定自定义的 --certificate-key,以后可以由 join 使用。 要生成这样的密钥,可以使用以下命令:

      1
      
      kubeadm alpha certs certificate-key
      

kubeadm-certs 密钥和解密密钥会在两个小时后失效。

正如命令输出中所述,证书密钥可访问群集敏感数据,并将其保密!

  1. 应用您选择的 CNI 插件: 请遵循以下指示 安装 CNI 提供程序。如果适用,请确保配置与 kubeadm 配置文件中指定的 Pod CIDR 相对应。

    在此示例中,我们使用 Weave Net:

    1
    
    kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
    
  2. 输入以下内容,并查看 pods 的控制平面组件启动:

    1
    
    kubectl get pod -n kube-system -w
    

其余控制平面节点的步骤

从 kubeadm 1.15 版本开始,您可以并行加入多个控制平面节点。 在此版本之前,您必须在第一个节点初始化后才能依序的增加新的控制平面节点。

对于每个其他控制平面节点,您应该:

  1. 执行先前由第一个节点上的 kubeadm init 输出提供给您的 join 命令。 它看起来应该像这样:

    1
    
    sudo kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07
    
    • 这个 --control-plane 命令通知 kubeadm join 创建一个新的控制平面。
    • --certificate-key ... 将导致从集群中的 kubeadm-certs 秘钥下载控制平面证书并使用给定的密钥进行解密。

外部 etcd 节点

使用外部 etcd 节点设置集群类似于用于堆叠 etcd 的过程, 不同之处在于您应该首先设置 etcd,并在 kubeadm 配置文件中传递 etcd 信息。

设置 ectd 集群

  1. 按照 这些指示 去设置 etcd 集群。

  2. 设置 SSH 在 描述。

  3. 将以下文件从集群中的任何 etcd 节点复制到第一个控制平面节点:

    1
    2
    3
    4
    
    export CONTROL_PLANE="ubuntu@10.0.0.7"
    scp /etc/kubernetes/pki/etcd/ca.crt "${CONTROL_PLANE}":
    scp /etc/kubernetes/pki/apiserver-etcd-client.crt "${CONTROL_PLANE}":
    scp /etc/kubernetes/pki/apiserver-etcd-client.key "${CONTROL_PLANE}":
    
    • 用第一台控制平面机的 user@host 替换 CONTROL_PLANE 的值。

设置第一个控制平面节点

  1. 用以下内容创建一个名为 kubeadm-config.yaml 的文件:

    apiVersion: kubeadm.k8s.io/v1beta2
    kind: ClusterConfiguration
    kubernetesVersion: stable
    controlPlaneEndpoint: "LOAD_BALANCER_DNS:LOAD_BALANCER_PORT"
    etcd:
        external:
            endpoints:
            - https://ETCD_0_IP:2379
            - https://ETCD_1_IP:2379
            - https://ETCD_2_IP:2379
            caFile: /etc/kubernetes/pki/etcd/ca.crt
            certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
            keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key
    

这里堆 etcd 和外部 etcd 之前的区别在于设置外部 etcd 需要一个 etcdexternal 对象下带有 etcd 端点的配置文件。 如果是堆 etcd 技术,是自动管理的。

  • 在您的集群中,将配置模板中的以下变量替换为适当值:

    • LOAD_BALANCER_DNS
    • LOAD_BALANCER_PORT
    • ETCD_0_IP
    • ETCD_1_IP
    • ETCD_2_IP

以下的步骤与设置堆集群是相似的:

  1. 在节点上运行 sudo kubeadm init --config kubeadm-config.yaml --upload-certs 命令。

  2. 编写输出联接命令,这些命令将返回到文本文件以供以后使用。

  3. 应用您选择的 CNI 插件。 给定以下示例适用于 Weave Net:

    1
    
    kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
    

其他控制平面节点的步骤

步骤与设置堆 etcd 相同:

  • 确保第一个控制平面节点已完全初始化。
  • 使用保存到文本文件的连接命令将每个控制平面节点连接在一起。建议一次加入一个控制平面节点。
  • 不要忘记默认情况下,--certificate-key 中的解密秘钥会在两个小时后过期。

列举控制平面之后的常见任务

安装工作节点

您可以使用之前存储的命令将工作节点加入集群中 作为 kubeadm init 命令的输出:

1
sudo kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866

手动证书分发

如果您选择不将 kubeadm init--upload-certs 命令一起使用, 则意味着您将必须手动将证书从主控制平面节点复制到 将要加入的控制平面节点上。

有许多方法可以实现这种操作。在下面的例子中我们使用 sshscp

如果要在单独的一台计算机控制所有节点,则需要 SSH。

  1. 在您的主设备上启动 ssh-agent,要求该设备能访问系统中的所有其他节点:

    1
    
    eval $(ssh-agent)
    
  2. 将 SSH 身份添加到会话中:

    1
    
    ssh-add ~/.ssh/path_to_private_key
    
  3. 检查节点间的 SSH 以确保连接是正常运行的

    • SSH 到任何节点时,请确保添加 -A 标志:

      1
      
      ssh -A 10.0.0.7
      
    • 当在任何节点上使用 sudo 时,请确保环境完善,以便使用 SSH 转发任务:

      1
      
      sudo -E -s
      
  1. 在所有节点上配置 SSH 之后,您应该在运行过 kubeadm init 命令的第一个控制平面节点上运行以下脚本。 该脚本会将证书从第一个控制平面节点复制到另一个控制平面节点:

    在以下示例中,用其他控制平面节点的 IP 地址替换 CONTROL_PLANE_IPS

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    USER=ubuntu # 可自己设置
    CONTROL_PLANE_IPS="10.0.0.7 10.0.0.8"
    for host in ${CONTROL_PLANE_IPS}; do
        scp /etc/kubernetes/pki/ca.crt "${USER}"@$host:
        scp /etc/kubernetes/pki/ca.key "${USER}"@$host:
        scp /etc/kubernetes/pki/sa.key "${USER}"@$host:
        scp /etc/kubernetes/pki/sa.pub "${USER}"@$host:
        scp /etc/kubernetes/pki/front-proxy-ca.crt "${USER}"@$host:
        scp /etc/kubernetes/pki/front-proxy-ca.key "${USER}"@$host:
        scp /etc/kubernetes/pki/etcd/ca.crt "${USER}"@$host:etcd-ca.crt
        scp /etc/kubernetes/pki/etcd/ca.key "${USER}"@$host:etcd-ca.key
    done
    

只需要复制上面列表中的证书。kubeadm 将负责生成其余证书以及加入控制平面实例所需的 SAN。 如果您错误地复制了所有证书,由于缺少所需的 SAN,创建其他节点可能会失败。

  1. 然后,在每个连接控制平面节点上,您必须先运行以下脚本,然后再运行 kubeadm join。 该脚本会将先前复制的证书从主目录移动到 /etc/kubernetes/pki

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    USER=ubuntu # 可自己设置
    mkdir -p /etc/kubernetes/pki/etcd
    mv /home/${USER}/ca.crt /etc/kubernetes/pki/
    mv /home/${USER}/ca.key /etc/kubernetes/pki/
    mv /home/${USER}/sa.pub /etc/kubernetes/pki/
    mv /home/${USER}/sa.key /etc/kubernetes/pki/
    mv /home/${USER}/front-proxy-ca.crt /etc/kubernetes/pki/
    mv /home/${USER}/front-proxy-ca.key /etc/kubernetes/pki/
    mv /home/${USER}/etcd-ca.crt /etc/kubernetes/pki/etcd/ca.crt
    mv /home/${USER}/etcd-ca.key /etc/kubernetes/pki/etcd/ca.key
    

设置aliyun的镜像源

网络问题不能翻墙的同学改用阿里云的源

初始化节点执行

kubeadm init –kubernetes-version=v1.17.0 –control-plane-endpoint=192.168.2.255 –pod-network-cidr=10.244.0.0/16 –image-repository registry.cn-hangzhou.aliyuncs.com/google_containers –upload-certs –v=5

其它Master节点执行

kubeadm config images pull –image-repository registry.cn-hangzhou.aliyuncs.com/google_containers

kubeadm join 192.168.2.255:6443 –token b4azax.83k81l0b4ucy82ng –discovery-token-ca-cert-hash sha256:e7ee5922caabf15af81886b7075deb367292b7d4bd10a6eaee9ca3e91ac9788f –control-plane –certificate-key debf552eac7379bb795f00ff887a8d377b9a9a8c78bd7f27d0ef163d2c095c42

其它配置

ssh免秘钥登录

1
2
ssh-keygen
ssh-copy-id root@192.168.1.201/202/203

初始化脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
cat init_env.sh 
#!/bin/bash
#关闭防火墙 SELINUX
systemctl stop firewalld && systemctl disable firewalld
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=disable/' /etc/selinux/config
#关闭swap
swapoff -a && sysctl -w vm.swappiness=0
sed -i 's/.*swap.*/#&/g' /etc/fstab
#设置Docker所需参数
cat > /etc/sysctl.d/k8s.conf << EOF
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
modprobe br_netfilter
sysctl -p /etc/sysctl.d/k8s.conf
#加载ip_vs模块
cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF
chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4
#安装docker18.06版本 
yum -y install yum-utils device-mapper-persistent-data lvm2 wget epel-release ipvsadm vim ntpdate
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce-18.06.1.ce-3.el7
cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "registry-mirrors": ["https://gco4rcsp.mirror.aliyuncs.com"],
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}
EOF
systemctl enable docker && systemctl daemon-reload && systemctl restart docker
#安装kube组件
cat > /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

Haproxy + Keepalive配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#apiserver高可用部署  Haproxy+Keepalived
for node in k8s-m{1,2,3};do ssh root@$node yum -y install haproxy keepalived;done
#keepalived配置文件,其余节点修改state为BACKUP,priority小于主节点即可;检查网卡名称并修改
cat > /etc/keepalived/keepalived.conf << EOF 
vrrp_script check_haproxy {
    script "/etc/keepalived/check_haproxy.sh"
    interval 3
}
vrrp_instance VI_1 {
    state MASTER
    interface ens192
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
    ${APISERVER_IP}
    }
     track_script {
        check_haproxy
     }
}
EOF
cat > /etc/keepalived/check_haproxy.sh <<EOF
#!/bin/bash
systemctl status haproxy > /dev/null
if [[ \$? != 0 ]];then
        echo "haproxy is down,close the keepalived"
        systemctl stop keepalived
fi
EOF
cat > /etc/haproxy/haproxy.cfg << EOF 
global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats
#---------------------------------------------------------------------
defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000
#---------------------------------------------------------------------
frontend  k8s-api 
   bind *:8443
   mode tcp
   default_backend             apiserver
#---------------------------------------------------------------------
backend apiserver
    balance     roundrobin
    mode tcp
    server  k8s-m1 192.168.1.201:6443 check weight 1 maxconn 2000 check inter 2000 rise 2 fall 3
    server  k8s-m2 192.168.1.202:6443 check weight 1 maxconn 2000 check inter 2000 rise 2 fall 3
    server  k8s-m3 192.168.1.203:6443 check weight 1 maxconn 2000 check inter 2000 rise 2 fall 3
EOF

参考

https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/high-availability/#%e4%bd%bf%e7%94%a8%e5%a0%86%e6%8e%a7%e5%88%b6%e5%b9%b3%e9%9d%a2%e5%92%8c-etcd-%e8%8a%82%e7%82%b9

https://github.com/kubernetes/website/edit/master/content/zh/docs/setup/production-environment/tools/kubeadm/high-availability.md