手工部署到自动化脚本:Kubernetes 集群一键安装实践与踩坑记录(containerd + kubeadm)
一、背景
本文提供基于 containerd + kubeadm 的一键部署脚本(k8s.sh,k8s-v2.sh,clean_cluster.sh),并记录部署过程中遇到的故障、定位思路与解决方案。读者能用本文脚本在 Rocky Linux 9 上快速复现与修复 containerd、CNI、kubelet 相关常见故障,仅供参考。
二、环境准备
OS版本:Rocky Linux 9.x
k8s版本:v1.29.15
CRI:containerd
安装方式:kubeadm & shell
网络插件:calico
脚本:k8s.sh,k8s-v2.sh,clean_cluster.sh
(脚本链接:通过网盘分享的文件:脚本
链接: https://pan.baidu.com/s/1XAW0t0HDMRTnzf4OuCgIDA?pwd=2mbw 提取码: 2mbw
–来自百度网盘超级会员v6的分享)
目标:
1、配置systemd、cgroup、sysctl、swap
2、自动安装containerd
3、安装kubeadm、kubelet、kubectl
4、自动init+网络插件
5、自动配置kubeconfig
6、最后能够通过kubectl命令成功看到node和pod的状态
使用containerd而非docker的原因
1、轻量
containerd是纯容器运行时(runtime),相较于docker,它更专注于容器运行时管理,组件少,故障少。
2、性能
containerd内存占用更低、启动速度快、资源消耗少。
3、原生支持
containerd内置CRI插件,原生支持k8s的runc接口标准,能够与kubelet直接交互,而docker需要通过dockershim转换,更复杂。
4、k8s1.24版本后不再支持docker
三、第一版脚本
(一)脚本的逻辑
| 阶段 | 任务 | 操作/配置 |
|---|---|---|
| 1、准备工作 | 环境与依赖的检查安装 | 检查root权限、os类型,安装wget、curl等工具 |
| 2、系统配置 | 配置容器运行时系统内核环境 | 加载overlay,br_netfilter模块,配置内核ip转发;禁用swap |
| 3、容器运行时 | 安装、配置containerd | 使用官方源安装,生成默认配置并适配国内镜像源,设置systemdcgroup驱动 |
| 4、k8s组件安装 | 安装kubeadm、kubelet、kubectl | 配置k8s的yum源,版本选定为1.29.15,配置kubelet使用containerd |
| 5、集群初始化 | 启动控制平面 | kubeadm init指定镜像仓库、pod/service网段、apiserver地址 |
| 6、网络插件 | 部署cni插件 | 修改pod的CIDR |
| 7、验证 | 验证结果 | 配置kubectl,检查节点状态,创建测试pod |
(二)脚本主要函数的功能
1、install_dependencies
作用:用于安装k8s所需基础依赖,包括网络、容器工具、负载均衡、流量控制、网络转发表、存储等。
#检查并安装系统依赖
install_dependencies() {
log_info "安装系统依赖"
if command -v yum &> /dev/null;then
#centos
yum install -y curl wget vim telnet net-tools conntrack-tools \
ipvsadm ipset sysstat libseccomp libtool-ltdl device-mapper-persistent-data \
lvm2 ebtables ethtool socat git
elif command -v apt-get &>/dev/null;then
#ubuntu
apt-get update
apt-get install -y curl wget vim telnet net-tools conntrack \
ipvsadm ipset sysstat libseccomp2 libltd17 device-mapper-persistent-data \
lvm2 ebtables ethtool socat git
else
log_error "不支持的linux发行版"
exit 1
fi
}
思路:单k8s节点除kubelet/kubeadm外,还需管理网络与iptables,否则CNI无法正常工作。
2、install_containerd()
#install & set containerd
install_containerd() {
log_info "install containerd..."
if command -v containerd &> /dev/null;then
log_warn "containerd is installd..."
return
fi
#set containerd
if command -v yum &> /dev/null;then
#centos
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y containerd.io
elif command -v apt-get &>/dev/null;then
#Ubuntu
apt-get install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg -dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y containerd.io
fi
#生成默认配置
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
#配置国内镜像加速
sed -i 's#registry.k8s.io#registry.aliyuncs.com/google_containers#g' /etc/containerd/config.toml
sed -i 's#k8s.gcr.io#registry.aliyuncs.com/google_containers#g' /etc/containerd/config.toml
#配置systemd cgroup驱动,与kubelet的cgroupDriver保持一致,避免cgroup路径冲突导致sandbox创建失败
sed -i 's#SystemdCgroup = false#SystemdCgroup = true#g' /etc/containerd/config.toml
#启动并设置开机自启
systemctl daemon-reload
systemctl enable containerd
systemctl start containerd
log_info "containerd installed successfully"
containerd --version
}
作用:自动安装containerd进行配置
逻辑:
使用docker官方仓库安装containerd;
containerd config default生成默认配置,修改sandbox镜像源与cgroup驱动;
(sandbox镜像源用于为pod中的其它容器提供linux共享namespace,因网络原因默认镜像源可能会拉取失败;修改SystemdCgroup = true的原因是使containerd与kubelet和linux主机的cgroup管理器保持一致,否则易出现cgroup不一致导致kubelet报错。)
启动containerd。
3、configure_system()
#set containerd system config
configure_system() {
log_info "set system config..."
#load kernel module
cat > /etc/modules-load.d/containerd.conf << EOF
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
#set system config
cat > /etc/sysctl.d/99-kubernetes-cri.conf << EOF
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl --system
#禁用swap
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
log_info "system config setting finished"
}
作用:准备linux内核环境
| 配置项 | 目的 | 不配置的影响 |
|---|---|---|
| 加载内核模块overlay/br_netfilter | 为容器提供联合文件系统支持和网络桥接能力 | 容器无法启动或节点通信异常 |
| 允许iptables桥接net.bridge.bridge-nf-call-iptables=1 | 确保同一节点内的pod通过service通信时,网络流量能被正确转发和跟踪 | service的负载均衡、网络策略依赖iptables规则,不开启的话,回包可能不经过iptables进行反向地址转换,同节点pod通过service通信(DNS)可能失败 |
| sysctl配置net.ipv4.ip_forward=1等 | 开启ip转发,允许节点像路由器一样转发数据包,实现跨节点pod通信 | 跨节点网络完全不通,pod无法与不同节点的pod或外部网络通信 |
| 关闭swap | 保证kubelet精准管理调度节点的内存,避免因磁盘交换导致性能不稳定和资源计算问题 | swap不关闭系统会将部分内存写入磁盘,导致磁盘io远慢于内存,性能下降,kubelet可能无法启动,或pod资源限制失效,节点被过度分配 |
4、install_kubernetes()
#install kubernetes
install_kubernetes() {
log_info "install kubernetes"
#set kubernetes
if command -v yum &> /dev/null;then
cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyuncs.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyuncs.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyuncs.com/kubernetes/yum/doc/rpm-key.gpg
EOF
yum install -y kubelet-1.29.15 kubeadm-1.29.15 kubectl-1.29.15 --disableexcludes=kubernetes
elif command -v apt-get &> /dev/null;then
apt-get install -y apt-transport-https ca-certificates
curl https://mirrors.aliyuncs.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
cat > /etc/apt/sources.list.d/kubernetes.list << EOF
deb https://mirrors.aliyuncs.com/kubernetes/apt/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet=1.29.15-00 kubeadm=1.29.15-00 kubectl=1.29.15-00
fi
#set kubelet use containerd
cat > /etc/default/kubelet << EOF
KUBELET_EXTRA_ARGS="--container-runtime=remote --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock"
EOF
# 在部分 Rocky9+containerd 版本下未自动安装 runc,会导致 "oci runtime 'runc' not found"
# 如遇 containerd 起不来 / sandbox 创建失败需手动装 runc
yum install -y runc
#start kubelet
systemctl daemon-reload
systemctl enable kubelet
systemctl start kubelet
log_info "kubernetes is installed successfully"
}
作用:安装k8s核心组件
使用官方源安装kubeadm、kubelet、kubectl。
设置kubelet接containerd:
5、init_cluster()
#init kubernetes cluster
init_cluster() {
log_info "init kubernetes cluster"
#获取本机ip
local_ip=$(hostname -I |awk '{print $1}')
#init control plane(containerd)
kubeadm init \
--image-repository registry.aliyuncs.com/google_containers \
--apiserver-advertise-address=${local_ip} \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=10.244.0.0/16 \
--cri-socket=unix:///var/run/containerd/containerd.sock \
--ignore-preflight-errors=all
#set kubectl
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
log_info "kubernetes cluster init finished"
}
作用:自动执行kubeadm init,自动初始化控制平面,无需手动复制命令。
固定service/pod CIDR;
指定containerd CRI;
使用阿里云镜像仓库加速。
6、install_network()
#install calico network plugins
install_network() {
log_info "install calico network plugins"
#download calico manifest
#curl -LO https://docs.projectcalico.org/manifest/calico.yaml
curl -LO https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml
#change pod cidr
sed -i "s#192.168.0.0/16#10.244.0.0/16#g" calico.yaml
#deploy calico
kubectl apply -f calico.yaml
#wait network plugins ready
sleep 30
kubectl get pods -n kube-system
}
作用:自动部署calico并替换pod CIDR
k8s集群在初始化时通过kubeadm init --pod-network-cidr=***已经指定了一个pod网段,网络插件的配置必须与集群初始化时指定的网段完全一致,否则网络插件无法正确为pod分配ip地址,会导致集群网络不通。
四、脚本执行的几种报错
(一)源链接失效换源
现象:

