☰
Kubernetes v1.35.2极速安装与排障实战:从kubeadm到Calico的完整指南
2026/10/17 2:44:11 网站建设 项目流程

“Kubernetes 装起来不难,装明白难”——这句话我从 v1.20 时代一路用到现在,到了 v1.35.2 依然成立。哪怕你已经装过二十遍集群,换一个新版本、换一台新机器,依然会遇到千奇百怪的问题。kubeadm init 一把过的场景当然存在,但更多时候你会碰见 kubelet 疯狂重启、coredns 一直 Pending、节点状态 NotReady 这些老熟人。这篇就以 v1.35.2 为目标版本,从极速安装到故障排查,把我实际踩过的坑和最后沉淀下来的流程一起写出来。适合正在搭测试环境的人,也适合准备把集群交付到生产环境前想系统过一遍细节的同学。

我不会只贴命令然后说“照着敲就行”。有些选择如果你不知道为什么,出问题的时候连排查方向都没有。比如为什么用 kubeadm 而不是二进制手搓?为什么运行时选 containerd 而不是 Docker?为什么网络插件推荐 Calico 而不是 Flannel?这些决策会直接影响你后面几个小时是顺利还是折腾。下面我们一关一关过。

1. 安装前的整体设计思路

1.1 组件选型:为什么选 kubeadm + containerd

装 Kubernetes 主流有三条路:二进制手动部署、发行版自带的部署工具(如 kubeadm)、第三方全家桶(比如各种离线交付平台)。我的选择一直是 kubeadm,理由很简单:它足够底层,但又帮你把流程自动化了。

二进制部署能让你从证书、etcd、apiserver 到 kubelet 全部亲手配置,学习价值确实很好,但每次升级都要重来一遍,维护成本太高。全自动交付平台省事,可出了问题你很难搞清楚它到底做了什么,对集群失控是很危险的。kubeadm 恰好卡在中间:它把 apiserver、controller-manager、scheduler、etcd、kubelet 都用静态 Pod 或者 systemd 管理起来,初始化、加入节点、证书续期都有标准命令,但只要你有需求,依然可以手动改/etc/kubernetes下的配置。上手速度、可维护性、可解释性三方面都说得过去。

运行时方面,Docker 早就不再是 Kubernetes 默认的 CRI 了。v1.35.2 里最省心的选择就是 containerd。你可能听过“containerd 是 Docker 的一部分抽出来的”这种说法,对,它既轻量又专精,本来就是做容器运行的。很多人从 Docker 迁移过来不习惯的一点是:之前docker ps的命令全部要换成crictl,我后面会专门提到这个。如果你坚持用 Docker 做运行时,需要额外安装 cri-dockerd 这样的垫片,架构上多一层,踩坑概率高不少,没必要。

1.2 网络方案怎么选

Kubernetes 本身不实现 Pod 间网络,它只要求你装的 CNI 插件满足几个条件:Pod 之间能互通、所有节点能访问所有 Pod、不需要 NAT 就能访问。没有网络插件,集群就是一个半残废状态——控制面起来了,但 Pod 调度过去后网络是断的,coredns 会一直 Pending,这是新手最容易困惑的现象之一。

目前主流的 CNI 有这三类:

  • Flannel:走 VXLAN 封装,配置最简单,测试环境首选,性能一般。
  • Calico:基于 BGP 路由 + iptables 策略,性能好,支持 NetworkPolicy,生产环境最常选。
  • Cilium:基于 eBPF,功能最强,网络策略、可观测性都做得漂亮,但对内核版本有要求,调试门槛也高。

我的选择是:如果只是本地或单节点测试,用 Flannel 能少折腾几条命令;如果一开始就是按生产标准搭,直接上 Calico。这篇后面就以 Calico 为例展开,因为多数读者用这篇的流程是要搭正经环境的。

