从 1.23 上车的 Kubernetes:在 Ubuntu 上完整部署实录
做集群部署的这些年,我有个习惯:每次 Kubernetes 发布新版本后,都不会急着跟风升级,而是先等社区跑一段时间,看看有没有共性问题。1.23 这个版本在我看来是比较特殊的一个,它是破天荒支持弃用 Docker 作为运行时之前,还保留着很多传统运维习惯的版本,也是很多企业生产环境至今仍在使用的主流版本。我自己在 Ubuntu 20.04 上从零部署过十几套 1.23 集群,有测试环境,也有实际承载业务的,过程中踩了不少坑,今天把这套流程完整写出来,希望给正准备上车的朋友一个参考。
这篇文章适合谁?适合那些已经会用 Linux、理解容器基本概念,但还没真正动手搭过 Kubernetes 集群的工程师。我会从环境准备开始,一路走到工作节点加入集群,并把常见故障的排查思路也放进来。我会尽量把每一步背后的原因说清楚,而不是只给命令。
1. 为什么是 1.23:在 1.24 移除 Docker 支持之前的一个重要版本
很多人会问,为什么偏偏选 1.23?现在都有更新的版本了。这里有个历史背景:Kubernetes 在 1.20 版本正式宣布弃用 dockershim(也就是让 kubelet 直接管理 Docker 容器的组件),1.23 是 dockershim 仍然可用的最后一个版本,到 1.24 就彻底移除了。虽然对大部分使用 containerd 的用户来说,Docker 是否被直接支持影响不大,但 1.23 毕竟是一个分水岭——之前的部署习惯、网络插件的兼容性、各种周边工具的状态,在这个版本上都是最成熟的。
从稳定性角度讲,1.23 属于 1.x 系列中比较"皮实"的版本。它的 API 还保留了不少旧版本字段,如果你手里有早期编写的资源配置清单,迁移成本比较低。还有一个隐性好处:很多云厂商的托管 Kubernetes 服务,在 1.23 时代经历了大规模验证,互联网上能搜到的踩坑案例也最丰富,真出问题的时候更容易找到参考。
我个人的建议是,如果你不是因为特殊兼容性要求必须用旧版本,1.23 也是一个非常值得练习和学习的版本。它不像 1.20 那么古老,很多新特性已经引入;又不至于像 1.28 之后那样,某些配置方式变得过于抽象,对新手不友好。在你完整掌握 1.23 之后,再往上升级,逻辑上会更顺。
2. 环境准备:操作系统、网络、时间的三个前置坑
2.1 操作系统版本与内核参数
首先明确基础镜像。我比较推荐 Ubuntu 20.04 LTS,原因很简单:生命周期长、内核版本合适(默认 5.4),与 Kubernetes 1.23 官方支持列表完全匹配。如果你手边是 Ubuntu 22.04,也不是不行,但内核变成了 5.15,个别驱动和模块需要额外确认,我后面会提到。不要用太小众的衍生版本,否则出了问题很难判断是你自己配置的问题还是系统底层的问题。
安装好系统之后,第一件事是确认主机名和 hosts 解析,这步很多人会忽略。我习惯把几台机器的 hostname 设置成有规律的名字,比如 k8s-master、k8s-node-1、k8s-node-2,然后在每台机器的/etc/hosts里把所有机器的 IP 和主机名都写进去。因为 Kubernetes 组件之间通信非常依赖主机名互访,尤其是 kubelet 向 apiserver 注册节点时,hostname 不对或者解析不了,会出现各种奇怪的连不上问题。
然后是内核参数。Kubernetes 要求网桥过滤和端口转发行为符合预期,需要加载br_netfilter模块,并把下面几个参数写入/etc/sysctl.d/k8s.conf:
cat <<EOF > /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执行完可以检查一下:
lsmod | grep br_netfilter sysctl net.bridge.bridge-nf-call-iptables如果grep没有输出,说明模块没加载,手动执行modprobe br_netfilter再查。这个模块不加载的后果很直接:集群内的 Pod 之间通信会出现随机超时,特别是使用 Overlay 网络时尤其明显。
2.2 网络规划与防火墙
部署前就要想好网段规划。Kubernetes 会涉及两个虚拟网段:一个是 Service 网段,一个是 Pod 网段。它们不能和宿主机物理网络重叠。举个例子,如果服务器所在局域网是 192.168.1.0/24,你再把 Pod 网段设成 192.168.0.0/16,不冲突,可以。但如果你把 Service 网段设成 192.168.1.0/24,那问题就大了,Kubernetes 会把原本访问网关的流量也当作集群内部流量处理,网络立刻瘫痪。
我这套环境用的规划是:
- Service 网段:10.96.0.0/12
- Pod 网段:10.244.0.0/16
Ubuntu 默认不带图形化防火墙,但有可能启用了 ufw。建议先确认一下,生产环境你可以根据自己的安全策略放行,但测试环境我建议直接关闭,避免干扰。Kubernetes 在初始化时如果发现某些端口被阻,会卡在某个节点注册阶段,报错又不会直接提示防火墙问题,排查起来很痛苦。
需要放行的关键端口我列在下面,如果之后接入了外部负载均衡器,再看情况调整。
| 方向 | 端口 | 用途 |
|---|---|---|
| 节点间 | 6443 | kube-apiserver |
| 节点间 | 2379-2380 | etcd |
| 节点间 | 10250 | kubelet |
| 节点间 | 10251 | kube-scheduler |
| 节点间 | 10252 | kube-controller-manager |
| 节点间 | 10255 | kubelet 只读端口(旧版本) |
2.3 时间同步和软件源
时间不同步是 Kubernetes 集群最隐蔽的隐患之一。etcd 对时间偏差非常敏感,默认情况下如果节点间时间差超过 1 秒,就会出现 leader 频繁切换或者请求失败。我在部署每台机器前都会执行:
apt install -y chrony systemctl enable --now chrony timedatectl set-ntp true然后检查chronyc sources -v,确保能看到^*开头的同步源。
软件源建议换成国内的镜像源,这样后续安装软件包会快很多。修改/etc/apt/sources.list的时候注意,Ubuntu 20.04 用的是focal版本代号,别写成其他版本的。我一般直接用阿里云或者中科大源,选一个固定下来就行,不用来回换。
3. 容器运行时准备:搭载 Containerd 的细节
3.1 为什么弃用 Docker 而用 Containerd
在 1.24 之前,你仍然可以把 Docker 作为 Kubernetes 的运行时,前提是 kubelet 通过 dockershim 与 Docker 通信。但 dockershim 本身是个临时组件,维护质量不高,官方也一直建议大家用 containerd。而且 containerd 的镜像管理、日志采集、性能表现都比 Docker 更轻量,kubelet 可以直接通过 CRI(容器运行时接口)与 containerd 通信,少一跳,故障点就更少。
我强烈建议从一开始就使用 containerd。即使你之前习惯用 Docker 的docker ps查看容器,在 Kubernetes 场景下,你更频繁使用的是crictl、ctr这些工具。早切换早适应。
3.2 安装并配置 Containerd
Ubuntu 20.04 自带的 apt 源里就有 containerd,但版本比较旧,和 Kubernetes 1.23 配合时可能会有兼容问题。我习惯从官方源安装最新稳定版。先把依赖装上:
apt install -y containerd但这里有个坑:apt 安装完的 containerd,默认配置里没有启用 CRI 插件。你需要手动生成完整配置:
containerd config default > /etc/containerd/config.toml然后修改两处关键内容。第一处是SystemdCgroup,把SystemdCgroup = false改成SystemdCgroup = true。原因是 Ubuntu 的 init 系统是 systemd,容器需要匹配 systemd 的 cgroup driver,否则 kubelet 会报 cgroup 驱动不一致的错误,节点状态会一直是 NotReady。
第二处是 sandbox 镜像地址。containerd 默认的 pause 镜像地址是registry.k8s.io/pause:3.6,这个地址在国内访问可能不通。你需要把它替换成可访问的地址,比如:
[plugins."io.containerd.grpc.v1.cri"] sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.6"改完后重启:
systemctl restart containerd systemctl enable containerd然后用crictl验证。如果还没装 crictl,可以先装:
apt install -y cri-tools crictl version正常能看到RuntimeName: containerd的相关输出。
3.3 Cgroup 驱动的选择:为什么不能只管一致
初学者最容易出现的问题是在 kubelet 里设置的 cgroup 驱动和容器运行时不一致。kubeadm 初始化时可以指定--cri-socket和节点配置,默认情况下 kubelet 的 cgroup 驱动是systemd,而 containerd 默认是cgroupfs。两者不一致时,kubelet 会把节点标记为 Ready 但 Pod 无法创建,或者直接报错。
还有一种情况是 systemd 本身管理 cgroup 的规则与 cgroupfs 冲突,导致系统负载过高。我的处理方式是统一使用 systemd 驱动,毕竟在 systemd 系统上这是最稳的。containerd 配置里已经改过了,kubelet 这边只需要在初始化参数中显式声明:
--node-ip=10.0.0.10 --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.6node-ip这个参数也很重要,尤其是在多网卡机器上。如果你的服务器有内网 IP 和外网 IP,不指定 node-ip 的话,kubelet 会拿默认路由对应的 IP,可能会把外网 IP 注册到集群里,导致别的节点访问不了。
4. 安装 kubeadm、kubelet、kubectl 的版本锁定
4.1 添加 Kubernetes 软件源
安装这几个工具,最重要的是版本指定。不能直接apt install kubelet,因为那样会装最新版本,和我们的 1.23 集群不匹配。我的做法是先添加 Kubernetes 官方 apt 源,然后列出可用版本,再精确安装。
先加签名密钥:
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.23/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg再写源文件(以 1.23 为例):
cat <<EOF > /etc/apt/sources.list.d/kubernetes.list deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.23/deb/ / EOF apt update注意这里的源路径是固定的core:/stable:/v1.23,不要自己乱改。如果你用的是旧版源方式(apt-key),Ubuntu 20.04 也支持,但毕竟apt-key已经弃用,新配置建议用上面这种方式。
4.2 指定具体版本安装
查看有哪些 1.23 的小版本:
apt-cache policy kubelet | grep "1.23"然后安装(版本号要以 apt 源列出的为准,比如 1.23.17-00):
apt install -y kubelet=1.23.17-00 kubeadm=1.23.17-00 kubectl=1.23.17-00这里有个小技巧:=后面的版本号必须完整匹配,不同小版本之间命令用法差异不大,但建议所有节点统一版本,否则升级或排查时会混乱。安装后立刻执行:
apt-mark hold kubelet kubeadm kubectl把这三个包锁定,防止哪天不小心执行apt upgrade把版本升级到另一个大版本,集群就再也起不来了。
4.3 使用 kubeadm init 初始化控制平面
在控制平面节点上,先执行镜像拉取的预检:
kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers这里指定了阿里云的镜像仓库,替代默认的k8s.gcr.io,不然大概率拉不下来。如果你的网络环境良好,也可以不指定。完成之后,执行:
kubeadm init \ --apiserver-advertise-address=10.0.0.10 \ --control-plane-endpoint=10.0.0.10:6443 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --kubernetes-version=v1.23.17 \ --image-repository registry.aliyuncs.com/google_containers参数说明一下:
--apiserver-advertise-address是控制平面节点对外提供 API 服务的地址,我这里用了内网 IP。--control-plane-endpoint如果后面要搭高可用集群,这里填负载均衡地址;单节点集群就填本机 IP,后边加:6443端口。--pod-network-cidr必须与你选择的网络插件需要的网段一致,我选的 Calico 默认支持 10.244.0.0/16,当然 Calico 也可以自定义,我习惯直接用这个值。--kubernetes-version不指定的话,kubeadm 会尝试拉取最新稳定版,导致与已安装的 kubelet 版本不匹配。
初始化过程大约几分钟,成功后会输出一段信息,包括如何配置 kubectl、如何让工作节点加入集群。这段输出是唯一的 join 凭证,记得保存到本地文件,最好复制到三处,丢了只能重新生成。输出末尾大致长这样:
Your Kubernetes control-plane has been initialized successfully! ... kubeadm join 10.0.0.10:6443 --token ... \ --discovery-token-ca-cert-hash sha256:...先按提示配置普通用户使用 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,这是正常的,因为还没安装网络插件。
5. 网络插件 Calico 的部署与调优
5.1 Calico 与网络模式选择
网络插件是 Kubernetes 集群的命脉。可选的方案很多:Flannel、Calico、Weave、Cilium 等等。我的选择是 Calico,理由有几点:性能好,支持网络策略,BGP 模式能适应较大规模的集群。Flannel 配置简单但功能弱;Cilium 强但依赖内核 BPF 特性,在 Ubuntu 20.04 旧内核上部分功能受限。Calico 是一个折中方案,在 1.23 时代成熟度非常高。
Calico 有两种工作模式:IPIP 模式(Overlay)和 BGP 模式(Underlay)。IPIP 模式对底层网络没有特殊要求,报文会被封装在隧道里,适合局域网环境,配置简单;BGP 模式要求所有节点能够互相建立 BGP 会话,适合跨网段、多机房场景。单集群局域网部署,用 IPIP 就够了。
5.2 部署 Calico 的完整步骤
下载 Calico 对应版本的清单文件。我用的版本是 3.23,与 Kubernetes 1.23 兼容性良好:
curl -O -L https://raw.githubusercontent.com/projectcalico/calico/v3.23.0/manifests/calico.yaml在应用之前,需要把清单里的 Pod 网段改成我们初始化时指定的网段。找到calico-ipam这个 ConfigMap,里面一般会有类似"10.244.0.0/16"这样的地址。如果下载的默认配置已经是这个网段,那就不用改。
然后应用:
kubectl apply -f calico.yamlCalico 组件会以 DaemonSet 方式在每个节点上运行,包括calico-node、calico-kube-controllers。等一分钟后,检查 Pod 状态:
kubectl get pods -n kube-system你会看到coredns-xxxxxxxxxx和calico-node-xxxxx等 Pod 陆续变为 Running。同时节点状态也会变成 Ready。
5.3 验证网络功能
网络插件部署完成不代表一定没问题。我每次都会做几个最基本的验证:
kubectl get nodes kubectl get pods -n kube-system kubectl run test-$RANDOM --image=nginx --restart=Never --port=80 kubectl expose pod test-xxxx --type=NodePort --port=80 --name=test-svc kubectl get svc用kubectl get pods -o wide查看测试 Pod 的 IP,再去另一个节点上ping这个 IP,能通就说明 Pod 网络的路由和封装是正常的。如果 ping 不通,检查 Calico 的calico-node日志,重点看 BGP peering 是否建立、IP 池是否有冲突。另一件很关键的事是查看 kube-proxy 是否正常。它是 DaemonSet,如果某个节点上的 kube-proxy 处于 CrashLoopBackOff,那么 Service 的转发就会失效,即使 Pod 网络通了,通过 Service 访问也会超时。
6. 工作节点加入集群:从 token 生成到节点就绪
6.1 生成新的 join token
控制平面初始化成功后,会有一个有效期为 24 小时的 token。如果超过时间,或者你直接忘了保存,也不用重新初始化集群,在控制平面节点上重新生成即可:
kubeadm token create --print-join-command这个命令会输出完整的kubeadm join命令,包含一个全新的 token 和 discovery-token-ca-cert-hash。如果你想设置 token 的有效期,可以加--ttl=0(表示永久),但生产环境不建议,毕竟 token 是集群的入场凭证,越短越安全。
另外,如果你的控制平面节点有多个,还需要额外为每个控制平面节点生成 cert 信息,这个后续再说,单控制平面场景用上述命令就够了。
6.2 工作节点的预处理
每台工作节点都需要重复前面提到的环境准备:装 containerd、装 kubelet/kubeadm/kubectl(可以用 apt-mark hold 固定版本)、时间同步、sysctl 参数。然后执行控制平面节点输出的 join 命令:
kubeadm join 10.0.0.10:6443 --token xxxxx \ --discovery-token-ca-cert-hash sha256:xxxxxxxx如果在 join 之前节点上还有以前的 Kubernetes 残留配置,最好先清理干净:
kubeadm reset -f rm -rf /etc/cni/net.d iptables -F && iptables -t nat -F这个不是每次都必须做,但如果同一个节点反复加入失败,大部分时候是之前的网络配置和kubelet缓存残留导致的。特别是/etc/cni/net.d目录,Calico 如果写入了旧的配置,新的节点加入时会认为网络已经配置好,从而跳过 Pod 网络创建。
6.3 加入集群后的检查
在控制平面节点上执行:
kubectl get nodes正常情况下,工作节点状态会先变为NotReady,然后在 10 秒到 30 秒内变成Ready。如果一直处于NotReady,去工作节点上查看 kubelet 状态:
systemctl status kubelet journalctl -u kubelet -f观察日志里是否有关于 CNI 配置、cgroup 驱动、证书过期之类的关键字。最常见的是container runtime is down,说明 kubelet 连不上 containerd,检查/var/run/containerd/containerd.sock是否存在,以及 containerd 是否启动。
等所有节点 Ready 后,我用几组简单命令验证集群整体功能:
kubectl get nodes -o wide kubectl get pods -A kubectl get svc -A再部署一个简单应用测试服务发现:创建一个 Deployment,暴露成 ClusterIP 服务,然后进入另一个 Pod 里用服务名访问。如果通了,说明 DNS 和 kube-proxy 都正常。
7. 踩坑实录:从失败日志到问题定位的完整链路
部署 Kubernetes 不可能一次成功,下面这几个坑是我自己反复遇到过的,写出来能帮你省下大量排查时间。
7.1 案例一:初始化时卡在"等待 kubelet 启动"
症状:kubeadm init执行后卡在[kubelet-start] Waiting for kubelet to start很长时间,最后超时失败。
排查思路:先看 kubelet 为什么没起来。执行:
journalctl -u kubelet -f systemctl status kubelet我遇到的一次是/var/lib/kubelet/config.yaml里指定的 cgroup-driver 与 containerd 不一致。日志里会出现类似failed to run Kubelet: misconfiguration: kubelet cgroup driver: "systemd" but systemd cgroup driver is not set的提示。解决办法很简单,重新初始化前手动改掉这个小配置文件,或者直接用正确的kubeadm init参数(比如加--cri-socket=/run/containerd/containerd.sock)重新来。
另一个常见原因是 swap 未关闭。Kubernetes 从 1.23 开始,如果检测到 swap 未关闭,kubelet 会直接罢工。Ubuntu 默认没开 swap,但如果你手动创建的 swap 分区或 swapfile 没关,也会出问题。执行:
swapoff -a sed -i '/ swap / s/^/#/' /etc/fstab7.2 案例二:coredns 一直 Pending
症状:kubectl get pods -n kube-system里 coredns 一直显示Pending不跑起来。
Pending 意味着调度器无法把 Pod 调度到任何节点上。最常见的原因是缺少资源,或者节点上有污点无法容忍。检查一下节点资源:
kubectl describe node <node-name>如果节点状态是 Ready,且没有污点,那很可能是 CNI 网络没有就绪,导致 Pod 无法分配 IP。注意,coredns 在 Calico 还没部署完成时处于 Pending 是正常的,因为 kubelet 在所有网络就绪之前会一直等待。等 Calico 的 Pod 变成 Running 后,coredns 会自动开始调度。
但如果 Calico 本身出现问题,比如calico-node一直在 CrashLoopBackOff,就需要深入了。我遇到过某台节点内核不支持nf_conntrack_proxy之类的模块,Calico 日志会报错。这种属于内核模块问题,重新编译内核不现实,更好的方案是换网络插件,比如 Flannel。Flannel 对内核要求低很多,普通环境都能跑。
7.3 案例三:工作节点加入后一直 NotReady
症状:join 命令执行成功,控制平面也能看到节点,但节点状态一直NotReady。
我遇到的最典型的根因是 kubelet 无法访问到 apiserver 的地址。检查工作节点上kubelet的启动参数,它会把 apiserver 地址写成--kubeconfig /etc/kubernetes/kubelet.conf,在这个文件里查看 server 字段:
grep server /etc/kubernetes/kubelet.conf如果这里的 IP 和控制平面实际 IP 不一致,比如你初始化时用了10.0.0.10,但工作节点却只能走某个网卡访问10.0.0.10,这时要么修正路由,要么在kubeadm join时加参数指定。再一个容易忽略的是工作节点的主机名。如果两台机器 hostname 重复,Kubernetes 会把它们当成同一个节点,后者加入时会直接报错。我必须在每台机器上检查hostname,保证全局唯一。
还有一种情况是证书哈希不匹配。你复制的 join 命令里--discovery-token-ca-cert-hash是从控制平面拿的,但如果有人重新生成过 CA 证书,那哈希就变了。重新跑一次kubeadm token create --print-join-command是最稳妥的。
7.4 案例四:Pod 之间可以 ping 通,但 Service 访问超时
这个问题在 Debian/Ubuntu 上比较常见。kube-proxy 默认使用 iptables 模式,如果主机上开启了反恶意软件或自己写了 NAT 规则,可能会导致 kube-proxy 的规则被覆盖。查看 kube-proxy 日志:
kubectl logs -n kube-system kube-proxy-xxxxx如果日志里有关于error acquiring contract的内容,大概率是 nftables 和 iptables 冲突。Ubuntu 20.04 默认使用 nftables 作为防火墙后端,但 kube-proxy 1.23 的 iptables 模式是基于 iptables 的,两边不能协同工作。解决办法是显式让 kube-proxy 使用 ipvs 模式,这个需要在 kubeadm 配置文件中指定。或者把系统自带的防火墙彻底关闭,保证 iptables 规则不被外部修改。
用 ipvs 模式需要在节点上加载内核模块:
modprobe ip_vs modprobe ip_vs_rr modprobe ip_vs_wrr modprobe ip_vs_sh然后在 kubeadm 的kube-proxy配置中加mode: ipvs。具体操作可以这样:初始化前,在配置文件中加入proxy: mode: ipvs,或者在初始化后通过 ConfigMap 修改,随后重启 kube-proxy。实测 ipvs 模式在高并发下稳定性更好,排查规则也更直观。
8. 部署完成之后的必要检查:从集群安全到运行机制
集群搭建完成不等于事情结束,还有几个容易被忽略的点,有些会影响安全,有些会影响后续维护。
8.1 kubeconfig 与权限隔离
控制平面初始化后,默认的管理员凭据在/etc/kubernetes/admin.conf,这个文件拥有集群最高权限。绝对不要把这个文件放到不安全的位置,也不要让普通用户直接拿到。日常操作建议创建最小权限用户,比如只授某个 namespace 权限的 ServiceAccount,这样即使泄漏,损失也可控。
另外,如果要从外部机器远程管理集群,把 admin.conf 拷贝到本地机器时,把里面的server地址改成外部可访问的地址。否则你会遇到一个很尴尬的问题:本机明明能访问集群,换个机器就不行了。
8.2 节点资源预留与 Pod 驱逐策略
Kubernetes 默认不会为系统进程预留资源,极端情况下 Pod 会把节点的 CPU 和内存耗尽,导致 SSH 都登不上。我习惯在每台节点的 kubelet 配置里设置system-reserved和eviction-hard。例如在/var/lib/kubelet/config.yaml中:
systemReserved: cpu: 500m memory: 1Gi evictionHard: memory.available: "500Mi"这样 kubelet 会主动驱逐 Pod,而不是让节点直接卡死。调完这些参数后需要重启 kubelet,并观察节点状态变化,确保没有误杀系统服务。
8.3 备份与升级思路
控制平面和 etcd 的数据备份是个大话题,但至少应该知道 etcd 数据目录在哪。在 1.23 中,如果使用 kubeadm 部署的 etcd,数据目录是/var/lib/etcd。最简单的验证性备份是:
etcdctl --endpoints=https://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 /data/backup/etcd-snapshot.db先验证集群可以正常备份,后续再考虑定期自动化。这个动作我在每个集群上线后第一时间执行一次,确保备份链路是通的。
9. 最后分享一个我实际部署时的小习惯
在整套流程跑通之后,我通常会把所有安装过程中执行过的命令整理成一个 shell 脚本,同时把每个节点的配置文件、证书备份、状态检查结果都记录在一个文档里。这样做的原因很简单:Kubernetes 的部署过程可以说是一次性的,很难保证将来重装时还能记得每一个细节。有了这些记录,下次不管是批量加节点还是整个环境重建,都能缩短大半时间。
特别是集群版本升级的时候,我一般会先在一个临时节点上模拟操作,确认无误后再在真实节点上执行。用 kubeadm 升级到更高小版本时,官方文档里的步骤很详细,但实际操作中每次都会遇到一些意外,比如镜像拉不下来、证书过期、某个 API 字段被删除,都只能靠同时查看官方变更记录和线上日志来解决。
对于刚接触 Kubernetes 的同学,我还是建议先把 1.23 这套部署流程完整走一遍,不要跳过网络插件和踩坑验证。因为后面很多运维问题,本质都出在这两个环节。把基础打牢了,再去看更高版本的变化,就会觉得一切顺理成章。