本文主要介绍如何使用keepalived+haproxy创建高可用集群,文中主要来源与作者在工作中的一些实际操作,本文仅供作者自己记录和读者参考。
关键词:高可用K8s集群
本文搭建的集群使用3个master节点和4个worker节点,为了节约资源,本文使用嵌入的etcd集群。
集群环境准备
集群拓扑
堆叠ETCD
- 使用keepalived+haproxy的架构,worker节点通过访问vip和haproxy的后端服务器,实现了K8s集群的高可用;
- 为了避免堆叠集群出现耦合失败的风险,应该为HA集群运行至少三个堆叠的控制节点;
- 以上是kubeadm中的默认拓扑。当使用
kubeadm init
和kubeadm join --control-plane --upload-certs时,在控制节点平面上自动创建etcd成员。
主机规划
| 192.168.100.101 |
192.168.100.101-master |
2C4G |
master + worker |
kube-apiserver、kube-controller-manager、kube-scheduler、etcd、kubelet、kube-proxy、containerd |
|
| 192.168.100.102 |
192.168.100.102-master |
2C4G |
master + worker |
kube-apiserver、kube-controller-manager、kube-scheduler、etcd、kubelet、kube-proxy、containerd |
|
| 192.168.100.103 |
192.168.100.103-master |
2C4G |
master + worker |
kube-apiserver、kube-controller-manager、kube-scheduler、etcd、kubelet、kube-proxy、containerd |
|
| 192.168.100.105 |
192.168.100.105-worker |
2C4G |
worker+LB1 |
kubelet、kube-proxy、containerd、keepalived、haproxy |
192.168.100.104 |
| 192.168.100.106 |
192.168.100.106-worker |
2C4G |
worker+LB1 |
kubelet、kube-proxy、containerd、keepalived、haproxy |
192.168.100.104 |
| 192.168.100.107 |
192.168.100.107-worker |
2C4G |
worker |
kubelet、kube-proxy、containerd |
|
| 192.168.100.108 |
192.168.100.108-worker |
2C4G |
worker |
kubelet、kube-proxy、containerd |
|
软件版本
| CentOS8 |
kernel版本:4.18.0-477.27.1.el8_8.x86_64 |
|
| kubernetes |
v1.28.2 |
|
| etcd |
v3.5.9 |
最新版本 |
| flannel |
v0.22.3 |
网络插件 |
| containerd |
v1.7.6 |
容器运行时 |
| haproxy |
v1.8.27 |
|
| keepalived |
v2.1.5 |
|
网络分配
| Node网络 |
192.168.100.101/108 |
集群节点网络 |
| Service网络 |
10.96.0.0/16 |
实现服务发现时所使用的网络 |
| Pod网络 |
10.244.0.0/16 |
|
集群部署
为了方便集群部署,本文使用自动化运维工具ansible配置整个集群。
主要编辑两个文件,一是ansible的配置文件
1 2 3 4 5 6
| [defaults]
inventory = ./hosts
host_key_checking = False
|
二是ansible的主机文件
1 2 3 4 5 6 7 8 9 10
| [master] 192.168.100.101 ansible_ssh_user=root 192.168.100.102 ansible_ssh_user=root 192.168.100.103 ansible_ssh_user=root [worker] 192.168.100.105 ansible_ssh_user=root 192.168.100.106 ansible_ssh_user=root 192.168.100.107 ansible_ssh_user=root 192.168.100.108 ansible_ssh_user=root
|
运行时下载
refer:https://github.com/containerd/nerdctl,需要下载full的文件
1 2 3 4
| tar Cxzvvf /usr/local nerdctl-full-*-linux-amd64.tar.gz systemctl enable buildkit.service --now systemctl enable containerd.service --now systemctl enable stargz-snapshotter.service --now
|
主机准备
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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
| - name: Set hosts hosts: all tasks: - ansible.builtin.shell: | cat > /etc/hosts << EOF 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.100.101 192.168.100.101-master 192.168.100.102 192.168.100.102-master 192.168.100.103 192.168.100.103-master 192.168.100.105 192.168.100.105-worker 192.168.100.106 192.168.100.106-worker 192.168.100.107 192.168.100.107-worker 192.168.100.108 192.168.100.108-worker EOF - name: Set master hostname hosts: master tasks: - hostname: name={{ ansible_enp6s18['ipv4']['address'] }}-master - name: set worker hostname hosts: worker tasks: - hostname: name={{ ansible_enp6s18['ipv4']['address'] }}-worker - name: Set k8s env hosts: all remote_user: root tasks: - ansible.builtin.shell: | systemctl disable firewalld --now
sed -i 's/enforcing/disabled/' /etc/selinux/config setenforce 0
sed -ri '/^[^#]*swap/s@^@#@' /etc/fstab swapoff -a
yum install ipvsadm ipset sysstat conntrack libseccomp iproute-tc -y
cat > /etc/modules-load.d/k8s.conf << EOF br_netfilter ip_vs ip_vs_lc ip_vs_wlc ip_vs_rr ip_vs_wrr ip_vs_lblc ip_vs_lblcr ip_vs_dh ip_vs_sh ip_vs_fo ip_vs_nq ip_vs_sed ip_vs_ftp ip_vs_sh nf_conntrack ip_tables ip_set xt_set ipt_set ipt_rpfilter ipt_REJECT ipip EOF
systemctl enable --now systemd-modules-load.service
lsmod | grep -e ip_vs -e nf-conntrack
cat > /etc/sysctl.d/k8s.conf << EOF vm.swappiness = 0 net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 fs.may_detach_mounts = 1 vm.overcommit_memory = 1 vm.panic_on.oom = 0 fs.inotify.max_user_watches = 89100 fs.file-max = 52706963 fs.nr_open = 52706963 net.netfilter.nf_conntrack_max = 2310720 net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_probes = 3 net.ipv4.tcp_keepalive_intvl = 15 net.ipv4.tcp_max_tw_buckets = 36000 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_max_orphans = 327680 net.ipv4.tcp_orphan_retries = 3 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 16384 net.ipv4.ip_contrack_max = 65536 net.ipv4.tcp_timestamps = 0 net.core.somaxconn = 16384 EOF
cat >> /etc/security/limits.d/k8s.conf <<EOF * soft nofile 1048576 * hard nofile 1048576 * soft noproc 1048576 * hard noproc 1048576 * soft memlock unlimited * hard memlock unlimited EOF exit 0
- name: Copy file with owner and permissions ansible.builtin.copy: src: ./nerdctl-full-1.6.0-linux-amd64.tar.gz dest: /tmp/nerdctl-full-1.6.0-linux-amd64.tar.gz - name: Unzip nerdctl files ansible.builtin.unarchive: src: /tmp/nerdctl-full-1.6.0-linux-amd64.tar.gz dest: /usr/local/ - name: Restart Service ansible.builtin.shell: | systemctl enable buildkit.service --now systemctl enable containerd.service --now systemctl enable stargz-snapshotter.service --now mkdir -p /etc/containerd containerd config default | tee /etc/containerd/config.toml
- name: Init containerd Service ansible.builtin.shell: | systemctl enable buildkit.service --now systemctl enable containerd.service --now systemctl enable stargz-snapshotter.service --now mkdir -p /etc/containerd containerd config default | tee /etc/containerd/config.toml sed -ri "s|registry.k8s.io|pkgs.fly97.cn|g" /etc/containerd/config.toml
- name: Install kubeadm repo ansible.builtin.shell: | cat > /etc/yum.repos.d/kubernetes.repo << EOF [kubernetes] name=Kubernetes for EPEL baseurl=https://pkgs.fly97.cn/repository/kubernetes-yum/ enabled=1 gpgcheck=0 EOF dnf makecache -y dnf install kubelet kubeadm kubectl -y systemctl stop kubelet
|
负载均衡
在105和106机器上分别安装keepalived+HAproxy
1
| sudo dnf install keepalived haproxy -y
|
haproxy
以下是haproxy的配置文件
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
| cat > /etc/haproxy/haproxy.cfg << EOF global log 127.0.0.1 local2 pidfile /var/run/haproxy.pid maxconn 65535 daemon
defaults timeout connect 5000 timeout client 50000 timeout server 50000 timeout http-request 15s timeout http-keep-alive 15s retries 3 maxconn 65535
frontend kubernetes-apiserver mode tcp bind *:6443 tcp-request inspect-delay 5s default_backend kubernetes-apiserver backend kubernetes-apiserver mode tcp balance roundrobin server master01 192.168.100.101:6443 check inter 2000 rise 2 fall 2 server master02 192.168.100.102:6443 check inter 2000 rise 2 fall 2 server master03 192.168.100.103:6443 check inter 2000 rise 2 fall 2 EOF
|
keepalived
以下是106机器keepalived的配置文件
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 > /etc/keepalived/keepalived.conf << EOF global_defs { max_auto_priority } vrrp_script check_apiserver { script "/etc/keepalived/check_apiserver_vip.sh" interval 2 weight 5 } vrrp_instance VI_02 { # BACKUP模式 state BACKUP nopreempt interface enp6s18 # 每个集群的router_id要一致 virtual_router_id 100 priority 50 unicast_src_ip 192.168.100.106 unicast_peer{ 192.168.100.105 } # 虚IP地址 virtual_ipaddress { 192.168.100.104/24 } track_script { check_apiserver } } EOF
|
以下是105机器keepalived的配置文件
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
| cat > /etc/keepalived/keepalived.conf << EOF global_defs { max_auto_priority } vrrp_script check_apiserver { script "/etc/keepalived/check_apiserver_vip.sh" interval 2 weight 5 timeout 1 } vrrp_instance VI_01 { # MASTER模式 state MASTER nopreempt interface enp6s18 # 每个集群的router_id要一致 virtual_router_id 100 priority 100 unicast_src_ip 192.168.100.105 unicast_peer { 192.168.100.106 } # 虚IP地址 virtual_ipaddress { 192.168.100.104/24 } track_script { check_apiserver } } EOF
|
以下是检查api_server的脚本文件
1 2 3 4 5 6 7 8 9 10 11 12 13
|
killall -0 haproxy if [ $? == 0 ];then http_code=$(curl -k -s -o /dev/null -w "%{http_code}\n" https://127.0.0.1:6443) if [ $http_code == 403 ];then exit 0 else exit 1 fi else exit 1 fi
|
部署master和node
在101主机上执行
1
| kubeadm init --config init-k8s.yml --upload-certs
|
没有问题的话会安装成功
![]()
在102、103master主机上执行上面第一行的命令,在剩下的worker节点执行下面的命令
| 102、103等master节点 |
kubeadm join 192.168.100.104:6443 --token xxx
--discovery-token-ca-cert-hash xxx --control-plane --certificate-key
xxx |
| 其他worker节点 |
kubeadm join 192.168.100.104:6443 --token xxx
--discovery-token-ca-cert-hash xxx |
执行成功后,在任一master节点下执行kubectl get nodes,会显示下面的节点状态
![]()
此时节点状态仍然是NotReady,通过kubelet日志可以看到网络插件尚未安装。
![]()
部署网络插件
本文使用flannel作为K8s的网络插件:Flannel
是一种简单易用的方法来配置专为 Kubernetes 设计的第 3 层网络结构。
refer:https://github.com/flannel-io/flannel
资源清单可以参考:https://github.com/flannel-io/flannel/blob/master/Documentation/kube-flannel.yml
主要需要修改image镜像地址,镜像可以参考:https://sci.nju.edu.cn/9e/05/c30384a564741/page.htm
1
| kubectl apply -f kube-flannel.yml
|
等待kube-flannel中pod状态变为running即可
![]()
此时集群状态正常,安装完毕
![]()
高可用检查
安装etcdctl
refer:https://github.com/etcd-io/etcd/releases/tag/v3.5.9
添加环境变量实现自动别名操作
1 2 3
| export ETCDCTL_API=3 alias etcdctl='etcdctl --endpoints=https://192.168.100.101:2379,https://192.168.100.102:2379,https://192.168.100.103:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key
|
查看etcd endpoint状态
![]()
查看etcd endpoint健康
![]()
未完待续。。。