Kubernetes集群实践(一)使用Kubeadm创建一个K8s集群
本文主要介绍如何使用一个kubeadm创建一个K8s集群。
关键词:docker,k8s
准备工作
首先确保你至少有两台机器,这里准备了四台机器:一台master节点,三台node节点,主机名分别为node1、node2、node3。
master节点配置了管理node集群的脚本,分别是批量执行命令的xcall脚本和批量拷贝文件的xsync脚本。
安装xcall和xsync脚本的机器需要先配置免密码登录,并安装rsync服务。
xcall
1 |
|
xsync
1 |
|
设置主机公钥确认
SSH 公钥检查是一个重要的安全机制,可以防范中间人劫持等黑客攻击。但是在特定情况下,严格的 SSH 公钥检查会破坏一些依赖 SSH 协议的自动化任务,就需要一种手段能够绕过 SSH 的公钥检查。
编辑/etc/ssh/ssh_config文件,修改StrictHostKeyChecking=no.
StrictHostKeyChecking=no最不安全的级别,当然也没有那么多烦人的提示了,相对安全的内网测试时建议使用。如果连接server的key在本地不存在,那么就自动添加到文件中(默认是known_hosts),并且给出一个警告。StrictHostKeyChecking=ask默认的级别,就是出现刚才的提示了。如果连接和key不匹配,给出提示,并拒绝登录。StrictHostKeyChecking=yes最安全的级别,如果连接与key不匹配,就拒绝连接,不会提示详细信息。
将两个脚本所在的目录添加到环境变量
1 | echo "export PATH=$PATH:~/.bin" >> ~/.bashrc |
检查脚本是否正常运行

安装kubeadm
开始之前
你需要准备:
- 一台兼容的 Linux 主机。Kubernetes 项目为基于 Debian 和 Red Hat 的 Linux 发行版以及一些不提供包管理器的发行版提供通用的指令
- 每台机器 2 GB 或更多的 RAM (如果少于这个数字将会影响你应用的运行内存)
- 2 CPU 核或更多
- 集群中的所有机器的网络彼此均能相互连接(公网和内网都可以)
- 节点之中不可以有重复的主机名、MAC 地址或 product_uuid。请参见这里了解更多详细信息。
- 开启机器上的某些端口。请参见这里 了解更多详细信息。
- 禁用交换分区。为了保证 kubelet 正常工作,你 必须 禁用交换分区。
禁用交换分区
临时关闭
1 | swapoff -a |
永久关闭
1 | vim /etc/fstab |
注释掉最后一行的swap

确保每个节点上 MAC 地址和 product_uuid 的唯一性
这里直接使用xcall脚本执行命令。
一般来讲,硬件设备会拥有唯一的地址,但是有些虚拟机的地址可能会重复。 Kubernetes 使用这些值来唯一确定集群中的节点。 如果这些值在每个节点上不唯一,可能会导致安装失败。
检查mac地址
1
ip link

检查product_uuid
1 | sudo cat /sys/class/dmi/id/product_uuid |

检查网络适配器
如果有一个以上的网络适配器,同时K8s通过默认路由不可达,此时需要先添加默认IP路由规则。
这里使用的是同一局域网的虚拟机,默认符合规则。
允许 iptables 检查桥接流量
检查br_netfilter模块是否被加载
1 | lsmod | grep br_netfilter |

这里没有加载,需要设置加载
显式加载
1 | sudo modprobe br_netfilter |
为了让Linux开机时就加载,需要修改sysctl 配置
1 | cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf |
安装IPVS所需要的模块
ipvs网络模式比iptables具有更优秀的性能
1 | cat <<EOF | sudo tee /etc/modules-load.d/k3s.conf |
实时加载模块
1 | sudo modprobe ip_vs |
安装查看ipvs表软件包
1 | sudo apt install ipvsadm -y |
服务起来以后,可以执行以下命令查看ipvs规则
1 | ipvsadm -ln |
检查所需端口
为了和K8s组件通信,需要检查特定端口
具体请查看:https://kubernetes.io/zh/docs/reference/ports-and-protocols/
安装容器运行时
设置运行时
1 | cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf |
K8s支持以下运行时
| 运行时 | 域套接字 |
|---|---|
| Docker Engine | /var/run/dockershim.sock |
| containerd | /run/containerd/containerd.sock |
| CRI-O | /var/run/crio/crio.sock |
由于在1.24之后的版本不在支持Dockershim ,因此不推荐使用Docker Engine作为K8s容器运行时,这里使用containerd。
方法1:使用docker-ce 镜像源
后期还可以通过安装docker-cli实现docker的一系列操作,如打包镜像、使用docker-compose运行容器等。
按照安装docker的方法,安装docker需要的apt源;
执行以下命令
1
sudo apt-get update && sudo apt-get install containerd.io
配置运行时
1 | sudo mkdir -p /etc/containerd |
重新启动 containerd
1 | sudo systemctl restart containerd |