还有 Pod CIDR 网段的选择,这里务必认真。kubeadm init --pod-network-cidr=10.244.0.0/16需要和网络插件默认网段匹配。Calico 默认是192.168.0.0/16,Flannel 默认是10.244.0.0/16。如果你 pod CIDR 写的是10.244.0.0/16,然后装 Calico 不想调整配置文件,就会发现 Calico 内部的网段和你给 kube-apiserver 声明的网段对不上,Pod 网络永远不会正常。我一般固定用 Calico,所以--pod-network-cidr=192.168.0.0/16,这样 Calico 的默认 manifest 可以直接用,不用改任何东西。

1.3 资源规划与前置条件

集群节点建议:控制面 2C4G 起步,工作节点 2C4G 起步,磁盘至少 20G 可用。如果只是在笔记本上用 kind 或者 minikube 玩,可以忽略这篇的节点规划,但真要用 kubeadm 装完整集群,资源太少会出现各种诡异的超时问题,别在这个环节省。

操作系统层面,我一般用 Ubuntu 系列或者 Debian 系列,systemd 作为 init 系统,内核版本 5.x 以上。v1.35.2 对 containerd 的版本要求不算苛刻,用官方源装最新稳定版基本不会出错。但有几个前置条件必须逐项确认:

  1. 主机名有意义且唯一。集群里的主机名不能重复,且不能带下划线之类的特殊字符,我见过主机名带下划线导致的证书 CN 匹配问题。
  2. swap 必须关掉。kubelet 默认要求 swap 关闭,如果不关,kubelet 会直接启动失败。虽然高版本支持配置 swap 启用,但我强烈建议所有节点swapoff -a并注释掉/etc/fstab里的 swap 行。
  3. 服务器时间必须同步。Kubernetes 的证书校验对时间非常敏感,偏差超过 5 分钟就会频繁出现证书相关报错。
  4. 节点之间网络连通性。尤其要确认 6443、2379、2380、10250、10259、10257 这些端口没有被防火墙莫名其妙挡掉。

这些看似不起眼,实际上我排障时有相当一部分问题都出在“忘了关 swap”或者“主机名带前缀重复”。把这些前置条件整理成一个检查清单,装完之后你会感谢自己。

2. 极速安装实战:从零到集群就绪

2.1 基础系统配置要点

这里建议直接用一段脚本把这些配置操作统一处理掉,避免手工漏项。我在每台节点上都会依次做下面四类操作。

首先关闭 swap:

swapoff -a sed -i '/swap/s/^/#/' /etc/fstab

然后加载内核模块并配置系统参数:

cat <<EOF | sudo tee /etc/modules-load.d/k8s.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

overlay是容器运行时做镜像分层用的文件系统模块,br_netfilter让 iptables 规则能作用于 Linux 网桥上的流量。这两个如果不加载,后面 kubelet 和 CNI 的行为会非常怪异。net.ipv4.ip_forward不开启,Pod 之间的流量转发会出现问题。做完这套配置之后,最好reboot一次,重启用时也就一分钟,比后面排查莫名其妙的问题省时间。

时间同步我直接用chrony或systemd-timesyncd,取决于系统默认带的是哪个。检查方式很简单:

timedatectl status

只要看到System clock synchronized: yes就说明时间同步正常。

2.2 安装 containerd 并完成关键配置

我习惯先装 containerd,因为 kubeadm init 时会去拉 pause 镜像,运行时不装好,init 必失败。

安装部分,用 apt 装:

apt-get update apt-get install -y containerd

装完第一件事是生成默认配置并修改SystemdCgroup:

mkdir -p /etc/containerd containerd config default | tee /etc/containerd/config.toml

然后在/etc/containerd/config.toml里找到SystemdCgroup这个字段,默认是false,必须改成true。这个字段的意思是:让 containerd 用 systemd cgroup 驱动来管理容器的资源限制。kubelet 这边一般也配置成 systemd,两边驱动不一致,后患无穷——最典型的故障是“kubelet 报 cgroup 相关的错误导致 Pod 无法启动”,这种错误日志藏得很深,新手几乎无从下手。所以在这里一步到位。

