kubadm 高可用

前言

使用kubeadm搭建一套kubernetes集群很简单,但是一个单节点的master并不能满足我们在生产环境的需求。因此必须得解决单点的问题后才能考虑在生产环境大面积推广应用。

实践目标

  1. 至少2个以上的master节点可以同时管理集群;
  2. 集群的所有功能正常可用;
  3. 支持后续扩展;

环境准备

参考master单节点安装,此处不再具体描述。
系统参数 修改系统内核参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
setenforce 0 
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
systemctl stop firewalld && systemctl disable firewalld
swapoff -a
sed -i 's/.*swap.*/#&/' /etc/fstab

cat >> /etc/sysctl.conf <<eof
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness=0
eof

sysctl -p
lsmod | grep ip

docker相关 安装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
26
27
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
EOF

yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache fast
yum -y install docker-ce
systemctl enable docker
systemctl start docker

docker --version

安装相关组件 安装kubeadm必须组件

1
2
3
4
5
# 安装其他组件
yum install -y kubelet kubeadm kubectl

# 也可以指定版本
yum install -y kubelet-1.23.4 kubeadm-1.23.4 kubectl-1.23.4 --disableexcludes=kubernetes

配置NGINX代理

kube-apiserver高可用可以直接起多个节点,很多使用keepalive,haproxy来进行高可用配置,但是在大部分的公有云上无法使用vip。公有云上可以使用公有云提供的LoadBalance,这里我们使用nginx来代替.
nginx stream 四层协议的转发、代理或者负载均衡.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
yum -y install nginx nginx-mod-stream

stream {
log_format main '$remote_addr [$time_local]'
'$protocol $status $bytes_sent $bytes_received'
'$session_time';
server {
listen 16443;
proxy_pass kubeapi;
access_log /var/log/nginx/access.log main;
}
upstream kubeapi {
server 192.168.1.109:6443;
server 192.168.1.217:6443;
}
}

部署集群

获取默认配置文件 导出

1
kubeadm config print init-defaults > kubeadm-config.yaml

根据实际情况修改配置文件 注意修改

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
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: abcdef.0123456789abcdef # 自定义token
ttl: 24h0m0s
usages:
- signing
- authentication
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.1.109 # master节点ip
bindPort: 6443
nodeRegistration:
criSocket: /var/run/dockershim.sock
imagePullPolicy: IfNotPresent
name: node
taints: null
---
apiServer:
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controlPlaneEndpoint: 192.168.1.109:16443 # 集群apiserver地址和端口
controllerManager: {}
dns: {}
etcd:
local:
dataDir: /var/lib/etcd # 修改etcd数据目录,也可以修改为外部etcd
imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers # 仓库地址
kind: ClusterConfiguration
kubernetesVersion: 1.23.4 # 版本
networking:
dnsDomain: cluster.local
serviceSubnet: 10.96.0.0/12
podSubnet: 10.244.0.0/16
scheduler: {}
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: cgroupfs
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs

配置说明:

  • controlPlaneEndpoint:为集群apiserver监听端口16443
  • imageRepository:由于国内无法访问google镜像仓库k8s.gcr.io,这里指定为私有仓库地址 registry.cn-hangzhou.aliyuncs.com/google_containers
  • podSubnet:指定的IP地址段与后续部署的网络插件相匹配

执行初始化

1
2
kubeadm init --config=kubeadm-config.yaml --upload-certs | tee kubeadm-init.log

该命令指定了初始化时需要使用的配置文件,其中添加–upload-certs参数可以在后续执行加入节点时自动分发证书文件。
低版本注意使用–experimental-upload-certs。

c6e534cbec32dfef14cfc2bb8f027c9e.png

kubeadm init主要执行了以下操作:

  1. [init]:指定版本进行初始化操作
  2. [preflight] :初始化前的检查和下载所需要的Docker镜像文件
  3. [kubelet-start]:生成kubelet的配置文件”/var/lib/kubelet/config.yaml”,没有这个文件kubelet无法启动,所以初始化之前的kubelet实际上启动失败。
  4. [certificates]:生成Kubernetes使用的证书,存放在/etc/kubernetes/pki目录中。
  5. [kubeconfig] :生成 KubeConfig 文件,存放在/etc/kubernetes目录中,组件之间通信需要使用对应文件。
  6. [control-plane]:使用/etc/kubernetes/manifest目录下的YAML文件,安装 Master 组件。
  7. [etcd]:使用/etc/kubernetes/manifest/etcd.yaml安装Etcd服务。
  8. [wait-control-plane]:等待control-plan部署的Master组件启动。
  9. [apiclient]:检查Master组件服务状态。
  10. [uploadconfig]:更新配置
  11. [kubelet]:使用configMap配置kubelet。
  12. [patchnode]:更新CNI信息到Node上,通过注释的方式记录。
  13. [mark-control-plane]:为当前节点打标签,打了角色Master,和不可调度标签,这样默认就不会使用Master节点来运行Pod。
  14. [bootstrap-token]:生成token记录下来,后边使用kubeadm join往集群中添加节点时会用到
  15. [addons]:安装附加组件CoreDNS和kube-proxy

