📖 文章目录
- [什么是 Kubernetes?](#什么是 Kubernetes?)
- [Kubernetes 的核心价值](#Kubernetes 的核心价值)
- 为什么需要离线部署?
- [1.1 kubeadm 部署的核心分层逻辑](#1.1 kubeadm 部署的核心分层逻辑)
- [1.2 环境规划](#1.2 环境规划)
- [表 1-1 Master 节点环境规划](#表 1-1 Master 节点环境规划)
- [表 1-2 Node01 节点环境规划](#表 1-2 Node01 节点环境规划)
- [表 1-3 Node02 节点环境规划](#表 1-3 Node02 节点环境规划)
- [1.3 全流程分步详解(1 主 2 从 + containerd + Calico + 离线环境)](#1.3 全流程分步详解(1 主 2 从 + containerd + Calico + 离线环境))
- [1.3.1 阶段 1:所有节点通用系统初始化(必须先做)](#1.3.1 阶段 1:所有节点通用系统初始化(必须先做))
- [1.3.2 阶段 2:所有节点部署 containerd 容器运行时](#1.3.2 阶段 2:所有节点部署 containerd 容器运行时)
- [1.3.3 阶段 3:所有节点安装 K8s 核心三件套](#1.3.3 阶段 3:所有节点安装 K8s 核心三件套)
- [1.3.4 阶段 4:仅 Master 节点执行集群初始化](#1.3.4 阶段 4:仅 Master 节点执行集群初始化)
- [1.3.5 阶段 5:配置 kubectl 与验证控制平面(仅 Master)](#1.3.5 阶段 5:配置 kubectl 与验证控制平面(仅 Master))
- [1.3.6 阶段 6:部署 Calico CNI 网络插件(最容易卡壳的一步)](#1.3.6 阶段 6:部署 Calico CNI 网络插件(最容易卡壳的一步))
- [1.3.7 阶段 7:Worker 节点加入集群](#1.3.7 阶段 7:Worker 节点加入集群)
- [1.3.8 阶段 8:集群最终验收](#1.3.8 阶段 8:集群最终验收)
- [1.4 常见踩坑汇总](#1.4 常见踩坑汇总)
- [1.5 部署流程步骤预览](#1.5 部署流程步骤预览)
什么是 Kubernetes?
Kubernetes(简称 K8s)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。它最初由 Google 设计,现在由云原生计算基金会(CNCF)维护。K8s 提供了一个强大的平台,能够管理跨多个主机的容器化应用,实现应用的自动化部署、弹性伸缩、服务发现和负载均衡等核心功能。
Kubernetes 的核心价值
- 自动化运维:自动部署、回滚、扩缩容应用,减少人工干预
- 高可用性:支持多副本部署和故障自动恢复,保障业务连续性
- 资源优化:智能调度容器到合适的节点,提高资源利用率
- 服务治理:内置服务发现、负载均衡和配置管理能力
- 生态丰富:拥有庞大的工具链和社区支持
为什么需要离线部署?
在实际生产环境中,特别是金融、政务、军工等对网络安全有严格要求的领域,服务器通常无法直接访问互联网。离线部署成为必须掌握的技能。针对 Kubernetes 1.24+ 版本,详细讲解在完全离线环境下使用 kubeadm 和 containerd 部署生产级集群的全过程。
⚠ 前置认知:docker images 里有 ≠ K8s 能用
从 K8s 1.24 开始,dockershim 被移除,K8s 只通过 CRI 接口跟 containerd 通信,只会去 containerd 的
k8s.io命名空间里找镜像。而 Docker 和 containerd 是两套独立的存储:
docker images看到的镜像 → 存在 Docker 自己的存储里K8s 通过 containerd 找镜像 → 只看
k8s.io命名空间所以即使
docker images里 10 个镜像全齐,K8s 依然会报ImagePullBackOff,因为它在 containerd 的k8s.io命名空间里啥都没看见。简单说:Docker 里的镜像是给 Docker 用的,K8s 看不见。
1.1 kubeadm 部署的核心分层逻辑
kubeadm 将集群部署拆解为四层依赖,自下而上逐层构建,下层出错上层必然卡死:
| 层级 | 核心组件 | 作用 | 失败现象 |
|---|---|---|---|
| 第 1 层 | 系统基础环境 | 内核、防火墙、SELinux、Swap、时间同步 | 预检报错、网络异常、节点失联 |
| 第 2 层 | 容器运行时 containerd | 真正创建运行容器,kubelet 通过 CRI 接口调用它 | 静态 Pod 无法启动,6443 端口无监听,初始化超时 |
| 第 3 层 | kubelet + kubeadm | 节点代理 + 集群安装工具,kubelet 负责启动所有 Pod | kubelet 循环崩溃,无法注册节点 |
| 第 4 层 | 控制平面 + CNI 网络 | apiserver/etcd 等核心组件 + Pod 跨节点网络 | 节点 NotReady,Pod 无法通信 |
版本不兼容 → cgroup 驱动不匹配 → 容器创建失败 → 控制平面起不来 → CNI 装不上 → 节点永远 NotReady。上述问题中的任何一个,都足以导致安装失败
1.2 环境规划
表 1-1 Master 节点环境规划
| IP | 主机名 | 角色 | 部署软件(离线包) | 服务/组件 | 监听端口 | 端口作用 |
|---|---|---|---|---|---|---|
| 192.168.30.125 | k8s-master01 | control-plane | containerd、kubeadm、kubelet、kubectl、etcd、CoreDNS、Calico | kube-apiserver | TCP 6443 | Kubernetes API Server |
| etcd | TCP 2379-2380 | etcd 客户端通信 | ||||
| kube-scheduler | TCP 10259 | 调度器自身健康/metrics | ||||
| kube-controller-manager | TCP 10257 | 控制器自身健康/metrics | ||||
| kubelet | TCP 10250 | Kubelet API | ||||
| Calico BIRD | TCP 179 | BGP 路由交换(Calico 默认) |
表 1-2 Node01 节点环境规划
| IP | 主机名 | 角色 | 部署软件(离线包) | 服务/组件 | 监听端口 | 端口作用 |
|---|---|---|---|---|---|---|
| 192.168.30.126 | k8s-node01 | worker | containerd、kubeadm、kubelet、kubectl、Calico 镜像、业务镜像 | kubelet | TCP 10250 | Kubelet API |
| kube-proxy | TCP 10256 | kube-proxy 健康检查 | ||||
| Calico BIRD | TCP 179 | BGP 路由交换 | ||||
| NodePort Service | TCP 30000-32767 | 对外暴露的业务服务端口 |
表 1-3 Node02 节点环境规划
| IP | 主机名 | 角色 | 部署软件(离线包) | 服务/组件 | 监听端口 | 端口作用 |
|---|---|---|---|---|---|---|
| 192.168.30.127 | k8s-node02 | worker | containerd、kubeadm、kubelet、kubectl、Calico 镜像、业务镜像 | kubelet | TCP 10250 | Kubelet API |
| kube-proxy | TCP 10256 | kube-proxy 健康检查 | ||||
| Calico BIRD | TCP 179 | BGP 路由交换 | ||||
| NodePort Service | TCP 30000-32767 | 对外暴露的业务服务端口 |
⚠️ 离线部署核心约束 :所有节点的镜像都必须提前导入到 containerd 的
k8s.ionamespace,否则 kubelet 看不到镜像会陷入ImagePullBackOff。docker images里有镜像 ≠ K8s 能用------K8s 1.24 起移除了 dockershim,只通过 CRI 接口在 containerd 的k8s.io命名空间里找镜像。
1.3 全流程分步详解(1 主 2 从 + containerd + Calico + 离线环境)
所有操作严格区分「所有节点执行」和「仅 Master 执行」,按顺序操作。
1.3.1 阶段 1:所有节点通用系统初始化(必须先做)
前置
依赖包下载地址:
bash
https://download.csdn.net/download/qq_44769717/93227487
解压到/opt目录下
bash
tar -xvzf k8s-offline-full.tar.gz
1. 主机名与域名解析
集群内节点名必须唯一,且所有节点能通过主机名互相通信:
bash
# Master 执行
hostnamectl set-hostname k8s-master01
# Node1 执行
hostnamectl set-hostname k8s-node01
# Node2 执行
hostnamectl set-hostname k8s-node02
# 所有节点配置 hosts 解析
cat >> /etc/hosts << EOF
192.168.30.125 k8s-master01
192.168.30.126 k8s-node01
192.168.30.127 k8s-node02
EOF
解读 :cat >> /etc/hosts << EOF 以追加方式写入本地 DNS 解析。离线环境没有 DNS 服务器,集群内节点互访(如 etcd 选主、kubelet 注册)全靠 hosts 解析,三条记录每个节点都必须有。
连通性测试脚本(完整版):
bash
# 测试所有节点主机名解析和网络连通性
for node in k8s-master01 k8s-node01 k8s-node02; do
echo "=== 测试节点: $node ==="
# 测试主机名解析
echo -n "主机名解析: "
if ping -c 1 -W 1 $node > /dev/null 2>&1; then
echo "✓ 成功"
else
echo "✗ 失败"
fi
# 获取对应 IP 并显示
ip=$(grep "$node" /etc/hosts | awk '{print $1}')
echo "对应 IP: $ip"
# 测试 IP 连通性
echo -n "IP 连通性: "
if ping -c 1 -W 1 $ip > /dev/null 2>&1; then
echo "✓ 成功"
else
echo "✗ 失败"
fi
# 显示 hosts 文件中的条目
echo -n "hosts 记录: "
grep "$node" /etc/hosts || echo "未找到"
echo ""
done
# 额外验证:反向测试(从当前节点到各节点)
echo "=== 当前节点到其他节点的连通性 ==="
current_host=$(hostname)
echo "当前节点: $current_host"
for ip in 192.168.30.125 192.168.30.126 192.168.30.127; do
echo -n "连接到 $ip: "
if ping -c 1 -W 1 $ip > /dev/null 2>&1; then
echo "✓ 可达"
else
echo "✗ 不可达"
fi
done
# 验证主机名唯一性
echo ""
echo "=== 验证主机名唯一性 ==="
echo "集群节点主机名:"
for node in k8s-master01 k8s-node01 k8s-node02; do
echo "$node"
done
解读 :ping -c 1 -W 1 表示只发 1 个包、超时 1 秒,适合批量快速探测;脚本依次验证「主机名能否解析 → 解析出的 IP 是否正确 → IP 是否可达」三个层面,任何一环失败都会在后续 kubeadm 预检中暴露。
连通性测试脚本(简化版,更实用):
bash
echo "开始测试集群节点连通性..."
for node in k8s-master01 k8s-node01 k8s-node02; do
printf "%-15s %-20s" "$node" "$(grep $node /etc/hosts | awk '{print $1}')"
if ping -c 1 -W 1 $node > /dev/null 2>&1; then
echo "✓ 连通正常"
else
echo "✗ 无法连通"
fi
done
# 验证 hosts 配置完整性
echo ""
echo "hosts 文件配置验证:"
cat /etc/hosts | grep -E "k8s-master01|k8s-node01|k8s-node02"
故障排查提示:
- ping 失败:检查网络接口
ip addr、防火墙systemctl status firewalld、路由表ip route - 主机名解析失败:检查
/etc/hosts文件权限、DNS 配置cat /etc/resolv.conf
2. 关闭防火墙与 SELinux
bash
# 关闭防火墙并禁用开机自启
systemctl stop firewalld
systemctl disable firewalld
# 临时关闭 SELinux
setenforce 0
# 永久关闭 SELinux
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
解读:
stop立即停止防火墙,disable取消开机自启,两条都要执行。firewalld 会拦截节点间 6443、10250、Calico BGP(179)等端口通信,生产环境通常用安全组/iptables 精细管控而非直接开防火墙。setenforce 0让 SELinux 当前立即进入 permissive 模式(不重启生效);sed修改配置文件保证重启后依然关闭。SELinux 开启时会阻止 kubelet 读取容器文件、挂载卷,是高频报错源。
3. 永久关闭 Swap
K8s 官方强制要求关闭 Swap,否则 kubelet 无法正常工作(不要用 --ignore-preflight-errors=Swap 跳过,属于隐患):
bash
# 临时关闭
swapoff -a
# 永久关闭(注释 fstab 中的 swap 行)
sed -i '/ swap/ s/^\(.*\)$/#\1/g' /etc/fstab
解读 :swapoff -a 关闭当前所有 swap 分区;sed 把 /etc/fstab 中含 swap 的行行首加 # 注释掉,防止重启后 swap 自动挂载。K8s 设计假设内存不足时直接驱逐 Pod 而不是用 swap 硬撑,开 swap 会导致调度器资源计算失真。
4. 加载内核模块与网络参数
这是 CNI 网络、Service 转发的底层基础,没开启会导致 Pod 网络完全不通:
bash
# 加载网桥过滤模块
modprobe br_netfilter
modprobe overlay
# 写入永久配置
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# 生效配置
sysctl --system
解读:
br_netfilter:让网桥流量能经过 iptables 过滤,是 Service(kube-proxy)转发和 Calico 策略生效的前提;overlay:containerd 使用 overlayfs 存储驱动的依赖。modprobe仅当前生效,建议配合/etc/modules-load.d/k8s.conf持久化。bridge-nf-call-iptables=1:二层网桥流量也走 iptables 规则,否则 Service 的 ClusterIP 转发失效;ip_forward=1:开启 IPv4 路由转发,Pod 跨节点通信必备。sysctl --system重新加载/etc/sysctl.d/下所有配置文件,使参数立即生效且重启保留。
5. 时间同步
集群证书、etcd 对时间差极度敏感,偏差超过 5 分钟会出现认证失败:
bash
# 启动 chronyd 服务
systemctl start chronyd
# 设置 chronyd 开机自启
systemctl enable chronyd
# 查看服务状态(可选,确认 Active 状态)
systemctl status chronyd --no-pager
解读 :所有节点都必须执行,保证开机自启。chrony 是 CentOS 7 默认的 NTP 客户端,--no-pager 直接输出状态不进入分页模式,方便脚本化查看。
6.时区设置
Kubernetes 集群通常要求统一使用 UTC 或统一的本地时区。在中国,通常设置为 Asia/Shanghai:
bash
# 设置系统时区为上海(东八区)
timedatectl set-timezone Asia/Shanghai
# 查看当前时区设置
timedatectl status | grep "Time zone"
解读:统一时区避免日志、证书有效期、CronJob 调度出现时区错乱。
时间同步状态验证------这是最关键的一步,需要确认时间不仅正确,而且确实在同步。
bash
#方法一:使用 chronyc 检查(推荐,最直接的方式)
# 1. 查看时间同步源
chronyc sources -v
# 2. 查看时间同步状态(核心指标)
chronyc tracking # Leap status 应为 Normal,Last offset 越小越好
关键指标解读(chronyc tracking):
- Reference ID:显示当前同步的时间服务器地址。如果是 203.107.6.88(阿里云)或 216.239.35.0(Google),说明正在同步公网时间。
- Stratum:层级。通常显示 3 或 4 是正常的(离基准时钟越近数字越小)。
- Last offset:最后一次同步的偏移量。这个值越小越好(通常在毫秒级别,如 0.000123)。
- RMS offset:偏移量的均方根。
- Leap status :应该是
Normal。
bash
#方法二:使用 timedatectl 检查
timedatectl status # 必须满足:NTP enabled: yes、NTP synchronized: yes
验证要点(必须全部满足):
NTP enabled: yesNTP synchronized: yes(这是最关键的指标,表示已成功同步)System clock synchronized: yesTime zone: Asia/ShanghaiRTC in local TZ: no(建议设为 no,避免双系统时间错乱)
bash
#方法三:集群内多节点一致性校验(For 循环)
# 在所有节点上执行,查看时间是否一致
for node in k8s-master01 k8s-node01 k8s-node02; do
echo -n "$node 时间: "
ssh $node "date '+%Y-%m-%d %H:%M:%S'"
done
1.3.2 阶段 2:所有节点部署 containerd 容器运行时
在 Kubernetes 集群里,master 和 node 都是"节点",都需要一个符合 CRI 标准的容器运行时来干活:
- Master:运行 API Server、Scheduler、Controller Manager 等控制面组件(它们本身也是容器)
- Node:运行业务 Pod + 网络插件(Calico 等)的 Pod
两者都离不开 containerd,所以这套步骤 node 节点要原封不动地再来一遍。
💡 简单理解 :K8s 集群里除了
kubeadm init(只在 master 跑一次)和kubeadm join(node 加入集群)这种"集群级"操作有主次之分外,容器运行时、kubelet 这类"节点级"组件,是每个节点都要独立安装的。
为什么 node 节点一样不能少:
| 组件 | Master 需要 | Node 需要 | 说明 |
|---|---|---|---|
| containerd | ✅ | ✅ | CRI 运行时,跑容器就离不开 |
| 配置 SystemdCgroup=true | ✅ | ✅ | kubelet 默认 systemd 驱动,不统一就沙箱创建失败 |
| 本地 pause 镜像 | ✅ | ✅ | 每个 Pod 都有一个 pause 沙箱容器 |
| 导入离线镜像 | ✅ | ✅ | Calico 等网络插件的 Pod 会被调度到 node 上跑 |
"cgroup 驱动不统一导致沙箱创建失败"的坑------node 节点如果不改 SystemdCgroup = true,照样会踩。所以这套配置在 node 上是必选项,不是可选项。
1. 安装 containerd
假设已通过 U 盘/内网仓库把 rpm 包和离线镜像 tar 包拷贝到了/opt节点上并解压(统一放在 /opt/k8s-offline/rpms 目录)。
bash
# 进入离线 rpm 包目录
cd /opt/k8s-offline/rpms
# 本地安装 containerd(yum 自动处理包间依赖)
yum localinstall -y containerd.io-1.6.21-3.1.el7.x86_64.rpm \
container-selinux-2.119.2-1.911c772.el7_8.noarch.rpm
解读:
- 使用
yum localinstall替代rpm -ivh,优势是 yum 会自动解析本地 rpm 包之间的依赖关系(containerd.io 依赖 container-selinux),避免rpm -ivh手动补依赖时报Failed dependencies的麻烦;-y自动确认,适合批量脚本化。 - 离线环境下
yum localinstall只解析命令行中给出的本地包,不会去连公网仓库。
安装后先确认版本:
bash
rpm -qa | grep containerd
containerd --version
预期输出:
[root@k8s-node01 rpms]# rpm -qa | grep containerd
containerd.io-1.6.21-3.1.el7.x86_64
[root@k8s-node01 rpms]# containerd --version
containerd containerd.io 1.6.21 3dce8eb055cbb6872793272b4f20ed16117344f8
解读:
- 如果输出是
containerd.io-1.6.21-3.1.el7.x86_64,说明版本完全匹配,直接往下走。 - 如果输出是更高版本(比如 1.6.28、1.6.33 之类),也没问题,containerd 已经在了,同样往下走即可。
- 只有在版本不对且想严格锁定 1.6.21 时,才需要先卸载再重装。
- 📌 离线部署 K8s 1.24+ 用 1.6.x 系列的 containerd 都没问题,不必纠结必须是 1.6.21。
2. 生成并修改核心配置(重中之重)
containerd 装好后,/etc/containerd/config.toml 默认是不存在的,需要手动生成:
bash
# 创建配置目录并生成默认配置
mkdir -pv /etc/containerd
containerd config default > /etc/containerd/config.toml
# 关键 1:cgroup 驱动统一为 systemd(不改必踩坑)
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
# 关键 2:指定本地 pause 镜像(离线环境必须改)
sed -i 's#sandbox_image=".*"#sandbox_image="registry.k8s.io/pause:3.7"#g' /etc/containerd/config.toml
# 验证修改生效
grep -E "SystemdCgroup|sandbox_image" /etc/containerd/config.toml
#如果不是3.7 则编辑文件/etc/containerd/config.toml 第61行进行修改
应看到:SystemdCgroup = true 和 sandbox_image="registry.k8s.io/pause:3.7"。
⚠️
pause:3.7必须和离线导入的 pause 镜像版本完全一致。如果离线包里是 pause:3.6 或 3.8,这里也要跟着改,否则启动 Pod 时会去公网拉镜像导致超时。
3. 启动服务并设置开机自启
bash
systemctl daemon-reload
systemctl restart containerd
systemctl enable containerd
#再次验证修改生效
grep -E "SystemdCgroup|sandbox_image" /etc/containerd/config.toml
4. 导入离线镜像到 k8s.io 命名空间(离线环境专属坑)
bash
# 业务基础镜像
ctr -n k8s.io images import /opt/k8s-offline/k8s-base-images.tar
# Calico 网络插件镜像
ctr -n k8s.io images import /opt/k8s-offline/calico-images.tar
# 验证
ctr -n k8s.io images list
⚠️
-n k8s.io绝对不能省。省了会导入到 default 命名空间,K8s 看不见;用docker load导入同样无效。
5. 确认 containerd 健康
bash
systemctl status containerd # 应为 active (running)
ctr version # 能看到 client 和 server 版本
1.3.3 阶段 3:所有节点安装 K8s 核心三件套
注意:做到这一阶段建议打个快照,以防后续出错
安装 kubelet(节点代理)、kubeadm(集群安装工具)、kubectl(命令行客户端)。kubelet 与控制平面版本偏差不能超过官方支持的 ±1 个小版本。
1. 安装指定版本
bash
cd /opt/k8s-offline/rpms
yum localinstall -y \
kubernetes-cni-1.1.1-0.x86_64.rpm \
cri-tools-1.24.0-0.x86_64.rpm \
*kubelet-1.24.17*.rpm \
*kubeadm-1.24.17*.rpm \
*kubectl-1.24.17*.rpm
💡 说明:yum 不支持
--nodeps。如果安装时卡在kubernetes-cni依赖上,把离线包里的kubernetes-cni-*.rpm放到同一目录一并yum localinstall(推荐;Calico 后续会自带 CNI 二进制,装上不影响)。
2. 设置 kubelet 开机自启
bash
systemctl enable kubelet
✅ 此时 kubelet 循环重启属于正常现象:它还没有配置文件,在等待 kubeadm 生成配置,集群初始化完成后会自动恢复正常。
3. 验证版本一致性
bash
kubelet --version
kubeadm version
kubectl version --client
三者必须全部为 v1.24.17,差一个版本都不要继续。
1.3.4 阶段 4:仅 Master 节点执行集群初始化
1. 初始化前确认(仅当之前 init 失败过)
必须先重置干净,否则会报端口占用、文件已存在:
注意:ipvsadm -C要安装ipvsadm才可以使用
bash
kubeadm reset -f
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X
ipvsadm -C
rm -rf /etc/kubernetes /var/lib/etcd /var/lib/kubelet $HOME/.kube
2. 执行初始化命令
bash
# 先安装conntrack-tools
cd /opt/k8s-offline/rpms
yum localinstall -y conntrack-tools-1.4.4-7.el7.x86_64.rpm
kubeadm init \
--apiserver-advertise-address=192.168.30.125 \
--kubernetes-version=v1.24.17 \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=10.244.0.0/16 \
--image-repository=registry.k8s.io \
--cri-socket=unix:///run/containerd/containerd.sock
核心参数说明:
--apiserver-advertise-address:apiserver 对外宣告的 IP,必须是节点本机 IP(192.168.30.125)--pod-network-cidr:Pod 的 IP 网段,必须和后续 Calico 配置的网段完全一致。注意:原文档为 192.168.0.0/16,与本次节点网段 192.168.30.0/24 冲突,已改为 10.244.0.0/16--cri-socket:指定 containerd 的 CRI 套接字,不指定会找不到运行时- 不要加
--ignore-preflight-errors=all,预检报错就是在提前提醒问题
初始化成功后会输出 kubeadm join 192.168.30.125:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx,请保存好该命令备用。
3. 初始化执行流程拆解
kubeadm init 内部按顺序执行 6 步,卡在哪一步就对应哪一层问题:
- 预检阶段:检查系统环境、版本、运行时 → 报错对应阶段 1/2/3
- 拉取镜像:拉取控制平面镜像 → 报错对应镜像不存在
- 证书生成:生成所有集群证书 → 报错对应主机名/IP 配置错误
- 配置 kubelet:写入
/var/lib/kubelet/config.yaml - 启动静态 Pod:kubelet 启动 etcd、apiserver、控制器、调度器
- 等待控制平面就绪:等待 6443 端口可用 → 超时 90% 是容器创建失败(cgroup 驱动/镜像问题)
1.3.5 阶段 5:配置 kubectl 与验证控制平面(仅 Master)
1. 配置管理员权限
bash
mkdir -pv $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
2. 验证控制平面
bash
kubectl get pods -n kube-system
正常输出:etcd、kube-apiserver、kube-controller-manager、kube-scheduler 四个静态 Pod 全部 Running。
✅ 此时节点状态为 NotReady 是正常中间状态:还没有部署 CNI 网络插件,kubelet 认为节点网络未就绪。
1.3.6 阶段 6:部署 Calico CNI 网络插件(最容易卡壳的一步)
⚠️ 死循环坑:节点 NotReady → 自带
node.kubernetes.io/not-ready污点 → Pod 无法调度 → 装不上 CNI → 永远 NotReady。
1. 破局:移除 Master 节点污点
单主集群推荐直接移除 Master 的调度污点,让网络组件可以调度上去,解开死循环:
bash
kubectl taint nodes k8s-master01 node-role.kubernetes.io/master-
末尾的 - 是固定语法,代表删除该污点。
2. 修改并部署 Calico 配置
离线环境确保:yaml 文件是标准 K8s 资源定义(不能是下载错的 HTML 网页);Calico 镜像已导入 containerd 的 k8s.io 命名空间;网段和 --pod-network-cidr 一致。
bash
# 删除旧的calico.yaml,把新的calico.yaml放在/opt/k8s-offline这个目录当中
# 新的calico.yaml以存放在资源包当中
kubectl apply -f /opt/k8s-offline/calico.yaml
3. 监控启动状态
bash
kubectl get pods -n kube-system -w
等待 calico-node-xxx 和 calico-kube-controllers-xxx 全部变为 Running。
4. 验证节点就绪
bash
kubectl get nodes
正常输出:k8s-master01 状态变为 Ready。
1.3.7 阶段 7:Worker 节点加入集群
Worker 节点必须提前做完「阶段 1 ~ 阶段 3」的所有操作,否则加入会失败。
1. Master 节点生成加入命令
bash
kubeadm token create --print-join-command
2. Worker 节点(192.168.30.126、192.168.30.127)执行加入命令
必须追加 --cri-socket 参数,适配 containerd 运行时(替换为实际的 token 和 hash):
bash
kubeadm join 192.168.30.125:6443 --token xxx \
--discovery-token-ca-cert-hash sha256:xxx \
--cri-socket=unix:///run/containerd/containerd.sock
# 直接复制Master生成的Token和sha256(生成的就是以上格式,可以直接使用)到30.126和30.127即可
3. Master 验证节点加入
bash
kubectl get nodes -w
两台 Worker 节点会自动调度 calico-node Pod,随后依次变为 Ready。
1.3.8 阶段 8:集群最终验收
bash
# 1. 所有节点状态为 Ready
kubectl get nodes -o wide
# 2. 所有系统 Pod 正常运行
kubectl get pods -n kube-system
# 3. 创建测试 Pod 验证网络
kubectl run test-nginx --image=nginx
kubectl get pods -o wide
1.4 常见踩坑汇总
| 序号 | 问题 | 说明 |
|---|---|---|
| 1 | 版本不兼容 | kubelet 与控制平面版本差超过 ±1 个小版本,直接不兼容 |
| 2 | cgroup 驱动不统一 | containerd 用 cgroupfs、kubelet 用 systemd,导致 runc 无法创建容器,控制平面静态 Pod 全部启动失败 |
| 3 | 镜像命名空间错误 | 离线镜像未导入 k8s.io 命名空间,K8s 识别不到镜像 |
| 4 | Calico 文件错误 | 下载的 yaml 实际是镜像站的 HTML 网页,不是 K8s 资源清单 |
| 5 | 调度死循环 | Master 节点污点 + NotReady 污点,导致 CNI Pod 无法调度,节点永远 NotReady |
| 6 | 中断残留 | 中途 Ctrl+C 终止初始化,端口和文件残留,导致下次 init 直接报错 |
| 7 | 未指定 CRI 套接字 | kubelet 无法自动识别 containerd,对接不上运行时 |
| 8 | Pod 网段与节点网段冲突 | 节点为 192.168.30.0/24 时不能再使用 192.168.0.0/16 作为 Pod 网段 |
1.5.部署流程步骤预览
下表汇总了从零开始部署一个 1 主 2 从 Kubernetes 集群的核心步骤,便于快速回顾:
| 阶段 | 步骤名称 | 执行节点 | 核心操作 | 关键产出/验证 |
|---|---|---|---|---|
| 阶段 1 | 系统初始化 | 所有节点 | 主机名、hosts、防火墙、Swap、内核参数、时间同步 | 节点间网络互通,系统参数符合 K8s 要求 |
| 阶段 2 | 部署容器运行时 | 所有节点 | 安装并配置 containerd,导入离线镜像 | containerd --version 正常,ctr -n k8s.io images list 有镜像 |
| 阶段 3 | 安装 K8s 核心组件 | 所有节点 | 安装 kubelet、kubeadm、kubectl,并设置开机自启 | kubelet --version、kubeadm version、kubectl version --client 均为 v1.24.17 |
| 阶段 4 | 初始化 Master 节点 | 仅 Master | 执行 kubeadm init,指定 Pod 网段和 CRI 套接字 |
获得 kubeadm join 命令,控制平面 Pod 全部 Running |
| 阶段 5 | 配置 kubectl | 仅 Master | 复制 admin.conf 到 ~/.kube/config |
kubectl get pods -n kube-system 可列出控制平面 Pod |
| 阶段 6 | 部署 CNI 网络插件 | 仅 Master | 移除 Master 污点,应用 Calico YAML | kubectl get nodes 显示 Master 状态为 Ready |
| 阶段 7 | Worker 节点加入集群 | 仅 Worker | 在各 Worker 节点执行 kubeadm join 命令 |
kubectl get nodes 显示所有节点状态为 Ready |
| 阶段 8 | 集群验收 | 仅 Master | 检查节点状态、系统 Pod 并创建测试应用 | 测试 Pod 可正常调度、运行并具备网络通信能力 |
提示:此表格为快速指引,具体操作细节、命令参数及避坑要点请参考上文各章节详细说明。