K8s 集群安装与排障记录
环境:debian06(Debian 12 bookworm,VMware 虚拟机),IP 192.168.88.46 版本:k8s v1.30.14 · containerd v2.3.3(containerd.io 源) · Flannel CNI 安装日期:2026-08-11(排障记录见文末"实战排障记录")
前置条件
- Debian 版本:12 (bookworm)(内核需 >= 5.10)
- 网络:节点间互通,Master 能访问外网拉镜像(或配置内网镜像仓库)
- 防火墙:如使用 UFW,需提前放行以下端口(详见第 7 步验证):
- 6443/tcp(API Server)
- 10250/tcp(kubelet)
- 10256/tcp(kube-proxy healthz)
- 8472/udp(Flannel VXLAN)
- Pod CIDR(10.244.0.0/16)的全部流量
- 资源:至少 2C4G(生产环境建议 4C8G+)
- 时区:建议统一为 Asia/Shanghai(
timedatectl set-timezone Asia/Shanghai)
以下步骤在所有机器(Master & Node)上均需执行,除非特别标注。
第 1 步:基础系统配置(所有节点)
# 1. 关闭 Swap(K8s 强制要求)
sudo swapoff -a
sudo sed -ri 's/.*swap.*/#&/' /etc/fstab
# 2. 加载内核模块
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# 3. 配置内核参数(支持网络转发)
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
第 2 步:安装容器运行时(containerd)
# 安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# 添加 Docker 官方源(containerd 来自此源)
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg
echo "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y containerd.io
# 生成默认配置
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
# 重启 containerd
sudo systemctl restart containerd
sudo systemctl enable containerd
containerd 2.x 必读(本次最大的坑,务必先看完)
containerd 2.x 的 CRI 配置节区彻底改名了。网上大量教程(针对 1.x)让你改的
[plugins."io.containerd.grpc.v1.cri"] 节区在 2.x 里被完全忽略(启动日志会出现
Ignoring unknown key in TOML for plugin ... key=sandbox_image plugin=io.containerd.grpc.v1.cri),
改了半天不生效。2.x 正确的配置位置:
# /etc/containerd/config.toml(2.x 有效节区,用单引号)
[plugins.'io.containerd.cri.v1.images'.pinned_images]
sandbox = 'registry.aliyuncs.com/google_containers/pause:3.9' # ← 沙箱 pause 镜像(国内必须换)
[plugins.'io.containerd.cri.v1.images'.registry.mirrors.'docker.io']
endpoint = ['https://docker.m.daocloud.io', 'https://docker.1panel.live'] # ← docker.io 加速
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
SystemdCgroup = true # ← 必须与 kubelet 的 cgroupDriver: systemd 一致
验证配置是否真的生效(不生效会浪费你 4 小时):
containerd config dump | grep -E "sandbox = |SystemdCgroup = |endpoint = "
journalctl -u containerd --since "1 minute ago" | grep -i "ignoring unknown key" # 有输出 = 有废配置
备注:
version = 4是 containerd 2.3.x 的正常配置版本,不是错误。imports = ['/etc/containerd/conf.d/*.toml']是合并覆盖入口,该目录默认不存在(空 glob 无影响), 以后若放文件进去会覆盖主配置,改配置前先留意。
第 3 步:安装 K8s 组件(kubeadm, kubelet, kubectl)
# 添加 K8s 官方源
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/kubernetes.gpg
echo "deb https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl # 锁定版本防止自动升级
# 启动 kubelet(此时会报错,等初始化后正常)
sudo systemctl enable --now kubelet
第 4 步:初始化集群(仅控制平面节点执行)
# 初始化:国内必须加 --image-repository 走阿里云镜像,否则拉镜像失败
sudo kubeadm init \
--apiserver-advertise-address=192.168.88.46 \
--pod-network-cidr=10.244.0.0/16 \
--image-repository=registry.aliyuncs.com/google_containers
# 配置 kubectl
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 复制输出的 kubeadm join 命令,稍后在 Worker 节点执行(token 24 小时后失效)
如果卡在 “Waiting for a healthy API server”(超时 4 分钟)
这不是 etcd/apiserver 起不来的问题,先查 kubelet 日志看真实原因:
journalctl -u kubelet --no-pager -n 50 | tail -50
本机当时的原因:pause 镜像没配置到 containerd 2.x 的正确节区(见第 2 步),
kubelet 一直尝试拉 registry.k8s.io/pause:3.10.2(被墙)→ 沙箱建不起来 → 控制面 Pod 全部无法启动。
修复后要重新初始化(镜像已本地,很快):
kubeadm reset -f
kubeadm init --apiserver-advertise-address=192.168.88.46 --pod-network-cidr=10.244.0.0/16 --image-repository=registry.aliyuncs.com/google_containers
第 5 步:安装网络插件 Flannel(仅 Master)
# 下载并应用(本机已保存一份在 /root/kube-flannel.yml,可离线复用)
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
# 或:kubectl apply -f /root/kube-flannel.yml
新版 flannel 镜像在 ghcr.io(
ghcr.io/flannel-io/flannel:v0.28.9等),国内直拉很慢。 拉不动时可用南大镜像加速,然后打上 kubelet 要的标签:crictl pull ghcr.nju.edu.cn/flannel-io/flannel:v0.28.9 crictl tag ghcr.nju.edu.cn/flannel-io/flannel:v0.28.9 ghcr.io/flannel-io/flannel:v0.28.9
第 6 步:加入工作节点(在每个 Worker 节点执行)
sudo kubeadm join 192.168.88.46:6443 --token vb052r.88ykc3ku8zrf5433 \
--discovery-token-ca-cert-hash sha256:608653ce829000ab5dc5112523f6f87176e3d0a53fd4186c12c68e7c34eb9bf4
-
上面的 token 已随安装生成(24 小时后失效),重新生成命令:
kubeadm token create --print-join-command -
Worker 节点若开着 UFW,需放行
10250/tcp(kubelet)和10256/tcp(kube-proxy 健康检查),否则节点加入后状态会异常。
Worker 节点加入后验证
# 在 Master 执行
kubectl get nodes -o wide # 看 IP、内核版本、容器运行时版本
kubectl describe node <node-name> # 看 Conditions、污点、资源容量
# Worker 节点上检查
systemctl status kubelet
journalctl -u kubelet --no-pager -n 20
第 7 步:验证集群
kubectl get nodes # 全部 Ready(等约 2 分钟)
kubectl get pods -A # 控制面 + coredns + flannel 全部 1/1 Running
crictl ps # 查看实际容器
本机验证结果(2026-08-11):
NAME STATUS ROLES VERSION
debian06 Ready control-plane v1.30.14
单节点集群:跑普通工作负载
控制面节点默认带 node-role.kubernetes.io/control-plane:NoSchedule 污点,
普通 Pod 无法调度。单节点想跑业务负载,去掉污点:
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
(不删污点的话,Pod 需要加 toleration,见下方排障记录中的冒烟测试命令。)
实战排障记录(2026-08-11)
故障 1:kubeadm init 超时 “Waiting for a healthy API server”
| 症状 | 卡 4 分钟超时;journalctl -u kubelet 反复报 failed to pull image "registry.k8s.io/pause:3.10.2" ... connection refused |
| 根因 | 教程让改的 [plugins."io.containerd.grpc.v1.cri"] 节区在 containerd 2.x 已废弃,pause 镜像配置没生效,containerd 用默认的 registry.k8s.io(被墙)拉沙箱镜像 → 所有控制面静态 Pod 起不来 |
| 判定方法 | journalctl -u containerd 搜 Ignoring unknown key;containerd config dump 对比生效值 |
| 修复 | 把 sandbox 配到 [plugins.'io.containerd.cri.v1.images'.pinned_images](阿里云 pause:3.9)→ systemctl restart containerd → kubeadm reset -f → 重新 kubeadm init |
故障 2:节点 NotReady,coredns 一直 0/1
| 症状 | 节点 Ready 了但 coredns 0/1;coredns 日志 dial tcp 10.96.0.1:443: i/o timeout;Pod 访问节点 IP 和 ClusterIP 全超时,但 Pod 间通信正常、宿主机访问 ClusterIP 正常 |
| 根因 | UFW 防火墙:INPUT 链默认策略 DROP。宿主机自身访问走 loopback 被 ufw 放行(极具迷惑性),而 Pod 流量从 cni0 进入 INPUT 被丢弃 |
| 判定方法 | iptables -t filter -L INPUT -n 看到 policy DROP;ufw status 为 active |
| 修复 | 加三条规则(保留原有规则不动):ufw allow from 10.244.0.0/16、ufw allow 6443/tcp、ufw allow 10250/tcp,约 30 秒后 coredns 自动恢复 1/1 |
故障 3:业务镜像拉取失败(ErrImagePull)
| 症状 | 部署 nginx 报 ErrImagePull |
| 根因 | 配置的 docker.io 加速 mirror.baidubce.com 已失效(2026-08 实测连接超时),docker.io 直连被墙 |
| 修复 | 换成实测可用的双端点:docker.m.daocloud.io(优先)+ docker.1panel.live(兜底),containerd 按顺序自动故障转移 |
故障排查速查
# 看容器实际状态(更直接)
crictl ps -a; crictl images
# 看 kubelet 在干什么(静态 Pod 起不来的第一现场)
journalctl -u kubelet --no-pager -n 50 | tail -50
# 看 containerd 是否忽略了配置(有输出 = 配置写错位置了)
journalctl -u containerd | grep "Ignoring unknown key"
# 看生效配置(以 containerd 实际读取的为准,而非文件)
containerd config dump | grep -E "sandbox = |SystemdCgroup = |endpoint = "
# 防火墙是否在 DROP(K8s 节点上最常见的地雷)
iptables -t filter -L INPUT -n | head -2 # policy DROP + ufw 链 = 中招
ufw status
# 测试 Pod 内连通性(flannel 是 hostNetwork,注意它不能代表普通 Pod)
kubectl exec -n kube-flannel <pod名> -- wget -qO- --no-check-certificate --timeout=5 https://10.96.0.1/
# 更准:钻进具体 Pod 的 netns(coredns 等无 shell 的镜像适用)
PID=$(crictl inspect $(crictl ps --name coredns -q | head -1) | grep -m1 '"pid"' | grep -oE '[0-9]+')
nsenter -t $PID -n -- curl -sk --connect-timeout 4 https://10.96.0.1/
关键结论(写给自己,下次装集群先看这里)
- containerd 2.x 的配置节区 ≠ 1.x,先
containerd -v确认版本,再按第 2 步的位置改。 - 装了 UFW 的机器装 K8s:Pod 网段和 k8s 端口必须放行,否则症状千奇百怪(这次是 coredns 超时)。
- 国内镜像源:kubeadm 镜像用
registry.aliyuncs.com/google_containers;docker.io 用 daocloud/1panel 双源;flannel 新版镜像在 ghcr.io(用 ghcr.nju.edu.cn 加速)。来源是否存活要实测(curl -sI https://镜像/v2/)。 - 故障优先看日志:kubelet / containerd / coredns 的日志会直接告诉你第一层原因,不要猜。
节点管理
#控制平面节点运行普通 Pod(单集群场景)
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
#给节点打标签(用于调度)
kubectl label nodes debian06 node-type=gateway
kubectl label nodes worker01 disk=ssd
#恢复污点(如需)
kubectl taint nodes debian06 node-role.kubernetes.io/control-plane=:NoSchedule
安全升级或重启
# 重启集群(不丢数据)
# 按顺序重启:节点 -> kubelet -> containerd
sudo systemctl restart containerd
sudo systemctl restart kubelet
# 等待约 30 秒,kubectl get nodes 自动恢复 Ready
# 升级 Kubernetes 小版本(谨慎操作)
# 先取消锁定,升级,再锁定
sudo apt-mark unhold kubeadm kubelet kubectl
sudo apt-get update && sudo apt-get install -y kubeadm=1.30.x-* kubelet=1.30.x-* kubectl=1.30.x-*
sudo apt-mark hold kubeadm kubelet kubectl
sudo systemctl restart kubelet