使用kubeadm搭建一个高可用k8s集群

1,852 阅读6分钟

65578040_p0_master1200.jpg 这是一篇关于如何使用kubeadm工具搭建Kubernetes集群的文章。 kubeadm是由kubernetes官方社区维护的一款kubernetes集群部署工具,kubeadm会帮我们部署kubernetes里的一些关键服务,以及一些必要的证书,Kubeadm还会通过运行一系列预检查来确保服务器具有运行Kubernetes所需的所有必要组件和配置,从而使整个部署过程变得简单。

集群整体架构

这里采用kubernetes官网的图片进行说明:

捕获.PNG 如图,每个控制面节点都运行一份API服务器(kube-apiserver)、调度器(kube-scheduler)和控制器管理器(kube-controller-manager)实例。API服务器通过负载均衡器暴露给工作节点。etcd分布式数据库分别运行一个实例在控制面节点上,而且每个控制面节点的API服务器只会与本机的etcd实例进行通信。

集群整体硬件配置

这次的部署是在虚拟机上进行的,总共使用了7台虚拟机,都是Ubuntu 22.04.2 LTS系统,详细如下:

主机名IPCPUMem作用
haproxy1192.168.1.92核2G负载均衡器
haproxy2192.168.1.102核2G负载均衡器
control1192.168.1.42核2G控制面节点
control2192.168.1.52核2G控制面节点
contorl3192.168.1.62核2G控制面节点
work1192.168.1.72核2G工作节点
work2192.168.1.82核2G工作节点

控制面节点就是我们所说的主节点,只不过kubernetes官网称为控制面节点。

如上表所示,由于是在我的个人电脑上部署,所以每个虚拟机分配的资源并不多,不过已经足够跑起来了。

我们还要确保每个节点上主机名、MAC地址和product_uuid的唯一性。主机名可以通过hostname命令查看,然后通过hostnamectl set-hostname 新主机名设置;MAC地址可以通过ip link查看;product_uuid可以通过cat /sys/class/dmi/id/product_uuid查看。

控制面节点和工作节点还需禁用交换分区:

swapoff -a
(crontab -l 2>/dev/null; echo "@reboot /sbin/swapoff -a") | crontab - || true

必要端口开放设置

请确保在防火墙配置中允许这些端口:

控制面节点:

捕获.PNG

工作节点:

捕获.PNG

工作节点里的端口范围30000-32767主要用于NodePort型的网络服务。

ubuntu系统中防火墙相关命令有:ufw allow 端口号ufw enableufw status

配置Kubernetes运行所需的内核模块和网络参数

在每个节点进行如下配置。

加载并启用Linux文件系统层叠功能模块和桥接设备的网络过滤模块:

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

modprobe overlay
modprobe br_netfilter

配置Linux网络内核参数:

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1 #这个参数设置 Linux 桥接设备在进行网络地址转换 (NAT) 时调用 iptables 进行数据包处理。
net.bridge.bridge-nf-call-ip6tables = 1 #类似于上一个参数,但针对 IPv6 数据包。
net.ipv4.ip_forward                 = 1 #这个参数启用 Linux 内核的 IP 转发功能,允许系统将接收到的数据包从一个网络接口路由到另一个网络接口.
EOF

#重新加载sysctl配置,以应用新的参数设置。--system 选项表示从/etc/sysctl.conf和/etc/sysctl.d/目录中的配置文件加载参数,并将这些参数应用到系统。
sysctl --system

安装负载均衡器

负载均衡器是实现集群高可用的关键,用于代理三台控制面节点的6443端口,也就是API服务器(kube-apiserver)。这里我们将使用两个节点(haproxy1和haproxy2)来部署负载均衡器,其中一个作为主负载均衡器,另一个作为备用。采用的技术架构是keepalived 和 HAProxy

Keepalived简单来讲就是一个主备切换工具,备用节点会不断的对主节点进行探活,当主节点故障时,备用节点会替代主节点进行工作,下文会大概介绍下它的工作原理。

HAProxy则是一款负载均衡器和代理服务器软件,这里不做过多介绍。

下面开始负载均衡器的具体搭建:

在节点haproxy1编写配置文件/etc/keepalived/keepalived.conf
! /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
    router_id LVS_DEVEL
}
vrrp_script check_apiserver {
  script "/etc/keepalived/check_apiserver.sh"
  interval 3
  weight -2
  fall 10
  rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface ens33
    virtual_router_id 51
    priority 101
    authentication {
        auth_type PASS
        auth_pass 42
    }
    virtual_ipaddress {
        192.168.1.120
    }
    track_script {
        check_apiserver
    }
}

上述文件里的一些关键配置:

  • vrrp_script: 配置的是Keepalived的探活脚本,后面将会提到;
  • state:只有MASTERBACKUP两种值,用于区分主备,节点haproxy1是主,所以这里写MASTER;
  • interface:网卡名;
  • virtaul_router_id:用于区分所属子网,在同一个kubernetes集群中所有Keepalived节点都配置相同的即可;
  • priority:优先级,主节点要高于备用节点才行;
  • auth_pass:用于Keepalived节点之间的认证,要配置成一样的;
  • virtual_ipaddress:这是一个关键配置,当所有Keepalived节点运行时,它们都会拥有一个相同的虚拟IP,而只有主Keepalived节点会通过ARP协议对外宣称拥有这个虚拟IP,而其它备用节点则会保持缄默。当备用节点发现主节点挂掉时,则会替代主节点对外宣称拥有这个虚拟IP,这就是Keepalived主备切换的大概原理;
在节点haproxy2编写配置文件/etc/keepalived/keepalived.conf
! /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
    router_id LVS_DEVEL
}
vrrp_script check_apiserver {
  script "/etc/keepalived/check_apiserver.sh"
  interval 3
  weight -2
  fall 10
  rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 100
    authentication {
        auth_type PASS
        auth_pass 42
    }
    virtual_ipaddress {
        192.168.1.120
    }
    track_script {
        check_apiserver
    }
}

配置和节点haproxy1的基本一样,主要是statepriority这两个配置不同。

在节点haproxy1和haproxy2编写探活脚本/etc/keepalived/check_apiserver.sh
#!/bin/sh
# /etc/keepalived/check_apiserver.sh
APISERVER_DEST_PORT=6443
APISERVER_VIP="192.168.1.120"
errorExit() {
    echo "*** $*" 1>&2
    exit 1
}
curl --silent --max-time 2 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/"
if ip addr | grep -q ${APISERVER_VIP}; then
    curl --silent --max-time 2 --insecure https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/"
fi
在节点haproxy1和haproxy2安装并运行Keepalived

运行以下命令进行安装:

apt-get update
apt-get install keepalived
systemctl enable keepalived --now

安装完成后,我们发现节点haproxy1和haproxy2都有了虚拟IP192.168.1.120

捕获.PNG

捕获.PNG

在节点haproxy1和haproxy2安装并运行HAProxy

运行以下命令进行安装:

apt-get update
apt-get install haproxy
systemctl enable haproxy --now
在节点haproxy1和haproxy2替换配置文件/etc/haproxy/haproxy.cfg,并重启HAProxy服务
# /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    log /dev/log local0
    log /dev/log local1 notice
    daemon

#---------------------------------------------------------------------
# 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 forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 1
    timeout http-request    10s
    timeout queue           20s
    timeout connect         5s
    timeout client          20s
    timeout server          20s
    timeout http-keep-alive 10s
    timeout check           10s

#---------------------------------------------------------------------
# apiserver frontend which proxys to the control plane nodes
#---------------------------------------------------------------------
frontend apiserver
    bind 0.0.0.0:6443
    mode tcp
    option tcplog
    default_backend apiserver

#---------------------------------------------------------------------
# round robin balancing for apiserver
#---------------------------------------------------------------------
backend apiserver
    mode tcp
    option tcp-check
    balance roundrobin
        server control1 192.168.1.4:6443 check
        server control2 192.168.1.5:6443 check
        server contorl3 192.168.1.6:6443 check

重启HAProxy:systemctl restart haproxy.service

通过配置文件我们可以看到HAProxy绑定了本地的6443端口,并且将请求转发到后面的三个控制面节点。

到这里我们两个节点的负载均衡器就搭建完成了,不过这时候你会看到HAProxy不停报后端不可达的错误,这是正常的,因为我们的控制面节点还没有搭建。

在控制面节点和工作节点安装容器运行时

