大纲 高可用集群的概述 高可用集群的介绍 Kubernetes 作为容器集群系统,通过健康检查与重启策略实现了 Pod 的故障自我修复能力,通过调度算法将 Pod 分布式部署,并持续监控其预期副本数。当节点发生故障时,系统能够自动在正常节点上重新启动 Pod,从而实现应用层的高可用性。在 Kubernetes 集群环境中,高可用性还需考虑以下两个层面:Etcd 存储库的高可用性,以及 Kubernetes Master 节点的高可用性。 Etcd 可以部署 3 集群个节点来保证高可用性。Kubernetes Master 节点承担总控中心角色,通过持续与工作节点(Worker)上的 Kubelet 和 Kube-Proxy 进行通信,维护整个集群的健康工作状态。一旦 Master 节点发生故障,将无法使用 kubectl 工具或 API 进行集群管理。Kubernetes Master 节点主要包含三个核心服务:API Server、Controller Manager 和 Scheduler。其中,Controller Manager 和 Scheduler 组件自身已通过内置的 Leader 选举机制实现了高可用。因此,Kubernetes Master 节点的高可用主要是针对 API Server 组件,该组件以 HTTP API 形式提供服务,其高可用实现方式与 Web 服务器类似,可以通过负载均衡器(比如 HaProxy / Nginx)对其多个实例进行负载均衡,支持水平扩展,并使用 Keepalived 来保证负载均衡器自身的高可用性。
Kubernetes 高可用集群的两种搭建方式
Kubernetes 高可用集群通常有两种部署方式:二进制手动部署 和 Kubeadm 自动化部署。在生产环境中,更推荐使用二进制部署方式,因为可控性高、组件版本可精细管理,便于根据业务需求进行深度定制。而在开发或测试环境,则可以使用 Kubeadm 快速搭建高可用集群,部署流程简单、效率更高。
高可用集群的架构 Kubernetes 多 Master 集群的整体架构如下,多个 Master 节点之间通过 HaProxy 实现反向代理和负载均衡,而 HaProxy 通过 Keepalived 来保证自身的高可用性(即双机主备,也叫双机热备)。
搭建多 Master 集群 本节将基于二进制包方式搭建多 Master 的 Kubernetes 集群,核心目标如下:
为 Etcd 、API Server、Kube-Proxy 自签证书 部署 Etcd 集群(3 个节点) 部署 Kubernetes 集群的 2 个 Master 节点 部署 Kubernetes 集群的 3 个 Node 节点 部署 Kubernetes 集群的 CNI 网络插件 部署 Kubernetes 集群的 DNS 组件(CoreDNS) 部署 HaProxy、Keepalived 术语说明
为了方便描述,本文使用 Node 节点 来替代 Kubernetes 的 Worker Node,使用 Master 节点 来替代 Master Node。
版本说明 软件 版本 安装方式 CentOS 7.9多个独立虚拟机 Docker 19.03.9二进制安装包 Kubernetes 1.19.10二进制安装包 Etcd 3.4.9二进制安装包 HaProxy 2.8.5源码编译安装 Keepalived 2.2.8源码编译安装
硬件要求 搭建 Kubernetes 高可用集群需要满足以下几个条件:
一台或多台机器,建议操作系统 CentOS 7(64 位) Master 节点的硬件配置:2GB 或更多 RAM,2 个 CPU 或更多 CPU,硬盘 20GB 或更多 Node 节点的硬件配置:4GB 或更多 RAM,4 个 CPU 或更多 CPU,硬盘 40GB 或更多 集群中所有机器之间的网络可以互通 系统内可以访问外网,需要拉取镜像 禁用 Swap 分区(必须) 服务器规划 本文所使用的 Kubernetes 集群服务器规划如下表所示。为了节省资源,Etcd 集群部署在与 Kubernetes 集群相同的服务器上。需要注意的是,在生产环境中,强烈建议将 Etcd 集群部署在独立的服务器上,以提升系统的性能和高可用性。
Host 名称 角色 IP CPU 核数 内存 磁盘 安装的组件 备注 k8s-master1 master 192.168.2.191 >= 2C >=2G >=20G kube-apiserver,kube-controller-manager,kube-scheduler,kubelet,kube-proxy,cni plugin, docker,etcd,haproxy,keepalived 部署 Etcd、HaProxy、Keepalived k8s-node1 node(worker) 192.168.2.112 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker,etcd 部署 Etcd k8s-node2 node(worker) 192.168.2.131 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker,etcd 部署 Etcd k8s-node3 node(worker) 192.168.2.236 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker 不部署 Etcd k8s-master2 master 192.168.2.148 >= 2C >=2G >=20G kube-apiserver,kube-controller-manager,kube-scheduler,kubelet,kube-proxy,cni plugin, docker,haproxy,keepalived 不部署 Etcd,部署 HaProxy、Keepalived
如果希望 Master 节点仅用于运行控制平面组件(如 API Server、Controller Manager 和 Scheduler),不承载用户 Pod 的调度和运行任务,则 Master 节点可以不部署 Kubelet、Kube-Proxy 和 Docker(容器运行时),如下表所示:
Host 名称 角色 IP CPU 核数 内存 磁盘 安装的组件 备注 k8s-master1 master 192.168.2.191 >= 2C >=2G >=20G kube-apiserver,kube-controller-manager,kube-scheduler,cni plugin,etcd,haproxy,keepalived 部署 Etcd、HaProxy、Keepalived,不部署 Kubelet、Kube-Proxy、Docker k8s-node1 node(worker) 192.168.2.112 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker,etcd 部署 Etcd k8s-node2 node(worker) 192.168.2.131 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker,etcd 部署 Etcd k8s-node3 node(worker) 192.168.2.236 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker 不部署 Etcd k8s-master2 master 192.168.2.148 >= 2C >=2G >=20G kube-apiserver,kube-controller-manager,kube-scheduler,kubelet,kube-proxy,cni plugin, haproxy,keepalived 不部署 Etcd、Kubelet、Kube-Proxy、Docker,部署 HaProxy、Keepalived
操作系统初始化 特别注意
在 Kubernetes 集群的所有节点(包括 Master 和 Node 节点)上分别执行以下系统初始化操作 。关闭防火墙
1 2 3 4 5 # systemctl stop firewalld# systemctl disable firewalld
关闭 selinux
1 2 3 4 5 # setenforce 0# sed -i 's/enforcing/disabled/' /etc/selinux/config
关闭 swap 分区
1 2 3 4 5 $ swapoff -a # sed -i 's/.*swap.*/#&/' /etc/fstab
系统时间同步(强烈建议使用 chrony 而不是 ntpdate)
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 # yum remove -y ntpdate# yum install -y chrony# systemctl enable chronyd# systemctl start chronyd# vim /etc/chrony.confserver ntp.aliyun.com iburst server ntp1.tencent.com iburst server cn.pool.ntp.org iburst # systemctl restart chronyd# systemctl status chronyd# chronyc tracking# chronyc makestep
设置主机名(比如 k8s-master1、k8s-master2、k8s-node1、k8s-node2、k8s-node3)
1 # hostnamectl set -hostname <hostname>
添加 hosts 配置信息
1 2 3 4 5 6 7 # vim /etc/hosts192.168.2.191 k8s-master1 192.168.2.148 k8s-master2 192.168.2.112 k8s-node1 192.168.2.131 k8s-node2 192.168.2.236 k8s-node3
将桥接的 IPv4 流量传递到 iptables 的链
1 2 3 4 5 6 7 # vim /etc/sysctl.d/k8s.confnet.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 # sysctl --system
安装 CFSSL 工具 特别注意
以下所有操作都是仅在 Kubernetes 集群的任意一个 Master 节点上(比如 k8s-master1)执行 。CFSSL 是 CloudFlare 开源的一款 PKI/TLS 工具,包含一个命令行工具和一个用于签名、验证并且捆绑 TLS 证书的 HTTP API 服务,使用 Go 语言编写。
1 2 3 4 5 6 7 8 9 10 # curl -L https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -o /usr/local /bin/cfssl# curl -L https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -o /usr/local /bin/cfssljson# curl -L https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -o /usr/local /bin/cfssl-certinfo# chmod +x /usr/local /bin/cfssl*# export PATH=/usr/local /bin:$PATH
安装 Docker 服务 特别注意
在 Kubernetes 集群的所有节点(包括 Master 和 Node 节点)上分别安装 Docker,这里采用二进制安装方式,CentOS 上还可以通过 YUM 安装 。如果 Master 节点仅用于运行控制平面组件(如 API Server、Controller Manager 和 Scheduler),不承载用户 Pod 的调度和运行任务,则 Master 节点可以不安装 Docker 服务。 下载并解压 Docker 的二进制包
1 2 3 4 5 6 7 8 # wget https://download.docker.com/linux/static/stable/x86_64/docker-19.03.9.tgz# tar -xvf docker-19.03.9.tgz# mv docker/* /usr/bin
创建 Docker 的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # mkdir /etc/docker# cat > /etc/docker/daemon.json << EOF{ "registry-mirrors" : [ "https://b9pmyelo.mirror.aliyuncs.com" , "https://ustc-edu-cn.mirror.aliyuncs.com" , "https://mirror.iscas.ac.cn" , "https://docker.nju.edu.cn" , "https://docker.m.daocloud.io" , "https://ccr.ccs.tencentyun.com" , "https://dockerhub.timeweb.cloud" ] } EOF
配置 Systemd 管理 Docker
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 # cat > /usr/lib/systemd/system/docker.service << EOF[Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com After=network-online.target After=firewalld.service Wants=network-online.target [Service] Type=notify ExecStart=/usr/bin/dockerd ExecReload=/bin/kill -s HUP $MAINPID LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity TimeoutStartSec=0 Delegate=yes KillMode=process Restart=on-failure StartLimitBurst=3 StartLimitInterval=60s [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 Docker 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # systemctl daemon-reload# systemctl enable docker# systemctl start docker# systemctl status docker# docker --version # docker info
搭建 Etcd 集群 Etcd 是一个分布式键值存储系统,Kubernetes 使用 Etcd 进行数据存储,所以先准备一个 Etcd 系统。为解决 Etcd 单点故障,建议采用集群方式部署,这里使用 3 台机器组建 Etcd 集群,可容忍 1 台机器故障。当然,也可以使用 5 台机器组建集群,可容忍 2 台机器故障。为了节省机器,这里复用 Kubernetes 节点的机器,也可以独立于 Kubernetes 集群之外部署,只要 Kubernetes 的 API Server 能够连接上就行。
生成 Etcd 证书 特别注意
以下所有操作都是仅在 Kubernetes 集群的任意一个节点上(比如,第一个 Master 节点)执行,千万不要在每个 Etcd 集群节点上单独生成证书,否则 Etcd 集群里的节点可能会因证书不一致而导致集群启动失败 。以后向 Etcd 集群中添加新节点时,只要将对应的证书拷贝到新节点上即可,通过 CFSSL 工具生成证书的详细使用教程请看 这里 。 创建存放 Etcd 证书的目录
1 2 3 4 5 mkdir -p ~/tls/etcd/ cd ~/tls/etcd/
创建 CA 证书的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # cat << EOF | tee ca-config.json{ "signing" : { "default" : { "expiry" : "87600h" }, "profiles" : { "www" : { "expiry" : "87600h" , "usages" : [ "signing" , "key encipherment" , "server auth" , "client auth" ] } } } } EOF
创建 CA 证书签名的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # cat << EOF | tee ca-csr.json{ "CN" : "etcd ca" , "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "C" : "CN" , "ST" : "BeiJing" , "L" : "BeiJing" , "O" : "k8s" , "OU" : "System" } ], "ca" : { "expiry" : "87600h" } } EOF
创建用于生成 Etcd 证书的配置文件,如果 hosts 字段不为空,则需要指定授权使用该证书的 IP 或域名列表。由于该证书后续会被 Etcd 集群使用到,因此通常指定为 Etcd 集群所有节点的 IP 集合。因为本文的 Etcd 集群节点和 Kubernetes 的集群节点共同安装在不同虚拟机内(即服务器复用),所以 IP 列表就是 Kubernetes 集群所有节点的 IP 集合。
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 # cat << EOF | tee server-csr.json{ "CN" : "etcd" , "hosts" : [ "192.168.2.191" , "192.168.2.148" , "192.168.2.112" , "192.168.2.131" , "192.168.2.236" ], "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "C" : "CN" , "ST" : "BeiJing" , "L" : "BeiJing" , "O" : "k8s" , "OU" : "System" } ] } EOF
使用自签 CA 签发 Etcd 证书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # ls ca-config.json ca-csr.json server-csr.json # cfssl gencert -initca ca-csr.json | cfssljson -bare ca -# ls ca*.pemca-key.pem ca.pem # cfssl gencert -ca =ca.pem -ca -key=ca-key.pem -config =ca-config.json -profile =www server-csr.json | cfssljson -bare server# ls server*.pemserver-key.pem server.pem # cfssl -certinfo -cert server.pem# openssl x509 -noout -text -in server.pem
部署 Etcd 集群 在第一个 Master 节点(k8s-master1)里面安装 Etcd
1 2 3 4 5 6 7 8 9 10 11 # wget https://github.com/etcd-io/etcd/releases/download/v3.4.9/etcd-v3.4.9-linux-amd64.tar.gz# mkdir -p /opt/etcd/{bin,cfg,ssl}# tar zxvf etcd-v3.4.9-linux-amd64.tar.gz# mv etcd-v3.4.9-linux-amd64/{etcd,etcdctl} /opt/etcd/bin/
在第一个 Master 节点(k8s-master1)里面拷贝上面生成的 CA 证书和 Etcd 证书到本地目录
1 2 # cp ~/tls/etcd/*.pem /opt/etcd/ssl
在第一个 Master 节点(k8s-master1)里面拷贝上面生成的 CA 证书和 Etcd 证书到其他 Master 节点里面(提供给 API Server 使用)
1 2 3 4 5 # ssh root@k8s-master2 "mkdir -p /opt/etcd/ssl/" # scp ~/tls/etcd/*.pem root@k8s-master2:/opt/etcd/ssl/
在第一个 Master 节点(k8s-master1)里面创建 Etcd 的配置文件,这里必须根据实际情况更改 Etcd 各节点的 IP、名称,切忌直接拷贝以下配置内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # cat > /opt/etcd/cfg/etcd.conf << EOFETCD_NAME="etcd-1" ETCD_DATA_DIR="/var/lib/etcd/default.etcd" ETCD_LISTEN_PEER_URLS="https://192.168.2.191:2380" ETCD_LISTEN_CLIENT_URLS="https://192.168.2.191:2379" ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.2.191:2380" ETCD_ADVERTISE_CLIENT_URLS="https://192.168.2.191:2379" ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.2.191:2380,etcd-2=https://192.168.2.112:2380,etcd-3=https://192.168.2.131:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_INITIAL_CLUSTER_STATE="new" EOF
1 2 3 4 5 6 7 8 9 10 # 配置说明: ETCD_NAME:节点名称,集群中唯一 ETCDDATADIR:数据目录路径 ETCD_LISTEN_PEER_URLS:集群通信监听地址 ETCD_LISTEN_CLIENT_URLS:客户端访问监听地址 ETCD_INITIAL_ADVERTISE_PEER_URLS:集群通告地址 ETCD_ADVERTISE_CLIENT_URLS:客户端通告地址 ETCD_INITIAL_CLUSTER:集群节点地址 ETCD_INITIAL_CLUSTER_TOKEN:集群 Token ETCD_INITIAL_CLUSTER_STATE:加入集群的当前状态,new 是新集群,existing 表示加入已有集群
在第一个 Master 节点(k8s-master1)里面使用 Systemd 管理 Etcd 服务
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 # cat > /usr/lib/systemd/system/etcd.service << EOF[Unit] Description=Etcd Server After=network.target After=network-online.target Wants=network-online.target [Service] Type=notify EnvironmentFile=/opt/etcd/cfg/etcd.conf ExecStart=/opt/etcd/bin/etcd \ --cert -file=/opt/etcd/ssl/server.pem \--key -file=/opt/etcd/ssl/server-key.pem \--peer -cert-file=/opt/etcd/ssl/server.pem \--peer -key-file=/opt/etcd/ssl/server-key.pem \--trusted -ca-file=/opt/etcd/ssl/ca.pem \--peer -trusted-ca-file=/opt/etcd/ssl/ca.pem \--logger =zapRestart=always RestartSec=10s LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
1 2 3 4 5 # systemctl daemon-reload# systemctl enable etcd
在第一个 Master 节点(k8s-master1)里面拷贝所有 Etcd 文件到其他 2 个 Node 节点,并在这 2 个 Node 节点里分别更改 Etcd 的配置文件和设置 Etcd 服务开机自启动,最终 Etcd 集群会有 3 个节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # scp -r /opt/etcd/ root@k8s-node1:/opt/# scp -r /opt/etcd/ root@k8s-node2:/opt/# scp /usr/lib/systemd/system/etcd.service root@k8s-node1:/usr/lib/systemd/system/# scp /usr/lib/systemd/system/etcd.service root@k8s-node2:/usr/lib/systemd/system/# vim /opt/etcd/cfg/etcd.conf# systemctl daemon-reload# systemctl enable etcd
比如,Etcd 集群第 2 个节点的配置文件(etcd.conf)内容如下:
1 2 3 4 5 6 7 8 9 10 11 ETCD_NAME="etcd-2" ETCD_DATA_DIR="/var/lib/etcd/default.etcd" ETCD_LISTEN_PEER_URLS="https://192.168.2.112:2380" ETCD_LISTEN_CLIENT_URLS="https://192.168.2.112:2379" #[Cluster] ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.2.112:2380" ETCD_ADVERTISE_CLIENT_URLS="https://192.168.2.112:2379" ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.2.191:2380,etcd-2=https://192.168.2.112:2380,etcd-3=https://192.168.2.131:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_INITIAL_CLUSTER_STATE="new"
比如,Etcd 集群第 3 个节点的配置文件(etcd.conf)内容如下:
1 2 3 4 5 6 7 8 9 10 11 ETCD_NAME="etcd-3" ETCD_DATA_DIR="/var/lib/etcd/default.etcd" ETCD_LISTEN_PEER_URLS="https://192.168.2.131:2380" ETCD_LISTEN_CLIENT_URLS="https://192.168.2.131:2379" #[Cluster] ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.2.131:2380" ETCD_ADVERTISE_CLIENT_URLS="https://192.168.2.131:2379" ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.2.191:2380,etcd-2=https://192.168.2.112:2380,etcd-3=https://192.168.2.131:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_INITIAL_CLUSTER_STATE="new"
启动 Etcd 集群 在 Etcd 集群的所有节点上分别执行以下命令来启动 Etcd,值得一提的是,必须在多个 Etcd 节点里同时执行 systemctl start etcd 命令来启动 Etcd 集群,否则单个 Etcd 节点是无法正常启动的
1 2 3 4 5 6 7 8 # systemctl start etcd# systemctl status etcd# journalctl -u etcd.service
若 Etcd 集群启动成功,可以在任意一个 Etcd 节点里执行以下命令来查看集群的运行状态,比如 Leader 选举结果(请自行更改 Etcd 集群节点的 IP 和端口,切忌直接拷贝执行以下命令 )
1 2 3 4 5 6 7 +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS | +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | https://192.168.2.191:2379 | 9a8c44f9310511 | 3.4.9 | 442 kB | true | false | 2 | 519 | 519 | | | https://192.168.2.112:2379 | a00042f5519886e3 | 3.4.9 | 451 kB | false | false | 2 | 519 | 519 | | | https://192.168.2.131:2379 | 9cb4eb07d38510b5 | 3.4.9 | 446 kB | false | false | 2 | 519 | 519 | | +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
若 Etcd 集群启动失败,可以在所有的 Etcd 节点里分别执行以下操作来解决
1 2 3 4 5 6 7 8 9 10 11 # systemctl stop etcd# rm -rf /var/lib/etcd/default.etcd# systemctl start etcd# systemctl status etcd
特别注意
Etcd 集群使用了 Raft 协议,很多操作要依赖时间戳和进行超时判断,因此必须安装类似 chrony 这样的工具来同步操作系统的时间,否则可能会影响 Etcd 集群的正常工作。
搭建 Kubernetes 集群 生成集群证书 特别注意
以下所有操作都是仅在 Kubernetes 集群的任意一个 Master 节点(比如 k8s-master1)上执行,千万不要在每个 Kubernetes 集群节点上单独生成 API Server 或者 Kube-Proxy 证书,否则可能会因证书不一致而导致 Kubernetes 集群启动失败 。简而言之,所有证书(包括 API Server、Kube-Proxy 等证书)都是在任意一个 Master 节点上面生成,其他 Kubernetes 集群节点不参与生成证书。以后向 Kubernetes 集群中添加任何新节点时,只要将对应的证书拷贝到新节点上即可 。值得一提的是,通过 CFSSL 工具生成证书的详细使用教程请看 这里 。 生成 API Server 证书 创建存放证书的目录
进入存放证书的目录
创建 CA 证书的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # cat > ca-config.json << EOF{ "signing" : { "default" : { "expiry" : "87600h" }, "profiles" : { "kubernetes" : { "expiry" : "87600h" , "usages" : [ "signing" , "key encipherment" , "server auth" , "client auth" ] } } } } EOF
创建 CA 证书签名的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # cat > ca-csr.json << EOF{ "CN" : "kubernetes" , "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "C" : "CN" , "L" : "Beijing" , "ST" : "Beijing" , "O" : "k8s" , "OU" : "System" } ] } EOF
创建用于生成 API Server 证书的配置文件,如果 hosts 字段不为空,需要指定授权使用该证书的 IP 或域名列表。由于该证书会被 Kubernetes 集群使用到,通常需要包含 Kubernetes 集群所有节点的 IP,以及 Kubernetes 服务的 Cluster IP(即 kube-apiserver.conf 配置文件中指定的 --service-cluster-ip-range 网段的第一个可用 IP,例如 10.0.0.1,具体取决于 Kubernetes 集群的 Service 网段配置),最后还要加上 Keepalived 的虚拟 IP(VIP)地址(比如 192.168.2.100)
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 # cat << EOF | tee server-csr.json{ "CN" : "kubernetes" , "hosts" : [ "10.0.0.1" , "127.0.0.1" , "192.168.2.191" , "192.168.2.148" , "192.168.2.112" , "192.168.2.131" , "192.168.2.236" , "192.168.2.100" , "kubernetes" , "kubernetes.default" , "kubernetes.default.svc" , "kubernetes.default.svc.cluster" , "kubernetes.default.svc.cluster.local" ], "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "C" : "CN" , "ST" : "BeiJing" , "L" : "BeiJing" , "O" : "k8s" , "OU" : "System" } ] } EOF
使用自签 CA 签发 API Server 证书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # ls ca-config.json ca-csr.json server-csr.json # cfssl gencert -initca ca-csr.json | cfssljson -bare ca -# ls ca*.pemca-key.pem ca.pem # cfssl gencert -ca =ca.pem -ca -key=ca-key.pem -config =ca-config.json -profile =kubernetes server-csr.json | cfssljson -bare server# ls server*.pemserver-key.pem server.pem # cfssl -certinfo -cert server.pem# openssl x509 -noout -text -in server.pem
将上面生成的 CA 证书和 API Server 证书拷贝到本地目录里面
1 2 3 4 5 # mkdir -p /opt/kubernetes/ssl/# cp ~/tls/k8s/*.pem /opt/kubernetes/ssl/
将上面生成的 CA 证书和 API Server 证书拷贝到其他 Master 节点里面
1 2 3 4 5 # ssh root@k8s-master2 "mkdir -p /opt/kubernetes/ssl/" # scp ~/tls/k8s/*.pem root@k8s-master2:/opt/kubernetes/ssl/
将上面生成的 CA 证书拷贝到所有 Node 节点里面
1 2 3 4 5 6 7 8 9 # ssh root@k8s-node1 "mkdir -p /opt/kubernetes/ssl/" # ssh root@k8s-node2 "mkdir -p /opt/kubernetes/ssl/" # ssh root@k8s-node3 "mkdir -p /opt/kubernetes/ssl/" # scp ~/tls/k8s/ca*.pem root@k8s-node1:/opt/kubernetes/ssl/# scp ~/tls/k8s/ca*.pem root@k8s-node2:/opt/kubernetes/ssl/# scp ~/tls/k8s/ca*.pem root@k8s-node3:/opt/kubernetes/ssl/
生成 Kube-Proxy 证书 进入存放证书的目录
创建用于生成 Kube-Proxy 证书的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # cat > kube-proxy-csr.json << EOF{ "CN" : "system:kube-proxy" , "hosts" : [], "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "C" : "CN" , "L" : "BeiJing" , "ST" : "BeiJing" , "O" : "k8s" , "OU" : "System" } ] } EOF
使用自签 CA 签发 Kube-Proxy 证书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ls ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem kube-proxy-csr.json server.csr server-csr.json server-key.pem server.pem # cfssl gencert -ca =ca.pem -ca -key=ca-key.pem -config =ca-config.json -profile =kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy# ls kube-proxy*.pemkube-proxy-key.pem kube-proxy.pem # cfssl -certinfo -cert kube-proxy.pem# openssl x509 -noout -text -in kube-proxy.pem
将上面生成的 Kube-Proxy 证书拷贝到本地目录里面
1 2 # cp ~/tls/k8s/kube-proxy*.pem /opt/kubernetes/ssl/
将上面生成的 Kube-Proxy 证书拷贝到其他 Master 节点里面
1 2 # scp ~/tls/k8s/kube-proxy*.pem root@k8s-master2:/opt/kubernetes/ssl/
将上面生成的 Kube-Proxy 证书拷贝到所有 Node 节点里面
1 2 3 4 # scp ~/tls/k8s/kube-proxy*.pem root@k8s-node1:/opt/kubernetes/ssl/# scp ~/tls/k8s/kube-proxy*.pem root@k8s-node2:/opt/kubernetes/ssl/# scp ~/tls/k8s/kube-proxy*.pem root@k8s-node3:/opt/kubernetes/ssl/
单独部署 Master 节点 特别注意
以下所有操作都是仅在 Kubernetes 集群的两台 Master 节点上执行,也就是分别在两台 Master 节点上部署 API Server、Controller Manager、Scheduler、HaProxy、Keepalived 。
下载二进制文件 下载地址
Kubernetes 相关的二进制包可以从 GitHub 下载得到,这里下载的版本是 v1.19.10。 当打开 GitHub 链接后,会发现里面有很多二进制包,只需要下载一个 Server 二进制包就够了,里面包含了 Master 节点相关的二进制包。 下载并解压二进制包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # wget https://dl.k8s.io/v1.19.10/kubernetes-server-linux-amd64.tar.gz# mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs}# tar zxvf kubernetes-server-linux-amd64.tar.gz# cd kubernetes/server/bin# cp kube-apiserver kube-scheduler kube-controller-manager /opt/kubernetes/bin/# cp kubectl /usr/bin/
部署 API Server 创建 API Server 的配置文件,使用转义符 \\ 是为了使 EOF 保留换行符,其中 --bind-address 与 --advertise-address 通常是当前 Master 节点的 IP 地址,请记得自行更改以下配置内容里的 IP 地址,切忌直接拷贝
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 # cat > /opt/kubernetes/cfg/kube-apiserver.conf << EOFKUBE_APISERVER_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --etcd-servers=https://192.168.2.191:2379,https://192.168.2.112:2379,https://192.168.2.131:2379 \\ --bind-address=192.168.2.191 \\ --secure-port=6443 \\ --advertise-address=192.168.2.191 \\ --allow-privileged=true \\ --service-cluster-ip-range=10.0.0.0/24 \\ --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \\ --authorization-mode=RBAC,Node \\ --enable-bootstrap-token-auth=true \\ --token-auth-file=/opt/kubernetes/cfg/token.csv \\ --service-node-port-range=30000-32767 \\ --kubelet-client-certificate=/opt/kubernetes/ssl/server.pem \\ --kubelet-client-key=/opt/kubernetes/ssl/server-key.pem \\ --tls-cert-file=/opt/kubernetes/ssl/server.pem \\ --tls-private-key-file=/opt/kubernetes/ssl/server-key.pem \\ --client-ca-file=/opt/kubernetes/ssl/ca.pem \\ --service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \\ --etcd-cafile=/opt/etcd/ssl/ca.pem \\ --etcd-certfile=/opt/etcd/ssl/server.pem \\ --etcd-keyfile=/opt/etcd/ssl/server-key.pem \\ --audit-log-maxage=30 \\ --audit-log-maxbackup=3 \\ --audit-log-maxsize=100 \\ --audit-log-path=/opt/kubernetes/logs/k8s-audit.log" EOF
配置参数说明
参数 含义 --logtostderr启用将日志输出到 stderr(通常设为 false 以输出到指定目录)。 --v日志详细级别(verbosity level),数值越大输出越详细。 --log-dir日志文件输出目录。 --etcd-serversEtcd 集群地址列表(用逗号分隔,支持 Https)。 --bind-addresskube-kube-apiserver 监听的 IP 地址,通常是 Master 节点的 IP 地址。 --secure-portHTTPS 安全端口(默认 6443)。 --advertise-address集群中对外通告的地址(其他组件访问时使用),通常是 Master 节点的 IP 地址。 --allow-privileged是否允许特权容器(启用授权相关特性)。 --service-cluster-ip-rangeService 虚拟 IP 地址段,用于分配 Cluster IP。 --enable-admission-plugins启用的准入控制插件列表(例如 NamespaceLifecycle, LimitRanger 等)。 --authorization-mode认证 / 授权模式(如 RBAC,Node 表示启用 RBAC 和节点自管理)。 --enable-bootstrap-token-auth启用基于 Bootstrap Token 的 TLS 引导认证。 --token-auth-fileBootstrap Token 的静态文件路径(比如 token.csv)。 --service-node-port-rangeNodePort 类型 Service 默认分配的端口范围(如 30000-32767)。 --kubelet-client-certificatekube-apiserver 访问 kubelet 时使用的客户端证书路径。 --kubelet-client-keykube-apiserver 访问 kubelet 时使用的客户端私钥路径。 --tls-cert-filekube-apiserver HTTPS 使用的证书文件。 --tls-private-key-filekube-apiserver HTTPS 使用的私钥文件。 --client-ca-file用于验证客户端证书(如 kubelet/client)的 CA 证书。 --service-account-key-file用于签名 Service Account Token 的私钥文件。 --etcd-cafile连接 Etcd 时验证其证书的 CA 证书路径。 --etcd-certfilekube-apiserver 连接 Etcd 时使用的客户端证书。 --etcd-keyfilekube-apiserver 连接 Etcd 时使用的客户端私钥。 --audit-log-maxage审计日志保留的最大天数。 --audit-log-maxbackup审计日志保留的备份数量。 --audit-log-maxsize单个审计日志文件的最大大小(MB)。 --audit-log-path审计日志输出路径。
TLS Bootstraping 机制介绍
在 Master 节点中,API Server 启用 TLS 认证后,Node 节点上的 kubelet 和 kube-proxy 必须使用由 CA 签发的有效客户端证书才能与 kube-apiserver 通信。当 Node 节点数量较多时,手动颁发这些客户端证书的工作量大,且会增加集群扩展的复杂度。为简化这一流程,Kubernetes 引入了 TLS Bootstrapping 机制:kubelet 会以低权限用户身份向 API Server 自动申请客户端证书,并由 API Server 动态签发,从而实现证书的自动化管理。TLS Bootstrapping 机制的工作流程如图所示 。
创建在 API Server 配置文件(kube-apiserver.conf)中通过 --token-auth-file 指定的 Token 文件,用于配置 TLS Bootstrapping 机制
1 2 3 4 # cat > /opt/kubernetes/cfg/token.csv << EOFc47ffb939f5ca36231d9e3121a252940,kubelet-bootstrap,10001,"system:node-bootstrapper" EOF
Token 文件的数据格式是 Token,用户名,UID,用户组,其中 UID 仅作标识使用,可以是任意唯一值;用户组 system:node-bootstrapper 是 Kubelet TLS Bootstrap 必需的组;Token 可以使用以下命令自行生成并替换掉
1 2 # head -c 16 /dev/urandom | od -An -t x | tr -d ' '
配置 Systemd 管理 API Server 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # cat > /usr/lib/systemd/system/kube-apiserver.service << EOF[Unit] Description=Kubernetes API Server Documentation=https://github.com/kubernetes/kubernetes After=network-online.target [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-apiserver.conf ExecStart=/opt/kubernetes/bin/kube-apiserver \$KUBE_APISERVER_OPTS Restart=on-failure [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 API Server 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # systemctl daemon-reload# systemctl enable kube-apiserver# systemctl start kube-apiserver# systemctl status kube-apiserver# journalctl -u kube-apiserver.service
授权 kubelet-bootstrap 用户允许请求(申请)证书(无论有多少个 Master 节点,都只需执行一次授权,重复执行授权会提示失败)
1 # kubectl create clusterrolebinding kubelet-bootstrap --clusterrole =system:node-bootstrapper --user =kubelet-bootstrap
部署 HaProxy 特别注意
在两台 Master 节点上,分别安装 HaProxy,两台 Master 节点上的 HaProxy 配置文件都是一样的 。生产级 Kubernetes 高可用集群通常采用 HAProxy + Keepalived 实现 API Server 的负载均衡(高性能四层负载均衡),而不是使用 Nginx + Keepalived 替代(可选)。同时,会结合使用 Nginx Ingress 处理应用流量负载,两者分层清晰、运维简单 。1. 安装概述
HAProxy 是一款广泛使用的反向代理与负载均衡软件,可以采用其四层(TCP)模式对 Kubernetes API Server 进行负载均衡,从而实现多 Master 节点的高可用访问。通过 HAProxy 构建 API Server 的高可用架构,其整体设计如下图所示:
安装说明 路径 HaProxy 的安装路径 /usr/local/sbin/haproxyHaProxy 的配置文件 /etc/haproxy/haproxy.cfgHaProxy 的 PID 文件 /var/lib/haproxy/haproxy.pidHaProxy 的 Socket 文件 /var/lib/haproxy/statsHaProxy 的 Systemd 服务配置文件 /usr/lib/systemd/system/haproxy.service
2. 安装步骤
安装依赖软件包
1 2 # yum install -y gcc make pcre-devel openssl openssl-devel systemd-devel wget
下载 HaProxy 源码包(官网下载地址 )
1 2 # wget https://www.haproxy.org/download/2.8/src/haproxy-2.8.5.tar.gz
下载 HaProxy 源码包
1 2 # tar -xvf haproxy-2.8.5.tar.gz
编译源码与安装
1 2 3 4 5 6 7 8 9 10 11 12 13 # cd haproxy-2.8.5# make TARGET=linux-glibc \ USE_OPENSSL=1 \ USE_PCRE=1 \ USE_SYSTEMD=1 \ USE_ZLIB=1 \ -j $(nproc)# make install
3. 创建用户
创建 HaProxy 用户与用户组
1 2 3 4 5 # groupadd --system haproxy# useradd --system --no -create-home --shell /sbin/nologin --gid haproxy haproxy
4. 配置服务
配置 Systemd 管理 HaProxy 服务
1 2 # vim /usr/lib/systemd/system/haproxy.service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [Unit] Description=HAProxy Load Balancer After=network.target [Service] User=haproxy Group=haproxy LimitNOFILE=1048576 Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/var/lib/haproxy/haproxy.pid" ExecStartPre=/usr/local/sbin/haproxy -f $CONFIG -c -q ExecStart=/usr/local/sbin/haproxy -Ws -f $CONFIG -p $PIDFILE ExecReload=/bin/kill -USR2 $MAINPID KillMode=mixed Type=notify [Install] WantedBy=multi-user.target
创建 HaProxy 的运行目录
1 2 3 4 5 6 7 8 # mkdir -p /var/lib/haproxy# chown -R haproxy:haproxy /var/lib/haproxy# chmod 750 /var/lib/haproxy
创建 HaProxy 的配置目录
1 2 # mkdir -p /etc/haproxy
创建 HaProxy 的配置文件
1 2 # vim /etc/haproxy/haproxy.cfg
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 76 77 #--------------------------------------------------------------------- # Global settings #--------------------------------------------------------------------- global # to have these messages end up in /var/log/haproxy.log you will # need to: # 1) configure syslog to accept network log events. This is done # by adding the '-r' option to the SYSLOGD_OPTIONS in # /etc/sysconfig/syslog # 2) configure local2 events to go to the /var/log/haproxy.log # file. A line like the following can be added to # /etc/sysconfig/syslog # # local2.* /var/log/haproxy.log # log 127.0.0.1 local2 # chroot /var/lib/haproxy # pidfile /var/run/haproxy.pid # user haproxy # group haproxy maxconn 4000 nbthread 4 # nbproc 1 # daemon # turn on stats unix socket stats socket /var/lib/haproxy/stats #--------------------------------------------------------------------- # common defaults that all the 'listen' and 'backend' sections will # use if not designated in their block #--------------------------------------------------------------------- defaults mode http log global option httplog option dontlognull option http-server-close 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 #--------------------------------------------------------------------- # kubernetes apiserver frontend which proxys to the backends #--------------------------------------------------------------------- frontend kubernetes-apiserver mode tcp bind *:16443 option tcplog default_backend kubernetes-apiserver #--------------------------------------------------------------------- # round robin balancing between the various backends #--------------------------------------------------------------------- backend kubernetes-apiserver mode tcp balance roundrobin server master01.k8s.io 192.168.2.191:6443 check server master02.k8s.io 192.168.2.148:6443 check #--------------------------------------------------------------------- # collection haproxy statistics message #--------------------------------------------------------------------- listen stats bind *:1080 stats auth admin:admin stats refresh 5s stats realm HAProxy\ Statistics stats uri /admin?stats
HaProxy 配置文件的使用说明
配置项 类型 默认值 说明 maxconn全局 / 前端 / 后端 2000最大连接数,控制 HAProxy 可以同时处理的并发连接数。 nbthread全局 1指定每个进程使用的线程数。多线程模式可提高单进程并发性能。 nbproc全局 1指定 HAProxy 启动的进程数。多进程模式用于多核优化,但 Systemd 下不推荐与 daemon 配合使用。 daemon全局 禁用 是否后台运行。Systemd 管理 HaProxy 时通常禁用,HAProxy 由 Systemd 控制前后台。 modedefaults/frontend/backend http连接模式,可选 http 或 tcp。可以使用 tcp 来实现 Kubernetes API Server 代理。 balancebackend roundrobin负载均衡算法,可选 roundrobin、leastconn、source 等。 stats authlisten 设置统计页面认证用户名和密码,例如 admin:admin。 stats refreshlisten 10s统计页面刷新时间,单位秒。 stats realmlisten HTTP Basic Auth 的 realm 名称,显示在认证弹窗中。 stats urilisten /HaProxy 统计页面访问 URI,例如 /admin?stats。
Haproxy 的配置建议
maxconn + nbthread 可以提升单进程并发性能。mode tcp 是代理 Kubernetes API Server 的标准配置,因为 API Server 是 HTTPS / TCP 流量。当使用 Systemd 管理 HaProxy 时,建议配置 nbproc = 1 + nbthread > 1 + 不启用 daemon ,这是最稳定和高效的配置方式。 5. 启动服务
设置开机自启动 HaProxy 服务
1 2 3 4 5 # systemctl daemon-reload# systemctl enable haproxy
立刻启动 HaProxy 服务
1 2 3 4 5 # systemctl start haproxy# systemctl status haproxy
当 HaProxy 的配置文件发生变更后,可以执行热加载(也叫平滑重载,不会中断现有连接),让配置文件生效
1 2 # systemctl reload haproxy
6. 验证服务
通过 HaProxy 的 16443 反向代理端口(可自定义),验证 HaProxy 的反向代理功能是否可以工作,正常情况下 API Server 的健康检测接口会返回字符串 ok
1 2 # curl --cacert /opt/kubernetes/ssl/ca.pem https://127.0.0.1:16443/healthz
浏览器通过 http://<master-ip>:1080/admin?stats 访问 HaProxy 的统计页面,默认的登录用户名和密码是 admin / admin;如果可以正常访问(如下图所示),则说明 HaProxy 可以正常运行
部署 Keepalived 特别注意
在两台 Master 节点上,分别安装 Keepalived,两台 Master 节点上的 Keepalived 配置文件是不一样的 。1. 安装概述
Keepalived 是一个高可用软件,基于 VIP 绑定实现服务器双机主备(也叫双机热备),在上述拓扑中,Keepalived 主要根据 HaProxy 的运行状态判断是否需要故障转移(VIP 切换)。 例如,当 HaProxy 主节点挂掉,VIP 会自动绑定在 HaProxy 备节点,从而保证 VIP 一直可用,实现 HaProxy 高可用。 安装说明 路径 Keepalived 的安装路径 /usr/local/keepalivedKeepalived 的配置文件 /usr/local/keepalived/etc/keepalived/keepalived.confKeepalived 的 Systemd 服务配置文件(自动生成) /usr/lib/systemd/system/keepalived.serviceKeepalived 检测 HaProxy 是否可用的 Shell 脚本 /usr/local/keepalived/etc/keepalived/scripts/haproxy_check.sh
2. 安装步骤
安装依赖软件包
1 2 # yum install -y gcc gcc-c++ make openssl-devel libnl libnl-devel net-snmp-devel popt-devel
下载 Keepalived 源码包(官网下载地址 )
1 2 # wget https://www.keepalived.org/software/keepalived-2.2.8.tar.gz
解压 Keepalived 源码包
1 2 # tar -xvf keepalived-2.2.8.tar.gz
编译源码与安装
1 2 3 4 5 6 7 8 9 10 11 # cd keepalived-2.2.8# make -j $(nproc)# make install
3. 创建脚本
创建用于 Keepalived 检测 HaProxy 服务是否可用的 Shell 脚本(比如 haproxy_check.sh)
1 2 3 4 5 # mkdir -p /usr/local /keepalived/etc/keepalived/scripts/# vim /usr/local /keepalived/etc/keepalived/scripts/haproxy_check.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 HAPROXY_COUNT=$(pgrep -x haproxy | wc -l ) if [ "$HAPROXY_COUNT " -eq 0 ]; then echo "HAProxy not running" exit 1 else echo "HAProxy running" exit 0 fi
授予 HaProxy 检测脚本可执行权限
1 2 # chmod +x /usr/local /keepalived/etc/keepalived/scripts/haproxy_check.sh
4. 配置服务
Keepalived 配置文件的使用说明
配置项 示例值 说明 router_idk8s本节点唯一标识,用于日志区分(不同节点可不同)。 script"killall -0 haproxy"检测 HAProxy 服务是否可用,可以是 Shell 命令,还可以是 Shell 脚本的绝对路径。返回 0 表示正常,返回非 0 表示失败。 interval2检测脚本执行间隔(秒),指定 Keepalived 多久执行一次健康检查脚本。 weight-100节点权重变化值,检测脚本失败时降低优先级或成功时增加优先级。负值表示降低权重,正值表示增加权重。 fall3连续失败次数阈值,当检测脚本连续失败次数达到 fall 时,节点会认为服务不可用,并扣减权重或触发 VIP 切换。 rise2连续成功次数阈值,当检测脚本连续成功次数达到 rise 时,节点会认为服务恢复,并恢复权重初始值。 stateMASTER节点初始角色(MASTER 或 BACKUP),最终由优先级决定。 interfaceenp0s3VRRP 使用的网络接口名称,可通过 ifconfig 命令得知。 virtual_router_id51VRRP 组 ID,两个节点必须一致。 priority200节点优先级(权重),数值越大优先级越高。 advert_int1VRRP 心跳间隔(秒),默认 1 秒。 auth_typePASSVRRP 认证方式(PASS 或 AH)。 auth_passceb1b3ec013d66163d6abVRRP 认证密码,两个节点必须一致。 virtual_ipaddress192.168.2.100Keepalived 提供的 VIP(虚拟 IP),由 MASTER 持有。 track_scriptcheck_haproxy指定用于健康检查的脚本,检测失败时会调节点整权重或触发 VIP 切换。
Keepalived 什么时候触发 VIP 切换
Keepalived 的 VIP 切换依赖节点权重变化,只有当 MASTER 节点的实时权重下降到低于 BACKUP 节点时,VIP 才会切换到权重更高的节点。 在 Keepalived 配置文件中,vrrp_script 可以通过 weight 动态调整节点权重:正值会增加节点权重,负值会降低节点权重。 当 Keepalived 脚本检测失败时,节点权重会根据 weight 设置下降,如果下降后 MASTER 节点的权重低于 BACKUP 节点,VIP 就会切换。 当 Keepalived 脚本检测成功时,且连续检测成功的次数达到 rise 次后,节点权重会恢复为初始值,原 MASTER 节点可能会重新成为 MASTER。 Keepalived 的 weight 必须设置足够大,否则即使 Keepalived 脚本检测失败,MASTER 节点的权重仍可能比 BACKUP 节点高,从而导致不会触发 VIP 切换。 Keepalived 调整节点权重的细节
Keepalived 不会因为检测脚本持续失败而反复扣减节点权重,只有当检测脚本的检测结果从 "成功" 变为 "失败" 时,才会触发一次节点权重扣减。 也就是说,第一次检测被判定为失败,会执行一次 weight 扣减;当后续检测脚本继续失败时,由于节点已经处于失败状态,不会再重复扣减节点权重。 当检测脚本恢复成功后,Keepalived 会依据 rise 参数进行节点权重恢复。当检测脚本连续成功达到 rise 次后,节点权重会恢复到初始值。 简而言之,Keepalived 的节点权重扣减是一次性的,直到检测脚本恢复成功,才再次生效 。同样的,在节点处于健康状态期间,检测脚本即便持续成功,也不会重复增加节点权重。在第一个 Master 节点 (比如 k8s-master)中,创建 Keepalived 的配置文件,请自行更改 interface(网卡接口名称)与 virtual_ipaddress(虚拟 IP)配置项
1 2 # vim /usr/local /keepalived/etc/keepalived/keepalived.conf
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 ! Configuration File for keepalived global_defs { router_id k8s } vrrp_script check_haproxy { script "/usr/local/keepalived/etc/keepalived/scripts/haproxy_check.sh" interval 2 weight -100 fall 3 rise 2 } vrrp_instance VI_1 { state MASTER interface enp0s3 virtual_router_id 51 priority 250 advert_int 1 authentication { auth_type PASS auth_pass ceb1b3ec013d66163d6ab } virtual_ipaddress { 192.168.2.100 } track_script { check_haproxy } }
在第二个 Master 节点 (比如 k8s-master2)中,创建 Keepalived 的配置文件,请自行更改 interface(网卡接口名称)与 virtual_ipaddress(虚拟 IP)配置项
1 2 # vim /usr/local /keepalived/etc/keepalived/keepalived.conf
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 ! Configuration File for keepalived global_defs { router_id k8s } vrrp_script check_haproxy { script "/usr/local/keepalived/etc/keepalived/scripts/haproxy_check.sh" interval 2 weight -100 fall 3 rise 2 } vrrp_instance VI_1 { state BACKUP interface enp0s3 virtual_router_id 51 priority 200 advert_int 1 authentication { auth_type PASS auth_pass ceb1b3ec013d66163d6ab } virtual_ipaddress { 192.168.2.100 } track_script { check_haproxy } }
5. 启动服务
设置开机自启动 Keepalived 服务
1 2 3 4 5 # systemctl daemon-reload# systemctl enable keepalived
立刻启动 Keepalived 服务
1 2 3 4 5 # systemctl start keepalived# systemctl status keepalived
当 Keepalived 的配置文件发生变更后,可以重启服务让其生效
1 2 # systemctl restart keepalived
6. 验证服务
通过 Keepalived 的 VIP(比如 192.168.2.100) + HaProxy 的 16443 反向代理端口(可自定义),验证 Keepalived 的 VIP 和 HaProxy 的反向代理功能是否可以正常工作,正常情况下 API Server 的健康检测接口会返回字符串 ok,请自行更改 VIP 的地址
1 2 # curl --cacert /opt/kubernetes/ssl/ca.pem https://192.168.2.100:16443/healthz
7. 验证高可用性
验证目标
本节旨在验证 HaProxy + Keepalived 是否能够实现双机主备(即双机热备)高可用机制。具体而言,当第一个 Master 节点上的 HaProxy 发生宕机时,Keepalived 能够将虚拟 IP(VIP)自动切换到第二个 Master 节点上。当第一个 Master 节点上的 HaProxy 恢复运行时,虚拟 IP(VIP)会自动切换回第一个 Master 节点上。这样,在 Kubernetes 集群内部仍可通过 VIP 访问 API Server,从而保障 Kubernetes 集群整体服务的高可用性。
(1) 在第一个 Master 节点(比如 k8s-master)中,查看指定网卡(比如 enp0s3)的详细 IP 地址与状态信息,请自行更改网卡接口名称 输出示例如下,可以看到 VIP(虚拟 IP)地址 192.168.2.100 绑定了网卡接口 enp0s3
1 2 3 4 5 6 7 8 9 10 11 12 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 08:00:27:de:6f:6b brd ff:ff:ff:ff:ff:ff inet 192.168.2.191/24 brd 192.168.2.255 scope global noprefixroute dynamic enp0s3 valid_lft 42617sec preferred_lft 42617sec inet 192.168.2.100/32 scope global enp0s3 valid_lft forever preferred_lft forever inet6 fe80::8b9e:77d3:4d79:eab9/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::6c1a:25c4:bdbd:dad3/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::8dee:f54a:fcd2:369b/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever
(2) 在第一个 Master 节点(比如 k8s-master)中,关闭 HaProxy 服务,并查看 Keepalived 的运行状态 1 2 # systemctl stop haproxy
1 2 # systemctl status keepalived
输出示例如下,可以看到 Keepalived 扣减了当前节点的权重(计算公式:250 - 100 = 150),同时 VIP 也被移除了
1 2 3 4 5 6 7 8 11月 08 14:14:40 k8s-master Keepalived_vrrp[30483]: Sending gratuitous ARP on enp0s3 for 192.168.2.100 11月 08 14:14:40 k8s-master Keepalived_vrrp[30483]: Sending gratuitous ARP on enp0s3 for 192.168.2.100 11月 08 14:18:02 k8s-master Keepalived_vrrp[30483]: Script `check_haproxy` now returning 1 11月 08 14:18:06 k8s-master Keepalived_vrrp[30483]: VRRP_Script(check_haproxy) failed (exited with status 1) 11月 08 14:18:06 k8s-master Keepalived_vrrp[30483]: (VI_1) Changing effective priority from 250 to 150 11月 08 14:18:09 k8s-master Keepalived_vrrp[30483]: (VI_1) Master received advert from 192.168.2.148 with higher priority 200, ours 150 11月 08 14:18:09 k8s-master Keepalived_vrrp[30483]: (VI_1) Entering BACKUP STATE 11月 08 14:18:09 k8s-master Keepalived_vrrp[30483]: (VI_1) removing VIPs.
(3) 在第二个 Master 节点(比如 k8s-master2)中,查看指定网卡(比如 enp0s3)的详细 IP 地址与状态信息,请自行更改网卡接口名称 输出示例如下,可以看到 VIP(虚拟 IP)地址 192.168.2.100 绑定了网卡接口 enp0s3,也就是 VIP 成功切换到 BACKUP 节点
1 2 3 4 5 6 7 8 9 10 11 12 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 08:00:27:78:05:48 brd ff:ff:ff:ff:ff:ff inet 192.168.2.148/24 brd 192.168.2.255 scope global noprefixroute dynamic enp0s3 valid_lft 41406sec preferred_lft 41406sec inet 192.168.2.100/32 scope global enp0s3 valid_lft forever preferred_lft forever inet6 fe80::8b9e:77d3:4d79:eab9/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::6c1a:25c4:bdbd:dad3/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::8dee:f54a:fcd2:369b/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever
(4) 在第一个 Master 节点(比如 k8s-master)中,重新启动 HaProxy 服务,并查看指定网卡(比如 enp0s3)的详细 IP 地址与状态信息,请自行更改网卡接口名称 1 2 # systemctl start haproxy
输出示例如下,可以看到 VIP(虚拟 IP)地址 192.168.2.100 又绑定了网卡接口 enp0s3,也就是 VIP 再次切换回 MASTER 节点
1 2 3 4 5 6 7 8 9 10 11 12 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 08:00:27:de:6f:6b brd ff:ff:ff:ff:ff:ff inet 192.168.2.191/24 brd 192.168.2.255 scope global noprefixroute dynamic enp0s3 valid_lft 42617sec preferred_lft 42617sec inet 192.168.2.100/32 scope global enp0s3 valid_lft forever preferred_lft forever inet6 fe80::8b9e:77d3:4d79:eab9/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::6c1a:25c4:bdbd:dad3/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::8dee:f54a:fcd2:369b/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever
此时,若再次在第一个 Master 节点(比如 k8s-master)中查看 Keepalived 的运行状态,可以看到 Keepalived 将当前节点的权重恢复为初始值,输出示例如下
1 2 3 4 5 11月 08 14:23:49 k8s-master Keepalived_vrrp[30483]: VRRP_Script(check_haproxy) succeeded 11月 08 14:23:49 k8s-master Keepalived_vrrp[30483]: (VI_1) Changing effective priority from 150 to 250 11月 08 14:23:52 k8s-master Keepalived_vrrp[30483]: Sending gratuitous ARP on enp0s3 for 192.168.2.100 11月 08 14:23:52 k8s-master Keepalived_vrrp[30483]: Sending gratuitous ARP on enp0s3 for 192.168.2.100 11月 08 14:23:52 k8s-master Keepalived_vrrp[30483]: Sending gratuitous ARP on enp0s3 for 192.168.2.100
部署 Controller Manager 创建 Controller Manager 的配置文件,使用转义符 \\ 是为了使 EOF 保留换行符。其中 --master 配置参数需要指定为 Keepalived 的虚拟 IP 地址(比如 192.168.2.100) + HaProxy 反向代理的端口(比如 16443),而不是直接使用 API Server 的 IP 地址。同时,--cluster-cidr 与 --service-cluster-ip-range 配置参数的值不要随意更改,否则可能会影响 Kubernetes 集群的正常运行。请记得自行更改以下的 --master 配置参数,切忌直接拷贝使用,其他配置参数通常不需要更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # cat > /opt/kubernetes/cfg/kube-controller-manager.conf << EOFKUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --leader-elect=true \\ --master=192.168.2.100:16443 \\ --bind-address=127.0.0.1 \\ --allocate-node-cidrs=true \\ --cluster-cidr=10.244.0.0/16 \\ --service-cluster-ip-range=10.0.0.0/24 \\ --cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \\ --cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem \\ --root-ca-file=/opt/kubernetes/ssl/ca.pem \\ --service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \\ --cluster-signing-duration=87600h0m0s" EOF
配置参数说明
参数 说明 --logtostderr是否将日志输出到标准错误(false 表示写到文件,需要配合 --log-dir 使用)。 --v日志详细级别,数字越大输出越详细(常用 2 作为调试级别)。 --log-dir指定日志文件存放目录。 --leader-elect是否启用领导者选举机制,用于多个 Controller Manager 实例之间选主。 --master指定 API Server 的地址,K8s 高可用集群中需要使用 Keepalived 的虚拟 IP(VIP)来替代。 --bind-addressController Manager 进程绑定监听的地址(127.0.0.1 表示仅监听本地)。 --allocate-node-cidrs是否为每个节点自动分配 Pod CIDR 地址段(通常与 --cluster-cidr 搭配使用)。 --cluster-cidrPod 网络的 CIDR 范围,分配给各节点的 Pod 网段从这里划分(示例:10.244.0.0/16)。 --service-cluster-ip-rangeService 虚拟 IP 地址段,用于分配 Cluster IP 类型的 Service。 --cluster-signing-cert-file集群签发证书时使用的 CA 证书路径(用于 kubelet、组件证书等)。 --cluster-signing-key-file集群签发证书时使用的 CA 私钥路径。 --root-ca-file提供给 Service Account 及其他客户端的根 CA 证书文件路径。 --service-account-private-key-file用于为 Service Account Token 签名的私钥文件路径。 --cluster-signing-duration集群签发证书的有效期(示例:87600h0m0s 表示 10 年)。
配置 Systemd 管理 Controller Manager 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # cat > /usr/lib/systemd/system/kube-controller-manager.service << EOF[Unit] Description=Kubernetes Controller Manager Documentation=https://github.com/kubernetes/kubernetes After=network-online.target [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-controller-manager.conf ExecStart=/opt/kubernetes/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS Restart=on-failure [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 Controller Manager 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # systemctl daemon-reload# systemctl enable kube-controller-manager# systemctl start kube-controller-manager# systemctl status kube-controller-manager# journalctl -u kube-controller-manager.service
部署 Scheduler 创建 Scheduler 配置文件,使用转义符 \\ 是为了使 EOF 保留换行符。其中 --master 配置参数需要指定为 Keepalived 的虚拟 IP 地址(比如 192.168.2.100)+ HaProxy 反向代理的端口(比如 16443),而不是直接使用 API Server 的 IP 地址。请记得自行更改以下的 --master 配置参数,切忌直接拷贝使用,其他配置参数通常不需要更改
1 2 3 4 5 6 7 8 9 # cat > /opt/kubernetes/cfg/kube-scheduler.conf << EOFKUBE_SCHEDULER_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --leader-elect \\ --master=192.168.2.100:16443 \\ --bind-address=127.0.0.1" EOF
配置参数说明
参数 说明 --logtostderr是否将日志输出到标准错误(false 表示写到文件,需要配合 --log-dir 使用)。 --v日志详细级别,数字越大输出越详细(2 常用于调试)。 --log-dir指定日志文件存放目录。 --leader-elect启用领导者选举机制,用于在多个 Scheduler 实例之间选主(保证同一时刻只有一个活跃调度器)。 --master指定 API Server 的地址,K8s 高可用集群中需要使用 Keepalived 的虚拟 IP(VIP)来替代。 --bind-addressScheduler 进程绑定监听的地址(127.0.0.1 表示仅监听本地)。
配置 Systemd 管理 Scheduler 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # cat > /usr/lib/systemd/system/kube-scheduler.service << EOF[Unit] Description=Kubernetes Scheduler Documentation=https://github.com/kubernetes/kubernetes After=network-online.target [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-scheduler.conf ExecStart=/opt/kubernetes/bin/kube-scheduler \$KUBE_SCHEDULER_OPTS Restart=on-failure [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 Scheduler 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # systemctl daemon-reload# systemctl enable kube-scheduler# systemctl start kube-scheduler# systemctl status kube-scheduler# journalctl -u kube-scheduler.service
在 Master 节点中,当 API Server、Controller Manager、Scheduler 组件都成功启动后,可以通过 kubectl 工具查看当前 Kubernetes 集群组件的运行状态
1 2 3 4 5 6 7 8 # kubectl get csNAME STATUS MESSAGE ERROR scheduler Healthy ok controller-manager Healthy ok etcd-0 Healthy {"health" :"true" } etcd-1 Healthy {"health" :"true" } etcd-2 Healthy {"health" :"true" }
提示
这个 kubectl get cs 命令只会显示部分组件的运行状态(主要是 Etcd、Controller Manager、Scheduler),不包括 API Server;因为 API Server 是 Kubernetes 的核心服务,一般需要通过其它方式进行监控。
部署 Master 与 Node 节点 特别注意
在 Kubernetes 集群的所有节点(包括 Master 和 Node 节点)上分别执行以下操作,除了特别说明之外(比如,只在 Master 节点批准 Kubelet 的证书签名请求)。简而言之,Master 和 Node 节点都需要安装 Kubelet、Kube-Proxy、CNI 网络插件。 如果 Master 节点仅用于运行控制平面组件(如 API Server、Controller Manager 和 Scheduler),不承载用户 Pod 的调度和运行任务,则 Master 节点可以不部署 Kubelet 和 Kube-Proxy,但还是必须安装 CNI 网络插件。。 下载二进制文件 下载地址
Kubernetes 相关的二进制包可以从 GitHub 下载得到,这里下载的版本是 v1.19.10。 当打开 GitHub 链接后,会发现里面有很多二进制包,只需要下载一个 Server 二进制包就够了,里面包含了 Master 节点相关的二进制包。 由于上面单独部署 Maser 节点时,已经下载并解压过 Kubernetes 的二进制包文件,因此这里只需要在所有 Node 节点上下载二进制包文件即可
1 2 3 4 5 # wget https://dl.k8s.io/v1.19.10/kubernetes-server-linux-amd64.tar.gz# tar zxvf kubernetes-server-linux-amd64.tar.gz
在所有节点(包括 Master 和 Node 节点)上,分别执行以下命令拷贝对应的可执行文件
1 2 3 4 5 6 7 8 9 10 11 # cd kubernetes/server/bin# mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs}# cp kubelet kube-proxy /opt/kubernetes/bin# cp -n kubectl /usr/bin/
部署 Kubelet 创建 Kubelet 的配置文件,使用转义符 \\ 是为了使 EOF 保留换行符,请自行更改 --hostname-override 的值为当前节点的主机名(比如 k8s-node1),切忌直接拷贝以下配置内容
1 2 3 4 5 6 7 8 9 10 11 12 13 # cat > /opt/kubernetes/cfg/kubelet.conf << EOFKUBELET_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --hostname-override=<当前节点的主机名> \\ --network-plugin=cni \\ --kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \\ --bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \\ --config=/opt/kubernetes/cfg/kubelet-config.yml \\ --cert-dir=/opt/kubernetes/ssl \\ --pod-infra-container-image=lizhenliang/pause-amd64:3.0" EOF
配置参数说明
参数 说明 --logtostderr是否将日志输出到标准错误(false 表示写入日志目录) --v日志详细级别,值越大日志越详细(2 为常用调试级别) --log-dir日志存放目录 --hostname-override覆盖节点的主机名,在集群中显示为指定名称,通常填写当前节点的主机名 --network-plugin使用的网络插件类型(比如 CNI) --kubeconfigkubelet 连接 API Server 的 kubeconfig 配置文件路径 --bootstrap-kubeconfigkubelet 首次启动时用于申请证书的引导配置文件路径 --configkubelet 主配置文件路径 --cert-dir存放 kubelet TLS 证书的目录 --pod-infra-container-imagePod 基础容器(Pause 容器)镜像,用于提供网络命名空间
创建 Kubelet 的配置参数文件,一般情况下,以下配置内容可以直接拷贝使用
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 # cat > /opt/kubernetes/cfg/kubelet-config.yml << EOFkind: KubeletConfiguration apiVersion: kubelet.config.k8s.io/v1beta1 address: 0.0.0.0 port: 10250 readOnlyPort: 10255 cgroupDriver: cgroupfs clusterDNS: - 10.0.0.2 clusterDomain: cluster.local failSwapOn: false authentication: anonymous: enabled: false webhook: cacheTTL: 2m0s enabled: true x509: clientCAFile: /opt/kubernetes/ssl/ca.pem authorization: mode: Webhook webhook: cacheAuthorizedTTL: 5m0s cacheUnauthorizedTTL: 30s evictionHard: imagefs.available: 15% memory.available: 100Mi nodefs.available: 10% nodefs.inodesFree: 5% maxOpenFiles: 1000000 maxPods: 110 EOF
生成 bootstrap.kubeconfig 配置文件,请自行修改这里环境变量中的 API Server 地址,且指定为 Keepalived 的虚拟 IP 地址(比如 192.168.2.100) + HaProxy 反向代理的端口(比如 16443),切忌直接拷贝环境变量的值
1 2 3 KUBE_APISERVER="https://192.168.2.100:16443" TOKEN="c47ffb939f5ca36231d9e3121a252940"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # kubectl config set -cluster kubernetes \--certificate -authority=/opt/kubernetes/ssl/ca.pem \--embed -certs=true \--server =${KUBE_APISERVER} \--kubeconfig =bootstrap.kubeconfig# kubectl config set -credentials "kubelet-bootstrap" \--token =${TOKEN} \--kubeconfig =bootstrap.kubeconfig# kubectl config set -context default \--cluster =kubernetes \--user ="kubelet-bootstrap" \--kubeconfig =bootstrap.kubeconfig# kubectl config use-context default --kubeconfig =bootstrap.kubeconfig
1 2 # mv bootstrap.kubeconfig /opt/kubernetes/cfg
配置 Systemd 管理 Kubelet 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # cat > /usr/lib/systemd/system/kubelet.service << EOF[Unit] Description=Kubernetes Kubelet Documentation=https://github.com/kubernetes/kubernetes After=network-online.target After=docker.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kubelet.conf ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS Restart=on-failure LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 Kubelet 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # systemctl daemon-reload# systemctl enable kubelet# systemctl start kubelet# systemctl status kubelet# journalctl -u kubelet.service
当 Kubelet 服务正常启动后,会自动向 API Server 发起 CSR(证书签名请求)。一旦该请求被批准,Kubelet 就会获得客户端证书,并开始与 API Server 通信,从而将它所在的 Node 节点加入到 Kubernetes 集群中,所以无需使用 kubeadm join 命令手动加入节点。
特别注意
由于还没有安装 CNI 网络插件,因此在查看 Kubelet 服务的运行状态时,可能会看到 Container runtime network not ready ...... network plugin is not ready: cni config uninitialized 这样的网络错误信息,可以暂时忽略掉。
在任意一个 Master 节点上执行以下命令 ,批准 Kubelet 证书签名请求并允许 Node 节点加入 Kubernetes 集群
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # kubectl get csrNAME AGE SIGNERNAME REQUESTOR CONDITION node-csr-B91KCv7T_4A4RCfvfEBfFOP3V5zCYKK5NQkVLDmyly4 86s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending node-csr-F4zdBAJxxKnDLsWJbYHVi_XKF8Uwh1MMarXNJiHAUy0 85s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending node-csr-GYbsPLYySA0LiUkuhbpXlApj-JC-TDnA7SVRPHWU2mI 88s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending node-csr-bLUS_w2or-Rq9c5JgCzaf5e6QYHND6PTEqQFTvDsY5g 96s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending node-csr-ndBBynU94khVf6ADv4pdmtrlsxDOXAnyxu18e_VnhBU 87s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending # kubectl certificate approve node-csr-B91KCv7T_4A4RCfvfEBfFOP3V5zCYKK5NQkVLDmyly4# kubectl certificate approve node-csr-F4zdBAJxxKnDLsWJbYHVi_XKF8Uwh1MMarXNJiHAUy0# kubectl certificate approve node-csr-GYbsPLYySA0LiUkuhbpXlApj-JC-TDnA7SVRPHWU2mI# kubectl certificate approve node-csr-bLUS_w2or-Rq9c5JgCzaf5e6QYHND6PTEqQFTvDsY5g# kubectl certificate approve node-csr-ndBBynU94khVf6ADv4pdmtrlsxDOXAnyxu18e_VnhBU# kubectl get nodeNAME STATUS ROLES AGE VERSION k8s-master1 NotReady <none> 20h v1.19.10 k8s-node1 NotReady <none> 4m4s v1.19.10 k8s-node2 NotReady <none> 3m46s v1.19.10 k8s-node3 NotReady <none> 3m53s v1.19.10
部署 Kube-Proxy 创建 Kube-Proxy 的配置文件,使用转义符 \\ 是为了使 EOF 保留换行符,以下配置内容可以直接拷贝使用
1 2 3 4 5 6 # cat > /opt/kubernetes/cfg/kube-proxy.conf << EOFKUBE_PROXY_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --config=/opt/kubernetes/cfg/kube-proxy-config.yml" EOF
配置参数说明
参数 说明 --logtostderr=false日志输出到文件而不是标准错误输出(stderr) --v=2设置日志详细级别为 2(数值越大日志越详细) --log-dir=/opt/kubernetes/logs指定日志文件存放目录 --config=/opt/kubernetes/cfg/kube-proxy-config.yml指定 kube-proxy 的配置参数文件路径
创建 Kube-Proxy 的配置参数文件,请自行更改 hostnameOverride 的值为当前节点的主机名(比如 k8s-node1),其中的 clusterCIDR 要与 kube-apiserver.conf 配置文件中的 --service-cluster-ip-range 一致,切忌直接拷贝以下配置内容
1 2 3 4 5 6 7 8 9 10 11 # cat > /opt/kubernetes/cfg/kube-proxy-config.yml << EOFkind: KubeProxyConfiguration apiVersion: kubeproxy.config.k8s.io/v1alpha1 bindAddress: 0.0.0.0 metricsBindAddress: 0.0.0.0:10249 clientConnection: kubeconfig: /opt/kubernetes/cfg/kube-proxy.kubeconfig hostnameOverride: <当前节点的主机名> clusterCIDR: 10.0.0.0/24 EOF
生成 kube-proxy.kubeconfig 文件,请自行修改这里环境变量中的 API Server 地址,切忌直接拷贝环境变量的值
1 2 KUBE_APISERVER="https://192.168.2.100:16443"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # kubectl config set -cluster kubernetes \--certificate -authority=/opt/kubernetes/ssl/ca.pem \--embed -certs=true \--server =${KUBE_APISERVER} \--kubeconfig =kube-proxy.kubeconfig# kubectl config set -credentials kube-proxy \--client -certificate=/opt/kubernetes/ssl/kube-proxy.pem \--client -key=/opt/kubernetes/ssl/kube-proxy-key.pem \--embed -certs=true \--kubeconfig =kube-proxy.kubeconfig# kubectl config set -context default \--cluster =kubernetes \--user =kube-proxy \--kubeconfig =kube-proxy.kubeconfig# kubectl config use-context default --kubeconfig =kube-proxy.kubeconfig
1 2 # mv kube-proxy.kubeconfig /opt/kubernetes/cfg
配置 Systemd 管理 Kube-Proxy 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # cat > /usr/lib/systemd/system/kube-proxy.service << EOF[Unit] Description=Kubernetes Proxy Documentation=https://github.com/kubernetes/kubernetes After=network-online.target After=docker.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-proxy.conf ExecStart=/opt/kubernetes/bin/kube-proxy \$KUBE_PROXY_OPTS Restart=on-failure LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 Kube-Proxy 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # systemctl daemon-reload# systemctl enable kube-proxy# systemctl start kube-proxy# systemctl status kube-proxy# journalctl -u kube-proxy.service
部署 CNI 网络插件 所有节点的操作 特别注意
在 Kubernetes 集群的所有节点(包括 Master 和 Node 节点)上面,分别下载并解压 CNI 网络插件的二进制包 。
1 2 3 4 5 6 7 8 9 # wget https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-amd64-v0.8.6.tgz# mkdir -p /opt/cni/bin# mkdir -p /etc/cni/net.d/# tar zxvf cni-plugins-linux-amd64-v0.8.6.tgz -C /opt/cni/bin
Master 节点的操作 特别注意
在 Master 节点上面部署 CNI 网络插件(Flannel),不需要在 Node 节点重复执行该操作。换言之,以下操作只在 Master 节点上执行 。
下载 CNI 网络插件(Flannel)的配置文件
1 2 3 4 5 # wget -P /opt/kubernetes/cfg/ https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml# kubectl apply -f /opt/kubernetes/cfg/kube-flannel.yml
查看 Kubernetes 集群中所有节点的状态
1 2 3 4 5 6 7 # kubectl get nodeNAME STATUS ROLES AGE VERSION k8s-master1 Ready <none> 23h v1.19.10 k8s-node1 Ready <none> 173m v1.19.10 k8s-node2 Ready <none> 172m v1.19.10 k8s-node3 Ready <none> 172m v1.19.10
CNI 网络插件的安装细节
这个 kubectl apply 命令会创建一个 DaemonSet 类型的资源,而 DaemonSet 的特性是:会在 Kubernetes 集群中所有「可调度」的 Node 节点上运行一个副本。换言之,在 Master 节点上,通过 kubectl apply 命令安装 CNI 网络插件后,默认会将 CNI 网络插件部署到整个 Kubernetes 集群的所有节点(包括 Master 和 Node),无需手动在每个节点上重复执行 kubectl apply 命令来安装 CNI 网络插件。
为什么必须安装 CNI 网络插件
Kubernetes 本身不包含网络实现,但它要求集群中的所有 Pod 能够彼此通信(无论位于哪个节点),这是 Kubernetes 网络模型的基本要求。 使用 kubeadm init 初始化 Master 节点后,默认只有控制平面功能,没有网络功能。 当没有安装 CNI 网络插件时,Pod 会卡在 ContainerCreating 状态,因为找不到网络(CNI)配置。 当安装 CNI 网络插件(如 Flannel)后,多个节点之间的 Pod 才能通信和正常运行。 创建授权 API Server 访问 Kubelet 的配置文件
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 # cat > /opt/kubernetes/cfg/apiserver-to-kubelet-rbac.yaml << EOFapiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: annotations: rbac.authorization.kubernetes.io/autoupdate: "true" labels: kubernetes.io/bootstrapping: rbac-defaults name: system:kube-apiserver-to-kubelet rules: - apiGroups: - "" resources: - nodes/proxy - nodes/stats - nodes/log - nodes/spec - nodes/metrics - pods/log verbs: - "*" --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: system:kube-apiserver namespace: "" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:kube-apiserver-to-kubelet subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: kubernetes EOF
授权 API Server 访问 Kubelet
1 2 # kubectl apply -f /opt/kubernetes/cfg/apiserver-to-kubelet-rbac.yaml
参考资料