news 2026/9/12 23:38:00

Kubernetes 1.31 一键部署:Containerd + kubeadm 脚本化实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kubernetes 1.31 一键部署:Containerd + kubeadm 脚本化实战

入行这些年,我最大的感悟是:装 Kubernetes 这事儿,本身一点不玄乎,真正费时间的往往是那些反反复复的“手动重复劳动”——关交换分区、装运行时、对版本、改配置、等镜像,然后踩一遍别人早就踩过的坑。尤其是跑到 1.31 这个版本,Docker 时代遗留的那套“先装 Docker 再装 K8s”的旧习惯已经彻底过时了,官方默认的容器运行时就是 Containerd。这篇博文我把整个部署过程收敛成一个可以直接跑的一键式脚本,你只要改几个环境变量,执行完基本就是一个能用的 Kubernetes 1.31 集群。适合想快速搭一套环境做验证、学 kubeadm 流程、或者带新同事入手的场景。

1. 整体设计与思路拆解:为什么这套方案值得直接抄作业

动手写脚本之前,我先把设计逻辑说清楚。这套部署方案不是把网上零散的教程拼在一起,而是有一个明确的取舍思路:尽可能贴近 kubeadm 的官方默认路径,同时把那些容易反复出错的“脏活”全部脚本化

1.1 为什么运行时选 Containerd 而不是 Docker

很多刚接触 Kubernetes 的人会惯性以为“先装 Docker 再装 K8s”是唯一正道,因为网上老教程太多,这个习惯一直延续到现在。但 Kubernetes 从 1.24 版本开始就把 dockershim 从代码里彻底移除了,换句话说K8s 官方已经不再内置对 Docker 的直接支持。现在的标准做法是:Kubernetes 通过 CRI(Container Runtime Interface)直接对接 Containerd,中间少了一层 Docker Daemon 的转发。

用一张简单的对比表就能看清差别:

项目DockerContainerd
与 K8s 关系需要额外适配层,已被官方移除K8s 直接通过 CRI 对接
进程模型Docker Daemon + 守护进程轻量守护进程,面向容器运行时
镜像构建支持不支持(构建交给 buildkit 等)
命令行工具dockerctr、crictl、nerdctl
资源占用相对较重更轻,生产环境首选

打个比方:Docker 是一台带全套厨具的餐车,Containerd 是专门负责“出餐”的后厨。K8s 只需要有人把菜端出来就行,不需要餐车的其他功能。所以 1.31 时代老老实实用 Containerd,既省资源又少出兼容性问题。

1.2 为什么选择 Kubernetes 1.31 这个版本

版本选择也是门学问。1.31 属于当前维护期内的稳定版本,它在 1.30 的基础上补齐了一批问题,生态兼容性已经很成熟。对于想正经搭环境的人来说,选太新的版本(比如刚发布的 RC 版)容易碰到某些组件没跟上导致的小毛病,选太老的版本又要面对支持周期快到期的焦虑。1.31 处于“新但已被广泛验证”的区间,是快速部署和学习的合适选择。

另外从实用角度看,我要用 kubeadm 部署,就必须保证 kubeadm、kubelet、kubectl 三者版本一致。如果三者版本错开,kubelet 和 kubeadm 之间偶发的不兼容问题会让你排查到怀疑人生。所以我的脚本里把K8S_VERSION="1.31.0"设成一个变量,三个组件统一装同一个小版本。

1.3 一键式部署脚本的定位:能跑,但更要能看懂

我也看到过很多“一键脚本”是跑到最后没人知道干了什么,出问题时两眼一抹黑。所以我这个脚本做了折中:所有关键参数都放在文件顶部,中间每个阶段都加注释和输出提示。你直接执行没问题,但我也建议你至少完整读一遍脚本,知道每个步骤在干什么,出问题才能按提示去查日志。

这套方案的目标是:

  • 新机器从零开始,一条命令完成基础环境和 K8s 组件安装
  • 初始化控制平面,装好网络插件
  • 打印出 worker 节点加入集群的命令
  • 不覆盖额外的高可用方案,不引入复杂的生产级配置

一句话:适合快速得到一个“能用”的集群,不适合直接当生产高可用标准来抄。

2. 部署前的环境准备:这几个参数搞错,后面全白搭