另外要注意sandbox_image,它决定 pause 镜像从哪里拉。默认配置里写的是registry.k8s.io/pause:3.10之类,这个不是随便填的,Kubernetes 版本不同对应的 pause 版本也不同。我在离线环境或拉取困难的情况下,会把这里指向公司内部的镜像仓库,或者提前把 pause 镜像导入到节点再用ctr images import加载。这个细节在生产环境里极其重要——如果卡在 ImagePullBackOff,第一个就要查 pause 镜像。

改完配置重启 containerd:

systemctl restart containerd systemctl enable containerd

顺便装一个crictl,它是 CRI 兼容的容器命令行工具,后面排查容器状态基本靠它:

apt-get install -y cri-tools

crictl ps -a能看到节点上所有运行中的容器,crictl logs <id>能直接查看某个容器日志。排查效率远超 kubectl 一层层绕。

2.3 安装 kubeadm、kubelet、kubectl

Kubernetes 官方在pkgs.k8s.io提供了软件源,按系统版本添加好源之后直接安装即可。这里不说具体厂商源了,以官方 DEB 源为例:

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.35.2/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.35.2/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list apt-get update apt-get install -y kubelet kubeadm kubectl apt-mark hold kubelet kubeadm kubectl

最后一行apt-mark hold很重要。如果不 hold 住,apt-get upgrade时 kubeadm、kubelet、kubectl 会被系统自动升级。而 Kubernetes 的版本升级是有流程的,不是随便apt upgrade就能完成的。自动升级会导致 kubelet 和 apiserver 版本不一致,然后集群直接失控。安装工具后先 hold 住,是很多老手都会默默做但很少写出来的技巧。

装完后确认一下版本:

kubeadm version kubelet --version kubectl version --client

三个版本都要是 v1.35.2,保持一致。

此时 kubelet 可能因为缺少配置文件而处于启动失败状态,这是正常的,不要慌。系统里还没有/etc/kubernetes/kubelet.conf,它当然起不来。等 kubeadm init 或 join 完成之后,kubelet 才会有配置。

2.4 kubeadm init 初始化控制面

在控制面节点上执行初始化。我把常用参数拆开解释一下,方便你按自己的环境改:

kubeadm init \ --apiserver-advertise-address=192.168.1.10 \ --control-plane-endpoint=192.168.1.10 \ --kubernetes-version=v1.35.2 \ --pod-network-cidr=192.168.0.0/16
  • --apiserver-advertise-address:本机 IP,也就是 apiserver 对外广播的地址。
  • --control-plane-endpoint:如果后面要扩展多个控制面节点,这里应该填负载均衡器的地址,单控制面时可以直接填本机 IP。
  • --kubernetes-version:指定目标版本,不要省略,避免它默认拉一个你没想到的版本。
  • --pod-network-cidr:前面说过,要和 CNI 默认网段一致。

init 过程一般两三分钟。它会自动完成证书生成、etcd 启动、apiserver 等静态 Pod 启动,最后输出一大段信息。成功之后它明确提示你需要保存的信息有三样:/etc/kubernetes/admin.conf的使用方式、kubeadm join 命令、kubeadm token。千万别关完终端就把 join 命令丢了,我见过多次因为 token 丢失然后kubeadm token create重新生成的情况,虽然不麻烦,但没必要。

然后把 kubectl 配置拷贝到当前用户:

mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config

此时执行kubectl get nodes,应该能看到控制面节点处于NotReady状态,这是正常的。原因就是网络插件还没装。接下来装 Calico:

curl -L https://raw.githubusercontent.com/projectcalico/calico/v3.29.1/manifests/calico.yaml -o calico.yaml

如果你不能访问这个地址,也可以先把 calico.yaml 文件放到内网,从另一个能联网的机器下载后传进来。kubectl apply -f calico.yaml之后,所有 Calico 相关组件会在kube-system命名空间里以 DaemonSet 的方式跑起来。

等待一两分钟之后:

kubectl get pods -n kube-system kubectl get nodes

看到控制面节点Ready,coredns 也是Running,说明网络部分已经通了。

2.5 工作节点加入集群

工作节点上需要安装好 containerd、kubeadm、kubelet、kubectl 这些基础组件,配置好系统参数,不需要执行kubeadm init。然后执行 init 完成后打印出来的 join 命令:

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