方法2:直接使用nerdctl打包好的containerd运行时和一系列插件
nerdctl是containerd项目下的和docker
cli兼容的命令行。现在已经支持docker-cli的大部分功能。甚至可以直接将nerdctl设置为docker,这样就可以无缝使用docker和docker
compose的命令啦。
1 | alias docker=`sudo nerdctl` |
项目地址:https://github.com/containerd/nerdctl
直接下载Full的版本
下载完毕以后执行安装命令,以0.20.0版本为例
1 | sudo tar Cxzvvf /usr/local nerdctl-full-0.20.0-linux-amd64.tar.gz |
设置自动启动
1 | sudo systemctl enable buildkit.service --now |
配置stargz-snapshotter
Fast container image distribution plugin with lazy pulling,项目地址:https://github.com/containerd/stargz-snapshotter
修改/etc/containerd/config.toml文件
1 | version = 2 |
设置Docker Hub镜像源
默认containerd在Docker Hub拉取镜像,这样速度很慢

在/etc/containerd创建mirrors.d文件夹

编辑hosts.toml,这里使用daocloud的源
1 | server = "https://docker.io" |
使用 systemd cgroup
驱动程序
结合 runc 使用 systemd cgroup 驱动,在
/etc/containerd/config.toml 中设置
1 | [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] |
如果您应用此更改,请确保再次重新启动 containerd:
1 | sudo systemctl restart containerd |
注意:此项更改并不能使用crictl info 、
config dump或nerdctl info查看,可以通过systemctl status containerd查看是否存在/opt/dev/bin/containerd-shim-runc-v2来判断是否生效。
https://kubernetes.io/zh/docs/setup/production-environment/container-runtimes/#containerd
https://github.com/containerd/containerd/issues/4900#issuecomment-756085464
安装 kubeadm、kubelet 和 kubectl
这里使用基于Debian的发行版
- 更新
apt包索引并安装使用 Kubernetesapt仓库所需要的包
1 | sudo apt-get update |
- 安装仓库密钥,这里使用阿里云的源
1 | curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add - |
官方源:
1 | curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - |
- 更新apt包索引,安装 kubelet、kubeadm 和 kubectl,并锁定其版本:
1 | sudo apt-get update |
kubelet 现在每隔几秒就会重启,因为它陷入了一个等待 kubeadm 指令的死循环。

配置容器运行时 cgroup 驱动
由于 kubeadm 把 kubelet 视为一个系统服务来管理,所以对基于 kubeadm
的安装, 推荐使用 systemd 驱动,不推荐
cgroupfs 驱动。
一个示例:
1 | # kubeadm-config.yaml |
启动控制节点
启用控制平面前应先启动containerd服务
1 | sudo systemctl enable containerd --now |
修改containerd.sock权限,不使用sudo也可以拉取镜像
1 | sudo chown -R wf09:wf09 /run/containerd/ |
运行master节点之前可以先拉取本地镜像
1 | kubeadm config images pull --config kubeadm-config.yaml |
在master节点运行以下命令,同时传递上文中说到的kubeadm-config.yaml
1 | kubeadm init --config kubeadm-config.yaml |
可以使用以下命令重新打印token和加入命令
1 | kubeadm token create --print-join-command |
删除控制节点
如果在启动后控制平面以后出现错误,必须先删除控制平面
更详细的可以参考卸载集群
1 | sudo kubeadm reset |

控制节点启动成功