写脚本之前,先看一下需要满足的机器条件。很多人喜欢跳过这一步直接跑脚本,结果跑到一半发现磁盘不够或者内核模块没启用,反而更费时间。

2.1 硬件与操作系统要求

硬件方面没有想象中那么夸张,但也不要太抠门。我列一个对照表,你可以按自己的用途对号入座:

用途CPU内存磁盘节点数
单机学习2 核4G40G1
基础实验2 核4G50G2-3
稍微正式的测试环境4 核8G100G3

操作系统优先推荐 Ubuntu 22.04/24.04 或 Debian 12,我的主脚本也是基于 Debian 系写的。如果你用的是 Rocky Linux / AlmaLinux / CentOS Stream,可以用 RPM 系的软件源做法,后面我会单独给替换示例。最重要的一点是:三台机器的系统版本尽量一致,内核版本差异太大会让排错变得复杂

2.2 主机规划:IP 地址、主机名、端口

集群通信是走端口的,这一步千万别偷懒。假设我给三台机器做规划:

节点角色主机名IP 地址
控制平面k8s-master192.168.1.10
Worker 节点k8s-node01192.168.1.11
Worker 节点k8s-node02192.168.1.12

主机名不要带下划线,不要有大写,Kubernetes 对主机名的要求比较严格,解析不干净会引发 kubelet 注册失败。

端口方面,控制平面至少要保证这些端口可通信:

  • 6443:kube-apiserver,所有客户端入口
  • 2379/2380:etcd 客户端和集群通信
  • 10250:kubelet 健康检查和日志获取
  • 10257:kube-controller-manager
  • 10259:kube-scheduler
  • 30000-32767:NodePort 服务端口段

如果你用的是云厂商的安全组,直接放行这些端口;如果是本机测试又懒得搞防火墙规则,临时关掉防火墙跑通流程,但正式环境一定要按最小放行原则配置。

2.3 系统基础项:关交换分区、加载内核模块、同步时间

Kubernetes 的 kubelet 强烈建议关闭 swap,原因很直接:Pod 的资源申请和限制是基于内存做的,如果系统出现 swap 交换,内存资源隔离就没意义了。脚本里会执行swapoff -a并注释掉/etc/fstab中 swap 相关行,保证重启后也不会自动挂载。

内核模块方面,需要加载overlaybr_netfilteroverlay是容器镜像分层存储的基础,br_netfilter让 iptables 能处理桥接流量,这是 Kubernetes 网络通信正常工作的前提。然后再设置一组 sysctl 参数:

cat <<EOF | sudo 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

最后是时间同步。etcd 对时钟偏移极度敏感,如果节点间时间差超过几百毫秒,集群可能会出现各种莫名其妙的间歇性故障。Ubuntu 上装个 chrony 或者直接用 systemd-timesyncd,只要保证节点时间一致即可。

3. 一键式部署脚本的实现:从零到集群可用

这一节是整篇的核心。我把完整脚本贴在下面,并且用注释标明每个阶段在做什么。先声明:脚本按 Ubuntu/Debian 系写的,你在 Rocky/CentOS 上跑,需要把包管理器部分替换成 dnf/yum 版,我后面会专门给出差异说明。

3.1 完整脚本内容

