K8S 群集的安装¶
本章目标¶
学完本章后,你将能够:
- 独立部署一个 3 节点 Kubernetes 集群(1 个 master + 2 个 worker)
- 理解 kubeadm 安装流程每一步在做什么(kubelet / kubeadm / kubectl 三件套、CRI、CNI;StorageClass 按教学顺序延迟到 实验 08 Lab 4 安装)
- 掌握国内网络环境下的连通性检查与镜像加速配置方法
- 用 kubectl 完成集群状态查看、命名空间与 Pod 的基本操作,熟悉交互方式
实验环境与时间预估¶
| 项 | 要求 |
|---|---|
| 节点数量 | 3 台(node1 = master,node2 / node3 = worker) |
| 节点 IP 约定 | 本手册后续章节统一按 node1=192.168.0.11、node2=192.168.0.12、node3=192.168.0.13 编写(实际部署请替换为你自己的 IP,输出示例基于此约定) |
| 操作系统 | Ubuntu 24.04(或其他主流发行版,部分命令略有差异) |
| 硬件规格 | 每台 2 核 4GB 起步(建议 4 核 8GB,后续 HPA 实验需要余量) |
| 网络 | 云主机或虚拟机均可;国内网络环境需先按"前置检查"实测连通性 |
| 预计时长 | 90~120 分钟(含前置检查与最终验证;网络状况差时会更久) |
⚠️ 如果只有一台机器,可先按文末「附录E:单节点快速安装」体验,但后续章节(02-09)的实验默认是 3 节点环境(调度、跨节点网络、NFS 存储等),强烈建议按正文完成 3 节点部署。
手动安装(3 节点·国内网络版)¶
基于 2026-07-22 在华为云虚机(Ubuntu 24.04,2核 4GB)上的实际部署经验整理。
文章概述¶
国内网络环境下用 kubeadm 从零部署标准 Kubernetes 集群,最大的坑不在 K8s 本身,而在于 registry.k8s.io、docker.io、ghcr.io、pkgs.k8s.io 这些海外基础设施在国内的连通性参差不齐——同一份检查脚本在不同机房跑出来的结果可能截然相反,没法照搬一份"绝对能行"的方案。
本文档把一次完整实战踩坑整理成可复用的操作指南:正文是从系统准备到集群验证通过的 10 个步骤(步骤 1-4 在三台节点上都要执行,步骤 5-10 在 master 上执行,步骤 7 在 worker 上执行 join),每一步先测连通性、按需切换国内加速源,重点解决了 containerd 2.x 版本镜像加速配置容易"配了却不生效"的坑;附录部分收录了实测过的国内镜像加速站清单、部分配置背后的原理说明、本次验证通过的组件版本组合,以及单节点快速安装的可选路径。
适合场景:拿到 3 台国内云主机(华为云/阿里云等),要从零装一个能正常工作的标准版 K8s 集群,且不确定这台机器对海外网络的连通性如何。
前置检查¶
务必先做,能节省后面排查的大量时间。三台节点都要执行。
1. SSH 连通性 + 系统信息¶
记录:CPU核数、内存、内网/公网 IP、系统版本(Ubuntu/CentOS 等)。master 的 内网 IP 后面 init 要用,worker 的 IP 用于 join 后确认。
2. 测网络连通性和真实带宽(关键!)¶
不要只看 connect 是否通,要实测速度:
# 测真实下载速度(用CDN测速点,不受地域限制)
curl -o /dev/null -s -w "speed=%{speed_download} bytes/s\n" --max-time 20 \
https://speed.cloudflare.com/__down?bytes=25000000
# 分别测关键域名连通性(docker registry类端点用/v2/探测,200/302/401都算"通",401是缺认证信息导致的正常响应)
for u in https://registry.k8s.io https://pkgs.k8s.io https://ghcr.io https://docker.1panel.live; do
curl -s -o /dev/null -w "$u => %{http_code} time=%{time_total}\n" --max-time 8 $u/v2/ 2>&1 || echo "$u FAILED"
done
# dl.k8s.io 是后面获取"最新稳定版"K8s版本号要用到的域名,单独测(不是registry API,不能用/v2/)
curl -s -o /dev/null -w "dl.k8s.io => %{http_code} time=%{time_total}\n" --max-time 8 https://dl.k8s.io/release/stable.txt
# 阿里云的两个备选源不是docker registry API,不能用/v2/探测,要用各自实际路径测试
curl -s -o /dev/null -w "aliyun-registry => %{http_code}\n" --max-time 8 https://registry.aliyuncs.com/v2/
curl -s -o /dev/null -w "aliyun-apt-source => %{http_code}\n" --max-time 8 https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.34/deb/Release
重要教训:整体带宽可能是好的(比如 7-8MB/s),但特定海外域名单独慢/不稳定,不能一概而论。docker.io 通常国内直连超时,但 registry.k8s.io 是否可达因机房而异,实测结果差异很大(同一份检查脚本在不同虚机上跑出过"直连超时"和"200直通"两种截然不同的结果),所以每次部署都要重新测,不能凭上次经验判断这次能不能通。
3. 镜像加速站预测试 + K8s 版本探测(关键!)¶
务必在最开始就做——不要等装完 containerd 才发现加速站不可用,也不要等 init 拉镜像才发现源不通。这一步同时确定两件事:用哪个加速站、装哪个 K8s 版本(默认安装当前最新稳定版)。
# ① 探测当前 K8s 最新稳定版(本手册默认安装"当前最新版",别照抄历史版本号)
curl -sL https://dl.k8s.io/release/stable.txt
# → 输出如 v1.36.3,记下这个版本号,步骤 4 安装时用它(或直接动态获取)
# ② 候选 docker.io 加速站逐个预测试(附录A 实测过 + 本次实测补充,多站备选)
# 返回 200/401/403 都算"可连接"(403 表示可达但可能对部分镜像受限)
for host in docker.1panel.live docker.m.daocloud.io docker.jiaxin.site proxy.vvvv.ee free.hubfast.cn; do
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 8 https://$host/v2/ 2>/dev/null)
echo "$host => ${code:-FAILED}"
done
# ③ 测试 kubeadm 控制面镜像仓库(registry.k8s.io 不可达时用阿里云替代)
curl -s -o /dev/null -w "registry.k8s.io => %{http_code}\n" --max-time 8 https://registry.k8s.io/v2/
curl -s -o /dev/null -w "aliyun-google_containers => %{http_code}\n" --max-time 8 https://registry.aliyuncs.com/v2/
根据输出确定三个关键选择(后面步骤会用到,建议记在纸上/文件里):
| 选择 | 判定 | 后续使用位置 |
|---|---|---|
主加速站 ACCEL_HOST |
②中返回 200 且速度快的(如 docker.1panel.live) |
步骤 3 配置 hosts.toml |
| 备用加速站(1-2 个) | ②中其他可连接的(如 docker.m.daocloud.io) |
步骤 8 calico 预拉失败时换源 |
镜像仓库 IMAGE_REPO |
③中 registry.k8s.io 返回 200 → 用官方源;不通 → registry.aliyuncs.com/google_containers |
步骤 5 kubeadm init |
⚠️ 实测教训:① 加速站对不同镜像可用性不同——同一批站里
docker.1panel.live对calico/node正常、但对calico/cni返回 403(换docker.m.daocloud.io成功);② 加速站可用性随时间变化,每次部署都要重新测,别沿用上次结果。
4. 资源规划¶
2核机器只够跑单节点 K8s 基础组件 + 少量应用 Pod。如果计划部署较多 Deployment/Pod(尤其自动扩容 HPA),提前评估好 CPU/内存总量,避免后续出现 Insufficient cpu 导致 Pod 一直 Pending。
部署步骤(kubeadm)¶
节点分工:步骤 1-4 在 3 台节点上都要执行;步骤 5-6、8-10 仅在 master(node1) 执行;步骤 7 在 worker(node2/node3) 执行。以下命令均假设你已是 root(或已
sudo -i切换到root)。如果习惯给每条命令单独加sudo,注意管道+tee写文件的场景要把sudo加在tee前面而不是cat前面,例如cat <<EOF | sudo tee file。
1. 系统准备(3 台节点)¶
关闭 swap、加载 br_netfilter/overlay 模块、配置 sysctl 转发参数
swapoff -a
sed -i '/ swap /s/^/#/' /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
apt-get update -qq
apt-get install -y -qq apt-transport-https ca-certificates curl gnupg
⚠️ 如果失败看这里:
modprobe: FATAL: Module br_netfilter not found→ 内核模块缺失,先apt-get install -y linux-modules-extra-$(uname -r)再试;sysctl --system报net.bridge相关错误 → 确认 br_netfilter 已加载(lsmod | grep br_netfilter)。
2. 装 containerd(3 台节点)¶
生成默认配置,改 SystemdCgroup=true
apt-get install -y -qq containerd
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl restart containerd
systemctl enable containerd
⚠️ 如果失败看这里:
containerd config default提示文件不存在 → 先确认 containerd 已安装(containerd --version);restart 后状态异常 →systemctl status containerd查看报错。
3. 配置 docker.io 镜像加速(3 台节点)¶
强烈建议在装完 containerd 后立刻做这一步,避免后面所有 docker.io 镜像拉取都超时。
需要同时做两件事:
1) 打开 use_local_image_pull 开关(containerd 2.x 默认关闭,不打开的话下面的镜像加速配置不会生效):
2) 配置镜像加速站(推荐地址见附录A,这里以 docker.1panel.live 为例):
mkdir -p /etc/containerd/certs.d/docker.io
cat > /etc/containerd/certs.d/docker.io/hosts.toml <<EOF
server = "https://docker.io"
[host."https://docker.1panel.live"]
capabilities = ["pull", "resolve"]
EOF
两步做完后重启 containerd 生效:
验证:
应在 10 秒内完成(而非超时)。
⚠️ 如果失败看这里:
crictl pull超时 → 先确认use_local_image_pull已是 true(grep use_local_image_pull /etc/containerd/config.toml),再确认 hosts.toml 里的加速站地址正确;还不行就换前置检查第3步选出的另一个加速站试试。原理见附录B。特别注意(实测踩坑):hosts.toml 是 TOML 格式,双引号不能丢——
server = "https://docker.io"和[host."https://加速站"]的引号必须保留,否则 containerd 解析失败、静默回退直连 docker.io(表现为"配置了加速但拉取还是超时")。写完用cat /etc/containerd/certs.d/docker.io/hosts.toml核对引号是否完整。
crictl需要单独安装(kubeadm 不带);如果当前机器还没装 crictl(或 GitHub 下载慢),可跳过本条验证,改用集群装好后步骤 10 的kubectl run test --image=busybox来验证 docker.io 加速是否生效(kubelet 走同样的 CRI 拉取路径)。
ghcr.io、registry.k8s.io等非 docker.io 的海外源,没有这类现成 CDN 加速,遇到需要拉取的场景见下方「应急方案」。
4. 装 kubelet / kubeadm / kubectl(3 台节点)¶
官方 apt 源是 pkgs.k8s.io/core:/stable:/v{MINOR}/deb/,先测一下连通性,不通则改用国内镜像源:
curl -s -o /dev/null -w "%{http_code}\n" --max-time 8 https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release
若返回非200/302(如超时或连接失败),改用阿里云的 kubernetes apt 镜像源
(https://mirrors.aliyun.com/kubernetes-new/core/stable/v{MINOR}/deb/)替代下面命令里的 pkgs.k8s.io。
LATEST=$(curl -sL https://dl.k8s.io/release/stable.txt)
MINOR=$(echo "$LATEST" | sed -E 's/^v([0-9]+\.[0-9]+)\..*/\1/')
mkdir -p /etc/apt/keyrings
# 注意:gpg 必须加 --batch(无 TTY 的自动化环境/脚本执行必需,否则报 cannot open '/dev/tty')
curl -fsSL https://pkgs.k8s.io/core:/stable:/v${MINOR}/deb/Release.key | gpg --batch --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v${MINOR}/deb/ /" | tee /etc/apt/sources.list.d/kubernetes.list
apt-get update -qq
apt-get install -y -qq kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl
⚠️ 如果失败看这里:
Release.key下载失败 →pkgs.k8s.io不通,按上面说明换阿里云源;apt-get update报 GPG 错误 → 确认 keyring 路径与signed-by一致。三台节点务必装同一个版本(都用动态获取,间隔不长则版本一致;若不一致,worker 节点手动指定与 master 相同的版本号,见附录C)。
5. kubeadm init(仅 master)¶
先根据前置检查第3步里 registry.k8s.io 的连通性结果决定要不要用国内镜像仓库(IMAGE_REPO 变量):如果那一步测试是通的(200),可以省掉 --image-repository 参数直接走官方源;如果超时/不通,用 registry.aliyuncs.com/google_containers 替代。本次实测以国内镜像仓库为例:
先预热镜像,并注入 kubelet 的沙箱(pause)镜像(关键!减少 init 失败重试的时间浪费,同时避免下面这个最常见的坑):
IMAGE_REPO=registry.aliyuncs.com/google_containers
# ① 预热全部控制面组件镜像(含 pause)
kubeadm config images pull --image-repository ${IMAGE_REPO}
# ② 注入 pause 沙箱镜像(关键预防步骤)
# 坑:kubelet 创建 Pod 沙箱时默认从 registry.k8s.io 拉 pause 镜像,国内拉不到会导致
# init 卡在 wait-control-plane(静态 Pod 全部起不来,日志报 failed to get sandbox image)。
# 注意 kubelet 内置的默认 pause 版本与 kubeadm 预热的版本可能不同(实测 kubeadm 拉 3.10.2、
# kubelet 要 3.10.1),所以把常见版本都 tag 一遍,确保全覆盖:
PAUSE_VER=$(kubeadm config images list --image-repository ${IMAGE_REPO} | grep pause | rev | cut -d: -f1 | rev)
ctr -n k8s.io images pull ${IMAGE_REPO}/pause:${PAUSE_VER}
for v in ${PAUSE_VER} $(echo ${PAUSE_VER} | cut -d. -f1-2) $(echo ${PAUSE_VER} | cut -d. -f1-2).1; do
ctr -n k8s.io images tag ${IMAGE_REPO}/pause:${PAUSE_VER} registry.k8s.io/pause:${v}
done
# 验证:ctr -n k8s.io images list -q | grep "registry.k8s.io/pause" 应看到多个 tag
关键参数(INNER_IP 用前置检查第1步 ip a | grep inet 里记录的内网IP,不是公网IP):
INNER_IP=<替换成你记录的内网IP,如 192.168.0.11>
kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=${INNER_IP} \
--image-repository ${IMAGE_REPO} \
--cri-socket=unix:///var/run/containerd/containerd.sock
⚠️ 不要用
192.168.0.0/16作为 pod-network-cidr,会跟节点自身网段冲突(很多云主机内网就是192.168.x.x/24)!
--image-repository ${IMAGE_REPO}指定控制面镜像仓库,避免拉取超时。⚠️ 如果失败看这里: - init 卡在
error execution phase wait-control-plane且journalctl -u kubelet报failed to get sandbox image "registry.k8s.io/pause:xxx"→ 就是 pause 沙箱镜像没注入成功,回到上面 ② 重新注入(确认ctr -n k8s.io images list -q | grep pause有registry.k8s.io/pause:<版本>),然后systemctl restart kubelet;不要急着 reset 重来 - init 卡在 wait-control-plane 且日志报PullImage超时(非 pause)→ 控制面镜像拉取失败,换IMAGE_REPO重试(先kubeadm reset -f清理) - 报[ERROR CRI]→ 确认 containerd 在运行且 SystemdCgroup 已改 - init 失败后kubectl get nodes报Forbidden(User "kubernetes-admin" cannot list ...)→ v1.36 起 kubeadm 用kubeadm:cluster-admins组(依赖 RBAC binding,init 中断时 binding 未创建),此时用super-admin.conf救急:export KUBECONFIG=/etc/kubernetes/super-admin.conf(该文件用户属于system:masters组,内置超级权限),再补建 binding:kubectl create clusterrolebinding kubeadm:cluster-admins --group=kubeadm:cluster-admins --clusterrole=cluster-admin
init 成功后务必保存输出末尾的 join 命令(kubeadm join ... --token ... --discovery-token-ca-cert-hash ...),步骤 7 要用;如果丢了也没关系,可用步骤 7 的方法重新生成。
6. 配置 kubeconfig(仅 master)¶
mkdir -p $HOME/.kube && cp /etc/kubernetes/admin.conf $HOME/.kube/config
# 写入 ~/.bashrc: export KUBECONFIG=/etc/kubernetes/admin.conf
⚠️ 如果失败看这里:
cp报权限错误 → 确认是 root 或在sudo -i状态;配完后kubectl get nodes报 connection refused → 检查 init 是否真的成功。
7. worker 节点加入集群(node2 / node3)¶
先取 join 凭证(在 master 上执行;kubeadm init 生成的默认 token 有效期只有 24 小时,这条命令随时可重新生成新凭证):
会输出类似这样的完整 join 命令:
kubeadm join 192.168.0.11:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:1234...cdef
再到每台 worker 节点上执行 join(node2、node3 各执行一次)。先注入 pause 沙箱镜像(worker 的 kubelet 创建 Pod 沙箱同样要拉 pause,步骤 5 的坑在 worker 上一样存在):
# 在 worker 节点上:注入 pause 沙箱镜像(与步骤5的②相同,IMAGE_REPO 保持一致)
IMAGE_REPO=registry.aliyuncs.com/google_containers
PAUSE_VER=$(kubeadm config images list --image-repository ${IMAGE_REPO} | grep pause | rev | cut -d: -f1 | rev)
ctr -n k8s.io images pull ${IMAGE_REPO}/pause:${PAUSE_VER}
for v in ${PAUSE_VER} $(echo ${PAUSE_VER} | cut -d. -f1-2) $(echo ${PAUSE_VER} | cut -d. -f1-2).1; do
ctr -n k8s.io images tag ${IMAGE_REPO}/pause:${PAUSE_VER} registry.k8s.io/pause:${v}
done
# 然后 join(--cri-socket 显式指定,避免探测出错)
kubeadm join 192.168.0.11:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:1234...cdef \
--cri-socket=unix:///var/run/containerd/containerd.sock
⚠️ 注意手动加上
--cri-socket=unix:///var/run/containerd/containerd.sock——如果节点上只装了 containerd(没有额外装 docker),这个参数通常可以省略,kubeadm 会自动探测到唯一的 CRI;但显式指定能避免探测出错,跟正文kubeadm init里的写法保持一致。⚠️ 如果 join 后节点长期
NotReady:先kubectl describe node <节点名>看 Conditions。若 kubelet 报 sandbox 镜像拉取失败 → 确认 pause 注入(上面命令);若等 CNI 装完才 Ready 属正常(步骤 8 装完 calico 后才会变 Ready)。
看到类似 This node has joined the cluster 的输出即代表加入成功。两台 worker 都 join 完,回到 master 确认:
⚠️ 如果失败看这里:join 报
token has expired→ 回 master 重新执行kubeadm token create --print-join-command;报[ERROR CRI]: container runtime is not running→ containerd 没装好/没启动,systemctl status containerd确认;新节点长期NotReady→ CNI 还没装(下一步就装),或镜像加速没配好,kubectl describe node <节点名>看 Conditions。
8. 装 CNI(Calico)(仅 master)¶
先在 3 台节点上都预拉 calico 镜像(calico 是 DaemonSet,每个节点都要跑;镜像在 docker.io,走加速站预拉 + tag 成本地镜像,避免 apply 后 ImagePullBackOff 再逐个排查;注意实测 docker.1panel.live 对 calico/cni 返回 403,所以用多站 fallback 链):
# 3 台节点都执行:预拉 calico 三件套(cni/node/kube-controllers),多加速站 fallback
# ACCEL_HOSTS 填前置检查第3步选出的主站 + 备用站(如 docker.1panel.live docker.m.daocloud.io)
ACCEL_HOSTS="docker.1panel.live docker.m.daocloud.io docker.jiaxin.site"
CALICO_VER=v3.29.1
for img in cni node kube-controllers; do
for host in ${ACCEL_HOSTS}; do
echo "--- 尝试 ${host}/calico/${img}:${CALICO_VER} ---"
ctr -n k8s.io images pull ${host}/calico/${img}:${CALICO_VER} 2>&1 | tail -1
if ctr -n k8s.io images list -q 2>/dev/null | grep -q "${host}/calico/${img}:${CALICO_VER}"; then
ctr -n k8s.io images tag ${host}/calico/${img}:${CALICO_VER} docker.io/calico/${img}:${CALICO_VER}
echo ">>> ${img} 就绪"
break
fi
done
done
# 验证:ctr -n k8s.io images list -q | grep "docker.io/calico" 应看到 cni/node/kube-controllers 三个
然后在 master 下载 manifest 并调整 Pod 网段:
curl -sL https://raw.githubusercontent.com/projectcalico/calico/${CALICO_VER}/manifests/calico.yaml -o /tmp/calico.yaml
# 该字段默认被注释掉,需要手工打开注释并把值改成与init时一致的网段。先定位所在行号:
grep -n "CALICO_IPV4POOL_CIDR" -A1 /tmp/calico.yaml
会看到类似这样的内容(行号可能不同):
用 vi/nano 打开文件跳到对应行号,把这两行的 # 去掉,并把网段值改成 10.244.0.0/16(跟 init 时一致),改完再 apply:
⚠️ 如果失败看这里:
apply后 Calico Pod 一直 ImagePullBackOff → 先确认 3 台节点的预拉都成功(ctr -n k8s.io images list -q | grep docker.io/calico三台都要有);某台缺哪个镜像就回上一步补拉(换 fallback 站)。apply 失败报 schema 错误 → 确认 YAML 里改的是value行且格式正确。等 1-3 分钟,此时所有节点应变为 Ready:
9. 装默认 StorageClass —— 本手册【延迟到 实验 08 Lab 4】安装¶
教学顺序说明:本手册刻意不在安装阶段装 StorageClass——"存储类(StorageClass)"是 实验 05 第 4 个实验的核心概念,为了在讲到概念时再动手、避免"没学过就先装好了",这里跳过。此时集群没有默认 StorageClass 是正常的(
kubectl get sc为空)。等到 实验 08 Lab 4「使用存储类动态交付」 时再安装(那节有完整安装命令)。如果需要提前装(比如想先跑别的需要 PVC 的实验),可手动执行:
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml kubectl patch storageclass local-path -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'⚠️ 如果失败看这里:local-path 的 helper Pod 一直 ContainerCreating 最后超时 → 该镜像在 docker.io,确认镜像加速配置,或走「应急方案」曲线导入。
10. 验证(master)¶
kubectl get nodes # 应为3个节点全部Ready
kubectl get pods -A # 系统Pod应全部Running
# 验证CNI+镜像拉取链路通畅
kubectl run test --image=docker.io/library/busybox --restart=Never -- sleep 3600
kubectl get pod test -o wide # 几秒内应变Running,并注意它被调度到哪个节点(worker)
kubectl delete pod test # 验证完清理掉测试Pod
💡 3 节点环境下,Pod 会被调度到 worker 节点(node2/node3)上,master 默认不承载业务负载——这是正常设计,不是故障。若看到
kubectl get nodes显示 master 有control-plane污点导致无法调度,属预期行为,后续 实验 04 会讲如何让 master 承载负载。
常见故障排查清单¶
| 现象 | 根因 | 解决 |
|---|---|---|
kubeadm init 卡在 error execution phase wait-control-plane,kubelet 日志报 failed to get sandbox image "registry.k8s.io/pause:xxx" |
pause 沙箱镜像拉不到(kubelet 默认从 registry.k8s.io 拉,国内不可达;--image-repository 不覆盖 kubelet 的沙箱镜像) |
按步骤5的②注入 pause 镜像(ctr 拉国内源 + tag 成 registry.k8s.io/pause:<版本>,常见版本都 tag),systemctl restart kubelet,不要急着 reset |
kubeadm init 卡在 error execution phase wait-control-plane(非 pause 报错) |
控制面组件镜像拉取超时 | 检查 journalctl -u kubelet 里的 PullImage 报错,换 IMAGE_REPO 后 kubeadm reset -f 重来 |
init 失败后 kubectl 报 Forbidden(User "kubernetes-admin" cannot list ...) |
v1.36 起 kubeadm 用 kubeadm:cluster-admins 组(走 RBAC binding),init 中断时 binding 未创建 |
用 export KUBECONFIG=/etc/kubernetes/super-admin.conf 救急(system:masters 超级组),补建 kubectl create clusterrolebinding kubeadm:cluster-admins --group=kubeadm:cluster-admins --clusterrole=cluster-admin |
| worker join 后 kubelet 报 sandbox 镜像拉取失败 | worker 的 pause 镜像没注入 | 在 worker 上执行步骤7的 pause 注入命令 |
Calico Pod ImagePullBackOff,Events 报 403 Forbidden |
加速站对该镜像受限(实测 docker.1panel.live 对 calico/cni 返回 403) |
按步骤8的多站 fallback 链换站(如 docker.m.daocloud.io)预拉 + tag |
Calico Pod ImagePullBackOff(无 403,纯超时) |
镜像加速没配好 / 该节点没预拉 | 确认该节点 hosts.toml 正确(注意引号不能丢,见步骤3)与 use_local_image_pull=true;或按步骤8预拉 |
| Pod 一直 ContainerCreating,Events 显示"Pulling"但 crictl images 显示镜像已存在 | (a) containerd debug 日志级别拖慢处理 (b) use_local_image_pull 配置未生效 | 检查 /etc/containerd/config.toml 的 level 改回 info;确认 use_local_image_pull 和 hosts.toml 配置 |
| PVC 一直 Pending,helper-pod 一直 ContainerCreating 最后超时(实验 08 Lab 4 安装 local-path 时) | local-path-provisioner 本身镜像被墙(docker.io) | 配置好镜像加速或走曲线导入法 |
| Pod Pending,Events 显示"Insufficient cpu" | 单节点资源不足 | 精简 Pod 数量/减少副本数/升级机器规格 |
| namespace 卡 Terminating 不消失 | 通常是残留 Pod 的 finalizer 依赖已删除的 controller 处理 | 先确认 kubectl get all -n <ns> 真的空了,再等一会或强制 delete 残留 Pod |
| 新节点 join 后长期 NotReady | CNI 未装或镜像拉不下来;新节点镜像加速没配 | 按步骤8装 CNI;在新节点确认 use_local_image_pull 和 hosts.toml,kubectl describe node 看报错 |
| worker 与 master 版本不一致 | 动态获取"最新版"间隔较长导致版本漂移 | worker 手动指定与 master 一致的版本号(见附录C),跨版本窗口 ±1 个小版本 |
以下这条不是K8s特有问题,是Linux环境的通用坑,一并收录方便查阅:
| 现象 | 根因 | 解决 |
|---|---|---|
| kubectl 在原生 shell 里提示 command not found 但文件存在 | 极可能是 ~/.bashrc 里 PATH 被意外覆盖(没有正确拼接 $PATH) |
检查 .bashrc/.profile 找出错误的 export PATH=... 赋值行并修复 |
应急方案:海外源(ghcr.io 等)镜像曲线导入法¶
当某个特定镜像域名没有国内 CDN 代理、直连又慢到几分钟都拉不完时(ghcr.io、registry.k8s.io 之外的自定义镜像仓库等):
1. 找一台网络更好的机器(如本地 WSL、开发机,实测可能有 7-8MB/s+ 的国际带宽):
2. 导出为 tar:
3. scp 传到目标机器(国内到国内传输通常很快,13MB/s+):
4. 在目标机器导入:
5. 重建对应 Pod(imagePullPolicy 为 IfNotPresent 时会直接用本地镜像,不再联网):
注意:大镜像(300MB+)在 target 机器上
ctr images import偶尔会莫名卡住不动(CPU/IO 都空闲,进程处于 sleeping 状态),此时表现为长时间无响应。这是本次实测中观察到的现象,具体成因未深究(可能与containerd版本、磁盘IO、镜像层数有关),遇到这种情况直接 kill 掉重试一次通常能解决,不必死等,但不保证百分百有效。
附录A:docker.io 镜像加速站实测清单¶
测试方法(可复用于验证新的加速站候选)¶
# 1. 连通性测试
curl -s -o /dev/null -w "http=%{http_code} time=%{time_total}s\n" --max-time 8 https://<候选站>/v2/
# 2. 配置为containerd镜像源
cat > /etc/containerd/certs.d/docker.io/hosts.toml <<EOF
server = "https://docker.io"
[host."https://<候选站>"]
capabilities = ["pull", "resolve"]
EOF
systemctl restart containerd
# 3. 清缓存后真实拉取测试(一个官方镜像 + 一个个人命名空间镜像,两类都要测)
ctr -n k8s.io images rm docker.io/library/nginx:latest 2>&1
ctr -n k8s.io images rm docker.io/<user>/<repo>:latest 2>&1
time crictl --runtime-endpoint unix:///var/run/containerd/containerd.sock pull nginx:latest
time crictl --runtime-endpoint unix:///var/run/containerd/containerd.sock pull <user>/<repo>:latest
注意:加速站的可用性和限流策略会随时间变化,这份清单有时效性,正式使用前建议重新跑一遍上述测试脚本验证。
实测结果(2026-07-22,华为云虚机,来源清单参考 dongyubin/DockerHub)¶
测试对象:官方镜像(nginx、postgres,即 library/xxx)+ 个人命名空间镜像(chengzh/backend、chengzh/frontend、katacoda/docker-http-server,即 user/repo 格式)
| 加速站 | 连通性 | 官方镜像 | 个人命名空间镜像 | 结论 |
|---|---|---|---|---|
docker.1panel.live |
通 | ✅ 1.5s | ✅ 4.1s | 全部可用,速度快,推荐 |
proxy.vvvv.ee |
通 | ✅ 2.1s | ✅ 2.3s | 全部可用,速度快,推荐 |
docker.jiaxin.site |
通 | ✅ 2.8s | ✅ 2.1s | 全部可用,速度快,推荐 |
free.hubfast.cn |
通 | ✅ 2.8s | ✅ 1.6s | 全部可用,速度快,推荐 |
dockerproxy.net |
通 | ✅ \~20s | ✅ 2s\~60s(波动大) | 全部可用,但速度不稳定 |
docker.m.daocloud.io |
通 | ✅ 成功 | ❌ 403 Forbidden | 官方可用,个人命名空间必403 |
docker.xuanyuan.me |
通 | ✅ 0.8s | ❌ 429 Too Many Requests(稳定复现,重试仍429) | 官方可用,个人镜像被限流 |
docker.1ms.run |
通 | ✅(同一批次测试中通过) | ❌ 404(白名单机制,不在白名单里的直接404) | 只镜像热门白名单镜像 |
dockerproxy.link |
通 | ❌ size validation failed(数据损坏) | ❌ 同样失败 | 不可用,返回内容与声明大小不符 |
registry.cyou |
❌ 无法建立连接 | - | - | 不可用 |
mirror.houlang.cloud |
❌ 超时(需登录token) | - | - | 不可用(需额外注册) |
推荐优先级:docker.1panel.live / proxy.vvvv.ee / docker.jiaxin.site / free.hubfast.cn,可以配置多个做 fallback。
国内镜像加速列表持续更新参考:https://github.com/dongyubin/DockerHub
附录B:镜像加速配置的原理笔记¶
这里记录正文「步骤3」和「步骤4」背后的排障过程和原理,仅供理解/排查参考,不影响正常部署照抄正文操作。
为什么要改 use_local_image_pull¶
/etc/containerd/certs.d/<domain>/hosts.toml 是 containerd 传统的 mirror 配置方式,但在 containerd 2.x 版本里可能不生效——因为新版本默认走一条新的 transfer service 拉取路径,这条路径不读取 certs.d 目录下的配置。
排障时的关键线索:开启 containerd debug 日志(config.toml 中 level = 'debug')后能看到日志显示走的是 resolving host=registry-1.docker.io,即镜像拉取压根没有经过配置的加速地址。
根因定位:/etc/containerd/config.toml 中 use_local_image_pull = false(这是新版本的默认值)时走新的 transfer service,忽略 hosts.toml;改成 true 后才会走老的 CRI pull 路径,认 hosts.toml 配置。
踩坑复盘:这个开关如果为了某次应急排障被临时改回 false,一定要记得在问题解决后改回 true,否则会"复发"——表现为"明明配置过加速,怎么又变慢了"。可以用 grep use_local_image_pull /etc/containerd/config.toml 快速自检这个状态有没有被意外改回。
关于 pkgs.k8s.io 连通性的实测背景¶
pkgs.k8s.io 背后是 Google 主导的 CDN,跟拉取镜像用的 registry.k8s.io 属于同一基础设施家族。这次在华为云虚机上实测是通的(curl -I https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release 返回302,apt-get install 一次性成功),但这只是单次、单台机器的验证结果——pkgs.k8s.io 的国内可达性完全可能因机房、运营商出口路由不同而表现不一致,换一台机器/换一个云厂商不能假设默认能通,一定要现场测试。
附录C:实测部署的组件版本汇总¶
以下是 2026-08 在 3 台国内云主机(Ubuntu 24.04)上从零实测通过的一套组件版本组合,供复现参考:
| 组件 | 版本 | 备注 |
|---|---|---|
| 操作系统 | Ubuntu 24.04.4 LTS | 4核 / 8GB内存 / 40GB磁盘(手册约定 2核4G 也能跑,资源更紧) |
| Kubernetes(kubelet/kubeadm/kubectl) | v1.36.3 | 通过 dl.k8s.io/release/stable.txt 动态获取的"当前最新稳定版"(实测时刻) |
| containerd | 2.2.1 | apt 默认源安装的版本 |
| CNI:Calico | v3.29.1 | manifest 版本,docker.io/calico/* 三件套(cni/node/kube-controllers)需预拉 + tag |
| CRI 镜像仓库(控制面组件) | registry.aliyuncs.com/google_containers | 本次实测 registry.k8s.io 不可达才用;其他环境按前置检查第3步测试,可达就用官方源 |
| 存储:local-path-provisioner | rancher/local-path-provisioner(master 分支 manifest) | 本手册按教学顺序在 实验 08 Lab 4 安装(安装阶段不装) |
| metrics-server | v0.9.0(components.yaml 最新版) | 需追加 --kubelet-insecure-tls 参数(见 实验 05 Lab 1,sed 定位注意端口可能不是 4443) |
| Pod 网段 | 10.244.0.0/16 | 需与节点自身网段(常见 192.168.x.x/24)区分开 |
| docker.io 镜像加速站 | docker.1panel.live(主)+ docker.m.daocloud.io(备) | 实测 1panel 对部分镜像(如 calico/cni)403,需备站 |
⚠️ 版本号随时间变化("最新稳定版"会持续推进),不要照抄此表——新部署一律用文档里的动态获取方式(
curl -sL https://dl.k8s.io/release/stable.txt拿当时最新版),本表只证明这套组合在实测时刻可协同工作。
附录D:worker 节点一键就绪脚本¶
如果觉得步骤 1-4 在三台节点上逐条执行太繁琐,可以把它们合并成一段脚本,在新 worker 节点上执行,跑完后节点环境就绪、等待 join:
#!/bin/bash
set -e
# 可配置项(先做前置检查第3步的预测试再填):
ACCEL_HOST="docker.1panel.live" # 主加速站(前置检查选出)
IMAGE_REPO="registry.aliyuncs.com/google_containers" # 控制面镜像仓库(registry.k8s.io 不通时)
# ===== 步骤1:系统准备 =====
swapoff -a
sed -i '/ swap /s/^/#/' /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
apt-get update -qq
apt-get install -y -qq apt-transport-https ca-certificates curl gnupg
# ===== 步骤2:装containerd =====
apt-get install -y -qq containerd
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl restart containerd
systemctl enable containerd
# ===== 步骤3:配置docker.io镜像加速 =====
sed -i "s/use_local_image_pull = false/use_local_image_pull = true/" /etc/containerd/config.toml
mkdir -p /etc/containerd/certs.d/docker.io
cat > /etc/containerd/certs.d/docker.io/hosts.toml <<EOF
server = "https://docker.io"
[host."https://${ACCEL_HOST}"]
capabilities = ["pull", "resolve"]
EOF
systemctl restart containerd
# ===== 步骤4:装kubelet/kubeadm/kubectl(当前最新稳定版)=====
LATEST=$(curl -sL https://dl.k8s.io/release/stable.txt)
MINOR=$(echo "$LATEST" | sed -E 's/^v([0-9]+\.[0-9]+)\..*/\1/')
mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v${MINOR}/deb/Release.key | gpg --batch --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v${MINOR}/deb/ /" | tee /etc/apt/sources.list.d/kubernetes.list
apt-get update -qq
apt-get install -y -qq kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl
# ===== 步骤5:注入 pause 沙箱镜像(关键,防 join 后 sandbox 拉取失败)=====
PAUSE_VER=$(kubeadm config images list --image-repository ${IMAGE_REPO} | grep pause | rev | cut -d: -f1 | rev)
ctr -n k8s.io images pull ${IMAGE_REPO}/pause:${PAUSE_VER}
for v in ${PAUSE_VER} $(echo ${PAUSE_VER} | cut -d. -f1-2) $(echo ${PAUSE_VER} | cut -d. -f1-2).1; do
ctr -n k8s.io images tag ${IMAGE_REPO}/pause:${PAUSE_VER} registry.k8s.io/pause:${v}
done
echo "=== 节点环境就绪,等待 join ==="
echo "join 命令在 master 上执行: kubeadm token create --print-join-command"
⚠️ 跟正文一样,先做前置检查再跑这个脚本:如果这台新节点的
pkgs.k8s.io不通,脚本里的下载会失败,需要按正文步骤4的方法换成阿里云的 kubernetes apt 镜像源。如果 K8s 版本要跟 master 保持一致(强烈建议同版本),可以跳过LATEST/MINOR的动态获取,改成写死 master 当时用的版本号(见附录C)。⚠️ 实测补充:① 脚本已加
gpg --batch(无 TTY 环境必需);② 若apt-get update报 GPG 错误或Release.key下载损坏(gpg: no valid OpenPGP data)→ 删掉/etc/apt/keyrings/kubernetes-apt-keyring.gpg后重跑本段即可(网络波动导致,重试通常成功);③ 本脚本已验证可让一台全新 worker 直接加入集群(含 pause 注入),join 后等 CNI 装好即 Ready。
附录E:单节点快速安装(可选)¶
如果只有一台机器,只想快速体验 K8s,可以按正文的步骤 1-6 装好 master 后,跳过步骤 7(不需要 worker),然后:
1. 去掉 master 的调度污点(否则 Pod 调度不上):
2. 继续步骤 8 装 CNI,步骤 10 验证(StorageClass 按教学顺序在 实验 08 Lab 4 安装)。
单节点下 Pod 会调度到 master 本机。注意:该形态与后续章节(02-09)默认的 3 节点环境不一致(单节点没有跨节点调度、没有多节点网络与 NFS 存储实验),仅适合快速体验或资源受限场景。
使用在线沙盒¶
如果没有足够的硬件资源,可以考虑使用 https://killercoda.com/ 提供的 Kubernetes Playground ,目前已经支持最新的 1.36 版本.
使用要点:
- 建议使用
Github账号进行注册,体验最好. - 如果没有
Github账号,可以使用国内的信箱进行注册,但是可能会在开启场景的时候用邮件再次激活. - 每个场景最长运行时间是1小时.
- 目前 Playground 环境免费.
Kubectl 基础与公共操作¶
本章节是后续所有章节(02-09)的公共基础:kubectl 命令体系、yaml 基本语法、命名空间、标签与选择器、上下文切换。后续章节不再重复讲解,直接引用本节。
1. kubectl 命令体系¶
kubectl 是操作 Kubernetes 的唯一命令行工具,命令格式统一为:
常用操作动词(作用于任意资源):
| 动词 | 作用 | 示例 |
|---|---|---|
get |
查看资源列表 | kubectl get pods |
describe |
查看资源详细信息 | kubectl describe pod nginx |
create |
创建资源(命令式) | kubectl create ns lab |
apply |
创建/更新资源(声明式,yaml) | kubectl apply -f deploy.yaml |
delete |
删除资源 | kubectl delete pod nginx |
logs |
查看 Pod 日志 | kubectl logs nginx |
exec |
进入 Pod 执行命令 | kubectl exec -it nginx -- bash |
edit |
在线编辑资源 | kubectl edit deployment nginx |
label |
增删改标签 | kubectl label pod nginx app=web |
常用输出参数:
| 参数 | 作用 |
|---|---|
-o wide |
显示更多列(IP、节点等) |
-o yaml |
输出资源定义(yaml 格式) |
-o json |
输出资源定义(json 格式) |
-n <namespace> |
指定命名空间 |
-A / --all-namespaces |
所有命名空间 |
-w |
持续监听变化 |
--dry-run=client -o yaml |
只生成 yaml 不创建(教学利器) |
2. 命名空间¶
命名空间(namespace)用于逻辑隔离资源。默认资源在 default 命名空间,系统组件在 kube-system。
查看集群状态
创建命名空间
查看命名空间
查看命名空间详细信息
查看命名空间的定义文件
3. yaml 基本语法与工作流¶
Kubernetes 一切资源都是声明式对象,用 yaml 描述"期望状态"。本手册的教学原则:简单操作用命令行,涉及多对象/复杂结构(卷、探针、多容器、Deployment 等)用 yaml 文件。
yaml 文件通用结构(所有资源一致):
apiVersion: apps/v1 # API 版本(资源类型决定)
kind: Deployment # 资源类型
metadata: # 元数据(名称、命名空间、标签)
name: myapp
namespace: default
labels:
app: myapp
spec: # 期望状态(每种资源不同)
...
推荐工作流(先命令生成骨架,再修改,再 apply):
kubectl create deployment myapp --image=nginx --dry-run=client -o yaml > myapp.yaml
nano myapp.yaml # 按需修改
kubectl apply -f myapp.yaml
用
--dry-run=client -o yaml生成骨架是避免手写缩进错误的推荐做法;kubectl apply是幂等的(可重复执行),kubectl create重复执行会报已存在。
资源类型与字段查询(写 yaml 前先查):
kubectl api-resources 输出示例(截取):
NAME SHORTNAMES APIVERSION NAMESPACED KIND
configmaps cm v1 true ConfigMap
deployments deploy apps/v1 true Deployment
namespaces ns v1 false Namespace
nodes no v1 false Node
pods po v1 true Pod
services svc v1 true Service
kubectl explain pods 输出示例(截取):
KIND: Pod
VERSION: v1
DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.
FIELDS:
apiVersion <string>
kind <string>
metadata <Object>
spec <Object>
写 yaml 的完整流程:
api-resources确认资源类型和 API 版本 →explain查看字段 →--dry-run生成骨架 → 修改 →apply。explain支持深层字段(如kubectl explain pods.spec.containers)。
4. 标签与选择器¶
标签(label)是挂在资源上的键值对,选择器(selector) 用它筛选/关联资源。这是后续所有章节(Deployment 管理 Pod、Service 选择后端等)的基础。
为命名空间添加标签:
查看标签:
修改标签(--overwrite):
删除标签(标签名后加 -):
按标签筛选(选择器):
5. Pod 基本操作¶
创建 pod(命令式)
查看新建的 pod
特别留意 pod 的 ip 地址,以及所在节点
使用 pod 的 ip 访问 pod
查看 pod 详细信息
查看 pod 的日志
查看 pod 的 yaml 文件
编辑 pod(查看/修改配置)
6. 清理¶
删除 pod
删除 ns
本章验收清单¶
完成本章后,逐项核对(全部 ✅ 才算达标):
| # | 检查项 | 验证命令 | 达标标准 |
|---|---|---|---|
| 1 | 节点就绪 | kubectl get nodes |
3 个节点(node1/node2/node3)全部 Ready |
| 2 | 系统组件正常 | kubectl get pods -A |
kube-system 等系统 Pod 全部 Running(或 Completed) |
| 3 | CNI 网络可用 | kubectl get pods -n kube-system \| grep calico |
calico 相关 Pod Running,无 CrashLoopBackOff |
| 4 | 镜像拉取链路通 | kubectl run test --image=busybox -- sleep 3600 |
test Pod 快速 Running,kubectl delete pod test 能清理 |
| 5 | 跨节点调度 | 查看第 4 项 Pod 的 NODE 列 | Pod 调度到 worker(node2/node3)而非 master |
| 6 | (延迟项)StorageClass | kubectl get storageclass |
本阶段为空属正常——按教学顺序在 实验 08 Lab 4 安装(此处不装) |
| 7 | kubectl 交互熟悉 | 完成「Kubectl 基础与公共操作」小节的命令 | 能独立完成查看/创建/描述/编辑/删除资源 |
| 8 | 网络连通性自查 | 回顾前置检查记录 | 了解本环境哪些海外域名可达、哪些走了加速/镜像源 |
附录F:2026-08 三节点实测记录(从空白机器到可用集群)¶
本节记录一次真实的从零安装全过程(3 台国内云主机,Ubuntu 24.04.4 / 4核8G,从空白系统开始),包含每一步的实际输出、踩到的坑、根因与修复。目的:让后来者知道"手册为什么这么写"——每个预防步骤背后都有一个真实事故。按正文步骤执行即可,本节供遇到问题时对照。
F.1 环境与最终版本¶
| 项 | 值 |
|---|---|
| 云厂商/规格 | 国内云 3 台,4核 / 7.4GB / 40GB,同内网(192.168.0.x/24) |
| 操作系统 | Ubuntu 24.04.4 LTS(内核 6.8.0-106) |
| 最终版本 | kubelet/kubeadm/kubectl v1.36.3、containerd 2.2.1、Calico v3.29.1、metrics-server v0.9.0(local-path 按教学顺序在 实验 08 Lab 4 安装) |
| 加速站 | docker.1panel.live(主)+ docker.m.daocloud.io(备) |
| 镜像仓库 | registry.aliyuncs.com/google_containers(registry.k8s.io 本环境不可达) |
F.2 实测时间线与踩坑记录¶
| # | 步骤 | 现象 | 根因 | 修复(已固化进正文) |
|---|---|---|---|---|
| 1 | 前置检查 | docker.io 直连超时;registry.k8s.io 可达(307);pkgs.k8s.io 可达(302);dl.k8s.io 返回 v1.36.3 |
国内网络对海外源连通性参差 | 前置检查第3步:加速站预测试 + 版本探测,输出主站/备站/镜像仓库三选 |
| 2 | 步骤5 init | init 卡 wait-control-plane,journalctl -u kubelet 报 failed to get sandbox image "registry.k8s.io/pause:3.10.1" |
kubelet 创建 Pod 沙箱默认从 registry.k8s.io 拉 pause;--image-repository 只作用于控制面组件、不覆盖 kubelet 的沙箱镜像;且 kubeadm 预热的 pause 是 3.10.2 而 kubelet 内置默认要 3.10.1(版本不一致) |
步骤5 ②:ctr 拉国内 pause + 把常见版本(3.10/3.10.1/3.10.2)全部 tag 成 registry.k8s.io/pause:*,重启 kubelet 即可,不要急着 reset |
| 3 | init 失败后 | kubectl get nodes 报 Forbidden(User "kubernetes-admin" cannot list ...) |
v1.36 起 kubeadm 把 admin 组从 system:masters 改为 kubeadm:cluster-admins(走 RBAC binding);init 中断时该 binding 未创建 |
用 export KUBECONFIG=/etc/kubernetes/super-admin.conf(system:masters 超级组)救急 + 补建 kubeadm:cluster-admins binding |
| 4 | 步骤3 加速 | hosts.toml 配了但拉取仍直连 registry-1.docker.io 超时 |
TOML 双引号丢失导致配置解析失败、containerd 静默回退直连(自动化传输/复制时引号最易丢) | 步骤3 特别提示:引号不能丢 + cat 核对 |
| 5 | 步骤8 calico | calico-node 全部 ImagePullBackOff |
calico 三件套(cni/node/kube-controllers)在 docker.io,加速站对部分镜像 403:实测 docker.1panel.live 对 calico/cni 返回 403,对 node/controllers 正常 |
步骤8:3 台节点先预拉 + 多站 fallback 链(1panel 失败自动换 daocloud/jiaxin),tag 成本地镜像 |
| 6 | 步骤10 冒烟 | busybox 秒拉、跨节点调度成功 | — | 验证通过(见 F.3) |
| 7 | 实验 09 metrics-server | kubectl top node 报 Metrics API not available,pod 日志报 kubelet 证书校验失败 |
官方 components.yaml 的 --secure-port 参数实际是 10250(非早期版本的 4443),按旧版 sed 定位加 --kubelet-insecure-tls 会加不上 |
实验 05 Lab 1 用 kubectl patch 或按实际参数定位追加 --kubelet-insecure-tls |
| 8 | worker 重装 | node3 完全重置(reset + 清镜像)后按附录 D 脚本重装 | — | 端到端验证通过:脚本可用、pause 预防生效(join 后无 sandbox 报错)、fallback 链实测复现(cni 走 daocloud)、node3 重新 Ready |
| 9 | 自动化环境 | 附录 D 脚本在无 TTY 下 gpg 报 cannot open '/dev/tty' |
gpg 需要 TTY;自动化/脚本环境没有 | gpg 加 --batch(正文步骤4 + 附录D 已同步) |
F.3 冒烟测试结果(实验 02-09核心 Lab)¶
| 验证项 | 命令 | 结果 |
|---|---|---|
| Deployment 扩缩容 | kubectl create deployment nginx --replicas=3 + scale 5 |
✅ 3→5 副本 |
| Service 访问 | kubectl expose deployment nginx --port=80 + 集群内 curl ClusterIP |
✅ 返回 nginx 页面 |
| PVC 动态卷 | PVC(1Gi) → local-path 自动 PV → Pod 写文件 | ✅ Bound + 数据写入 |
| ConfigMap | create configmap + 挂载读取 | ✅ user= bob |
| SA + RBAC | create sa + create token + rolebinding | ✅ eyJ token + view 角色 |
| metrics-server | 部署 + kubectl top node |
✅ 三节点 CPU/内存数据 |
| HPA | kubectl autoscale --cpu=50%(旧语法 --cpu-percent=50 已弃用) |
✅ cpu: 0%/50% 有真实指标 |
F.4 结论¶
正文 + 附录 A-E 在实测环境下按步骤执行可一次成功(本记录中的坑均已在前置检查/步骤/故障清单中固化为预防或处理手段)。两个最关键的预防动作:① init 前注入 pause 沙箱镜像(坑 #2);② calico 预拉用多站 fallback(坑 #5)。新环境部署请务必先做前置检查第 3 步(加速站 + 版本预测试),不要跳过。