1. 版本确认先行:1.33.7 的兼容性边界
1.1 版本号背后不只是更新日志
后台问 Kubernetes 1.33.7 安装部署的人又多了起来。装 K8s 这件事,说难不难,说简单也简单,但绝大多数半途放弃的人,都栽在版本兼容这类最基础的细节上。我现在的习惯是:任何大版本升级或全新部署,先别急着复制命令,先花十分钟读 release note 和 compatibility matrix。Kubernetes 1.33.7 这个版本,我以它为例来说,实际动手前一定要确认官方 release 页面里是否已经正式发布该小版本;如果还没有,就使用最近一个可用的 1.33.x 小版本,部署思路完全一样。
版本号之所以重要,是因为 K8s 的组件之间遵循严格的版本偏差策略:kubeadm、kubelet、kubectl 建议与控制平面保持在同一大版本内,小版本可以略低;容器运行时和 CNI 插件则各自维护一套兼容矩阵。1.33.7 如果只是控制平面的补丁版本,那它通常只修复稳定性问题,不会带来 API 变化,但对 etcd、containerd 的版本要求可能已经悄悄变了。用旧文档里的参数去装新版,经常会出现“文档没错、环境也没错,但就是装不上”的尴尬。
我建议把下面几项提前核对一遍:官方 release 页面的 CHANGELOG、kubeadm 最低支持的 kubelet 版本、containerd 与 CRI 的版本要求、以及你要用的 CNI 插件是否已经声明支持该版本。把这些写进部署前的 checklist,比装完出问题再查日志省时间得多。
1.2 兼容性核对清单
以下是我个人整理的关键版本对应关系,实际以官方兼容矩阵为准:
| 组件 | 推荐版本 | 备注 |
|---|---|---|
| Kubernetes 控制平面 | 1.33.7 | 以 release 页正式发布为准 |
| kubeadm / kubelet / kubectl | 与集群版本一致 | 建议安装 1.33.7-00 |
| 容器运行时 | containerd 1.7.x 以上 | CRI 端点走 unix socket |
| etcd | 内置镜像版本 | kubeadm 自动拉取,无需手动指定 |
| CNI 插件 | Calico v3.28+ 或 Cilium 1.16+ | 按组网需求选择 |
| CoreDNS | 随 kubeadm 镜像发布 | 无需单独管理 |
这三张表要在部署时反复确认:操作系统与内核、容器运行时与 K8s 版本、CNI 与 K8s 版本。K8s 官方对内核的要求正在逐步提高,1.33.x 建议内核 5.15 以上。如果你还在用老旧的 CentOS 7 内核 3.10,很多网络特性、cgroup v2 支持都会出问题。不要在这种地方图省事。
1.3 硬件与系统底线
控制平面节点建议 2 核 4G 以上,工作节点可以根据负载往上加。这里的“建议”不是随便说说:kubeadm init 时如果内存低于 2G,etcd 很容易被 OOM 干掉,控制平面直接不稳定。磁盘方面,etcd 对 IO 延迟敏感,SSD 是底线;节点上如果跑日志采集和本地镜像,/var分区最好留出充裕空间。
系统层面我通常选择 Ubuntu 22.04 LTS 或 Rocky Linux 9,内核 5.15 起步,容器运行时用 containerd。选操作系统的原则只有一个:你用起来最熟、能长期维护的那个,才是生产环境的最优解。这篇教程里的命令以 Debian/Ubuntu 系的 apt 为主,Rocky 系替换成 dnf 即可。
2. 环境初始化四件事:内核、Swap、模块与主机名
2.1 内核模块与系统参数
装 K8s 之前,第一步不是装 kubeadm,而是把 Linux 内核的网络转发和桥接参数调好。K8s 的 Service、Pod 网络依赖 iptables/ipvs,而流量从容器网桥出去时,需要 br_netfilter 模块在网桥层过滤 IPv4/IPv6 包。
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter 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 sudo sysctl --system这一步特别容易踩的坑是:执行sysctl --system后提示net.bridge.bridge-nf-call-iptables文件不存在。原因通常是你没有先modprobe br_netfilter,或者系统没装 bridge 相关内核模块。Kubeadm 的 preflight 检查会直接报这个错,提前在这里解决能省一次完整的初始化失败重试。
2.2 关闭 Swap 与配置 cgroup 驱动
K8s 官方要求 kubelet 和容器运行时使用同一套 cgroup 驱动。在 systemd 为主流的 Linux 发行版上,两个都应该使用 systemd cgroup 驱动。如果你一块用 systemd、一块用 cgroupfs,节点状态会一直 NotReady,kubelet 日志里全是 cgroup 相关的报错。
先关 Swap,并让重启后也不会自动开启:
sudo swapoff -a sudo sed -i '/ swap / s/^/#/' /etc/fstab这里要强调一句:swapoff -a只对当前会话生效,如果不改/etc/fstab,重启后 Swap 又挂上,kubelet 会拒绝运行。很多“重启之后节点就没了”的问题,根源就是这个。
2.3 主机名、DNS 与时间同步
K8s 集群内部靠主机名互相识别,控制平面初始化时指定的--control-plane-endpoint会被写进证书和 kubeconfig。建议在每台机器上把/etc/hosts写清楚:
192.168.1.10 k8s-master 192.168.1.11 k8s-node1 192.168.1.12 k8s-node2同时确认 DNS 能解析、时间同步开启。etcd 对时间漂移非常敏感,如果节点间时间差超过几百毫秒,选举和心跳都会出现诡异问题。装完系统后先跑timedatectl set-ntp true,这是成本最低的稳定性投资。
3. 容器运行时:为什么选 containerd,以及怎么配置 systemd cgroup
3.1 为什么不是 Docker
很多初学者会问:装了 Docker 是不是就有容器运行时了?在 K8s 1.24 之后,dockershim 被移除,Kubelet 直接通过 CRI(Container Runtime Interface)与容器运行时通信。Docker 自己不做 CRI,需要额外的 cri-dockerd 适配层,徒增复杂度。所以我建议直接装 containerd,它是 CNCF 的毕业项目,也是当前 K8s 默认支持的运行时之一,性能和稳定性都有保障。
如果你有强烈的 Docker CLI 使用习惯,也没有问题:containerd 提供的ctr和crictl可以覆盖大部分镜像管理需求,习惯之后反而更直接。
3.2 containerd 安装与默认配置修正
安装 containerd 最省事的方式是用 Docker 官方 apt 源,因为 containerd 的发行版源码包并不是每个发行版都齐全:
sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /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" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install -y containerd.io装完先别急着用,生成默认配置并修改 cgroup 驱动:
sudo mkdir -p /etc/containerd sudo containerd config default | sudo tee /etc/containerd/config.toml sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml sudo systemctl restart containerd第一处是 cgroup 驱动,改成 systemd 以匹配 kubelet;第二处是 sandbox 镜像地址。默认配置里的sandbox_image指向registry.k8s.io/pause:3.x,如果所处网络环境拉这个仓库很慢,可以改成你常用的镜像源或内网镜像仓库。改完配置后要记得 restart containerd。
3.3 pause 镜像与拉取慢的处理
每个 Pod 启动前,Kubelet 会先拉起 pause 容器用于持有网络命名空间。如果 pause 镜像拉不动,Pod 会一直处于 ContainerCreating。处理办法是在 containerd 配置里改sandbox_image,改成内网或加速镜像对应的 pause 版本,注意 tag 要与 containerd 默认的兼容性匹配。改完配置后执行:
sudo systemctl restart containerd sudo crictl pull mirror.example.com/pause:3.9想要快速验证 containerd 是否正常,执行sudo ctr version,能出来版本号就没有大问题。如果crictl还没有,可以等 kubeadm 工具一起装。
4. 控制平面初始化:kubeadm init 参数拆解与失败排错
4.1 安装 kubeadm、kubelet、kubectl
容器运行时就绪后,开始装 Kubernetes 组件。官方源已经迁移到pkgs.k8s.io,Debian 系用以下命令:
sudo apt-get install -y apt-transport-https ca-certificates curl gpg curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33.7/deb/Release.key | \ sudo 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:/v1.33.7/deb/ /" | \ sudo tee /etc/apt/sources.list.d/kubernetes.list > /dev/null sudo apt-get update sudo apt-get install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl最后一行apt-mark hold很重要。如果你某天执行apt upgrade,这三个组件会在不知情的情况下升级到不兼容的版本,集群重启后直接旧新组件混跑,排查起来非常头疼。生产环境里我见过不止一次因为自动升级导致集群出问题的事故。
4.2 kubeadm init 参数逐项拆解
初始化控制平面,参数不要背,但要知道每一个在做什么:
sudo kubeadm init \ --apiserver-advertise-address=192.168.1.10 \ --control-plane-endpoint=k8s-master:6443 \ --pod-network-cidr=192.168.0.0/16 \ --kubernetes-version=v1.33.7 \ --cri-socket=unix:///run/containerd/containerd.sock--apiserver-advertise-address:api-server 对外广播的 IP,一般是主节点 IP。如果节点有多个网卡,必须显式指定,否则 kubelet 可能绑定到错误网卡。--control-plane-endpoint:控制平面入口,单节点时可以填主节点名:6443,高可用集群则填负载均衡器地址。这个地址会写进证书 SAN,所以最好从一开始就想好,后期改很麻烦。--pod-network-cidr:Pod 网段,必须和 CNI 插件的默认网段一致或至少不冲突。我后面用 Calico 时习惯用 192.168.0.0/16,这是 Calico 默认网段。--cri-socket:告诉 kubeadm 用哪个 CRI 端点。如果系统里同时装过 Docker,这里不显式指定可能会认错。
初始化成功后,按提示执行三条命令配置 kubectl:
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config4.3 初始化失败的真实排除链路
我来还原一个常见的失败现场:执行kubeadm init后,卡在 preflight 阶段报:
[ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1处理链路是:先确认br_netfilter是否加载,lsmod | grep br_netfilter;没有就modprobe br_netfilter,并把模块写进/etc/modules-load.d/;再检查/proc/sys/net/bridge/bridge-nf-call-iptables是否等于 1;不满足就重新执行sysctl --system。如果这一步还是报错,看看是不是缺bridge内核模块,安装bridge-utils或对应的内核包即可。
另一类高频问题是镜像拉取超时。kubeadm init 会从镜像仓库拉取 etcd、api-server、controller-manager、scheduler、coredns 等镜像。网络不好的环境经常卡在这一步。可以先手动执行:
sudo kubeadm config images list --kubernetes-version=v1.33.7 sudo kubeadm config images pull --kubernetes-version=v1.33.7 --cri-socket=unix:///run/containerd/containerd.sock如果拉取缓慢,在 containerd 配置中配置 mirror 或改用加速源。逐个镜像拉取成功后再重新执行kubeadm init。每次失败后,用kubeadm reset清理现场,但不能在同一台机器上反复初始化却从不 reset,残留的 etcd 数据和证书会让下一次初始化更诡异。
5. CNI 网络插件:Calico 和 Cilium 的选型与部署要点
5.1 CNI 解决什么问题
控制平面初始化成功后,kubectl get nodes看到 master 是 NotReady 是很正常的,因为没有安装 CNI 插件。CNI 干的事是给每个 Pod 分配 IP、打通跨节点通信、实现网络策略。没有 CNI,Pod 和 Service 的网络就是空的,调度上去了也是永远不可用。
选型时我主要看三点:组网模式、运维复杂度、性能需求。Flannel 最简单,只做 Overlay 二层网络,但它不支持 NetworkPolicy;Calico 用 BGP 或 VXLAN,支持网络策略,覆盖面广;Cilium 基于 eBPF,性能和数据平面能力最好,但内核要求更高、上手门槛也更高。
5.2 Calico 部署要点
用 Calico 的话,推荐用官方 manifest:
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml kubectl apply -f calico.yaml要点有三个:--pod-network-cidr必须和 Calico 默认的192.168.0.0/16对齐;安装前确认 BGP 模式下节点之间 179 端口能互通;如果改了 Calico 的 IP 池网段,所有节点要统一,否则会看到 Pod 拿到错误网段的 IP。
部署后观察kubectl get pods -n kube-system,等calico-node全部 Running,master 节点状态通常会在一两分钟内变成 Ready。
5.3 Cilium 部署要点与性能收益
如果对网络延迟敏感,Cilium 是很值得尝试的方向。安装方式直接用 Helm:
helm repo add cilium https://helm.cilium.io/ helm install cilium cilium/cilium --version 1.16.0 \ --namespace kube-system \ --set ipam.mode=kubernetesCilium 会在节点上加载 eBPF 程序,因此需要内核开启CONFIG_BPF、CONFIG_DEBUG_INFO_BTF等选项。Ubuntu 22.04 的内核通常没问题,老内核就直接放弃吧。Cilium 的运维复杂度比 Calico 高一些,但换来的是 Service 转发路径短、可观测性数据丰富。小规模集群用 Calico 更省心,大规模或对网络性能敏感的场景再考虑 Cilium。
5.4 切换 CNI 时的残留清理
如果一开始装了 Flannel,后来想换成 Calico,千万不要只执行一个kubectl apply -f calico.yaml完事。要先删除旧 CNI 的 DaemonSet/Deployment,清理节点上的 CNI 配置目录/etc/cni/net.d,重启 kubelet,再把新 CNI 装上去。旧 CNI 没清干净的话,新建 Pod 可能同时被两套插件处理,网络直接乱掉。
6. 工作节点接入与用 nginx 验证集群
6.1 kubeadm join 的原理与 token 处理
控制平面 Ready 后,初始化成功的输出里会给出一段 join 命令。原理是这样:工作节点通过 kubeadm join 向 api-server 发起带 token 的请求,通过 bootstrap 机制拿到自己的身份证书,然后把 kubelet 连到控制平面。默认 token 有效期是 24 小时,过期后不需要重新初始化,重新生成即可:
sudo kubeadm token create --print-join-command在工作节点上执行后,回到主节点用kubectl get nodes等节点变为 Ready。如果节点一直 NotReady,先看systemctl status kubelet的输出,再用journalctl -u kubelet -f看实时日志。绝大多数节点加入失败都是 cgroup 驱动不一致、主机名解析不到、或者 join 命令里的 token 过期。
6.2 用 nginx 验证 Pod 调度与网络
节点全部 Ready 之后,我会先用一个最小负载验证集群是否真的能干活。nginx 是最好的探路石:
kubectl create deployment nginx --image=nginx:1.27 kubectl scale deployment nginx --replicas=2 kubectl expose deployment nginx --port=80 --type=NodePort kubectl get pods -o wide kubectl get svc nginx看到两个 Pod 在不同节点上 Running,Service 的 NodePort 端口能访问,说明调度、kube-proxy、CNI 都正常。这里如果 Pod 一直 ContainerCreating,优先kubectl describe pod nginx-xxxx看事件,大部分原因逃不出镜像拉不到、CNI 未就绪、资源不足这三类。
6.3 NodePort、LoadBalancer 与 Ingress 的取舍
验证完 NodePort,很多人会纠结要不要配 LoadBalancer 和 Ingress。我的建议是:离线调试用 NodePort 够了;云上直接用云厂商的 LoadBalancer;统一流量入口最终还是要上 Ingress。K8s 的 Service 本身解决的是 Pod IP 漂移带来的服务发现问题,真正对外发布能力要靠 Ingress Controller,比如 ingress-nginx。这些都可以在集群稳定后再逐步补齐,第一天的目标是把集群装起来、确认所有组件健康,不要一次上太多东西。
7. 装完不是终点:证书、备份、健康检查与维护习惯
7.1 装完先跑几条检查命令
我每次装完新集群,固定会跑下面这几条:
kubectl get nodes -o wide kubectl get pods -A -o wide kubectl get events --all-namespaces --sort-by=.lastTimestamp | tail -20 sudo kubeadm certs check-expiration前三条确认调度和运行状态,最后一条看证书剩余时间。K8s 的默认证书有效期是一年,但很多小团队装完就忘了,等证书过期后整个集群的 API 全部不可用,才手忙脚乱去查。kubeadm certs renew all可以续期,但最好还是把证书过期时间写进监控。
7.2 etcd 备份是最后一道防线
K8s 的集群状态全部存在 etcd 里,包括所有资源的定义、期望状态和配置。一旦误删 Namespace、错误应用了一个摧毁性的 manifest,日常操作无法恢复时,etcd 备份就是救命稻草。etcd 备份最实用的方式是快照:
sudo ETCDCTL_API=3 etcdctl \ --endpoints=127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ snapshot save /backup/etcd-$(date +%F).db建议配合定时任务每天跑一次,并同步到异机存储。恢复流程虽然简单,但不要在没有演练过的情况下第一次就上生产。
7.3 我长期带着的几个排查命令
最后分享几个我平时排查用的顺手命令。节点 NotReady 时,journalctl -u kubelet -f是第一个要看的地方;Pod 调度失败时,kubectl describe pod <name>能告诉你事件;DNS 解析异常时,先确认 CoreDNS Pod 是不是 Running,再用一个临时 pod 做nslookup kubernetes.default这类探针测试。
这些命令看着基础,但真实排障时比翻各种文档都管用。Kubernetes 1.33.7 的部署流程本身并不复杂,真正拉开稳定运维差距的,是这些“装完之后”的细节。你只要把环境、运行时、网络、验证、备份这条链路都走顺,整个集群的底座就算扎实了。