Kubernetes——k8s v1.24离线部署(Containerd + Calico)

📖 文章目录

  • [什么是 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 的核心价值

  1. 自动化运维:自动部署、回滚、扩缩容应用,减少人工干预
  2. 高可用性:支持多副本部署和故障自动恢复,保障业务连续性
  3. 资源优化:智能调度容器到合适的节点,提高资源利用率
  4. 服务治理:内置服务发现、负载均衡和配置管理能力
  5. 生态丰富:拥有庞大的工具链和社区支持

为什么需要离线部署?

在实际生产环境中,特别是金融、政务、军工等对网络安全有严格要求的领域,服务器通常无法直接访问互联网。离线部署成为必须掌握的技能。针对 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.io namespace,否则 kubelet 看不到镜像会陷入 ImagePullBackOffdocker 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"

故障排查提示:

  1. ping 失败:检查网络接口 ip addr、防火墙 systemctl status firewalld、路由表 ip route
  2. 主机名解析失败:检查 /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: yes
  • NTP synchronized: yes(这是最关键的指标,表示已成功同步)
  • System clock synchronized: yes
  • Time zone: Asia/Shanghai
  • RTC 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 = truesandbox_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. 预检阶段:检查系统环境、版本、运行时 → 报错对应阶段 1/2/3
  2. 拉取镜像:拉取控制平面镜像 → 报错对应镜像不存在
  3. 证书生成:生成所有集群证书 → 报错对应主机名/IP 配置错误
  4. 配置 kubelet:写入 /var/lib/kubelet/config.yaml
  5. 启动静态 Pod:kubelet 启动 etcd、apiserver、控制器、调度器
  6. 等待控制平面就绪:等待 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-xxxcalico-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 --versionkubeadm versionkubectl 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 可正常调度、运行并具备网络通信能力

提示:此表格为快速指引,具体操作细节、命令参数及避坑要点请参考上文各章节详细说明。

相关推荐
崔亮的博客5 小时前
K8S GPU 调度——Operator 自动管理GPU
云原生·容器·kubernetes
小义_5 小时前
K8s 全网高频常用命令大全
docker·容器·kubernetes
雨辰AI11 小时前
K8s 国产数据库慢 SQL 自动监控|Prometheus+Grafana 可视化全落地
数据库·sql·安全·容器·kubernetes·grafana·prometheus
GGG76611 小时前
k8s的 Node and Cluster实战
容器·kubernetes
Geek-Chow11 小时前
在 EKS Fargate 上做微服务蓝绿发布:把切换点放在 ALB 上
微服务·云原生·架构·devops·蓝绿部署
半支烟隐 pzishuo12 小时前
Kubernetes Gateway API 完全指南
容器·kubernetes·gateway
兵bing12 小时前
Docker Compose 配置文件归纳总结-千问
运维·docker·容器
蓝田~13 小时前
Java镜像1.2GB→200MB的瘦身之路:Docker多阶段构建+K8s Pod OOMKilled(137)排查,后端部署避坑指南
java·docker·kubernetes
运维大师14 小时前
【K8S 运维实战】37-金融企业多集群落地
运维·金融·kubernetes