如果使用非root用户运行kubectl,需要运行以下命令
1 | mkdir -p $HOME/.kube |
如果使用root用户,则可以运行
1 | export KUBECONFIG=/etc/kubernetes/admin.conf |
还需要记录一下token,后面的节点加入集群需要这个
1 | kubeadm join 192.168.15.201:6443 --token 57hnhv.sy8wb7c1gsrzbotp \ |
安装 Pod 网络附加组件
必须部署一个基于Pod网络插件的容器网络接口,以便的Pod可以相互通信
安装之前需要先设置网络插件地址,在kubeadm-config.yaml添加以下配置
1 | networking: |
这里使用flannel
1 | kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml |

检查节点状态
1 | kubectl get nodes |

当STATUS状态为Ready时说明集群已经启动成功了。
设置Master为可调度节点
默认情况下,Master 不参与 Pod
调度,也就是说不会在 Master 节点上部署其他非系统
Pod。可以使用以下命令调整这个策略
1 | # 允许 Master 部署 Pod |
启动工作节点
准备工作首先也需要完成安装kubeadm的步骤。
剩下的步骤就比较简单了,找到上文中成功启动K8s控制节点中的token,在工作节点执行
1 | sudo kubeadm join 192.168.15.201:6443 --token 57hnhv.sy8wb7c1gsrzbotp \ |

如果有类似的回显说明已经加入成功了。
如果忘记master节点的token可以在master节点上执行命令重新创建一个token
1 | sudo kubeadm token create --print-join-command |
集群启动成功

Kubernetes排错
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory “/etc/kubernetes/manifests”. This can take up to 4m0s
查看kubelet日志
1 | journalctl -xeu kubelet |
出现频率较高的就是sandbox,应该和这个镜像有关

查看containerd默认配置
1 | cat /etc/containerd/config.toml |

发现默认配置是k8s.gcr.io域的镜像,国内拉取不到。
上文中已经拉取了阿里云的镜像,执行以下命令可以查看镜像的拉取地址
1 | crictl images |
如果报错应该是权限问题,即containerd
的创建的unix
socket文件权限是root,可以试试加上sudo

解决方案
将k8s.gcr.io/pause镜像更改为本地拉取的阿里云镜像即可。
加入节点时,提示"getting status of runtime: rpc error: code = Unimplemented desc = unknown service runtime.v1alpha2.RuntimeService"
解决方案
检查/etc/containerd/config.toml文件,将SystemdCgroup设置为true

重启containerd服务
1 | sudo systemctl restart containerd |
failed to set bridge addr: "cni0" already has an IP address different from 10.244.1.1/24
在集群出现异常,需要重置集群的时候可能会出现此问题。如果是第一次安装则不会,因为此时是集群第一次安装cni网络组件,不会出现地址冲突的问题。
解决方案
1 | ifconfig cni0 down |
FATA[0000] failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error running hook #0: error running hook: exit status 1, stdout: , stderr: time="2022-05-24T02:29:02Z" level=fatal msg="failed to call cni.Setup: plugin type="bridge" failed (add): incompatible CNI versions; config is "1.0.0", plugin supports ["0.1.0" "0.2.0" "0.3.0" "0.3.1" "0.4.0"]"
未安装CNI组件导致容器运行失败
解决方案
直接使用nerdctl打包好的containerd运行时和一系列插件即可,参考上文安装kubeadm——安装容器运行时——方法2
May 24 06:17:48 aws-jp-4G kubelet[184529]: E0524 06:17:48.193006 184529 kubelet.go:2419] "Error getting node" err="node "aws-jp-4g" not found"
一般是因为API Server的IP没有绑定成功,导致无法访问到对应的API Server端口,从而kubelet认为节点没有注册成功。
解决方案
重新初始化集群,从新注册网络即可。
https://github.com/flannel-io/flannel
https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#pod-network
https://github.com/containerd/containerd/blob/main/docs/cri/registry.md
删除Terminating的Pod
在执行删除命令时应该使用一下两个flag
1 | kubectl delete pod <PODNAME> --grace-period=0 --force --namespace <NAMESPACE> |
如果已经执行了删除命令而pod没有被立即删除,使用以下脚本删除Terminating的Pod
1 | kubectl get pods --all-namespaces | grep Terminating | while read line; do |