Errors during downloading metadata for repository ‘kubernetes’: - Curl error (35): SSL connect error for https://mirrors.aliyuncs.com/kubernetes/yum/repos/kubernetes-el7-x86_64/repodata/repomd.xml [OpenSSL SSL_connect: SSL_ERROR_ZERO_RETURN in connection to mirrors.aliyuncs.com:443 ] 错误:为仓库 ‘kubernetes’ 下载元数据失败 : Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried
这里是yum install kubelet的阶段,该报错提示阿里云的k8s yum源链接失效。
然后我换了清华源:
cat > /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.tuna.tsinghua.edu.cn/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.tuna.tsinghua.edu.cn/kubernetes/yum/doc/yum-key.gpg https://mirrors.tuna.tsinghua.edu.cn/kubernetes/yum/doc/rpm-key.gpg
EOF
但它也报错了:

Errors during downloading metadata for repository ‘kubernetes’:
- Status code: 404 for https://mirrors.tuna.tsinghua.edu.cn/kubernetes/yum/repos/kubernetes-el7-x86_64/repodata/repomd.xml (IP: 198.18.3.57)
错误:为仓库 ‘kubernetes’ 下载元数据失败 : Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried
原因分析:
机器是Rocky Linux 9 / CentOS Stream 9 体系,而这里用的阿里源、清华源是EL7的k8s源,对RHEL9系列不适用。
解决方法:
使用k8s官方仓库(pkgs.k8s.io)来处理。
cat <<EOF >/etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
EOF
(二)残留环境干扰reset
现象:

error creating clusterinfo RBAC rules: dial tcp 11.0.1.173:6443: connect: connection refused
这里又报错了,kubeadm init在最后的bootstrap-token阶段失败(bootstrap-token是k8s中节点加入集群进行身份认证和证书申请的初始阶段),即kubeadm在给kube-public namespace创建RBAC role时,访问API Server失败(拒绝连接)。
原因分析:
1、之前的k8s环境残留干扰(之前改过k8s源重跑脚本,对应的证书、配置等未清理);
2、containerd sandbox image不一致(日志有pause版本mismatch警告)。
解决方法:
这里准备reset后重新init。
kubeadm reset -f
systemctl stop kubelet
systemctl stop containerd
rm -rf /etc/kubernetes/*
rm -rf /var/lib/etcd/*
rm -rf /var/lib/kubelet/*
rm -rf /etc/cni/net.d/*
rm -rf /opt/cni/bin/*
# 清理网络规则
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
ipvsadm --clear
# 重启服务
systemctl restart containerd
systemctl restart kubelet
kubeadm init --pod-network-cidr=10.244.0.0/16 --kubernetes-version=v1.29.15
(三)kubelet启动失败与CNI插件问题
现象:


原因分析:
类似之前残留环境干扰,这里报错kubelet尝试清理旧的pod沙箱,但CNI插件未初始化导致失败。(环境没清理干净)
本质是kubeadm reset时kubelet/containerd还在试图清理network namespace,CNI文件已经被删掉,所以sanbox无法删除,因此报错"CNI plugin not initialized"。
解决方法:
这个错误可安全忽略,不影响后续集群初始化,但是reset一定要在rm -rf /etc/cni/net.d/* & rm -rf /opt/cni/bin/*之前执行,倒序清理会产生上述报错。
(四)containerd sock丢失/CRI不可用
现象:

validate CRI v1 image API for endpoint \"unix:///var/run/containerd/containerd.sock\": rpc error: code = Unavailable desc = connection error: desc = \"transport: Error while dialing: dial unix /var/run/containerd/containerd.sock: connect: no such file or directory\""
, error: exit status 1
这里可以看到提示containerd接口有问题,于是查看containerd状态:


原因分析:
containerd的配置里显示containerd CRI模式(k8s接口)被关闭了,containerd没创建socket,所以kubeadm连接不上runtime产生socket报错。
解决方法:
1、生成新配置
containerd config default > /etc/containerd/config.toml
2、启用systemd cgroup
sed -i 's/SystemdCgroup = false/SystemdCgroup = true' /etc/containerd/config.toml
3、reset(避免残留干扰)
kubeadm reset -f
rm -rf /etc/kubernetes
rm -rf /var/lib/kubelet
rm -rf /etc/cni/net.d/*
但是这里还是报错了:

查看一下containerd.sock文件是否存在:

原因分析:
报错里提示文件不存在,但实际存在,containerd之前的配置文件也修改了,之前看到containerd没起,起一下再执行kubeadm reset。


这里可以继续reset就成功了,也能看到节点的状态:

这里顺便再来验证下,containerd运行状态的影响:
[root@rocky ~]# systemctl stop containerd.service
[root@rocky ~]# crictl info |head -n 5
FATA[0000] validate service connection: validate CRI v1 runtime API for endpoint "unix:///run/containerd/containerd.sock": rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial unix /run/containerd/containerd.sock: connect: no such file or directory"
可以看到,containerd停了后,出现了跟之前同样的接口报错。
结论:
socket文件存在不等于runtime正常,本质是CRI服务不可用,后续遇到此类接口报错,也可以通过观察对应服务状态进行判断排查。
(五)kubeadm init 超时 & containerd 报 OCI runtime create failed
现象:
这是另一种情况,改了一下containerd的配置并重启后去看节点状态,报错了,这里我选择了reset+重新init:
但还是报错了,我看到了kubelet is not running,一般来说kubelet没起的原因是由于containerd没起导致的,所以我选择查看containerd状态和sock文件:
sock文件存在;
containerd和kubelet是正常运行的,那么问题在哪里呢?
我注意到这些日志
11月 03 21:10:13 rocky containerd[22640]: time="2025-11-03T21:10:13.203919873+08:00" level=warning msg="cleaning up after shim disconnected" id=163f9aba82b8c4013df8e22716dfabfa3058fc5e114de3eaba2945cbac925a7c namespace=k8s.io
11月 03 21:10:13 rocky containerd[22640]: time="2025-11-03T21:10:13.203944041+08:00" level=info msg="cleaning up dead shim" namespace=k8s.io
11月 03 21:10:13 rocky containerd[22640]: time="2025-11-03T21:10:13.209232040+08:00" level=warning msg="cleanup warnings time=\"2025-11-03T21:10:13+08:00\" level=warning msg=\"failed to remove runc container\" error=\"exec: \\\"runc\\\">
11月 03 21:10:13 rocky containerd[22640]: time="2025-11-03T21:10:13.209473036+08:00" level=error msg="copy shim log" error="read /proc/self/fd/13: file already closed" namespace=k8s.io
11月 03 21:10:13 rocky containerd[22640]: time="2025-11-03T21:10:13.211233781+08:00" level=error msg="RunPodSandbox for &PodSandboxMetadata{Name:etcd-rocky,Uid:7910301a94a08bddf162e9951ad067fb,Namespace:kube-system,Attempt:0,} failed, e>
11月 03 21:10:23 rocky kubelet[23176]: E1103 21:10:23.239438 23176 kuberuntime_sandbox.go:72] "Failed to create sandbox for pod" err="rpc error: code = Unknown desc = failed to create containerd task: failed to create shim task: OCI r>
11月 03 21:10:23 rocky kubelet[23176]: E1103 21:10:23.239452 23176 kuberuntime_manager.go:1184] "CreatePodSandbox for pod failed" err="rpc error: code = Unknown desc = failed to create containerd task: failed to create shim task: OCI >
11月 03 21:10:23 rocky kubelet[23176]: E1103 21:10:23.239494 23176 pod_workers.go:1298] "Error syncing pod, skipping" err="failed to \"CreatePodSandbox\" for \"kube-controller-manager-rocky_kube-system(bb145c53c6d5b781af791fcd1822a06e>
11月 03 21:10:24 rocky kubelet[23176]: E1103 21:10:24.045098 23176 controller.go:145] "Failed to ensure lease exists, will retry" err="Get \"https://11.0.1.173:6443/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/rocky?t>
11月 03 21:10:24 rocky kubelet[23176]: E1103 21:10:24.210744 23176 remote_runtime.go:193] "RunPodSandbox from runtime service failed" err="rpc error: code = Unknown desc = failed to create containerd task: failed to create shim task: >
11月 03 21:10:24 rocky kubelet[23176]: E1103 21:10:24.210781 23176 kuberuntime_sandbox.go:72] "Failed to create sandbox for pod" err="rpc error: code = Unknown desc = failed to create containerd task: failed to create shim task: OCI r>
11月 03 21:10:24 rocky kubelet[23176]: E1103 21:10:24.210797 23176 kuberuntime_manager.go:1184] "CreatePodSandbox for pod failed" err="rpc error: code = Unknown desc = failed to create containerd task: failed to create shim task: OCI >
11月 03 21:10:24 rocky kubelet[23176]: E1103 21:10:24.210838 23176 pod_workers.go:1298] "Error syncing pod, skipping" err="failed to \"CreatePodSandbox\" for \"etcd-rocky_kube-system(7910301a94a08bddf162e9951ad067fb)\" with CreatePodS>
11月 03 21:10:24 rocky kubelet[23176]: I1103 21:10:24.388140 23176 kubelet_node_status.go:73] "Attempting to register node" node="rocky"
11月 03 21:10:24 rocky kubelet[23176]: E1103 21:10:24.388353 23176 kubelet_node_status.go:96] "Unable to register node with API server" err="Post \"https://11.0.1.173:6443/api/v1/nodes\": dial tcp 11.0.1.173:6443: connect: connection >
这里

原因分析:
首先可以排除runc缺失的问题,因为"failed to remove runc container…",如果runc缺失的话,应报错”runc: command not found"或者exec: “runc”: executable file not found in $PATH。
同时,“failed to create shim task: OCI"表明containerd调用runc创建pod sandbox失败。
再结合我之前的操作kubeadm reset后立即init,sock文件也存在,推测可能是containerd中残留的sandbox/shim还未完全退出,runtime还在清理旧任务,而kubeadm就已经开始创建新的静态pod了,即race condition(不同步)。
初步结论:kubelet——runtime调用——shim busy/清理未完成——卡住
解决方法:
我们可以选择重装runc,也可以选择重启containerd等待几秒后再进行init,如果立即init可能会再次导致该问题。
(六)reset 后 kubectl 报 localhost:8080 connection refused & Node NotReady
刚刚在执行了reset+重启containerd+等待几秒再init后获取node节点状态报了如下错误。
现象:

原因分析:
kubernetes是强一致性系统,证书与配置文件由kubeadm管理,当执行reset和重新init后,当前集群就成为了新集群,客户端必须重新指向新的kubeconfig。
然而此时,/etc/kubernetes/admin.conf会被清理掉重新生成,但$HOME/.kube/config不会被清理,所以kubeconfig不会被重新加载,kubectl会默认访问之前的kubeconfig环境(可能不存在或不匹配)。当kubectl找不到有效的kubeconfig时就会fallback到默认API地址:http://localhost:8080,因此报错。
解决方法:
export KUBECONFIG=/etc/kubernetes/admin.conf
看到执行完该命令后,可以看到node节点状态了。
包括下面的这个报错也可以这么解决,本质上都是kubeconfig不匹配,统一kubeconfig即可。
E1104 01:08:14.220186 1255087 memcache.go:265] couldn't get current server API group list: Get "https://11.0.1.174:6443/api?timeout=32s": tls: failed to verify certificate: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "kubernetes")

但有一个小问题是node节点的状态还是not ready,其实这个很简单,因为reset后kubeadm的init只负责:
清理、重建控制平面;重新生成证书;启动etcd、api、kubelet。
正如它不会帮你恢复kubeconfig,它也不会自动安装CNI网络插件,所以显示not ready。
重新安装下CNI:
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml #flannel
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml #calico
过一会再检查就能看到ready了。

(七)手动统一镜像版本
然后为了解决pause镜像不一致的问题,手动先拉取好镜像统一版本:

此时可以看到,系统中有两个不同版本的pause镜像,且配置文件存在冲突,/etc/containerd/config.toml文件中的镜像为3.8,kubelet启动参数也为3.9

这里统一使用pause3.9版本
vim /etc/containerd/config.toml
#将sandbox_image改为:
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"
#重启containerd
systemctl restart containerd

(八)排查故障过程中用过的部分命令
systemctl status containerd kubelet
journalctl -u kubelet -f 20 --no-pager
ss -ntlp
kubectl get nodes -owide |grep -A 20 -i events
crictl info
kubeadm reset -f
(九)验证
#检查节点状态
kubectl get nodes
#部署测试应用
kubectl create deployment nginx --image=nginx:alpine
kubectl expose deployment nginx --port=80 --type=NodePort


(十)总结
| 问题来源 | 场景 |
|---|---|
| containerd sock不一致 | containerd残留影响 |
| /run/containerd/containerd.sock: connect: no such file or directory | contained未起 |
| CNI plugin not initialized | kubeadm reset前CNI已被删 |
| pause mismatch | containerd default vs kubeadm default |
| Rocky9下载k8s仓库报错 | EL7与EL9体系不一致 |
| kubeadm init 超时 & containerd 报 OCI runtime create failed | kubeadm reset后立即init导致sandbox/shim残留,卡住 |
| localhost:8080 connection refused & Node NotReady | 执行reset和重新init后,kubeconfig未同步 |
五、第二版脚本
(一)第一版脚本的不足
1、问题、报错难以定位;
2、containerd没有检查环节,若遇到环境残留干扰,不会生成配置文件;
3、crictl需要手动配置;
4、脚本可移植性不足,endpoint被写死,可以将其改为变量;
5、–ignore-preflight-errors=all,导致部署完成后健康状态可能存在一定的问题;
6、重复执行的话可能使环境变得混乱;
7、遇到问题重复reset+init较繁琐。
(二)具体优化板块
1、问题与错误定位
第一版:
set -e #遇到错误立即退出
脚本运行中遇到错误及问题直接退出,难以定位查找原因。
第二版:
set -Eeuo pipefail
IFS=$'\n\t'
trap 'log_error "失败在${BASH_SOURCE}:${LINEO}处 (函数:${FUNCNAME[0]:-main})"' ERR
-u防止未定义变量;
-o pipefail捕获管道中间失败;
-E让trap捕获错误;
IFS防止带空格变量被错误切分;
trap能给出具体的报错行号和函数名,更快定位。
改进点:
执行第二版脚本时,任何一步失败立即退出(避免执行到一半),同时可以输出函数名与脚本号,便于回滚;-u可避免变量空值导致“脏写”。
2、containerd安装板块添加了残留环境里+sock强检验功能
第一版:
不会清理残留containerd(如有),启动后不检验sock是否存在,如果containerd运行不正常但文件存在,会误以为“已安装”。
第二版:
#若存在自定义unit,恢复官方unit
if systemctl cat containerd |grep -q "/etc/systemd/system/containerd.service";then
systemctl disable --now containerd || true
rm -f /etc/systemd/system/containerd.service
systemctl daemon-reload
fi
#启动后强检验sock
if [ ! -S /var/run/containerd/containerd.sock ];then
log_error "containerd.sock不存在..."
exit 1
fi
改进点:
避免重复创建containerd服务,同时不让历史systemd污染;
会清理残留的containerd,防止之前的containerd干扰导致“启动但不可用”;
将服务启动成功改为服务可用,避免“空sock误判为真”,看到sock才继续执行,防止containerd半初始化假成功。
3、自动配置cri
第一版:需手动配置;
第二版:
cat >/etc/crictl.yaml <<EOF
runtime-endpoint: ${CRI_SOCKET}
image_endpoint: ${CRI_SOCKET}
timeout: 10
debug: false
EOF
改进点:
可避免crictl连错CRI。
4、kubelet指定参数变量化
第一版:
KUBELET_EXTRA_ARGS="--container-runtime=remote --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock"
第二版:
KUBELET_EXTRA_ARGS="--container-runtime=remote --container-runtime-endpoint=${CRI_SOCKET}"
改进点:
端点可配置,增强可移植性。
5、k8s源适配 & 去除kubelet提前启动
第一版:
机器是Rocky Linux 9 / CentOS Stream 9 体系,脚本中的源是EL7的k8s源,对RHEL9系列不适用。
第二版:
使用k8s官方仓库(pkgs.k8s.io)来安装下载k8s组件,提高适配度;
去除systemctl start kubelet,仅enable,启动由init触发。
改进点:
6、增加预检函数
preflight_check() {
log_info "preflight: 检查containerd / runc / socket / swap"
command -v containerd >/dev/null || { log_error "containerd】未安装";exit 1;}
command -v runc >/dev/null || { log_error "runc 未安装";exit 1;}
#socket
if [ ! -S /var/run/containerd/containerd.sock ];then
log_error "containerd.sock不存在:/var/run/containerd/containerd.sock"
systemctl status containerd -l || true
journalctl -u containerd -n 50 --no-pager || true
exit 1
fi
#swap
if swapon --summary | grep -q '.';then
log_warn "check swap not close,try to close"
swapoff -a || true
fi
#crictl endpoint
crictl info >/dev/null 2>&1 || log_warn "crictl 连接失败(稍后kubeadm会再次检测)"
}
改进点:
重复运行脚本前会检查containerd、runc、sock、swap、CRI的状态,发现旧状态立即退出,避免流程执行到一半发现containerd/CRI等不达标,避免坏状态继续执行,降低失败率。
7、init环节去除–ignore-preflight-errors=all
第二版:
kubeadm init \
--image-repository registry.aliyuncs.com/google_containers \
--apiserver-advertise-address=${local_ip} \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=10.244.0.0/16 \
--cri-socket=unix:///var/run/containerd/containerd.sock
改进点:不忽略所有错误,避免脏环境写入系统,不留隐患,提高健康度。
8、增加单独reset脚本
#clean_cluster.sh
#!/bin/bash
kubeadm reset -f || true
systemctl stop kubelet containerd || true
rm -rf /etc/kubernetes /var/lib/etcd /var/lib/kubelet /etc/cni/net.d
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X
ipvsadm --clear 2>/dev/null || true
systemctl restart containerd
改进点:
通过增加单独的reset脚本,当遇到混乱的环境及万不得已的情况下时可执行该脚本(生产环境慎用!该脚本会清理etcd的数据引发更大的灾难!如需使用也请事先备份快照)。
(三)执行结果
执行sh k8s-v2.sh命令后,可以看到一切输出正常:
更多推荐

所有评论(0)