简介:在云原生实践与容器编排学习中,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(默认 6443);keepalived 维护一个 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-proxy(master 与 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_IP=192.168.100.10 VIP_INTERFACE=ens192 # 网段划分,按需调整,部署后不可修改 SERVICE_CIDR=10.96.0.0/12 POD_CIDR=10.244.0.0/16 CLUSTER_DNS=10.96.0.10 # 版本与目录 K8S_VERSION="v1.28.2" ETCD_VERSION="v3.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 2>&1 | tee -a /tmp/k8s-init.log # 2. 只在 master1 上执行证书签发,脚本会自动分发到其他 master bash 02-cert.sh 2>&1 | 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_API=3 etcdctl --cacert=/etc/etcd/ssl/ca.pem \ --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem \ --endpoints=https://${ip}:2379 endpoint health done # kube-system 下核心 Pod kubectl get pods -n kube-system我目前看到的正常输出是:kubectl get cs返回 controller-manager 与 scheduler 均为 healthy;etcd endpoint health 全部返回is healthy;kube-system 下至少能看到 etcd、kube-apiserver 等静态 Pod 正常,coredns 与网络插件因为还没部署 CNI,处于 Pending 状态是预期行为。
这一步的意义在于把证书、etcd、控制面三个最容易出问题的点隔离出来。后续无论哪里出故障,你都能快速定位是哪个环节的问题,而不是一头扎进 Pod 日志里看半天。
4. 避坑实录:二进制部署最常见的五个翻车点
4.1 证书 SAN 缺失导致 kubelet 无法连接 apiserver
现象:kubectl get nodes只能看到本机,其他 master 节点 NotReady,kubelet 日志持续报Unable to connect to the server: x509: certificate is valid for ... not for ...。
原因:kube-apiserver 证书的 SAN(Subject 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 一直 ContainerCreating,kubectl 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-cidrs=true --cluster-cidr=10.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-runtime=remote --container-runtime-endpoint=unix:///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 私钥备份到离线介质,然后根据企业安全策略重新调整证书有效期再执行一次签发流程。从那以后,我每次部署完都会强制走一遍故障演练流程,再把证书备份清单交给客户,这个习惯帮我避免了至少两次线上事故。希望这套部署脚本和这些踩坑记录能帮到你,少走点我走过的弯路。
本文还有配套的精品资源,点击获取