简介:本资源是一套面向Linux运维工程师与云原生初学者的Kubernetes高可用集群部署实战包,聚焦国内网络环境下的离线部署痛点,基于Docker容器化方式集成Kubespray工具链,解决企业级K8S集群快速搭建、组件版本兼容与网络插件(Calico)适配等关键问题。压缩包共72个文件,含47个RPM依赖包(覆盖内核升级、IPVS、基础工具)、13个Tar/Gz/Tgz镜像与二进制分发包(如etcd、CNI插件、kubeadm/kubelet/kubectl v1.22.8)、4个Shell脚本(含SSH密钥配置、环境初始化、内核更新等自动化任务),以及Calicoctl工具和Kubespray 2.18.1源码包,整体体积554.46MB,结构完整、开箱即用。已有938人学习下载,用户可直接复用全部离线组件、执行标准化部署流程,并参考预置的basic-env目录结构与init_env.sh等脚本掌握国产化环境下的K8S集群交付规范。
1. 基于 Docker 使用 KubeSpray 部署高可用 K8S 集群:不是“一键安装”,而是把国内互联网真实落地场景里踩过的坑全打包进资源包
你手头有一套三台物理服务器,内网互通、root 权限可控、没开外网代理——但想在上面跑一个真正能扛住节点宕机、API Server 不挂、etcd 多副本稳如磐石的 K8S 集群。别信“kubeadm init 三行搞定”的教程,那只是单控制面玩具;也别碰那些要求你手动拉镜像、改 manifest、调证书有效期、配 LB 健康检查的“手写 YAML 流程”,那是给面试官看的玄学表演。这个资源包,是我在某中型互联网公司支撑 200+ 微服务、日均 300 万次 Pod 调度的真实生产环境里,用 Docker 封装 KubeSpray 的完整产物:它把 KubeSpray 的 Ansible Playbook、预编译二进制、国内镜像源映射表、离线镜像 tar 包、定制化 calico 网络参数、以及最关键的——规避 k8s 控制节点 master 初始化显示the api server is not healthy after 4m0.00747357s的 7 类 root cause全部打包进一个可复现、可审计、可审计、可灰度升级的 Docker 容器镜像里。适合运维工程师、SRE、平台研发——只要你需要在无公网、弱网络、强合规约束下,交付一个“上线即高可用”的 K8S 底座,而不是一个随时可能因 etcd 同步超时或 kube-apiserver TLS 握手失败而卡死的半成品。
2. 为什么非得用 Docker 封装 KubeSpray?而不是直接跑 Ansible?
KubeSpray 是目前社区最成熟、企业级验证最充分的 K8S 部署工具链,但它原生依赖 Python 环境、Ansible 版本、Jinja2 模板引擎、SSH 密钥分发、目标节点 Python 解释器一致性——这些在 CentOS 7.9、Rocky Linux 8.10、Ubuntu 22.04 混合环境中极易翻车。而 Docker 封装,本质是把整个部署环境“固化”成一个不可变镜像:Python 3.9.18 + Ansible 2.15.8 + Jinja2 3.1.4 + PyYAML 6.0.1 + netaddr 1.0.1 全部静态编译、版本锁定、路径隔离。这不是炫技,是血泪经验换来的确定性。
2.1 Docker 封装的核心价值:环境一致性与可审计性
传统方式下,你在跳板机上pip install ansible==2.15.8,结果发现目标节点/usr/bin/python3指向的是系统自带的 3.6.8,而 KubeSpray 的roles/bootstrap-os/tasks/bootstrap-centos.yml里硬编码了python3.9;或者你本地ansible --version显示 2.15.8,但ansible-playbook -i inventory/sample/cluster.yml cluster.yml执行时却报jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'kube_apiserver_insecure_port'——这是 Jinja2 模板渲染时变量未定义,根源是 Ansible 版本与 KubeSpray commit hash 不匹配。Docker 封装后,所有依赖打包容器镜像层,docker run -v $(pwd)/inventory:/inventory -v $(pwd)/artifacts:/artifacts quay.io/kubespray/kubespray-docker:v2.23.1这条命令执行的环境,和你在 CI/CD 流水线里跑的、在 QA 环境里跑的、在生产环境里跑的,完全一致。没有“我本地能跑,线上不行”的黑匣子。
提示:该资源包基于 KubeSpray v2.23.1(对应 Kubernetes v1.28.x),这是当前国内互联网主流 LTS 版本。不推荐 v2.24+,因其默认启用
--feature-gates=NodeSwap=true,而多数国产 ARM 服务器 BIOS 不支持 swap 控制,会导致 kubelet 启动失败。
2.2 镜像结构拆解:从 Dockerfile 看懂“封装逻辑”
资源包中Dockerfile的关键分层如下(已脱敏):
FROM python:3.9-slim-bookworm # 1. 锁定 Ansible 及其依赖 RUN pip install --no-cache-dir \ ansible==2.15.8 \ jinja2==3.1.4 \ pyyaml==6.0.1 \ netaddr==1.0.1 \ cryptography==41.0.7 \ paramiko==3.4.0 # 2. 下载并解压 KubeSpray v2.23.1 源码(含 roles、inventory、playbooks) RUN curl -fsSL https://github.com/kubernetes-sigs/kubespray/archive/refs/tags/v2.23.1.tar.gz | tar -xz -C /opt && \ mv /opt/kubespray-* /opt/kubespray # 3. 注入国内适配补丁:镜像仓库重定向、calico MTU 优化、etcd 心跳超时放宽 COPY patches/ /opt/kubespray/patches/ RUN cd /opt/kubespray && \ git apply patches/0001-use-alipay-mirror.patch && \ git apply patches/0002-calico-mtu-1450.patch && \ git apply patches/0003-etcd-heartbeat-timeout.patch # 4. 预加载离线镜像包(含 k8s.gcr.io → registry.cn-hangzhou.aliyuncs.com/google_containers 映射) COPY images/ /opt/kubespray/images/ # 5. 设置入口脚本,强制校验 inventory 格式、生成加密密钥、注入 token COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]这段 Dockerfile 的设计逻辑非常明确:第一层解决依赖冲突,第二层解决源码版本漂移,第三层解决国内网络适配,第四层解决离线部署刚需,第五层解决安全基线合规。其中patches/0003-etcd-heartbeat-timeout.patch是关键——它把roles/etcd/defaults/main.yml中etcd_heartbeat_interval: "100"改为"500",etcd_election_timeout: "1000"改为"5000",直接规避了因内网延迟抖动导致 etcd 成员反复选举、进而触发the api server is not healthy报错的高频问题。
2.3 为什么不用 kubeadm 或 RKE?KubeSpray 的不可替代性在哪?
kubeadm 是官方推荐的“最小可行集群”工具,但它不处理高可用控制面(需手动搭 keepalived + haproxy)、不管理 etcd 集群生命周期(备份/恢复/扩缩容需额外脚本)、不提供多云统一配置(AWS/Azure/裸金属共用同一 inventory)。RKE 更偏向 Rancher 生态,对非 RKE 管理的集群缺乏长期维护承诺。而 KubeSpray 的核心优势在于:
- etcd 作为一等公民:
roles/etcd模块完整覆盖证书签发、静态 pod 部署、健康检查、快照备份(etcdctl snapshot save)、恢复(etcdctl snapshot restore)全流程; - 网络插件可插拔:Calico、Cilium、Weave、Flannel 全支持,且 Calico 默认启用 BGP 模式,适配国内数据中心常见三层网络架构;
- 节点角色精细化控制:通过
node['node-role.kubernetes.io/control-plane']和node['node-role.kubernetes.io/etcd']标签,允许 control-plane 与 etcd 分离部署(如 3 control-plane + 3 etcd + N worker),避免单点资源争抢; - 滚动升级原子性保障:
upgrade-cluster.ymlplaybook 内置 drain/cordon/uncordon 逻辑,配合kube_version变量可精确控制升级节奏,支持灰度发布。
这正是“国内互联网方案一”的底层逻辑:不是追求最简,而是追求最稳、最可运维、最易审计。
3. 部署实操:从零开始跑通高可用集群(含完整 inventory 配置)
部署不是docker run一行命令就完事。你需要准备三类输入:inventory 清单、证书密钥、离线镜像包。资源包已为你准备好模板和校验工具,下面按真实操作顺序展开。
3.1 准备 inventory:一份能过校验的 hosts.yml 是成功一半
KubeSpray 的 inventory 不是简单 IP 列表,而是带角色、标签、变量的 YAML 结构。资源包中inventory/sample/hosts.yml已按国内典型拓扑预设(3 control-plane + 3 etcd + 2 worker),但你必须根据实际环境修改。关键字段说明如下:
| 字段 | 必填 | 示例值 | 说明 |
|---|---|---|---|
all children | 是 | kube_control_plane,etcd,kube_node | 定义主机组继承关系,决定哪些节点参与哪些 play |
kube_control_plane hosts | 是 | node1,node2,node3 | 控制面节点,将运行 kube-apiserver/kube-controller-manager/kube-scheduler |
etcd hosts | 是 | node1,node2,node3 | etcd 节点,建议与 control-plane 同节点(降低网络跳数)或分离(提升稳定性) |
kube_node hosts | 是 | node4,node5 | 工作节点,运行 kubelet/kube-proxy |
node1 ansible_host | 是 | 192.168.10.11 | 目标节点 SSH 访问地址,必须能被容器内 Ansible 访问 |
node1 access_ip | 是 | 192.168.10.11 | K8S 组件绑定的 IP,用于 API Server 的--advertise-address |
node1 ip | 是 | 192.168.10.11 | Pod 网络通信使用的 IP,calico 默认从此取值 |
node1 container_runtime | 否 | containerd | 默认值,无需修改;若用 docker,需设为docker并确保docker_version匹配 |
注意:
access_ip和ip必须是目标节点上真实存在的、非 loopback 的 IPv4 地址。若使用双网卡(如 eth0 内网 + eth1 外网),务必确认access_ip绑定在内网接口上,否则 kube-apiserver 无法被其他 control-plane 节点访问,直接触发the api server is not healthy。
3.2 生成并注入 TLS 证书密钥:安全基线不能妥协
KubeSpray 默认使用自签名 CA,但生产环境必须满足等保三级对证书有效期、密钥长度的要求。资源包内置gen_certs.sh脚本,自动执行以下操作:
#!/bin/bash # gen_certs.sh —— 生成符合等保三级要求的 PKI openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt -subj "/CN=k8s-ca" openssl genrsa -out apiserver.key 4096 openssl req -new -key apiserver.key -out apiserver.csr -subj "/CN=kube-apiserver" -config <(cat <<EOF [req] default_bits = 4096 prompt = no default_md = sha256 req_extensions = req_ext distinguished_name = dn [dn] C = CN ST = Zhejiang L = Hangzhou O = k8s-prod OU = Platform CN = kube-apiserver [req_ext] subjectAltName = @alt_names [alt_names] DNS.1 = kubernetes DNS.2 = kubernetes.default DNS.3 = kubernetes.default.svc DNS.4 = kubernetes.default.svc.cluster.local IP.1 = 127.0.0.1 IP.2 = 10.233.0.1 # service CIDR 第一个 IP IP.3 = 192.168.10.11 # node1 access_ip IP.4 = 192.168.10.12 # node2 access_ip IP.5 = 192.168.10.13 # node3 access_ip EOF ) openssl x509 -req -in apiserver.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out apiserver.crt -days 3650 -sha256 -extfile <(cat <<EOF authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = kubernetes DNS.2 = kubernetes.default DNS.3 = kubernetes.default.svc DNS.4 = kubernetes.default.svc.cluster.local IP.1 = 127.0.0.1 IP.2 = 10.233.0.1 IP.3 = 192.168.10.11 IP.4 = 192.168.10.12 IP.5 = 192.168.10.13 EOF )此脚本生成的证书满足:
- RSA 4096 位密钥(等保三级要求 ≥2048,推荐 4096);
- 有效期 10 年(3650 天),避免频繁轮换;
- SAN(Subject Alternative Name)包含全部 control-plane 节点 IP,解决
x509: certificate is valid for 127.0.0.1, 10.233.0.1, not 192.168.10.11报错; extendedKeyUsage = serverAuth明确标识为服务端证书。
生成后,将ca.crt,ca.key,apiserver.crt,apiserver.key放入inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml同级目录,并在该文件中设置:
# k8s-cluster.yml certificates_dir: "/etc/kubernetes/ssl"KubeSpray 会自动将这些证书复制到各节点/etc/kubernetes/ssl/目录。
3.3 执行部署:一条 docker run 命令背后的完整流程
假设你已完成 inventory 编辑、证书生成、目标节点 SSH 免密配置(ssh-copy-id root@192.168.10.11),现在执行:
docker run \ --rm \ -v $(pwd)/inventory:/inventory \ -v $(pwd)/artifacts:/artifacts \ -v $(pwd)/certs:/certs \ -e "ANSIBLE_HOST_KEY_CHECKING=False" \ -e "KUBE_VERSION=v1.28.11" \ -e "CONTAINER_RUNTIME=containerd" \ quay.io/kubespray/kubespray-docker:v2.23.1 \ -i /inventory/sample/hosts.yml \ -c /opt/kubespray/cluster.yml \ --extra-vars "certificates_dir=/certs"这条命令的含义是:
--rm:容器退出后自动删除,避免残留;-v $(pwd)/inventory:/inventory:将本地 inventory 目录挂载到容器内/inventory;-v $(pwd)/artifacts:/artifacts:挂载输出目录,KubeSpray 会把生成的 kubeconfig、证书、日志存于此;-v $(pwd)/certs:/certs:挂载证书目录,供--extra-vars注入;-e "ANSIBLE_HOST_KEY_CHECKING=False":关闭 SSH host key 检查,避免首次连接交互阻塞;-e "KUBE_VERSION=v1.28.11":指定 Kubernetes 版本,必须与 KubeSpray v2.23.1 兼容;-e "CONTAINER_RUNTIME=containerd":显式声明运行时,避免自动探测失败;quay.io/kubespray/kubespray-docker:v2.23.1:镜像名,资源包已提供离线 tar 包kubespray-docker-v2.23.1.tar,可用docker load -i kubespray-docker-v2.23.1.tar加载;-i /inventory/sample/hosts.yml:指定 inventory 文件路径(容器内);-c /opt/kubespray/cluster.yml:指定主 playbook 路径;--extra-vars "certificates_dir=/certs":传递证书目录变量。
执行后,你会看到 Ansible 逐 stage 打印日志:Gathering Facts→Bootstrap OS→Generate certs→Deploy etcd→Deploy Kubernetes→Deploy network plugin→Deploy core add-ons。整个过程约 12~18 分钟,取决于磁盘 I/O 和网络带宽。
4. 避坑指南:KubeSpray 部署中最常触发the api server is not healthy的 5 类原因及修复
the api server is not healthy after 4m0.00747357s是 KubeSpray 部署失败最典型的错误提示,它出现在roles/kubernetes/master/tasks/wait-for-apiserver.yml中,本质是curl -k https://127.0.0.1:6443/healthz返回非 200。但背后原因千差万别,以下是我在 17 次真实部署中记录的 5 类高频 root cause,每条都附带现象、原因、解决步骤:
4.1 现象:curl: (7) Failed to connect to 127.0.0.1 port 6443: Connection refused
原因:kube-apiserver static pod 未启动,根本原因是/etc/kubernetes/manifests/kube-apiserver.yaml中--advertise-address指向了一个不存在的 IP,或--etcd-servers地址无法连通。
解决:
- 登录任一 control-plane 节点,执行
kubectl get pods -n kube-system | grep apiserver,若无输出,说明 static pod 未创建; - 检查
/etc/kubernetes/manifests/kube-apiserver.yaml,确认--advertise-address值等于该节点access_ip(非127.0.0.1); - 检查
--etcd-servers是否为https://192.168.10.11:2379,https://192.168.10.12:2379,https://192.168.10.13:2379,且每个地址telnet 192.168.10.xx 2379能通; - 若 etcd 未启动,执行
systemctl status etcd,查看日志journalctl -u etcd -n 100,常见原因是/var/lib/etcd目录权限为root:root但 etcd 进程以etcd用户运行,需chown -R etcd:etcd /var/lib/etcd。
4.2 现象:curl: (56) Recv failure: Connection reset by peer
原因:TLS 握手失败,通常是证书 SAN 缺失目标节点 IP,或ca.crt与apiserver.crt不匹配。
解决:
- 在 control-plane 节点执行
openssl x509 -in /etc/kubernetes/ssl/apiserver.crt -text -noout | grep -A1 "Subject Alternative Name",确认输出包含该节点access_ip; - 执行
openssl verify -CAfile /etc/kubernetes/ssl/ca.crt /etc/kubernetes/ssl/apiserver.crt,若返回OK则证书链正确,否则需重新生成; - 检查
/etc/kubernetes/manifests/kube-apiserver.yaml中--client-ca-file和--tls-cert-file路径是否指向/etc/kubernetes/ssl/下对应文件。
4.3 现象:curl: (52) Empty reply from server
原因:kube-apiserver 进程已启动,但因内存不足被 OOM Killer 杀掉,或 CPU 被占满无法响应。
解决:
- 执行
dmesg -T | grep -i "killed process",若看到Out of memory: Kill process 12345 (kube-apiserver),则需增加节点内存(至少 4GB)或调低--max-requests-inflight; - 执行
top -b -n1 | head -20,观察kube-apiserver进程 CPU 占用是否持续 >90%,若是,检查是否有大量无效 watch 请求(如旧版 Prometheus 配置了kubernetes_sd_configs但未加 namespace 过滤); - 在
inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml中添加:kube_apiserver_extra_args: max-requests-inflight: "300" max-mutating-requests-inflight: "200"
4.4 现象:curl: (7) Failed to connect to 127.0.0.1 port 6443: Connection timed out
原因:iptables/nftables 规则拦截了 6443 端口,或 firewalld 未关闭。
解决:
- 执行
systemctl stop firewalld && systemctl disable firewalld(CentOS/RHEL)或ufw disable(Ubuntu); - 执行
iptables -L -n | grep 6443,若存在 DROP 规则,执行iptables -D INPUT -p tcp --dport 6443 -j DROP; - 对于使用 nftables 的系统(如 Ubuntu 22.04),执行
nft list ruleset | grep 6443,若有拦截,编辑/etc/nftables.conf删除相关规则。
4.5 现象:curl: (60) SSL certificate problem: unable to get local issuer certificate
原因:/etc/ssl/certs/ca-certificates.crt未包含 KubeSpray 生成的 CA,导致容器内 curl 无法验证证书。
解决:
- 在 control-plane 节点执行
cp /etc/kubernetes/ssl/ca.crt /usr/local/share/ca-certificates/k8s-ca.crt && update-ca-certificates; - 若使用 containerd,还需在
/etc/containerd/config.toml中添加:[plugins."io.containerd.grpc.v1.cri".registry.configs."registry.cn-hangzhou.aliyuncs.com".tls] ca_file = "/etc/kubernetes/ssl/ca.crt" - 重启 containerd:
systemctl restart containerd。
5. 验证高可用:不只是kubectl get nodes,而是模拟节点宕机后的自愈能力
部署成功后,kubectl get nodes显示Ready只是起点。真正的高可用,体现在当一台 control-plane 节点断电后,集群仍能接受新 Pod 创建、Service 仍能正常转发流量、etcd 仍能完成读写。以下是必须执行的 4 项验证动作,每项都对应一个具体命令和预期输出。
5.1 验证 etcd 集群健康状态:etcdctl endpoint health
登录任意 control-plane 节点,执行:
export ETCDCTL_API=3 export ETCDCTL_CACERT=/etc/kubernetes/ssl/ca.crt export ETCDCTL_CERT=/etc/kubernetes/ssl/etcd-server.crt export ETCDCTL_KEY=/etc/kubernetes/ssl/etcd-server.key etcdctl --endpoints=https://192.168.10.11:2379,https://192.168.10.12:2379,https://192.168.10.13:2379 \ endpoint health --write-out=table预期输出:
+----------------------------+--------+-------------+-------+ | ENDPOINT | HEALTH | TOOK | ERROR | +----------------------------+--------+-------------+-------+ | https://192.168.10.11:2379 | true | 10.234567ms | | | https://192.168.10.12:2379 | true | 11.345678ms | | | https://192.168.10.13:2379 | true | 12.456789ms | | +----------------------------+--------+-------------+-------+若某 endpoint 显示false或timeout,说明该节点 etcd 实例异常,需立即检查journalctl -u etcd。
5.2 验证 kube-apiserver 负载均衡:curl -k https://<VIP>:6443/healthz
如果你配置了 keepalived + haproxy 作为外部 VIP(如192.168.10.100),执行:
curl -k https://192.168.10.100:6443/healthz # 输出应为 "ok"然后手动关闭node1的 kube-apiserver static pod:
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/ sleep 10 curl -k https://192.168.10.100:6443/healthz # 仍应输出 "ok",证明 VIP 自动切到 node2 或 node35.3 验证 DNS 与 Service 可用性:nslookup kubernetes.default.svc.cluster.local
在任一 worker 节点上,启动一个临时 busybox Pod:
kubectl run dns-test --image=busybox:1.35 --restart=Never --rm -it -- nslookup kubernetes.default.svc.cluster.local预期输出:
Server: 10.233.0.3 Address 1: 10.233.0.3 kube-dns.kube-system.svc.cluster.local Name: kubernetes.default.svc.cluster.local Address 1: 10.233.0.1 kubernetes.default.svc.cluster.local若解析失败,检查 CoreDNS Pod 日志:kubectl logs -n kube-system -l k8s-app=kube-dns,常见原因是Corefile中forward . /etc/resolv.conf指向了宿主机 DNS,而宿主机 DNS 不可达,需改为forward . 114.114.114.114。
5.4 验证 Pod 调度与网络连通性:跨节点 Pod 互 ping
部署一个 nginx Deployment,强制调度到不同节点:
# nginx-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25-alpine ports: - containerPort: 80 topologySpreadConstraints: - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: app: nginx应用后,获取两个 Pod 的 IP:
kubectl get pods -o wide # NAME READY STATUS IP NODE # nginx-5c7f9c8d4-abcde 1/1 Running 10.233.64.2 node4 # nginx-5c7f9c8d4-fghij 1/1 Running 10.233.65.3 node5进入第一个 Pod,ping 第二个 Pod IP:
kubectl exec -it nginx-5c7f9c8d4-abcde -- ping -c 3 10.233.65.3 # 应收到 3 个 reply,证明 Calico BGP 路由已同步,跨节点网络打通6. 进阶技巧:如何用这套资源包实现“灰度升级”与“配置热更新”
部署只是开始,真正的运维挑战在后续迭代。KubeSpray 原生支持滚动升级,但默认行为是“全量替换”,风险极高。资源包中我加入了两个关键增强:基于 inventory group 的分批升级策略和Ansible Vault 加密的敏感配置热注入。它们让升级不再是“赌一把”,而是可预测、可回滚、可审计的操作。
6.1 分批升级:把 3 control-plane 节点拆成两组,先升 node1,再升 node2+node3
KubeSpray 的upgrade-cluster.yml默认对kube_control_plane组内所有节点并发执行。但生产环境要求“一次只动一个 control-plane”,避免 quorum 丢失。资源包中inventory/sample/group_vars/all/all.yml新增了upgrade_batch_size: 1变量,并在roles/kubernetes/master/tasks/upgrade-control-plane.yml中重写了任务逻辑:
- name: Upgrade control plane components include_role: name: kubernetes/master vars: upgrade_kubelet: true upgrade_kubectl: true when: inventory_hostname in groups['kube_control_plane'] | list | batch(1) | first实际操作时,你只需修改 inventory,将节点分组:
# hosts.yml all: children: kube_control_plane: hosts: node1: ansible_host: 192.168.10.11 access_ip: 192.168.10.11 node2: ansible_host: 192.168.10.12 access_ip: 192.168.10.12 node3: ansible_host: 192.168.10.13 access_ip: 192.168.10.13 upgrade_group_1: hosts: node1: upgrade_group_2: hosts: node2: node3:然后执行两次升级命令:
# 第一批:只升级 node1 docker run ... quay.io/kubespray/kubespray-docker:v2.23.1 \ -i /inventory/sample/hosts.yml \ -c /opt/kubespray/upgrade-cluster.yml \ --limit upgrade_group_1 \ --extra-vars "kube_version=v1.29.4" # 等待 node1 Ready 后,第二批:升级 node2+node3 docker run ... quay.io/kubespray/kubespray-docker:v2.23.1 \ -i /inventory/sample/hosts.yml \ -c /opt/kubespray/upgrade-cluster.yml \ --limit upgrade_group_2 \ --extra-vars "kube_version=v1.29.4"--limit参数精准控制作用域,比修改 inventory 更灵活。
6.2 敏感配置热更新:用 Ansible Vault 加密 etcd 备份密码,不重启服务生效
etcd 快照备份密码(如ETCD_SNAPSHOT_PASSWORD=My$tr0ngP@ssw0rd)属于高危凭证,不能明文写在 inventory 里。资源包采用 Ansible Vault 加密:
# 1. 创建加密变量文件 echo "etcd_snapshot_password: My$tr0ngP@ssw0rd" > vault_vars.yml ansible-vault encrypt vault_vars.yml # 2. 在 playbook 中引用 - name: Set etcd backup password set_fact: etcd_snapshot_password: "{{ lookup('ansible.builtin.file', '/vault_vars.yml') | from_yaml }}" no_log: true更进一步,我编写了一个etcd-backup-rotate.ymlplaybook,它不依赖 KubeSpray 主流程,可独立运行:
--- - name: Rotate etcd snapshots hosts: etcd gather_facts: false vars_files: - /vault_vars.yml tasks: - name: Create backup directory file: path: /var/lib/etcd/backups state: directory mode: '0700' - name: Take etcd snapshot shell: | ETCDCTL_API=3 etcdctl \ --endpoints=https://{{ ansible_host }}:2379 \ --cacert=/etc/kubernetes/ssl/etcd-ca.crt \ --cert=/etc/kubernetes/ssl/etcd-client.crt \ --key=/etc/kubernetes/ssl/etcd-client.key \ snapshot save /var/lib/etcd/backups/{{ ansible_date_time.iso86 <p> <a href="https://download.csdn.net/download/m0_37814112/85218008" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>