#!/usr/bin/env bash set -euo pipefail # ===================================================== # 快速搭建 Kubernetes 1.31 + Containerd 一键式脚本 # 适用系统:Ubuntu 22.04 / 24.04、Debian 12 # 用法: # 1. 修改下方 MASTER_IP、POD_CIDR 等变量 # 2. 以 root 或 sudo 权限执行本脚本 # ===================================================== # ---------- 按需修改的参数 ---------- K8S_VERSION="1.31.0" MASTER_IP="192.168.1.10" POD_CIDR="10.244.0.0/16" SERVICE_CIDR="10.96.0.0/12" # 如果你的网络环境访问 registry.k8s.io 较慢, # 可以把 IMAGE_REPO 改成你本地网络更容易访问的镜像仓库。 IMAGE_REPO="registry.k8s.io" # ---------- 全局变量 ---------- LOG_PREFIX="[K8s-Deploy]" log() { echo "$(date '+%F %T') ${LOG_PREFIX} $*" } # ---------- 1. 系统基础配置 ---------- log "关闭 swap..." swapoff -a sed -i '/ swap / s/^/#/' /etc/fstab log "加载内核模块..." cat <<EOF | tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilter log "配置内核参数..." 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 >/dev/null log "设置主机名(可选,当前主机名:$(hostname))..." # 如果你需要统一主机名,取消下面这行并改成你的节点名称 # hostnamectl set-hostname k8s-master # ---------- 2. 安装 Containerd ---------- log "安装 Containerd..." apt-get update -y apt-get install -y ca-certificates curl gnupg install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ tee /etc/apt/sources.list.d/docker.list >/dev/null apt-get update -y apt-get install -y containerd.io log "生成 Containerd 默认配置..." mkdir -p /etc/containerd containerd config default | tee /etc/containerd/config.toml >/dev/null log "开启 SystemdCgroup..." sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml log "重启 Containerd..." systemctl enable containerd systemctl restart containerd # ---------- 3. 安装 kubeadm / kubelet / kubectl ---------- log "添加 Kubernetes apt 源..." curl -fsSL https://pkgs.k8s.io/core:/stable:/v${K8S_VERSION}/deb/Release.key | \ gpg --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${K8S_VERSION}/deb/ /" | \ tee /etc/apt/sources.list.d/kubernetes.list >/dev/null apt-get update -y apt-get install -y kubelet=${K8S_VERSION}-* kubeadm=${K8S_VERSION}-* kubectl=${K8S_VERSION}-* apt-mark hold kubelet kubeadm kubectl log "启动 kubelet(初始化前可能处于异常状态,正常现象)..." systemctl enable kubelet # ---------- 4. 拉取镜像并初始化控制平面 ---------- log "拉取 Kubernetes 组件镜像..." kubeadm config images pull --image-repository=${IMAGE_REPO} log "初始化控制平面..." kubeadm init \ --kubernetes-version=${K8S_VERSION} \ --control-plane-endpoint=${MASTER_IP} \ --pod-network-cidr=${POD_CIDR} \ --service-cidr=${SERVICE_CIDR} \ --image-repository=${IMAGE_REPO} \ --v=5 log "配置 kubectl..." mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config # ---------- 5. 安装网络插件 Flannel ---------- log "安装 Flannel CNI..." kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml log "等待控制平面 Pod 就绪..." kubectl wait --for=condition=Ready pods --all -n kube-system --timeout=300s || true log "部署完成!当前节点状态:" kubectl get nodes log "如果你要加入 Worker 节点,请复制下面的命令:" kubeadm token create --print-join-command

3.2 脚本里最容易写错的两个地方

第一个是SystemdCgroup。Containerd 默认生成的配置里这一项是false,而 kubelet 如果通过 systemd 管理(kubeadm 默认就是),两边 cgroup 驱动不一致,节点会一直卡在 NotReady。脚本里用 sed 直接把它改成true,这步漏掉,后面必然翻车。

第二个是--pod-network-cidr和 CNI 插件的关系。我用的是 Flannel,它默认使用的网段就是10.244.0.0/16。如果你用 Calico,通常会把网段设置成10.244.0.0/16或者192.168.0.0/16,具体以你安装的插件为准。这里一旦不一致,Pod 能创建出来,但分配到的 IP 和路由对不上,网络直接不通。

3.3 在 Rocky Linux / AlmaLinux 上怎么改

如果你用的是 RPM 系系统,脚本的第三部分要换成这样:

cat <<EOF | tee /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.31/rpm/ enabled=1 gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.31/rpm/repodata/repomd.xml.key EOF dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

Containerd 的安装也可以直接用系统源里的containerd.iocontainerd包,但一定要确认版本不要太老。我建议 RPM 系直接参考 Docker 官方仓库的安装步骤,或者使用系统自带的容器工具包,前提是版本至少为 1.7.x。版本太老的 Containerd 和 K8s 1.31 在 CRI 适配上有概率出现兼容问题。

3.4 脚本的实际执行流程

执行方式很简单:

chmod +x k8s-deploy.sh sudo ./k8s-deploy.sh

整个流程跑完大概需要 5-10 分钟,具体看网络速度和机器配置。看到最后kubectl get nodes输出里 master 节点是Ready状态,就说明控制平面起来了。

注意:如果最后一步kubectl wait超时,不要急着重启一切,先看 CNI 插件是否装好,再看 kube-system 命名空间里的 Pod 状态。这属于常见问题,我在第 5 节专门讲。