join 命令执行时如果 kubelet 无法启动,可以先查一下 token 是否还在有效期。默认 token 有效期是 24 小时,如果超过这个时间,去控制面重新生成:

kubeadm token create --print-join-command

--print-join-command会直接把完整命令打出来,复制粘贴到工作节点上就能用,省得自己拼参数。这是我用下来最顺手的命令。

工作节点加入之后,在控制面执行:

kubectl get nodes

能看到两个节点都是Ready,集群搭建就算基本完成了。然后用一个测试应用验证调度是否正常,比如起一个nginxDeployment,扩容到 3 个副本,确认副本能打散到不同节点上跑起来。如果调度、网络、DNS 都正常,这套环境就可以交付使用了。

3. 生产环境要素补充

3.1 控制面高可用与证书续期

单控制面节点适合测试,生产环境建议至少三个控制面节点做 HA。做法是先把--control-plane-endpoint指向一个负载均衡地址,然后第一个节点 init 成功后,用kubeadm join --control-plane把其他控制面节点加进来。etcd 会自动组成集群,apiserver 也会注册到负载均衡后面。这个过程需要注意:负载均衡器必须支持 TCP 443 端口转发,健康检查指向 6443 端口;此外控制面节点的时间同步要求会更高,etcd 对时间一致性极其敏感,三节点里有一个时间漂移,选举就会乱。

证书续期是容易被忽略的长线问题。kubeadm 生成的证书有效期一般是一年,组件之间的通信证书到期后,节点会呈现一种“连接被拒绝”的状态。用以下命令可以一键续期所有证书:

kubeadm certs renew all systemctl restart kubelet

续期完成后还需要手动更新 kubeconfig:

kubeadm init phase kubeconfig admin --config=/etc/kubernetes/kubeadm-config.yaml

不过更规范的姿势是:在集群里部署 cert-manager 这类证书自动续期工具,或者用外部 PKI 体系管理。如果只是一个测试集群,可以写个 cron 脚本每个月跑一次证书检查和续期。证书过期导致的问题在现网上排起来非常隐蔽,因为一开始报错都是“超时”“拒绝连接”,很难直接想到是证书,建议从第一天就养成定期检查证书的习惯。

3.2 节点标签、污点与资源规划

集群搭建完,功能通了,但不等于生产可用了。工作节点往往有不同的用途,比如有的节点挂着 GPU,有的节点网卡性能更高,有的节点专门跑数据库。此时需要给节点打标签:

kubectl label node worker-01 node-type=storage kubectl label node worker-02 node-type=gpu

然后部署应用时,在 Pod 的 spec 里加上nodeSelector,就能让定向负载落到指定节点上。

污点的作用正好相反,它是“排斥”而不是“吸引”。所谓污点,就是让调度器默认不要往这个节点上调度 Pod,除非 Pod 显式容忍这个污点。典型场景是控制面节点,kubeadm 初始化时会给它打上node-role.kubernetes.io/control-plane:NoSchedule,所以普通业务 Pod 不会跑到控制面上去。如果你想在专用节点上跑一些特殊负载,比如监控、日志采集这类系统组件,可以给这些节点打相似污点,避免其他业务“蹭”资源。

节点层面的资源规划,我建议提前给命名空间配置ResourceQuota和LimitRange。前者限制命名空间总资源量,后者给每个 Pod 设置默认的 request 和 limit。没限制的研发环境是很容易被一个失控的测试脚本打满集群资源的,这种坑不用踩,配置一下才一页 yaml 的事。

3.3 版本升级与回滚思路

集群搭好之后迟早面临升级。v1.35.2 到 v1.36.x 的升级流程大概是:先升级 kubeadm,再升级 kubelet,最后执行kubeadm upgrade plan和kubeadm upgrade apply。顺序反了会出现 kubelet 和 apiserver 协议不匹配的问题。

Kubernetes 官方支持跨一个次要版本的升级,比如 1.34 可以直接升 1.35,但不能直接从 1.33 跳到 1.35。升级前务必先备份/etc/kubernetes,如果你的 etcd 是外部部署的,etcd 数据也要单独做快照。命令也很简单:

