尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

二进制高可用Kubernetes集群一键部署脚本实战解析

二进制高可用Kubernetes集群一键部署脚本实战解析 简介这是一套面向云原生初学者与运维工程师的二进制高可用Kubernetes集群一键部署工具专为深入理解k8s控制平面原理而设计解决手动部署etcd、kube-apiserver、scheduler等组件时步骤繁琐、证书配置复杂、高可用拓扑易出错等痛点。资源共13个文件包含4个核心Shell脚本如install_HA_k8s.sh、install_etcd.sh、2个预编译二进制压缩包etcd与kubernetes-server、2个CNI网络插件YAMLCalico与CoreDNS、1个详细readme.txt说明文档以及CFSSL系列证书工具和Docker离线安装包总大小398.89MB覆盖环境准备、证书签发、组件部署、VIP漂移与网络插件集成全流程。已有1574人学习下载使用者可直接执行脚本快速构建具备多master节点、负载均衡与自动故障转移能力的生产级k8s集群同时通过可读性强的脚本结构与注释清晰掌握各组件启动逻辑、证书路径规划及服务依赖关系是理论结合实践的优质学习载体。 我接触 Kubernetes 这些年部署方式换了好几轮从最早的 minikube 到 kubeadm再到纯二进制手工拉集群最后沉淀成一套自动化脚本。说实话每次手动从零把二进制高可用集群搭起来至少要花大半天而且证书、etcd 成员、apiserver 的 SAN 一不留神就出问题。后来我把整套流程压缩成了“二进制高可用 k8s 集群一键部署脚本”现在的效率比原来翻了好几倍。这篇东西就把这个脚本的设计思路、核心细节和实操过程完整展开适合正在搞生产环境部署、或者想深入理解 k8s 控制平面原理的运维和架构师参考。1. 项目背景与整体设计思路1.1 为什么在这套方案里选二进制方式很多朋友一听到 k8s 部署第一反应就是 kubeadm。确实kubeadm 在社区里普及率很高初始化集群、生成证书、启动静态 Pod一条 init 加一条 join 就能拉起一个集群。但我在实际项目里发现kubeadm 并非万能的尤其在下面几类场景里就不太友好内网隔离环境没法访问 Google 的 registrykubeadm 的镜像拉取逻辑需要额外配置一堆镜像仓库替换需要深度定制 apiserver、controller-manager 的启动参数比如调整审计日志、自定义 admission pluginskubeadm 的配置模板虽然能改但要写一大堆 kubeadm 配置间接层太多排障和定位问题时二进制方式下每个组件就是一个 systemd 服务日志、参数、启动顺序一目了然而 kubeadm 把很多逻辑封装在了静态 Pod 和 kubelet 的引导流程里出了问题反而绕有些客户的安全规范要求所有软件包必须经过内部仓库审批二进制包分发比容器镜像分发更容易走完合规流程。所以在“可控性优先”的场景下我选择直接用官方 release 的二进制包把 apiserver、controller-manager、scheduler、kubelet、kube-proxy、etcd 一个个装成系统服务脚本负责串联整个流程。这个选择牺牲了一点点便捷换来了极强的确定性和排查透明度在我看来非常值。1.2 高可用架构如何设计高可用的核心是“消除单点”。Kubernetes 里最怕单点的是两个东西控制平面apiserver、controller-manager、scheduler和 etcd。我的方案是三个 master 节点上同时部署控制平面和 etcd三个节点组成一个 etcd 集群同时控制平面的三个组件各自通过 leader 选举机制保证同一时刻只有一个实例在干活。具体架构是这样的3 台 master每台上跑 apiserver、controller-manager、scheduler、etcd前面再叠一层 HAProxy 和 Keepalived对外提供一个虚拟 IPVIP所有客户端和 kubelet 都通过 VIP 访问 apiserver。Worker 节点数量可以根据业务扩机器上只跑 kubelet、kube-proxy 和 CNI 插件。这个设计考虑了两个点。第一etcd 和控制平面混部在 master 上这是 etcd 官方文档认可的小规模高可用方案少于 5 个 etcd 成员时推荐混部可以减少两台机器运维成本也低。第二HAProxy 只做四层 TCP 转发不解析 HTTP 内容性能开销很小Keepalived 负责 VIP 漂移配合健康检查能自动把挂掉的 master 从转发池里摘出去。对比一下 kubeadm 的高可用方案通常是 kubeadm 管控制平面外部再挂负载均衡原理上和我这套一致。但二进制方案里haproxy.cfg、keepalived.conf 都是明文的漂移逻辑、健康检查策略想怎么调就怎么调不存在“黑盒”问题。1.3 脚本模块化设计思路写一键部署脚本最大的坑就是“一坨到底”。如果所有逻辑塞进一个 deploy.sh后续改一个参数都要满文件找排障时还分不清是哪一步失败了。所以我把整个部署流程拆成 8 个独立脚本每个脚本只干一件事入口脚本再按顺序调用01_env_prepare.sh统检查 root 权限、操作系统版本、主机名和 IP 匹配关系、关闭 swap、设置内核参数、加载 ipvs 相关模块、配置 hosts 解析02_cert_generate.sh基于 cfssl 生成 etcd 和 kubernetes 两套 CA以及所有组件的 client/server 证书03_etcd_install.sh把 etcd 二进制和证书分发到三个 master 节点生成 systemd 单元按顺序启动并等待集群 healthy04_k8s_base_install.sh在 master 上部署 apiserver、controller-manager、scheduler生成各自的 kubeconfig 和审计策略05_lb_install.sh在三个 master 上部署 HAProxy 和 Keepalived配置 VIP 转发到后端 644306_node_install.sh在 worker 节点上部署 kubelet、kube-proxy 和 CNI 插件完成节点加入07_cluster_init.sh生成本地 kubectl 配置初始化 RBAC 和 bootstrap token部署必要的附加组件08_check.sh自动检查集群状态、证书有效期、组件健康情况输出报告。每个脚本内部再按函数封装比如generate_cert()、start_etcd()、wait_etcd_healthy()这样某个环节挂了可以手动单跑对应脚本继续不需要全量重来。日志统一输出到/var/log/deploy-k8s/每步写时间戳和退出码回放的时候能精确看到卡在哪。这个设计后来帮我省了无数查错时间。2. 核心细节解析与实操要点2.1 证书体系设计二进制部署 k8s 最容易出问题的就是证书。整个集群的证书体系分成两条线etcd 一套 CAkubernetes 一套 CA两者互不交叉。为什么要拆开因为 etcd 的证书策略面向的是 peer 通信和 client 校验而 kubernetes 的证书要覆盖 apiserver 的对外服务、kubelet 的双向 TLS、以及各类管理组件的身份认证混在一起会导致权限边界模糊也不好轮转。我用 cfssl 而不是 openssl 来生成证书坦白说 openssl 也能做但 cfssl 的优势是可以把所有证书的 CSR 信息写成一个 JSON 文件批量生成时改几个 IP 和主机名就行脚本处理起来非常顺手。这里我特别提醒一句apiserver 的证书 SAN 一定要包含三样东西——VIP 地址、所有 master 节点的 IP、以及 Service 网段的第一个 IP一般是 10.96.0.1。如果漏了 VIPkubelet 通过 VIP 访问 apiserver 时就会报证书校验失败而集群内部走 Pod 网络访问 service 时也一样会出问题。etcd 的证书要给三个节点分别生成每个节点需要 server 证书给 client 连接用和 peer 证书给节点间通信用并且 peer 证书的 SAN 里要包含另外两个节点的 IP 和主机名。我一开始图省事用一套证书三个节点共用etcd 集群能起来但日志里一直刷 hostname 校验失败的警告集群成员变更时还容易出幺蛾子。后来老老实实每节点一套问题立刻消失。整个脚本用到的证书清单大概是这样证书用途所属 CA涉及节点关键 SANetcd server peeretcd-ca每个 master 各一套本机 IP、所有 master IP、hostnamekube-apiserverkubernetes-ca每个 masterVIP、Master IP、10.96.0.1kube-controller-managerkubernetes-ca每个 master本机 IPkube-schedulerkubernetes-ca每个 master本机 IPkubeletkubernetes-ca每个节点节点 IP hostnamekube-proxykubernetes-ca每个节点节点 IPadminkubernetes-ca运维机本机 IP2.2 关键配置文件与参数证书生成之后真正的重头戏是组件的启动参数。apiserver 是整个集群的入口参数最复杂。我列出核心的几个供参考--bind-address0.0.0.0监听所有网卡保证 VIP 切换后仍然能正常接流量--secure-port6443安全的 HTTPS 端口也是 HAProxy 的后端端口--etcd-servers写成三个 master 的https://10.0.0.11:2379,https://10.0.0.12:2379,...形式--service-cluster-ip-range10.96.0.0/12Service 的虚拟 IP 网段规划后不能随意改否则存量 Service 全失效--authorization-modeNode,RBAC节点授权和 RBAC 同时开启这是生产环境的基本要求--kubelet-client-certificate和--kubelet-client-keyapiserver 访问 kubelet 时使用的客户端证书这一步不配好kubectl logs 和 kubectl exec 都会失败--audit-log-path和--audit-log-maxsize审计日志路径和单文件大小限制安全审计必须要。controller-manager 和 scheduler 的高可用核心是--leader-electtrue这个参数默认就是 true但很多人没意识到三个 master 上都会启动这两个组件只有成为 leader 的那一个才能真正干活。leader 选举依赖 etcd 的租约机制所以 etcd 挂了这两个组件也会“瘫痪”但不退出进程排查时容易误判。kubelet 的参数里最容易踩坑的是--cgroup-driver。Docker 默认 cgroup driver 是cgroupfs而 kubelet 如果在 kubeadm 场景下默认是systemd两者不一致会导致 Pod 状态异常报failed to run Kubelet之类的错。我在脚本里把 kubelet 的--cgroup-drivercgroupfs写死和 Docker 保持一致。当然现在新版本很多集群直接上 containerdcgroup driver 一般推荐 systemd这就需要在变量配置文件里留开关别写死在代码里。2.3 负载均衡方案要点高可用集群的关键一环是 apiserver 的入口不能是某个固定 IP否则这台机器挂了整个集群的管理面就断了。我的方案是在三台 master 上各部署一个 HAProxy 实例同时部署 Keepalived。通过 VRRP 协议三台机器共享一个 VIP正常情况下 VIP 由优先级最高的那台持有一旦这台宕机或 haproxy 进程异常Keepalived 会把 VIP 漂移到另一台机器上。HAProxy 的配置核心就两句话监听 8443 端口做 TCP 转发后端指向三台 master 的 6443 端口健康检查用option httpchk GET /healthz这样 apiserver 进程假死但端口还在的时候也能被自动摘除。这里我补充一个细节健康检查的用户名密码不能省略否则 apiserver 的匿名请求可能被拒绝健康检查误报失败VIP 半天不切换集群就变“脑裂”了。Keepalived 的配置里我设置了nopreempt避免 VIP 在主节点恢复后频繁来回漂移。生产环境里 VIP 的抖动比“当前在哪台机器上”更影响稳定性网络策略、防火墙、监控报警都可能因为 IP 切换产生连锁反应。这个决策后来在真实故障演练时验证是正确的主节点意外重启后 VIP 稳定在备用节点上直到我手动干预。3. 实操过程与核心环节实现3.1 使用脚本前的环境准备脚本虽然是一键执行但不代表你可以拿一台裸机就直接跑。我在01_env_prepare.sh里做了不少前置校验但建议你在跑之前自己先确认几件事能省掉一半的排障时间。操作系统我主要在 CentOS 7.9 和 Ubuntu 22.04 上做验证其他的发行版理论支持但没系统测过。所有节点必须配置静态 IP主机名不能带下划线且要和/etc/hosts里的记录一致。这是 k8s 集群一个隐藏很深的约束kubelet 拿到的 node name 默认就是 hostname如果 hostname 和实际 IP 对应不上apiserver 的证书校验、kube-proxy 的转发都会出问题。网络层面三个 master 之间要放通 2379、2380、6443、8443、10250、10259 这些端口的安全组/防火墙规则。很多公司内部有统一的 security group我建议提前把端口规则梳理清楚别等部署到一半才发现 apiserver 起不来curl 一下发现端口不通那种问题最耗时间。另外所有节点都要能出网下载二进制包或者你得提前把二进制包上传到内网源服务器上。我建议在 hosts 配置文件里直接维护所有节点的 IP、主机名、角色的映射关系脚本统一从这里读取。这样比每个节点单独设置变量要清晰得多后期加节点也只要改这一个文件。3.2 一键部署执行过程所有环境准备好之后部署就变成了一条命令的事bash deploy.sh --config cluster.cfgdeploy.sh 的工作流程并不复杂大体上就是读取配置文件、格式校验、按阶段调用前面说的 8 个模块、把每步输出实时打印到终端和日志文件。但脚本内部有几个逻辑值得单拎出来讲。第一是证书生成的“幂等性”。脚本检测到已有证书文件时默认不覆盖除非你显式传--force-certs。这是为了避免某一步失败重跑时证书换了但节点上旧的 systemd 服务还引用着旧证书导致奇怪的 TLS 报错。如果确认要重新生成证书我还是建议全量重来一遍更稳妥。第二是 etcd 的启动顺序。三个 etcd 节点不能同时无脑启动否则它们各自发现对端还没起来可能一直卡在等待投票状态。脚本的做法是先启动第一台等它变成 leader 并且/health返回 healthy再并发启动另外两台。用etcdctl endpoint health做探活而不是单纯 sleep 固定秒数这样在慢机器和多网络环境下更稳健。第三是 HAProxy 和 Keepalived 的启动顺序。必须先启动 haproxy 再启动 keepalived因为 keepalived 的监控脚本会检查本地 8443 端口如果 haproxy 还没起来监控脚本直接判定 VIP 不应该在本机VIP 永远不会出现在这台机器上。这个顺序在 systemd 里用Afterhaproxy.service和Requireshaproxy.service来控制。整个部署流程跑完大概 8 到 10 分钟大部分时间花在二进制分发和 etcd 集群收敛上。脚本最后会自动执行08_check.sh输出类似这样的状态检查结果检查项预期结果实测结果etcd 集群健康healthyhealthyVIP 可达性192.168.10.100:8443 可通可通node 状态6 台 Ready6 台 Ready核心组件 Pod 状态RunningRunning证书剩余有效期 180 天约 3500 天3.3 集群验证与功能测试脚本检查通过不代表集群真的一切正常。我的习惯是部署完成后还会手动做几轮功能验证这一步我也会把它们写进一个单独的验证脚本里方便后续每次变更后跑一遍回归。第一轮验证控制平面的高可用。把当前持有 VIP 的那台 master 上的haproxy进程手动 kill 掉观察 VIP 是否在 3 到 5 秒内漂移到另一台 master同时用kubectl get nodes连续执行确认 apiserver 没有中断。注意 kubectl 客户端要指向 VIP 加 8443并且 kubeconfig 里的 server 地址写成 VIP不要写具体 IP否则你验证的是单节点而不是整个高可用入口。第二轮验证数据面的基本功能。创建一个 nginx deployment暴露成 NodePort 或 LoadBalancer如果是裸金属环境一般用 MetalLB确认 Service IP 能通Pod 能跨节点迁移集群 DNS 能正常解析 Service 名。很多时候集群部署成功但 DNS 插件没装好业务一上来直接扑街。第三轮验证 etcd 容错。把其中一个 etcd 节点的 etcd 服务停掉然后写入一个 deployment 并观察调度是否正常。etcd 是少数派时集群只读不可写如果这时候建 Pod 没反应说明 etcd 的 leader 和成员关系有问题需要立刻回查。最后验证节点重启后的自愈能力。随机找一台 worker 执行 reboot等它回来后看 kubelet 是否自动注册、节点状态是否从 NotReady 恢复到 Ready。这轮测试尤其重要因为很多环境里 kubelet 的服务依赖没有配置好机器重启后 kubelet 起不来Pod 全挤到其他节点上谈不上高可用。4. 常见问题与排查技巧实录4.1 证书相关报错怎么定位我见过最多的部署失败都跟证书有关而且报错信息特别有迷惑性。比如kubectl get nodes报x509: certificate signed by unknown authority第一反应是~/.kube/config里的certificate-authority-data是不是写错了但往往实际原因是对端 apiserver 的证书里 SAN 没有包含你访问的那个 VIP 或 IP。这种问题在本地用openssl s_client -connect VIP:8443 -showcerts可以很快看出 apiserver 实际下发的是哪个证书再对比证书的 SAN 列表基本立刻定位。另一种常见情况是 etcd 日志刷client certificate is not trusted。这说明 apiserver 连接 etcd 时用的客户端证书不是 etcd 的 CA 签发的。常见原因是生成的证书文件虽然分发了但 etcd 的--client-cert-auth和--trusted-ca-file配置的 CA 路径不对或者 apiserver 的--etcd-cafile指向了 kubernetes-ca 而不是 etcd-ca。这种低级错误配置检查时多看一眼就能避免。我还整理了一个排查套路凡是组件间 TLS 握手失败先统一时间证书校验对时间敏感再验证 CA 链再看 SAN最后看文件权限。顺序别乱因为前一种原因会直接掩盖掉后一种。比如时间不同步时你花半小时查 SAN 是没意义的。4.2 etcd 集群起不来怎么办etcd 集群起不来的经典场景是三个节点同时启动结果谁都无法选主。日志里反复出现failed to connect to member或者leader changed此时先用etcdctl endpoint status查看当前成员状态确认三个节点互相能看到。如果只有第一个节点能起来后两个加入失败多半是--initial-cluster里的成员 URL 写错了或者 peer 端口不通。--initial-cluster-statenew和--initial-cluster-stateexisting选错也是重灾区。集群第一次初始化用new之后哪怕整体重启都必须改成existing否则 etcd 会认为要创建一个全新集群直接报member exists之类的错误。我之前在调试重启流程时被这个坑过后来把 etcd 的启动参数单独抽出一个模板按 state 自动拼接才算彻底解决。如果 etcd 数据目录损坏导致集群起不来别急着删数据。可以先用etcdutl snapshot restore从备份恢复单个节点再启动其他成员。虽然脚本不强制配置备份但我强烈建议你在03_etcd_install.sh之后接一个定时快照任务etcd 的数据是 k8s 的“唯一事实来源”丢不起。4.3 kubelet 无法注册节点kubelet 起不来或者注册不了节点的报错最常见的是failed to get node: node xxx not found。这个报错往往让人误以为节点不存在实际上是因为 bootstrap token 或者--kubeconfig里的客户端证书没有创建对应节点的 CSR 授权。二进制部署方式下我一般不依赖 kubelet 的动态 bootstrap而是直接在生成证书时就把每个节点的 kubelet kubeconfig 写好再配合 RBAC 给每个节点用户授权。另外一个影响 kubelet 注册的是--node-ip参数。如果机器有多块网卡不指定的话 kubelet 会自动选一块网卡的 IP 作为 node IP一旦选成内网管理网或者 docker0 网桥地址节点状态虽然能 Ready 但 Pod 跨节点通信直接异常。脚本里强制指定--node-ip为配置文件中规划的节点 IP代价是每台机器的 kubelet 参数略有不同但这正是自动化的意义——差异交给脚本处理。还有一类情况是 CNI 插件没装好节点能注册但一直 NotReady。kubectl describe node 会显示network plugin is not ready: cni config uninitialized。确认 CNI 的二进制和配置文件是否分发到/opt/cni/bin和/etc/cni/net.d/以及 kubelet 的--cni-conf-dir和--network-plugincni是否设置正确。这一步卡住时不用怀疑 k8s 本身有问题99% 是路径或权限问题。4.4 负载均衡故障切换异常HAProxy 和 Keepalived 组合下比较烦人的是“VIP 没漂移”或者“VIP 一直在但后端全挂”。如果 VIP 没漂移先看 Keepalived 的日志确认是否收到了对端的 VRRP 广播。很多时候是云平台的安全组拦了 VRRP 的组播协议224.0.0.18导致互相感知不到对方的存在。解决方案是改成单播模式在 keepalived.conf 里显式指定对端 IP这在大多数内网环境里是标配。如果 VIP 状态正常但访问 8443 失败检查 HAProxy 后端的健康检查。haproxy -c -f /etc/haproxy/haproxy.cfg可以做配置语法检查再通过echo show servers state | socat stdio /var/run/haproxy.sock查看后端服务器的状态。如果后端全是DOWN大概率是 health check 的 URI 或端口没对上如果只有一台DOWN那才是这一台 master 的 apiserver 真的有问题需要去查 apiserver 进程。值得说一句的是Keepalived 不做真正的业务流量转发它只负责让 VIP 出现在某台机器上真正的流量是 HAProxy 接的。所以排查顺序永远是先看 apiserver再看 haproxy 后端状态最后看 keepalived 的 VIP 归属。方向反了容易把一个简单问题越查越乱。4.5 常见问题速查表现象可能原因解决方向kubectl 报 unknown authorityapiserver 证书 SAN 缺 VIP/IP重新生成证书并加入 SANapiserver 连不上 etcdetcd-ca 路径配置错检查 apiserver 的 etcd CA 参数etcd 频繁选主失败peer URL 或端口不通核对 initial-cluster 配置和防火墙kubelet 注册但 NotReadyCNI 配置缺失检查 CNI 配置目录节点一直 NotReady 且报 cgroup 错误cgroup driver 不一致统一 docker/containerd 与 kubelet driverVIP 无法访问 8443haproxy 后端全 DOWN检查 health check URI 和 apiserver 状态kubectl logs 执行失败apiserver 到 kubelet 的客户端证书缺失配置 kubelet-client-certificate节点重启后未自动加入kubelet 服务未启用开机自启执行 systemctl enable kubeletPod 跨节点通信失败node-ip 选了错误网卡显式指定 --node-ip5. 脚本扩展与运维心得5.1 怎么用脚本扩容新节点集群跑了一阵子业务量上来了需要加 worker 节点。这是脚本一个很重要且常被忽略的能力。扩节点时不需要在已有节点上做太多操作只需要在 cluster.cfg 里把新节点的 IP、主机名加入 worker 段然后单独执行bash deploy.sh --config cluster.cfg --stage node脚本会为新节点生成 kubelet 和 kube-proxy 的证书与配置然后通过 SSH 分发并启动服务。这里我建议在配置里准备一个可用的 bootstrap token或者复用脚本生成的独立 kubeconfig避免每次都要重新生成 admin 证书。扩容过程中不会影响现有节点和业务。但如果你的新节点之前装过 docker 或者其他容器运行时先确认 cgroup driver 和 kubelet 的参数是否一致否则节点注册完大概率是 NotReady。还有就是在加入前先清理掉该节点上残留的/etc/kubernetes目录和旧的 CNI 配置别让历史配置干扰新集群。5.2 升级与回滚注意事项版本升级是我最谨慎的操作也是最容易毁集群的操作。二进制方式升级理论上很简单下载新版本二进制替换系统里的文件重启对应 systemd 服务。但实际操作时我有一套顺序先升级 etcd再升级 apiserver然后 controller-manager 和 scheduler最后 kubelet 和 kube-proxy。组件版本必须遵循 k8s 的版本偏差策略apiserver 不能领先其他组件超过一个 minor 版本。回滚我一般依赖“保留旧版本的二进制文件”这个习惯。升级前把当前使用的二进制复制一份到/opt/k8s/binaries/backup-version/升级后如果发现问题直接把旧版本二进制推回去重启服务即可。kubelet 的版本会直接影响节点上运行的 Pod所以 kubelet 的升级我尽量滚动进行一台一台来避免整个集群的 Pod 同时重建。我个人的建议是升级前先在一套测试环境跑一遍脚本的--stage base和--stage node确认新版本参数没有废弃项。实在没有测试环境也至少把所有组件的 startup 参数对照官方 changelog 过一遍。etcd 的版本升级更得谨慎跨大版本升级必须走数据迁移路径不能直接替换二进制重启。最后再分享一个经验这个脚本本质上解决的是“从零搭一套可用的高可用集群”这件事。但集群真正的挑战从来不在部署那一刻而是在后续的变更、升级、故障恢复里。脚本能帮你把初始状态做到一致、可预期这对后续所有运维工作都是最好的基础。我自己每次做灾备演练或者版本升级前都会用这套脚本快速起一套同版本的环境来验证变更已经形成习惯了。希望这篇拆解对你也有同样的价值。本文还有配套的精品资源点击获取
返回列表