搭 K8s 环境踩过这 4 个坑,你就能少走半个月弯路【系列一】
系列说明:本文是「K8s 避坑指南」系列第 1 篇,覆盖环境搭建阶段最高频的 4 个坑。后续三篇分别对应开发、生产运维、权限网络,建议收藏整个系列。
适用版本:Kubernetes 1.24 ~ 1.30 | 系统:Ubuntu 20.04/22.04、CentOS 7/8
很多人第一次搭 K8s,光kubeadm init这一步就能卡半天。不是文档没看,是有些前置条件根本没人告诉你。这篇把环境初始化阶段最容易踩的 4 个坑整理出来,每个坑都给出根本原因和可直接执行的解决方案。
坑 1:kubeadm init 卡住或直接报错退出
报错现象
[ERROR Swap]: running with swap on is not supported. Please disable swap [ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: contents are not set to 1或者命令卡在[wait-control-plane] Waiting for the kubelet to boot up...超时退出,什么有用信息都没有。
根本原因
k8s 对底层环境有 3 项强制要求,漏一个就跑不起来:
- Swap 必须关闭:k8s 调度器假设 Swap 不存在,启用后内存计算不准,官方直接拒绝启动。
- 内核网桥模块未加载:
br_netfilter缺失,Pod 间流量无法被 iptables 拦截处理。 - cgroup driver 不一致:containerd 用
systemd,kubelet 还配着cgroupfs,两边对不上导致容器创建失败。
解决方案
按顺序执行,一次搞定:
# 第一步:永久关闭 Swap(两行都要执行) swapoff -a sed -i 's/.*swap.*/#&/' /etc/fstab # 第二步:加载内核模块并持久化 cat <<EOF | tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilter # 第三步:配置内核参数 cat <<EOF | 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 sysctl --system # 第四步:确认 containerd 使用 systemd cgroup driver containerd config default | tee /etc/containerd/config.toml sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml systemctl restart containerd验证环境是否准备好:
# 预检,有 ERROR 就按提示修复,WARNING 可以忽略 kubeadm init phase preflight坑 2:国内环境 kubeadm init 镜像拉取超时
报错现象
Failed to pull image "registry.k8s.io/pause:3.9": rpc error: context deadline exceeded或者kubectl get pods -n kube-system里看到核心组件ImagePullBackOff。
根本原因
registry.k8s.io(旧版是k8s.gcr.io)在国内访问超时或被屏蔽。kubeadm 默认从这里拉 pause、apiserver、etcd 等核心镜像,一个拉不下来整个初始化就卡死。
解决方案
方法一(推荐):init 时直接指定国内镜像源
kubeadm init \ --image-repository registry.aliyuncs.com/google_containers \ --pod-network-cidr=10.244.0.0/16 \ --apiserver-advertise-address=<你的主节点 IP>方法二:提前手动拉镜像并改 tag
# 查看当前版本需要的完整镜像列表 kubeadm config images list # 从阿里云拉取后打 tag(以 v1.28 为例) REPO="registry.aliyuncs.com/google_containers" TARGET="registry.k8s.io" IMAGES=( "kube-apiserver:v1.28.0" "kube-controller-manager:v1.28.0" "kube-scheduler:v1.28.0" "kube-proxy:v1.28.0" "pause:3.9" "etcd:3.5.9-0" "coredns/coredns:v1.10.1" ) for img in "${IMAGES[@]}"; do ctr images pull ${REPO}/${img} ctr images tag ${REPO}/${img} ${TARGET}/${img} echo "✓ ${img} 完成" done业务镜像加速(containerd 镜像源配置)
# /etc/containerd/config.toml [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] endpoint = ["https://xxxx.mirror.aliyuncs.com"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"] endpoint = ["https://registry.aliyuncs.com/google_containers"]systemctl restart containerd坑 3:kubectl 无法连接集群
报错现象
情况一(本机):
The connection to the server localhost:8080 was refused情况二(其他机器):
Unable to connect to the server: x509: certificate signed by unknown authority根本原因
- 情况一:
kubeconfig未配置,kubectl 默认去找localhost:8080,但 API Server 实际监听的是 6443,且需要认证。 - 情况二:kubeconfig 里的证书和当前集群不匹配,常见于重建集群后还在用旧的
~/.kube/config。
解决方案
# Master 节点上执行(kubeadm 初始化后的标准操作,这步很多人忘了) mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config # 验证 kubectl cluster-info kubectl get nodes在其他机器上访问集群:
# 把 admin.conf 复制到目标机器 scp root@<master-ip>:/etc/kubernetes/admin.conf ~/.kube/config # 如果集群重建了,旧 config 要删掉重复制 rm ~/.kube/config scp root@<master-ip>:/etc/kubernetes/admin.conf ~/.kube/config多集群切换(日常运维必备):
# 合并多个 kubeconfig export KUBECONFIG=~/.kube/config-prod:~/.kube/config-dev kubectl config get-contexts # 列出所有集群 kubectl config use-context <name> # 切换 kubectl config current-context # 查当前坑 4:Pod 一直 Pending,永远调度不上去
报错现象
kubectl get pods看到 Pod 长时间Pending,kubectl describe pod <name>的 Events 里报:
0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 node(s) had insufficient memory.根本原因
Pending 只有 3 类根因:
| 原因 | 典型场景 |
|---|---|
| 节点资源不足 | 节点 CPU/内存满了,Pod 的requests无法被满足 |
| 污点未容忍 | 单节点学习环境,Master 有control-plane污点,拒绝普通 Pod |
| 选择器不匹配 | nodeSelector或nodeAffinity里的标签写错了 |
解决方案
# 查看节点实际可分配资源 kubectl describe nodes | grep -A 8 "Allocated resources" # 查看节点上的污点 kubectl describe node <node-name> | grep -i taint # ——— 单节点开发环境:去掉 control-plane 污点 ——— # 这样 Master 节点也可以跑普通 Pod kubectl taint nodes --all node-role.kubernetes.io/control-plane- # ——— 排查标签匹配问题 ——— kubectl get nodes --show-labels # 对比 Pod 的 nodeSelector 是否和节点标签一致 kubectl get pod <name> -o yaml | grep -A 5 nodeSelector如果是资源不足,3 个方向解决:
- 扩容节点(加机器或升配)
- 降低 Pod 的
requests设置 - 清理占资源的僵尸 Pod
快速检查清单(环境篇)
□ swap 已关闭:free -h 看 Swap 一行是否全 0 □ 内核模块已加载:lsmod | grep br_netfilter □ sysctl 参数已生效:sysctl net.bridge.bridge-nf-call-iptables □ containerd cgroup driver 是 systemd:grep SystemdCgroup /etc/containerd/config.toml □ kubeconfig 已配置:kubectl get nodes 不报错 □ 节点状态 Ready:kubectl get nodes -o wide □ 系统组件正常:kubectl get pods -n kube-system小结
环境阶段的坑几乎都是「默认配置和 k8s 要求不一致」导致的,把上面的检查清单过一遍基本能排完。
下一篇预告:环境搭好之后,应用跑起来又是另一回事。Pod 反复 CrashLoopBackOff、OOMKilled、Service 访问不通、ConfigMap 热更新不生效……这些开发阶段的坑,下一篇逐一拆解。