etcdctl snapshot save /backup/etcd-snapshot.db

升级完如果发现核心组件异常,回滚思路是:降级 kubeadm、kubelet 到原版本,用备份的/etc/kubernetes恢复配置,重启 kubelet,再恢复 etcd 快照。整个过程最好在变更窗口里做,并且在测试环境先完整演练一遍。我见过太多人直接在生产环境升级,然后因为一个特性开关差异导致长期处于“半坏不坏”状态,非常尴尬。

4. 排障实战:我自己踩过的坑

4.1 问题速查表

直接把最常见的问题做成了一张速查表,用的时候先对号入座,省去大量搜索时间。以下问题我全都亲自遇到过。

现象常见原因排查命令 / 处理方法
kubelet 启动失败swap 未关闭、cgroup 驱动不一致journalctl -u kubelet -f看日志;swapoff -a
coredns 一直 Pending网络插件没装或安装失败kubectl describe pod -n kube-system查看调度失败原因
节点 NotReadyCNI 没起来、kubelet 异常kubectl describe node看 condition,查 kubelet 日志
Pod 一直 ContainerCreating镜像拉取失败、卷挂载失败kubectl describe pod看事件;crictl ps -a
join 后节点 Ready 但 Pod 无法互相访问CNI 网段冲突或防火墙规则检查 pod CIDR 与 CNI 默认网段是否一致
apiserver 频繁重启etcd 异常或证书失效kubectl logs -n kube-system kube-apiserver
调度器把 Pod 都堆到一个节点缺少亲和性配置或所有节点无差异kubectl get nodes --show-labels检查标签
ImagePullBackOff镜像地址错误或镜像仓库鉴权失败kubectl describe pod查事件;crictl images确认本地镜像
KubeAPI 端口连接超时防火墙或安全组没放行nc -vz <ip> 6443测试端口连通性
token 已过期token 默认 24h 有效期kubeadm token create --print-join-command重新生成

这张表能解决大部分“装完连不通”的问题。注意我这里的重点是让你知道“现象 → 原因 → 动作”是连贯的,不要一看到报错就重装集群。重装只能解决不会排查的问题,但真正到了出问题频率高的生产阶段,重装一次的成本你也承受不起。

4.2 典型故障排查实录

分享一个我印象很深的案例。某次部署时,工作节点安装完成后kubectl get nodes发现节点是 NotReady。基础检查做了一遍:swap 已关、iptables 没问题、主机名不冲突。然后我journalctl -u kubelet -f看日志,才发现 kubelet 不断报错,说“Failed to run kubelet: failed to run Kubelet: misconfiguration: kubelet cgroup driver: "systemd" but systemd is not the container runtime cgroup driver”。

这个报错本质上就是我前面提到的 cgroup 驱动不一致。我节点上 containerd 的SystemdCgroup忘记改成true,结果 kubelet 用的是 systemd 驱动,运行时用的是cgroupfs,两边对不上。修改 containerd 配置、重启 containerd、删除旧的 kubelet 相关状态、重启 kubelet 后,节点才恢复 Ready。

这类问题的特点是:日志里没有任何“致命错误”字样,但节点就是起不来。排查顺序极其重要,先看 kubelet 的日志,再看容器运行时状态。很多人一上来就kubectl describe node,其实 node 层面的描述信息里不会告诉你底层 cgroup 驱动不匹配,你还是得回到节点上查日志。

4.3 常用排查命令集

把命令整理成一个顺手集,遇到问题照着跑一遍,基本能覆盖 90% 的情况:

# 查看 kubelet 实时日志(最常用的第一条) journalctl -u kubelet -f # 查看 kubelet 服务状态 systemctl status kubelet # 查看节点条件和事件 kubectl describe node <node-name> # 按命名空间查看所有 Pod kubectl get pods -n kube-system -o wide # 查看 Pod 详细事件 kubectl describe pod <pod-name> -n <namespace> # 查看容器日志 kubectl logs <pod-name> -n <namespace> kubectl logs <pod-name> -n <namespace> --previous # CRI 视角查看容器 crictl ps -a crictl logs <container-id> # 查看 CNI 相关 Pod kubectl get pods -n kube-system | grep calico # 查看 CNI 日志 kubectl logs -n kube-system <calico-pod-name>