我们采用containerd作为容器运行时。 执行以下命令来安装containerd:

apt update
apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install containerd.io

然后我们初始下containerd的默认配置:

containerd config default | tee /etc/containerd/config.toml

我们还需要把containerd的cgroup驱动设置为systemd,在配置文件/etc/containerd/config.toml里设置:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
  ...
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
    SystemdCgroup = true

我们还要给containerd配置一个国内的sanbox镜像文件,不然可能会出现拉取不了镜像的问题,在配置文件/etc/containerd/config.toml里设置:

[plugins."io.containerd.grpc.v1.cri"]
  ...
  sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9"

重启containerd:systemctl restart containerd.service

在控制面节点和工作节点安装kubeadm、kubectl、kubelet工具

apt-get update && apt-get install -y apt-transport-https
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - 
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list 
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF  
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

工作节点可以不安装kubectl。

在节点control1执行kubeadm init

kubeadm init --pod-network-cidr="10.51.32.0/24" --node-name "control1" --control-plane-endpoint "192.168.1.120:6443" --upload-certs --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers

其中,

--pod-network-cidr配置的是我们kubernetes内部的网络段;

--control-plane-endpoint配置的是我们虚拟IP;

--upload-certs表示要将kubeadm自动生成的证书上传至etcd,因为我们是多控制面节点,其它节点加入时可以从etcd获取相关证书信息;

--image-repository是用来配置国内镜像源,因为kubeadm初始化时,会自动安装API服务器(kube-apiserver)、调度器(kube-scheduler)、控制器管理器(kube-controller-manager)和etcd等这些必要组件, 而且这些组件都是以pod的形式运行,所以配置个国内镜像源,这些pod的yaml文件都会被kubeadm在初始化时放在/etc/kubernetes/manifests

kubeadm 会尝试连接一些已知的端点列表(也就是诸如这些东东:unix:///var/run/containerd/containerd.sock)来检测容器运行时。使用不同的容器运行时或在预配置的节点上安装了多个容器运行时,可以为 kubeadm init 指定 --cri-socket 参数; 除非另有说明,否则 kubeadm 使用与默认网关关联的网络接口来设置此控制平面节点 API server 的广播地址。 要使用其他网络接口,请为 kubeadm init 设置 --apiserver-advertise-address= 参数。 要部署使用 IPv6 地址的 Kubernetes 集群, 必须指定一个 IPv6 地址,例如 --apiserver-advertise-address=2001:db8::101。

kubeadm init命令运行成功后,我们将会看到这样的一个输出:


...
You can now join any number of control-plane node by running the following command on each as a root:
    kubeadm join 192.168.1.120:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07

Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use kubeadm init phase upload-certs to reload certs afterward.

Then you can join any number of worker nodes by running the following on each as root:
    kubeadm join 192.168.1.120:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866

这里有两种kubeadm join命令,一种用于控制面节点的加入,另一种用于工作节点的加入。

在其余控制面节点和工作节点分别执行对应的kubeadm join命令

都加入完成后,我们可以执行kubectl get nodes -o wide命令查看节点状态。

安装calico网络组件

安装calico组件前,还需让各节点开放179端口。 执行以下命令进行安装:

curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml -o calico.yaml
kubectl apply -f calico.yaml

执行apply后,你可能发现你的calico组件pod一直在重启,那可能是因为它不知道关联主机上的哪个网络接口,所以用这一步指定calico使用哪个网络接口,我的虚拟机网络接口都是ens33,所以我就写成ens*,各位可根据自身情况配置,比如eth* 等等:

kubectl set env daemonset/calico-node -n kube-system IP_AUTODETECTION_METHOD=interface=ens*

安装Metrics Server

安装Metrics Server组件前,还需让各节点开放4443端口。

# 记得修改yaml文件里使用的镜像为国内源,不然你可能拉取不到镜像,比如配置成:registry.aliyuncs.com/google_containers/metrics-server:v0.6.2
curl  https://raw.githubusercontent.com/techiescamp/kubeadm-scripts/main/manifests/metrics-server.yaml -o metrics.yaml
kubectl apply -f metrics.yaml

完成

这些步骤都正常执行完成后,通过kubectl get pods -n kube-system你应该就能够看到集群里的pod状态了:

捕获.PNG