1. 为什么在 Debian 11 上手动部署 K8s 不再是“硬核玩家专属”?
你点开这篇,大概率不是为了看一句“已成功”就关掉——而是正卡在某个环节:kubeadm init报错cgroup driver mismatch,containerd启动后crictl ps一片空白,或者apt update一跑就是十分钟,镜像拉到一半超时失败。我试过三次重装 Debian 11,前两次都栽在同一个地方:系统默认配置、容器运行时与 Kubernetes 官方要求之间那几处看似微小、实则致命的错位。这不是环境问题,是 Debian 11 的设计哲学和 K8s 生产级部署规范之间的“代际摩擦”。
Debian 11(bullseye)自带的systemd默认启用unified cgroup hierarchy(cgroup v2),而早期 containerd 和 kubelet 对 v2 的兼容性并不完善;它默认的apt源走的是archive.debian.org,延迟高、镜像少、GPG 密钥易过期;它的内核虽然稳定,但overlay2存储驱动需要显式确认启用,否则containerd会静默降级到vfs——这会导致镜像层无法共享、磁盘暴涨、Pod 启动慢如蜗牛。这些都不是 bug,是 Debian 坚守“稳定压倒一切”的代价,而 K8s 要求的是“精确控制每一处底层行为”。所以,“已成功”三个字背后,其实是把 Debian 11 从一个通用服务器系统,精准校准为 Kubernetes 的专用运行底座的过程。
这个过程不需要你成为内核专家,但必须理解每一步“为什么非得这样改”。比如,为什么必须关闭swap?不是因为 K8s 代码里写了if swap_enabled { die() },而是因为kubelet的内存驱逐逻辑依赖cgroup.memory的精确统计,而swap会让这个统计失效,导致 OOM Killer 在错误时间杀死关键组件。再比如,为什么阿里云镜像源要分两层配置?一层给apt(系统包),一层给containerd(容器镜像),因为它们根本不在同一个协议栈上运行——apt走 HTTP/HTTPS,containerd走 gRPC + 镜像仓库 API,混用会直接报failed to resolve reference。这些细节,官方文档不会展开讲,但你在生产环境重启一次 master 节点,就会记住一辈子。
这篇文章不讲“K8s 是什么”,也不堆砌kubeadm init --pod-network-cidr=10.244.0.0/16这种命令。我要带你走一遍从裸机 Debian 11 ISO 刷入,到kubectl get nodes显示Ready状态的完整链路,每一步都标注清楚:这是在修复哪个默认偏差?这个参数影响哪一层组件?如果跳过会出什么具体现象?我会用真实终端日志片段佐证,而不是只给你结论。你照着做,能跑通;你跳过某步,我能告诉你接下来三小时你会看到什么报错——这才是“已成功”背后真正值得复用的经验。
2. 系统级预处理:让 Debian 11 主动“卸下稳定包袱”
Debian 11 的稳定,体现在它拒绝自动更新内核、拒绝启用新特性、拒绝修改已有行为。但 K8s 要求它主动适配。这一步不是可选项,是所有后续操作的前提。我把它拆成四个不可跳过的子任务,每个都对应一个具体的、可验证的系统状态变更。
2.1 关闭 swap 并永久禁用
很多人执行sudo swapoff -a就以为完事了,结果 reboot 之后kubectl get nodes卡在NotReady,journalctl -u kubelet里刷屏failed to run Kubelet: unable to load bootstrap kubeconfig。其实问题早埋下了:kubelet启动时会检查/proc/swaps,只要发现任何 swap 分区或文件,就会直接退出,并留下一条极不友好的日志:“cgroup memory controller not enabled”。它没说错,但误导了所有人——真正的问题是 swap 本身。
正确操作流程:
# 1. 查看当前 swap 使用情况 sudo swapon --show # 如果输出为空,说明已关闭;否则继续 # 2. 临时关闭所有 swap sudo swapoff -a # 3. 永久禁用:注释掉 /etc/fstab 中所有 swap 行 # 先备份 sudo cp /etc/fstab /etc/fstab.bak # 再用 sed 批量注释(比手动 vi 更可靠,避免漏行) sudo sed -i '/swap/d' /etc/fstab # 注意:这里用 /swap/d 而不是 /swap/ s/^/#/,因为有些 swap 行可能写成 UUID=xxx none swap sw 0 0,用 d 直接删除更彻底 # 4. 验证:reboot 后执行 free -h | grep Swap # 输出应为:Swap: 0B 0B 0B提示:别信
cat /proc/swaps输出为空就万事大吉。systemd可能在启动时根据/etc/crypttab或其他机制重新激活 swap。free -h是唯一可信指标。
2.2 启用 iptables-legacy 并配置 sysctl
Debian 11 默认使用nftables作为iptables的后端,但kube-proxy的iptables模式(目前仍是默认且最稳定模式)与nftables存在兼容性问题,典型表现是CoreDNSPod 一直ContainerCreating,kubectl logs -n kube-system coredns-xxx显示readiness probe failed,而iptables -t nat -L KUBE-SERVICES却是空的。这是因为kube-proxy写入的是iptables-legacy规则,而nftables不识别它们。
解决方案是强制回退到iptables-legacy:
# 1. 切换默认 iptables 版本 sudo update-alternatives --set iptables /usr/sbin/iptables-legacy sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy sudo update-alternatives --set arptables /usr/sbin/arptables-legacy sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy # 2. 验证切换结果 sudo iptables -V # 输出应为:iptables v1.8.7 (legacy) # 3. 加载必要内核模块(Debian 11 默认不加载) sudo modprobe br_netfilter sudo modprobe overlay # 4. 永久化内核参数(创建独立配置文件,避免污染 /etc/sysctl.conf) echo 'net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 vm.swappiness = 0' | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf # 5. 应用配置 sudo sysctl --system注意:
sysctl --system会重新加载/etc/sysctl.d/*.conf下所有文件,比sysctl -p更可靠。vm.swappiness = 0是 K8s 官方强烈建议值,防止内核在内存压力下过度交换,影响 Pod 响应。
2.3 配置阿里云 apt 源并更新系统
Debian 官方源在国内访问缓慢且不稳定,apt update经常卡在Reading package lists...。阿里云镜像站(https://mirrors.aliyun.com/debian/)针对国内网络做了深度优化,CDN 节点多,同步及时。但直接替换/etc/apt/sources.list有风险:Debian 11 的bullseye-security和bullseye-updates源路径与主源不同,必须严格匹配。
安全替换步骤:
# 1. 备份原文件 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak # 2. 生成新的 sources.list(注意:bullseye-security 的路径是 security.debian.org,不是 mirrors.aliyun.com) sudo tee /etc/apt/sources.list << 'EOF' deb https://mirrors.aliyun.com/debian/ bullseye main non-free contrib deb https://mirrors.aliyun.com/debian/ bullseye-updates main non-free contrib deb https://mirrors.aliyun.com/debian-security bullseye-security main non-free contrib EOF # 3. 更新并升级(--fix-missing 强制修复损坏的依赖) sudo apt update && sudo apt upgrade -y --fix-missing # 4. 清理无用包 sudo apt autoremove -y关键细节:
bullseye-security源必须指向https://mirrors.aliyun.com/debian-security,这是阿里云专门维护的安全更新镜像,而非mirrors.aliyun.com/debian/下的子目录。用错路径会导致安全补丁无法安装,apt list --upgradable会永远显示debian-security相关包可升级却无法获取。
2.4 验证并启用 overlay2 存储驱动
containerd默认尝试使用overlay2,但如果内核不支持或未启用,它会无声降级到vfs。vfs驱动对每个容器都复制完整镜像层,10 个 nginx Pod 就会占用 10 份 120MB 镜像空间,磁盘迅速告急,且ctr images pull速度极慢。我们必须确保overlay2真正生效。
验证与启用方法:
# 1. 检查内核是否支持 overlay grep -o "overlay" /proc/filesystems # 输出应为:overlay(若为空,则需升级内核) # 2. 检查当前挂载的 overlay 文件系统 findmnt -t overlay # 正常应有输出,如:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs ... overlay ... # 3. 如果没有,手动加载模块并验证 sudo modprobe overlay echo 'overlay' | sudo tee -a /etc/modules # 4. 创建测试目录验证 overlay2 是否可用 sudo mkdir -p /tmp/overlay-test/{upper,work,merged} sudo mount -t overlay overlay -o lowerdir=/,upperdir=/tmp/overlay-test/upper,workdir=/tmp/overlay-test/work /tmp/overlay-test/merged sudo umount /tmp/overlay-test/merged sudo rm -rf /tmp/overlay-test实操心得:
findmnt -t overlay是最直接的验证方式。很多教程让你看/proc/mounts,但那里信息太杂,容易误判。findmnt专为过滤挂载点设计,结果干净明确。如果这一步失败,containerd启动后ctr info里snapshotter字段会显示native而非overlayfs,这就是vfs驱动的信号。
3. Containerd 深度配置:不只是安装,而是定义其行为边界
containerd不是 Docker 的精简版,它是 CNCF 毕业项目,是 K8s CRI(Container Runtime Interface)的参考实现。它的配置文件/etc/containerd/config.toml是整个集群容器运行时的“宪法”。Debian 11 自带的containerd包(1.4.x)版本较老,且配置文件是空的,必须手动初始化并填充关键参数。跳过这一步,kubeadm init会卡在waiting for the control plane to become ready,因为kubelet根本连不上containerd的 socket。
3.1 安装指定版本 containerd 并初始化配置
Debian 11 仓库中的containerd是 1.4.12,而 K8s 1.24+ 推荐使用 1.6.x。我们选择1.6.30(截至 2023 年底最稳定的 LTS 版本),从官方 GitHub Release 页面下载二进制包,而非apt install——因为apt安装的包会把配置文件放在/usr/share/containerd/config.toml,而containerd默认读取/etc/containerd/config.toml,极易混淆。
# 1. 下载并解压 containerd 1.6.30 cd /tmp curl -LO "https://github.com/containerd/containerd/releases/download/v1.6.30/containerd-1.6.30-linux-amd64.tar.gz" sudo tar Czxvf /usr/local/bin containerd-1.6.30-linux-amd64.tar.gz # 2. 创建 systemd service 文件(Debian 11 默认没有) sudo tee /etc/systemd/system/containerd.service << 'EOF' [Unit] Description=containerd container runtime Documentation=https://containerd.io After=network.target [Service] ExecStartPre=/sbin/modprobe overlay ExecStartPre=/sbin/modprobe br_netfilter ExecStart=/usr/local/bin/containerd KillMode=process Delegate=yes LimitNOFILE=1048576 LimitNPROC=1048576 LimitCORE=infinity TasksMax=infinity Type=notify NotifyAccess=all [Install] WantedBy=multi-user.target EOF # 3. 初始化默认配置(生成 /etc/containerd/config.toml) sudo mkdir -p /etc/containerd sudo containerd config default | sudo tee /etc/containerd/config.toml # 4. 重载 systemd 并启动 sudo systemctl daemon-reload sudo systemctl enable containerd sudo systemctl start containerd为什么不用
apt install containerd?因为apt安装的containerd服务文件由deb包维护,升级时可能被覆盖;而手动安装的服务文件完全可控。containerd config default生成的配置是“最小可行”,我们需要在此基础上修改,而不是从零手写。
3.2 配置阿里云镜像加速器与私有仓库
containerd的镜像拉取不走apt源,它直连 Docker Hub 或其他 OCI Registry。国内直连 Docker Hub 极慢,且kubeadm init默认拉取k8s.gcr.io镜像(已被墙)。阿里云提供了registry.cn-hangzhou.aliyuncs.com/google_containers镜像代理,但必须在config.toml中显式配置,否则ctr images pull会超时。
编辑/etc/containerd/config.toml,重点修改以下三处:
# 1. 设置默认 runtime(必须!否则 kubeadm 会报错 "no available runtime") [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] base_runtime_type = "io.containerd.runtime.v1.linux" runtime_type = "io.containerd.runc.v2" runtime_engine = "" runtime_root = "" privileged_without_host_devices = false # 关键:启用 systemd cgroup 驱动,与 kubelet 保持一致 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true # 2. 配置镜像仓库(核心!) [plugins."io.containerd.grpc.v1.cri".registry] [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] endpoint = ["https://registry.cn-hangzhou.aliyuncs.com/google_containers"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"] endpoint = ["https://registry.cn-hangzhou.aliyuncs.com/google_containers"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"] endpoint = ["https://registry.cn-hangzhou.aliyuncs.com/google_containers"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"] endpoint = ["https://registry.cn-hangzhou.aliyuncs.com/google_containers"] # 3. 配置 sandbox 镜像(kubeadm init 必需) [plugins."io.containerd.grpc.v1.cri".sandbox_image] # 使用阿里云镜像,避免拉取失败 sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9"关键原理:
mirrors配置是“域名映射”,当containerd尝试拉取k8s.gcr.io/kube-apiserver:v1.24.0时,它会自动重定向到registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.24.0。sandbox_image是 Pod 的基础容器(pause 容器),kubeadm init会先拉取它,如果失败,整个初始化就终止。SystemdCgroup = true是必须项,它告诉containerd使用systemd而非cgroupfs来管理 cgroup,这与kubelet的--cgroup-driver=systemd参数严格对应,否则会出现cgroup driver: systemd is different from docker类似错误。
3.3 验证 containerd 配置有效性
配置改完不能直接systemctl restart containerd,必须先验证语法,否则服务会启动失败,journalctl -u containerd里全是toml: line X: invalid syntax。
# 1. 语法检查(关键!) sudo containerd config validate # 2. 重启服务 sudo systemctl restart containerd # 3. 验证 socket 连通性 sudo ctr -n k8s.io info | grep -E "(runtime|snapshotter)" # 正常输出应包含:runtime: io.containerd.runc.v2, snapshotter: overlayfs # 4. 测试拉取镜像(用阿里云镜像) sudo ctr -n k8s.io images pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9 # 成功后应显示:unpacking linux/amd64 sha256:... done # 5. 查看镜像列表 sudo ctr -n k8s.io images ls # 应能看到 pause:3.9 镜像实操避坑:
sudo ctr -n k8s.io中的-n k8s.io是命名空间,K8s 使用k8s.io命名空间,不是default。如果用错命名空间,ctr images ls会显示空,但镜像其实已拉取到k8s.io下。ctr -n k8s.io info的输出是最终裁判,它显示的runtime和snapshotter必须与我们配置的一致。
4. Kubeadm 初始化:从零构建控制平面的精确控制点
kubeadm init不是黑盒命令,它是 K8s 官方提供的“控制平面组装器”。它会生成证书、启动 etcd、部署kube-apiserver等组件,但所有行为都受命令行参数和配置文件控制。Debian 11 上的常见失败,90% 源于参数与系统实际状态不匹配。我们必须放弃“一键初始化”幻想,用kubeadm config文件进行声明式配置。
4.1 创建 kubeadm-config.yaml:声明所有关键参数
kubeadm init的默认行为(如--pod-network-cidr=10.244.0.0/16)只是推荐值,不是强制值。Flannel 网络插件要求--pod-network-cidr必须是10.244.0.0/16,而 Calico 要求192.168.0.0/16。我们必须在配置文件中显式声明,而不是依赖命令行参数,因为配置文件可以版本化、可审计、可复现。
# 保存为 /root/kubeadm-config.yaml apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration bootstrapTokens: - token: "abcdef.0123456789abcdef" description: "kubeadm bootstrap token" ttl: "24h" nodeRegistration: criSocket: unix:///run/containerd/containerd.sock taints: - key: node-role.kubernetes.io/control-plane effect: NoSchedule --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.24.0 controlPlaneEndpoint: "192.168.1.100:6443" # 替换为你的 master 节点 IP networking: podSubnet: 10.244.0.0/16 # Flannel 要求 serviceSubnet: 10.96.0.0/12 certificatesDir: /etc/kubernetes/pki clusterName: kubernetes imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers --- apiVersion: kubeadm.k8s.io/v1beta3 kind: KubeProxyConfiguration mode: iptables参数详解与选择依据:
criSocket: 必须指向containerd的 Unix socket。Debian 11 上containerd默认 socket 是/run/containerd/containerd.sock,不是/var/run/dockershim.sock(Docker 时代)。controlPlaneEndpoint: 这是集群的 VIP(虚拟 IP),用于高可用场景。单节点可设为本机 IP,但必须确保该 IP 能被所有节点访问。6443是kube-apiserver默认端口,不可更改。podSubnet:必须与后续网络插件严格一致。Flannel 的kube-flannel.yml文件里硬编码了10.244.0.0/16,如果这里设成10.245.0.0/16,Flannel DaemonSet 启动后会报错Failed to create SubnetManager: error retrieving pod spec for 'kube-system/kube-flannel-ds-xxx': pods "kube-flannel-ds-xxx" not found。imageRepository: 指定所有 K8s 系统镜像(kube-apiserver,coredns等)的拉取源。必须与containerd配置中的mirrors对应,否则kubeadm init会尝试从k8s.gcr.io拉取并失败。KubeProxyConfiguration.mode: 显式指定iptables模式,避免kube-proxy自动选择ipvs(需要额外内核模块)。
为什么不用
kubeadm init --config命令行?因为配置文件可以清晰看到所有参数的依赖关系。例如,podSubnet和imageRepository是强关联的,写在 YAML 里一目了然;而命令行参数是扁平的,容易遗漏。
4.2 执行初始化并处理常见故障
配置文件准备好后,执行初始化。这不是等待进度条结束,而是全程监控日志,预判并拦截失败。
# 1. 执行初始化(加 -v 5 输出详细日志) sudo kubeadm init --config /root/kubeadm-config.yaml -v 5 # 2. 如果失败,立即查看日志定位(不要盲目重试) sudo journalctl -u kubelet -n 100 --no-pager | grep -i "error\|fail\|warn"高频故障与精准修复:
| 故障现象 | 根本原因 | 精准修复命令 |
|---|---|---|
[ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables] | sysctl配置未生效 | sudo sysctl --system && sudo systemctl restart kubelet |
[ERROR Port-10250]: Port 10250 is in use | kubelet已在运行且占用了端口 | sudo systemctl stop kubelet && sudo rm -rf /var/lib/kubelet/pki && sudo kubeadm reset -f |
failed to pull image "k8s.gcr.io/coredns:v1.8.6" | imageRepository未生效或containerd配置错误 | sudo ctr -n k8s.io images pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.8.6,然后重试kubeadm init |
timed out waiting for the condition | containerdsocket 不可达 | sudo ss -tulnp | grep containerd,确认/run/containerd/containerd.sock存在且权限正确(srw-rw---- 1 root containerd) |
关键经验:
kubeadm init失败后,不要直接kubeadm reset。先用journalctl看kubelet日志,90% 的问题都能定位到具体组件。kubeadm reset会清理所有证书和配置,重试成本极高。sudo ss -tulnp | grep containerd是终极诊断命令,它能确认containerd进程是否真的在监听 socket,比systemctl status containerd更底层、更可靠。
4.3 初始化成功后的必要收尾操作
kubeadm init成功输出Your Kubernetes control-plane has initialized successfully!后,还有三步不可省略,否则kubectl无法工作,Node 无法加入。
# 1. 配置 kubectl(必须用 root 用户执行,因为 kubeadm 生成的证书在 /etc/kubernetes/admin.conf) mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config # 2. 部署 Flannel 网络插件(必须在 kubectl 配置好后执行) kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml # 3. 验证集群状态(耐心等待 2-3 分钟) watch -n 2 'kubectl get nodes,po -A' # 正常状态:NODES 显示 STATUS=Ready,PODS 在 kube-system 命名空间下全部 Running注意:
kubectl apply -f的 URL 必须是raw.githubusercontent.com,不是github.com。后者返回的是 HTML 页面,kubectl会报错error: unable to parse "https://github.com/...": couldn't get version/kind; json: unknown field "site"。watch -n 2每 2 秒刷新一次,比手动kubectl get更高效,能实时观察corednsPod 从Pending到ContainerCreating再到Running的全过程。
5. 验证与排错:用真实场景检验“已成功”的含金量
“已成功”不是kubectl get nodes显示Ready就结束,而是要通过一系列破坏性测试,证明集群具备生产就绪的基础能力。我在某次部署后,get nodes是Ready,但kubectl run nginx --image=nginx却卡在ContainerCreating,kubectl describe po nginx显示FailedCreatePodSandBox。这说明containerd的sandbox_image配置或拉取失败,表面成功,底层脆弱。
5.1 基础功能验证:从 Pod 到 Service
测试 1:部署无状态应用
# 1. 创建 nginx Pod kubectl run nginx --image=nginx:alpine --port=80 # 2. 检查 Pod 状态 kubectl get po -o wide # 正常:STATUS=Running, NODE=你的节点名, IP=非空 # 3. 进入容器执行 curl 测试 kubectl exec -it nginx -- sh -c 'curl -s http://127.0.0.1 | head -5' # 应输出 nginx 欢迎页 HTML 片段 # 4. 暴露为 ClusterIP Service kubectl expose pod nginx --port=80 --target-port=80 --type=ClusterIP # 5. 从另一个 Pod 访问该 Service kubectl run test --rm -i --tty --image=busybox:1.35 -- sh # 在 busybox 容器内执行: # wget -qO- http://nginx.default.svc.cluster.local # 应返回 nginx 欢迎页关键验证点:
wget -qO- http://nginx.default.svc.cluster.local。这测试了 K8s 的 DNS 服务(CoreDNS)和 Service 的 ClusterIP 功能。如果失败,kubectl get po -n kube-system会发现corednsPod 处于CrashLoopBackOff,原因通常是podSubnet配置错误或flannelDaemonSet 未正常运行。
测试 2:验证网络插件连通性
Flannel 部署后,会为每个 Node 分配一个/24子网(如10.244.1.0/24),Pod IP 必须属于该子网。我们手动检查路由表,确认 Flannel 正确注入了跨节点路由。
# 1. 查看本机路由表 ip route | grep 10.244 # 正常应有:10.244.0.0/24 via 10.244.0.0 dev flannel.1 # 本机 Pod 网段 # 10.244.1.0/24 via 192.168.1.101 dev eth0 # 其他节点 Pod 网段,下一跳是对方 Node IP # 2. 获取另一个 Node 的 IP(如果是单节点,此步跳过) kubectl get nodes -o wide # 3. 从本机 ping 对方 Node 的 Pod 网段网关(flannel.1 接口 IP) # 先查对方 flannel.1 IP:kubectl get nodes -o wide | awk '{print $6}' | tail -n +2 # 假设是 10.244.1.0,ping 其网关 10.244.1.0 ping -c 3 10.244.1.0原理:Flannel 的
host-gw后端模式,会在每个 Node 上添加一条路由,指向其他 Node 的flannel.1接口 IP。ip route | grep 10.244是最直接的验证方式,比kubectl get ds -n kube-system更底层、更可靠。
5.2 常见“伪成功”现象与根因定位
很多部署者看到kubectl get nodes是Ready就认为成功,但实际存在隐患。以下是三种典型的“伪成功”,以及如何用一条命令快速识别:
| 现象 | 根因 | 诊断命令 | 修复方向 |
|---|---|---|---|
kubectl get nodes显示Ready,但kubectl get po -A中coredns处于Pending | flannelDaemonSet 的tolerations未匹配node-role.kubernetes.io/control-plane:NoSchedule污点 | kubectl describe ds kube-flannel-ds -n kube-system | grep Tolerations | 编辑kube-flannel.yml,在spec.template.spec.tolerations下添加key: "node-role.kubernetes.io/control-plane" |
kubectl get po -A全部Running,但curl http://<node-ip>:30000无法访问 NodePort Service | iptables-legacy未生效,kube-proxy写入的规则被nftables忽略 | sudo iptables -t nat -L KUBE-NODEPORTS | wc -l(应 > 0) | 重新执行2.2节的update-alternatives和sysctl步骤 |
kubectl run创建的 Pod IP 是10.244.0.2,但ip a show flannel.1显示inet 10.244.0.0/32 | flannel配置的Network与kubeadm-config.yaml的podSubnet不一致 | kubectl get cm kube-flannel -n kube-system -o yaml | grep Network | 删除kube-flannelConfigMap 和 DaemonSet,重新kubectl apply -f kube-flannel.yml |
实操技巧:
kubectl describe ds kube-flannel-ds -n kube-system | grep Tolerations这条命令能瞬间定位污点容忍问题。Tolerations是 DaemonSet 能在 Master 节点上运行的关键,kubeadm init生成的kube-flannel.yml默认不包含对control-plane污点的容忍,必须手动添加,否则flannelPod 根本不会被调度到 Master 节点,coredns就永远Pending。
5.3 性能基线测试:确认底层运行时健康
最后,用一个轻量级压力测试,确认containerd和kubelet的协同是高效的。我们部署一个stress-ngPod,模拟 CPU 和内存压力,观察系统响应。
# 1. 部署 stress-ng Pod(限制资源,避免压垮节点) cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod