首页 / 资讯中心 / 文章详情

用二进制脚本一键部署高可用K8s集群:内网离线也能自动签证书

用二进制脚本一键部署高可用K8s集群:内网离线也能自动签证书 ★ FEATURED ARTICLE
简介在云原生实践与容器编排学习中Kubernetes 集群的二进制部署往往门槛较高。这份资源提供基于阿良二进制部署文档设计的一键部署脚本面向需要快速搭建高可用 k8s 环境或深入理解控制平面组件的开发与运维人员。脚本覆盖 etcd、apiserver、scheduler、controller-manager 等核心组件并内置负载均衡与 VIP 配置帮助降低手工部署复杂度。包体共 13 个文件约 398.89MB以 Shell 部署脚本为主辅以 etcd、Docker、k8s 等二进制压缩包、Calico/CoreDNS 的 YAML 配置以及 cfssl 证书生成工具和 readme 说明结构清晰便于按步骤执行与排错。目前已有 1581 人学习下载适合作为 k8s 进阶实践的参考样例。借助该脚本读者可快速获得一个可运行的高可用集群同时结合 readme 中的环境准备、节点初始化、网络插件安装等说明加深对 k8s 各组件协作关系的理解为后续生产环境部署打下扎实基础。脚本内置了证书签发、VIP 漂移等细节能帮助读者规避常见配置错误。1. 用二进制把 k8s 集群装进内网一份连证书都能自动签的脚本生产环境里用二进制方式部署一套高可用的 k8s 集群对很多团队来说意味着要熬夜加班、查一晚上证书问题甚至搭完才发现 VIP 起不来。而这份一键部署脚本把整个过程压缩成几条命令从系统初始化到 etcd、apiserver、kubelet 全部自动完成连证书签发、token 生成都替你做了。它解决的是离线内网、无外网环境中从零搭建 k8s 集群的痛点适合运维、DevOps 和技术负责人拿来直接落地生产或搭建测试环境新手跟着改一遍配置就能跑通熟手也能用它省掉重复劳动。2. 为什么是二进制而不是 kubeadm高可用架构与脚本设计2.1 二进制安装与 kubeadm 的差别kubeadm 是目前官方主推的部署工具它把 apiserver、controller-manager、scheduler 都做成静态 Pod由 kubelet 拉起。这种方式在能联网拉镜像的环境下非常简单但有两个问题一是组件以容器方式运行排查问题时要先进容器看日志二是证书、kubeconfig、审计策略都是按默认方式生成想改参数你会发现可改的地方不多调整一个配置往往要重新生成一堆文件。二进制安装则完全不同。每个组件都是一个独立的 systemd 服务运行路径、启动参数、证书目录全部自己掌控。比如 kube-apiserver 的--audit-log-path、--service-account-key-file这些参数你可以直接写进 service 文件重启即可生效不依赖 kubeadm 的那套kubeadm config。生产环境里需要对接自建 CA、统一证书有效期、指定数据盘目录时二进制方案明显更灵活。我一般会选择二进制部署的场景有三类一是客户的内网隔离环境镜像仓库拉不了 kubeadm 需要的镜像二是对组件运行方式有硬性要求比如安全审计要求所有组件以宿主机进程方式运行三是已有自建 CA 体系需要把 k8s 证书纳入企业统一证书管理。2.2 高可用三要件haproxy keepalived etcd 集群高可用 k8s 集群的核心有三块etcd 集群通过 Raft 协议保证数据一致三个节点允许挂一个apiserver 是控制面的唯一入口必须保证它始终可访问kubelet、kube-proxy 等节点组件通过 apiserver 的 VIP 连接控制面。这份脚本选择的接入层方案是 haproxy keepalived这也是生产环境最常见的组合。haproxy 监听在每台 master 上把 8443 端口的请求转发到各 master 的 apiserver默认 6443keepalived 维护一个 VIP 地址哪台 master 的 haproxy 还活着VIP 就落在哪台上。集群内的 kubelet 和 kubectl 只需要连接 VIP 的 8443 端口任意一台 master 挂了VIP 会自动漂移请求不会断。etcd 部分走的是三节点独立部署不使用 kubeadm 的 embedded etcd。独立部署的好处是可以把 etcd 的数据目录单独放到高速磁盘同时可以把--auto-compaction-retention这类参数按需调整。脚本会为 etcd 单独签发一套证书与 k8s 的 CA 分开管理这个细节很多人会忽略后面避坑部分我会展开讲。2.3 脚本文件结构与执行流水线k8s-binary-install/ ├── env.sh # 全局参数配置IP、网段、VIP、版本号 ├── 01-init.sh # 系统初始化内核参数、时间同步、组件依赖 ├── 02-cert.sh # 使用 openssl 签发全套 k8s 证书 ├── 03-etcd.sh # 部署三节点 etcd 集群 ├── 04-master.sh # 部署 kube-apiserver/controller-manager/scheduler ├── 05-node.sh # 部署 kubelet/kube-proxymaster 与 node 共用 ├── 06-join.sh # node 节点加入集群自动生成 token └── pki/ # 证书模板与配置目录执行流水线是严格的串行关系先初始化再签发证书接着 etcd然后 master 控制面最后才部署节点组件。每步脚本结束后我都建议手动检查对应服务的 systemd 状态确认无 error 再继续下一步。脚本的执行顺序不能打乱特别是 02-cert.sh 必须在 03-etcd.sh 之前因为 etcd 的证书依赖整个 PKI 体系建立。env.sh 是唯一需要手动修改的文件所有 IP、网段、版本号都集中在里面这也是这份脚本最值得看的地方。3. 一键部署实操从改配置到集群就绪3.1 环境准备与 inventory 配置这套脚本基于 CentOS 7.9 / Rocky Linux 9 验证过三台 master 加两到三台 node 是常见配置。硬件上 master 建议 4C8G 起步node 按业务负载调整etcd 如果复用 master 节点磁盘一定要用 SSD。先把三台机器的 IP、hostname、防火墙规则规划好下面是我常用的分组方式master1: 192.168.100.11 (etcd1) master2: 192.168.100.12 (etcd2) master3: 192.168.100.13 (etcd3) node1: 192.168.100.21 node2: 192.168.100.22 vip: 192.168.100.10拿到脚本后第一件事就是改 env.sh#!/bin/bash # 集群网络规划 MASTER_IPS(192.168.100.11 192.168.100.12 192.168.100.13) NODE_IPS(192.168.100.21 192.168.100.22) VIP_IP192.168.100.10 VIP_INTERFACEens192 # 网段划分按需调整部署后不可修改 SERVICE_CIDR10.96.0.0/12 POD_CIDR10.244.0.0/16 CLUSTER_DNS10.96.0.10 # 版本与目录 K8S_VERSIONv1.28.2 ETCD_VERSIONv3.5.9 K8S_INSTALL_DIR/opt/kubernetes三个参数最容易出错VIP_INTERFACE必须和机器实际网卡名一致用ip addr查看POD_CIDR决定了后续 flannel 或 calico 的子网分配范围一旦集群运行后就无法修改CLUSTER_DNS必须落在SERVICE_CIDR内通常取网段第 10 个地址。这里说明一下版本策略脚本默认使用 v1.28.x 与 etcd v3.5.x这是当前生产环境验证比较充分的组合。如果你要跑更新的版本直接把两个变量改掉即可脚本会从包目录读取对应二进制文件。要注意的是 apiserver 与 kubelet 版本差异不要超过两个 minor 版本否则会出现版本协商失败的问题。3.2 执行脚本的三个阶段与关键日志环境配置完成后在三台 master 上依次执行前三个脚本# 1. 所有节点都执行初始化 bash 01-init.sh 21 | tee -a /tmp/k8s-init.log # 2. 只在 master1 上执行证书签发脚本会自动分发到其他 master bash 02-cert.sh 21 | tee -a /tmp/k8s-cert.log02-cert.sh是整套脚本里最核心的一步。它做的事是用 openssl 生成一个独立的根 CA然后用这个 CA 签发 apiserver、etcd、kubelet、service-account 等 9 套证书最后通过scp把证书和 kubeconfig 分发到所有 master 节点。证书签发阶段最容易混淆的是kube-apiserver的证书要包含多个 SAN 地址即所有 master 的 IP、VIP、域名都要写进openssl.cnf的alt_names里。如果少写了某个 master IP后续该节点上的 kubelet 连接 apiserver 时就会报证书校验失败。脚本里已经把这个列表从MASTER_IPS和VIP_IP自动生成不需要手工维护。接着部署 etcd 与 master 控制面# 3. 三台 master 依次执行每台执行前确认上一台正常 bash 03-etcd.sh # 4. master 控制面组件部署 bash 04-master.sh04-master.sh 会把 kube-apiserver、kube-controller-manager、kube-scheduler 分别注册为 systemd 服务并生成对应的 kubeconfig 和审计策略文件。脚本还顺手做了两件事一是把kubectl的 kubeconfig 写到/root/.kube/config让你可以在任意 master 上直接使用kubectl二是启动 haproxy 和 keepalived完成 VIP 绑定。这一步执行完你应该能在任意一台 master 上用kubectl get nodes看到三台 master 全部 Ready$ kubectl get nodes NAME STATUS ROLES AGE VERSION master1 Ready master 2m v1.28.2 master2 Ready master 2m v1.28.2 master3 Ready master 3m v1.28.2如果节点状态是 NotReady先别急着往后面走用journalctl -u kubelet -f看 kubelet 日志绝大多数原因是 CNI 插件还没有部署这是正常的后面装完网络插件才会变 Ready。3.3 手工复核八个核心组件状态脚本执行成功不代表集群正常我习惯在部署完做一次全量状态检查# 控制面组件状态 kubectl get cs # etcd 集群健康状态 for ip in 192.168.100.11 192.168.100.12 192.168.100.13; do ETCDCTL_API3 etcdctl --cacert/etc/etcd/ssl/ca.pem \ --cert/etc/etcd/ssl/etcd.pem --key/etc/etcd/ssl/etcd-key.pem \ --endpointshttps://${ip}:2379 endpoint health done # kube-system 下核心 Pod kubectl get pods -n kube-system我目前看到的正常输出是kubectl get cs返回 controller-manager 与 scheduler 均为 healthyetcd endpoint health 全部返回is healthykube-system 下至少能看到 etcd、kube-apiserver 等静态 Pod 正常coredns 与网络插件因为还没部署 CNI处于 Pending 状态是预期行为。这一步的意义在于把证书、etcd、控制面三个最容易出问题的点隔离出来。后续无论哪里出故障你都能快速定位是哪个环节的问题而不是一头扎进 Pod 日志里看半天。4. 避坑实录二进制部署最常见的五个翻车点4.1 证书 SAN 缺失导致 kubelet 无法连接 apiserver现象kubectl get nodes只能看到本机其他 master 节点 NotReadykubelet 日志持续报Unable to connect to the server: x509: certificate is valid for ... not for ...。原因kube-apiserver 证书的 SANSubject Alternative Name列表里没有包含当前访问节点的 IP。二进制安装不像 kubeadm 会自动把所有节点 IP 写进证书openssl 签发时必须手动维护 alt_names。如果某个 master 的 IP 没有加进去该节点上的所有 kubelet 和 kube-proxy 连接 apiserver 都会证书校验失败。解决重新执行 02-cert.sh 前检查env.sh里的MASTER_IPS是否包含全部 master 的 IP且VIP_IP也在里面。证书重新签发后必须重启所有 master 上的 kube-apiserver 服务以及所有节点的 kubelet否则旧的证书还在内存里。4.2 etcd 证书与 k8s 证书混用现象etcd 集群健康检查失败etcd 日志报client certificate is for kube-apiserver, not etcd。原因图省事把 kube-apiserver 的证书直接用于 etcd 的 client 认证。两个组件的证书 CN 不同etcd 在验证 client 证书时会校验 CN 必须匹配etcd或etcd-client否则拒绝连接。解决etcd 必须使用独立的一套 PKI 体系。脚本里 02-cert.sh 已经为 etcd 单独生成了etcd-ca.pem、etcd.pem、etcd-key.pem部署时确认03-etcd.sh引用的是这套证书而不是 pki 目录下的 apiserver 证书文件。这是一个从架构上就必须分开的设计决策不要图省事合并。4.3 keepalived 的 VIP 无法漂移现象手动kill掉 master1 的 haproxy 进程后VIP 没有漂移到 master2集群入口仍然指向死掉的节点外部访问报连接拒绝。原因keepalived 配置里没有设置unicast_peer默认走 VRRP 组播。在部分云环境或交换机禁用了组播的机房网络中VRRP 通告发不出去节点之间互相感知不到存活状态VIP 自然不漂移。解决在 keepalived.conf 里显式配置unicast_peer把其他 master 的 IP 写进去让 VRRP 通告走单播。脚本里默认已经做了这个配置如果你是自己手搭这步是个很隐蔽的坑。vrrp_instance VI_1 { state BACKUP interface ens192 virtual_router_id 51 priority 100 unicast_src_ip 192.168.100.11 unicast_peer { 192.168.100.12 192.168.100.13 } virtual_ipaddress { 192.168.100.10 } }4.4 haproxy 后端健康检查导致 503现象通过 VIP 访问 apiserver 时偶发 503 或者请求超时直接访问各 master 的 6443 端口一切正常。原因haproxy 的option httpchk探测路径返回的不是 2xx。apiserver 的/healthz接口在认证正常时返回 200但如果探测用的请求头没设置Host字段或者探测频率过高导致 apiserver 限流健康检查就会被认为失败haproxy 把该后端标记为 down。解决把健康检查配置为option httpchk GET /healthz并调大inter间隔到 5 秒以上。另外要确认 haproxy 转发到后端的端口是 6443 而不是 8443这两个端口搞反是常见失误。4.5 网络插件与 POD_CIDR 不匹配现象部署完 flannel 后 Pod 一直 ContainerCreatingkubectl describe pod显示 CNI 网络初始化失败flannel 日志提示subnet env not set或网段冲突。原因flannel 默认从 kube-controller-manager 的--allocate-node-cidrs参数获取每个节点的 Pod 子网如果该参数没有开启或者 flannel 的配置与POD_CIDR不一致节点子网就分配不了。解决确认04-master.sh中 kube-controller-manager 的启动参数包含--allocate-node-cidrstrue --cluster-cidr10.244.0.0/16且 flannel 的net-conf.json里的Network字段与之相同。如果用的是 calico只需把 CNI 相关 manifest 里的网段改成POD_CIDR的值两者原理一样。5. 扩容与验收把 worker 节点加进来并验证故障转移5.1 worker 节点添加与自动签发证书控制面部署完成后node 节点的加入过程被压缩成一个脚本# 在 node1 节点执行 bash 05-node.sh这个脚本做的事有三件第一从/opt/kubernetes/cfg读取预生成的 kubelet、kube-proxy 的 kubeconfig 和证书第二把 kubelet 和 kube-proxy 注册为 systemd 服务并启动第三向 apiserver 发起 CSR 自动签发请求让 kubelet 获得与 apiserver 通信的客户端证书。证书签发这里有个前提需要注意脚本里KUBELET_KUBECONFIG指向的kubelet.kubeconfig需要在 master 上预先用kubelet-csr.json签发好或者开启 apiserver 的--cluster-signing-cert-file与--cluster-signing-key-file让 kubelet 自己走 CSR 流程。前者适合对证书有严格管控的场景后者适合追求自动化的场景。新节点加入后在 master 上执行kubectl get node -o wide输出里能看到 node1 的状态从 NotReady 变为 Ready这个转变需要等 kubelet 启动并成功上报状态一般 30 秒到 1 分钟。如果一直 NotReady用journalctl -u kubelet -f看日志KubeletNotReady的原因无非就两种容器运行时没起来或者 CNI 没配置好。容器运行时方面脚本默认使用 containerd部署时通过 01-init.sh 完成安装和配置kubelet 的--container-runtime-endpoint指向 containerd 的 sock 文件。如果你所在环境已有 docker 并想复用需要把 kubelet 的启动参数改为--container-runtimeremote --container-runtime-endpointunix:///var/run/dockershim.sock但注意新版本 k8s 已移除 dockershim建议直接跟随 containerd 方案。worker 节点全部加入后记得在所有节点上都安装一遍 CNI 插件这一步是在配置网络插件时完成的而不是在加入节点时。5.2 用故障演练验证高可用集群搭好了高可用到底能不能扛住故障不能靠嘴说我每次都会做两组演练。第一组杀掉一台 master 上的 kube-apiserver 服务。systemctl stop kube-apiserver然后从客户端机器上用 VIP 地址执行kubectl get nodes。正常情况下 5-10 秒内请求恢复VIP 已经漂移到另一台 master。这个操作验证的是 keepalived 的探活能力和 haproxy 的故障转移是整个高可用架构里最重要的一环。第二组停掉一个 etcd 成员。systemctl stop etcd再执行kubectl get pods -A确认控制面仍然可以工作但会产生一个etcd-leader-change事件。这里要特别注意etcd 三节点允许挂一个但如果同时挂两个Raft 无法达成多数派整个集群会只读不可写。所以生产环境的 etcd 备份策略不能依靠节点冗余必须做定期快照。演练结束后把服务恢复再确认一遍kubectl get nodes所有节点 Ready。这个过程建议写入部署验收报告作为集群交付的硬性标准。6. 留在生产环境里的小技巧把脚本改造成可二次使用的模板二进制部署脚本最大的价值不是跑一遍而是变成团队内部可复用的部署模板。我通常会在拿到脚本后做三处改造让它能适应不同项目和网络规划。第一处改造是把env.sh拆成env.sh和env.local.sh两个文件。env.sh存放所有默认参数env.local.sh按项目覆盖 IP、VIP、网段。这样脚本本身就是文档每个项目改什么一目了然不会再出现改了主配置忘了改从配置的情况。第二处改造是加上证书到期检查。二进制集群没有kubeadm certs check-expiration可用我用一个 cron 任务定期扫描证书目录提前 30 天告警find /etc/kubernetes/pki /etc/etcd/ssl -name *.pem \ -exec openssl x509 -enddate -noout -in {} \; \ | grep -E notAfter | awk -F {print $2} | while read date; do if [ $(date -d $date %s) -lt $(date -d 30 days %s) ]; then echo WARNING: certificate expires at $date fi done第三处改造是给脚本加幂等保护。原始脚本如果执行到一半失败重新执行可能会重复生成证书或重复写 systemd 文件。我会在每个脚本开头加一段判断检测到目标文件已存在且内容一致就跳过避免二次执行导致不可预知的覆盖。这套脚本我从测试环境一路用到客户机房最大的教训是生产环境的证书有效期不能照搬默认的一年所有证书签发完成后第一件事就是把 CA 私钥备份到离线介质然后根据企业安全策略重新调整证书有效期再执行一次签发流程。从那以后我每次部署完都会强制走一遍故障演练流程再把证书备份清单交给客户这个习惯帮我避免了至少两次线上事故。希望这套部署脚本和这些踩坑记录能帮到你少走点我走过的弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站