有人说排障就是“看日志”,这没错,但要学会分层看。我的习惯路径是:先kubectl get nodes看大面,再kubectl get pods -A -o wide找到异常 Pod,然后kubectl describe看事件,最后落到 kubelet 或容器运行时日志。不要从一坨日志直接开盲盒。

另外,kubeadm reset是个双刃剑。它可以在节点上把 kubeadm 装的组件全部重置,相当于“卸载干净再装”。这个命令确实是排障时快速恢复环境的好帮手:先kubeadm reset -f,再手动清理/etc/kubernetes、/var/lib/kubelet、/var/lib/etcd这些目录,然后重新 init。但请记住,kubeadm reset不是修 bug 的办法,它是“站点重建”级别的操作。如果连 bug 原因都没搞清楚就 reset,下次大概率还会踩同一个坑。

5. 实操心得与避坑清单

5.1 从多次安装中沉淀的几点体会

第一,整个安装过程最容易出错的环节并不是kubeadm init本身,而是 init 之前的系统准备。我复盘过多次安装失败,几乎都是“忘记关 swap”“忘记加载 br_netfilter”“containerd 的 SystemdCgroup 没改成 true”这类低级问题。所以我的习惯是先写一个自动化脚本把系统配置做完,脚本执行完检查一遍再开始 init,五分钟的系统准备永远比三小时的排障便宜。

第二,网络插件一定要第一时间装。很多人 init 完成后看到节点没有 Ready,第一反应是重启 kubelet 或者检查防火墙,其实节点 NotReady 的常见原因很简单——网络插件没装。你想想,apiserver 都启动起来了,kubelet 也知道自己有配置了,但 kubelet 要和 CNI 一起干活才能上报节点状态,CNI 不在场,节点当然起不来。所以 init 成功后立刻装网络插件,能避免很多无用功。

第三,不要害怕使用crictl系列命令。它和 Docker 的命令几乎一一对应,不会也不会很复杂。你只需要记住crictl ps -a看所有容器、crictl logs看容器日志、crictl images看本地镜像,排障时带来的信息量比 kubectl 直查 Pod 还要多一层容器的视角。调过几次底层问题之后你就会爱上它。

第四,文档和注释一定要边做边留。kubeadm 的 join 命令、admin.conf 的备份位置、证书到期时间、节点标签规划,这些信息看似小事,但你半年后再来维护集群的时候,会发现当年的随手记录比记忆可靠一百倍。

5.2 核心检查项对照表

最后附上一张我在每次交付前都会逐项打勾的检查表,你可以直接拿去当团队验收清单用:

检查项完成标准备注
节点系统配置swap 已关闭、内核参数生效、时间同步正常所有节点统一执行
containerd 配置SystemdCgroup=true、sandbox_image 可拉取修改后必须重启
基础组件版本kubeadm/kubelet/kubectl 均为 v1.35.2安装后执行apt-mark hold
控制面初始化kubeadm init 成功,admin.conf 可用保存 join 命令
网络插件Calico 或对应 CNI 的 Pod 全部 Running节点状态必须是 Ready
工作节点加入kubeadm join 成功,节点 Ready检查 kubectl get nodes
功能验证示例 Deployment 正常调度、DNS 解析正常Pod 间互通用 busybox 测试
证书与 token证书有效期检查是干净的kubeadm certs check-expiration
资源配额命名空间配置了 Quota 和 LimitRange防止测试环境资源失控
备份机制etcd 快照备份任务已配置定期执行才能救命

这份检查表看起来很长,但实际上每项对应的系统命令也就一两条。把检查表固化到自己的部署流程里,比临时翻阅排障手册更有价值。毕竟“极速安装”的重点不是快在敲命令上,而是快在“一遍就通”上。我当初反复踩坑之后才意识到这一点,现在你如果能直接带走这张表,至少能帮你省掉一两天的折腾时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询