4. 集群初始化后的验证与基础配置

脚本跑完不等于万事大吉,至少要做一轮验证,确保新节点能正常调度 Pod。下面是我每次部署完都会走的检查清单。

4.1 检查控制平面核心组件

先看节点状态:

kubectl get nodes

期望输出大概是:

NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane 10m v1.31.0

如果 STATUS 是NotReady,先排查 kube-system 里的 Pod:

kubectl get pods -n kube-system

重点看 coredns 和 flannel 是否 Running。如果这两个组件都正常,节点从 NotReady 到 Ready 一般只是时间问题;如果一直 Pending 或 CrashLoopBackOff,问题大概率出在镜像拉取或 CNI 配置上。

4.2 kubectl 权限配置与自动补全

kubeadm init 完成后,默认情况下面向普通用户需要用admin.conf。脚本里已经帮你 copy 到了$HOME/.kube/config。如果你是 root 跑的,要记得给普通用户也配置相同内容。

顺便配置一下补全,省得后面敲命令太痛苦:

echo 'source <(kubectl completion bash)' >> ~/.bashrc source ~/.bashrc

4.3 用一条命令验证集群调度能力

光看节点 Ready 还不够,我习惯起一个临时 Pod 做冒烟测试:

kubectl create deployment smoke-test --image=nginx:alpine kubectl expose deployment smoke-test --port=80 --type=NodePort kubectl get pods -o wide

如果 Pod 顺利 Running,说明从镜像拉取、调度到网络分配整条链路都没问题。测试完删掉这两个资源就行:

kubectl delete service smoke-test kubectl delete deployment smoke-test

4.4 把 Worker 节点加进集群

如果脚本是在所有节点上跑的,那么你只需要在 Worker 节点上执行之前打印出来的 join 命令,就能把节点加进来:

kubeadm join 192.168.1.10:6443 --token <token> \ --discovery-token-ca-cert-hash sha256:<hash>

token 默认有效期是 24 小时,如果你跑完脚本忘了保存 join 命令,可以在 master 上重新生成:

kubeadm token create --print-join-command

Worker 节点加入后,回到 master 上kubectl get nodes就能看到新节点上线。

5. 实际部署中最容易翻车的几个问题

这部分是我最想写给你们的,全是实操中踩过的坑。我按出现频率从高到低排列。

5.1 镜像拉取失败或超时

症状:kubeadm init卡在拉镜像阶段,或者kube-system里的 Pod 频繁ImagePullBackOff

排查思路就三步:

# 1. 手动拉一下,确认仓库可访问 kubeadm config images pull --image-repository=registry.k8s.io # 2. 如果超时,就换成网络环境容易访问的镜像仓库,再重新 init kubeadm init --image-repository=<可访问的镜像仓库> ... # 3. 查看运行时里到底有没有镜像 crictl images

这里我要强调一句:改镜像仓库不是让你绕过网络限制,而是换一个更近、更快的源。部署前最好先确认自己所在网络到达registry.k8s.io的通畅程度,不通就直接把 IMAGE_REPO 替换成你网络环境更容易访问的镜像仓库,然后重新执行脚本。

5.2 cgroup 驱动不一致

症状:kubelet 一直在CrashLoopBackOffjournalctl -u kubelet里能看到类似failed to run Kubelet: failed to create kubelet component ... cgroup driver cgroupfs的报错。

这个问题的根源我在前面提过:Containerd 默认是cgroupfs,kubeadm 默认把 kubelet 配成systemd。两边不一致,kubelet 直接起不来。

解决办法就是保证两边统一,在/etc/containerd/config.toml中确认:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true

改完重启 containerd,再重启 kubelet:

systemctl restart containerd systemctl restart kubelet

5.3 kubelet 无法启动,日志没有任何有用信息

有些时候journalctl -u kubelet打出来的日志很少,拼命刷content of /var/lib/kubelet/config.yaml之类。这种情况第一个要看的文件是/var/lib/kubelet/kubeadm-flags.env,确认 kubelet 启动参数是否正确。

另外还要检查 kubelet 的证书目录/etc/kubernetes/pki,如果 kubeadm init 因为前面某步失败导致残留文件不完整,再跑 init 时会卡在证书校验。老练一点的处理方法:

kubeadm reset -f rm -rf /etc/cni/net.d rm -rf /var/lib/kubelet/*

然后再重新执行一次脚本,干干净净重来,比花半小时抠日志更快。

5.4 Flannel Pod 一直 CrashLoopBackOff

这个问题我遇到好几次,原因通常是旧的 flannel.1 网卡残留。当你重复执行脚本,或者切换过 CNI 插件时,节点上可能残留老网卡和路由,导致新的 flannel 起不来。

处理方式:

ifconfig flannel.1 down ip link delete flannel.1 # 清理 CNI 配置 rm -rf /etc/cni/net.d/*

然后重新 apply Flannel 清单。

如果还是不行,检查节点之间的内网是否放行了UDP 8472端口,这是 Flannel VXLAN 模式用的端口。

5.5 常见问题速查表

症状大概率原因处理方式
node 一直是 NotReadyCNI 没装好或 cgroup 不一致检查 kube-system Pod 状态;确认 SystemdCgroup=true
coredns PendingCNI 没就绪,或节点有污点先装 flannel/calico;kubectl describe pod看事件
镜像拉取失败仓库不可达换镜像仓库后 kubeadm reset 重来
join 命令失效token 过期在 master 上重新生成 join 命令
Pod 之间 ping 不通网段与 CNI 不匹配检查 POD_CIDR 与 flannel/calico 配置
kubectl 命令提示拒绝连接KUBECONFIG 没配置export KUBECONFIG=/etc/kubernetes/admin.conf

6. 写在最后的几句话

如果你是个新手,我强烈建议拿到脚本之后,先不要急着闭眼跑,而是打开脚本对着 kubeadm 官网文档一段一段读一遍。自动化的价值不是让你放弃理解,而是让你在理解之后免于重复劳动。我第一次搭集群全程手动敲,整整折腾了大半天,后来把所有步骤沉淀成脚本,后面每次重建环境基本都能在十分钟内搞定,这才是“一键式脚本”真正值钱的地方。

这套部署方案后续还有很多可以往外扩展的地方:比如把单控制平面改成高可用,给 etcd 加证书备份,用 Ansible 把脚本改造成批量执行,甚至把 Worker 节点初始化做成独立 playbook。但不管怎么扩展,底层核心还是这篇文章里这些东西。把它们吃透了,后面再怎么变,你都有一条清晰的排查路径,而不是遇到问题只会重装系统。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 23:37:22

普通人怎么用国产AI?真实场景下的工具匹配指南

1. 这不是“AI工具测评”&#xff0c;是普通人在真实生活里怎么用国产AI的实操笔记最近三个月&#xff0c;我帮身边27个朋友——包括刚退休的阿姨、初中语文老师、开小餐馆的老板、做电商客服的00后姑娘、还有两个正在准备考研的大学生——一起梳理他们每天实际要解决的问题&am…

作者头像 李华
网站建设 2026/9/12 23:37:16

PSO-LSTM粒子群算法优化LSTM超参数,提升时间序列预测精度

简介&#xff1a;基于粒子群算法优化长短期记忆神经网络的时间序列预测完整项目&#xff0c;包含可直接运行的源程序与配套数据集&#xff0c;面向计算机、电子信息、数学等专业学生&#xff0c;适用于课程设计、期末大作业、毕业设计等场景&#xff0c;也适合刚接触深度学习的…

作者头像 李华
网站建设 2026/9/12 23:33:51

AI论文写作工具对比:千笔与灵感风暴AI的专科生应用

1. 项目概述&#xff1a;AI论文写作工具的双雄对决2026年的学术写作领域正在经历一场前所未有的技术变革。作为一名长期关注教育科技发展的从业者&#xff0c;我亲眼见证了AI写作工具从简单的语法检查进化到如今能够辅助完成完整学术论文的跨越式发展。在众多工具中&#xff0c…

作者头像 李华
网站建设 2026/9/12 23:33:48

Spring Boot实战:从零搭建游戏创意工坊与推广平台

简介&#xff1a;面向Java后端与Web全栈初学者、毕业设计选题学生&#xff0c;这套基于Spring Boot的游戏创意工坊与推广平台源码包&#xff0c;完整实现了游戏创意分享、浏览、评论互动与开发者推广等核心业务&#xff0c;可帮助理解前后端分离开发与MySQL持久化设计。压缩包共…

作者头像 李华