无论是初始化失败或者集群已经完全搭建成功,你都可以直接执行kubeadm reset命令清理集群或节点,然后重新执行kubeadm init或kubeadm join相关操作即可。
注意清理.kube目录

配置网络插件 calico

1
2
kubectl apply -f https://docs.projectcalico.org/v3.21/manifests/calico.yaml

其他节点加入集群

master join

1
2
3
4
kubeadm join 192.168.1.109:16443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:b9852b8ec72c5f26988763325e9da155de6136a3c3a62f48749bb4b6e09c37c0 \
--control-plane

执行后会自动生成相关文件和目录

1
2
3
4
5
6
7
8
[root@k8s-node-217 kubernetes]# ll
total 28
-rw------- 1 root root 5638 Feb 22 17:07 admin.conf
-rw------- 1 root root 5673 Feb 22 17:07 controller-manager.conf
-rw------- 1 root root 1955 Feb 22 17:07 kubelet.conf
drwx------ 2 root root 96 Feb 22 17:07 manifests
drwxr-xr-x 2 root root 288 Feb 22 17:07 pki
-rw------- 1 root root 5621 Feb 22 17:07 scheduler.conf

如果在某些特殊情况下需要手动上传的话,注意传输以下文件即可:
admin.conf
pki/ca.*
pki/front-proxy*
pki/sa*

worker 新node

1
2
kubeadm join 192.168.1.109:16443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:b9852b8ec72c5f26988763325e9da155de6136a3c3a62f48749bb4b6e09c37c0

无论在master节点或node节点,要能够执行kubectl命令必须进行以下配置

1
2
3
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

检查集群
两台master节点展示信息一致。

1
2
3
4
5
6
7
8
9
10
11
12
[root@uat-109 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-node-217 Ready control-plane,master 8m2s v1.23.4
master Ready control-plane,master 30h v1.23.4
test-216-tips Ready <none> 27h v1.23.4


[root@k8s-node-217 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-node-217 Ready control-plane,master 8m9s v1.23.4
master Ready control-plane,master 30h v1.23.4
test-216-tips Ready <none> 27h v1.23.4

其他

删除节点
在master节点上执行:
kubectl drain node-xxx --delete-local-data --force --ignore-daemonsets

当节点变成不可调度状态时候 SchedulingDisabled,执行
kubectl delete node node-xxx

ipvs

参考:https://www.jianshu.com/p/89f126b241db

IPVS简介:

尽管 Kubernetes 在版本v1.6中已经支持5000个节点,但使用 iptables 的 kube-proxy 实
际上是将集群扩展到5000个节点的瓶颈。 在5000节点集群中使用 NodePort 服务,如
果有2000个服务并且每个服务有10个 pod,这将在每个工作节点上至少产生20000个
iptable 记录,这可能使内核非常繁忙。

ipvs (IP Virtual Server) 实现了传输层负载均衡,也就是我们常说的4层LAN交换,作为
Linux 内核的一部分。ipvs运行在主机上,在真实服务器集群前充当负载均衡器。ipvs
可以将基于TCP和UDP的服务请求转发到真实服务器上,并使真实服务器的服务在单个
IP 地址上显示为虚拟服务。

我们知道kube-proxy支持 iptables 和 ipvs 两种模式, 在kubernetes v1.8 中引入了 ipvs
模式,在 v1.9 中处于 beta 阶段,在 v1.11 中已经正式可用了。iptables 模式在 v1.1 中
就添加支持了,从 v1.2版本开始 iptables 就是 kube-proxy 默认的操作模式,ipvs 和
iptables 都是基于netfilter的。ipvs 会使用 iptables 进行包过滤、SNAT、masquared。
具体来说,ipvs 将使用ipset来存储需要DROP或masquared的流量的源或目标地址,
以确保 iptables 规则的数量是恒定的,这样我们就不需要关心我们有多少服务了。

启动ipvs的要求:

k8s版本 >= v1.11
使用ipvs需要安装相应的工具来处理”yum install ipset ipvsadm -y“
确保 ipvs已经加载内核模块, ip_vs、ip_vs_rr、ip_vs_wrr、ip_vs_sh、
nf_conntrack_ipv4。如果这些内核模块不加载,当kube-proxy启动后,会退回到iptables模式。

先前基于iptables规则表的DNAT->SNAT方式来处理外部客户端到k8s集群pod内的流量
和集群内部流量(cluster-ip到pod ip),无需在宿主机上管理cluster-ip都由iptables来进行
管理。

使用IPVS后是需要对vs(虚拟服务也就是vip)进行管理,由于IPVS的DNAT钩子挂在
INPUT链上,因此必须要让内核识别 VIP(cluster-ip) 是本机的 IP。k8s 通过设置将
service cluster ip 绑定到虚拟网卡kube-ipvs0,其中下面的10.96.x.x都是VIP,也就
是cluster-ip。

9b5302edcad44e440bbb8dbb5ccb128e.png

kube-proxy 开启 ipvs

1
yum install ipset ipvsadm -y

修改ConfigMap的kube-system/kube-proxy中的config.conf,mode: “ipvs”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@master k8s]# kubectl edit cm kube-proxy -n kube-system
...
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
strictARP: false
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: "ipvs"
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
resourceContainer: /kube-proxy
...

configmap/kube-proxy edited

对于Kubernetes来说,可以直接将这三个Pod删除之后,会自动重建。

1
2
3
4
[root@master k8s]# kubectl get pods -n kube-system|grep proxy
kube-proxy-fn68r 1/1 Running 0 75s
kube-proxy-hrjls 1/1 Running 0 78s
kube-proxy-ltbqk 1/1 Running 0 77s

批量删除 kube-proxy

1
kubectl get pod -n kube-system | grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}'

由于你已经通过ConfigMap修改了kube-proxy的配置,所以后期增加的Node节点,会直接使用ipvs模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@master ~]#  kubectl logs kube-proxy-hrjls -n kube-system
I0222 09:25:27.699369 1 node.go:163] Successfully retrieved node IP: 192.168.1.109
I0222 09:25:27.699464 1 server_others.go:138] "Detected node IP" address="192.168.1.109"
I0222 09:25:27.774905 1 server_others.go:269] "Using ipvs Proxier"
I0222 09:25:27.774932 1 server_others.go:271] "Creating dualStackProxier for ipvs"
I0222 09:25:27.775016 1 server_others.go:491] "Detect-local-mode set to ClusterCIDR, but no IPv6 cluster CIDR defined, , defaulting to no-op detect-local for IPv6"
E0222 09:25:27.775284 1 proxier.go:379] "Can't set sysctl, kernel version doesn't satisfy minimum version requirements" sysctl="net/ipv4/vs/conn_reuse_mode" minimumKernelVersion="4.1"
I0222 09:25:27.775454 1 proxier.go:438] "IPVS scheduler not specified, use rr by default"
E0222 09:25:27.775637 1 proxier.go:379] "Can't set sysctl, kernel version doesn't satisfy minimum version requirements" sysctl="net/ipv4/vs/conn_reuse_mode" minimumKernelVersion="4.1"
I0222 09:25:27.775756 1 proxier.go:438] "IPVS scheduler not specified, use rr by default"
I0222 09:25:27.775850 1 ipset.go:113] "Ipset name truncated" ipSetName="KUBE-6-LOAD-BALANCER-SOURCE-CIDR" truncatedName="KUBE-6-LOAD-BALANCER-SOURCE-CID"
I0222 09:25:27.775890 1 ipset.go:113] "Ipset name truncated" ipSetName="KUBE-6-NODE-PORT-LOCAL-SCTP-HASH" truncatedName="KUBE-6-NODE-PORT-LOCAL-SCTP-HAS"
I0222 09:25:27.776321 1 server.go:656] "Version info" version="v1.23.4"
I0222 09:25:27.783258 1 conntrack.go:52] "Setting nf_conntrack_max" nf_conntrack_max=131072
I0222 09:25:27.783815 1 config.go:317] "Starting service config controller"
I0222 09:25:27.783883 1 shared_informer.go:240] Waiting for caches to sync for service config
I0222 09:25:27.783942 1 config.go:226] "Starting endpoint slice config controller"
I0222 09:25:27.783963 1 shared_informer.go:240] Waiting for caches to sync for endpoint slice config
I0222 09:25:27.884501 1 shared_informer.go:247] Caches are synced for endpoint slice config
I0222 09:25:27.884570 1 shared_informer.go:247] Caches are synced for service config

日志中打印出了Using ipvs Proxier,说明ipvs模式已经开启。

使用ipvsadm测试,可以查看之前创建的Service已经使用LVS创建了集群。

1
2
3
4
5
6
7
8
9
10
[root@master ~]# ipvsadm
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP master:https rr
-> master:sun-sr-https Masq 1 0 13
-> master1:sun-sr-https Masq 1 0 14
TCP master:domain rr
TCP master:9153 rr
UDP master:domain rr