【K8S 运维实战】02-生产级集群搭建

📅 2026/7/18 23:14:16
【K8S 运维实战】02-生产级集群搭建
生产级集群搭建:kubeadm vs 二进制 vs 托管集群的选型与落地一句话定位:三种搭建方式的成本、自由度、运维边界全对比,给团队一份能落地的选型表。写在前面我们公司要上 K8s,用哪种方式搭?这是我被问过最多的问题之一。问的人期待一个标准答案,但现实是:不同规模、不同团队、不同业务,答案完全不同。我见过 5 个人的创业团队硬上二进制部署,3 个运维天天救火;也见过金融客户因为合规要求,只能用自建 kubeadm,把托管集群直接否掉;还见过某互联网公司为了省事用托管集群,结果被厂商绑定,改云成本翻倍。搭建方式的选择,本质是运维成本、自由度、合规约束三者的权衡。这三种方式没有绝对优劣,只有是否匹配你的场景。这篇我会把 kubeadm、二进制、托管集群三种方式掰开:各自适合什么场景、生产实践的关键点(尤其是 kubeadm 的 HA 控制面和证书管理,这是新手最容易踩雷的地方)、托管集群的成本陷阱。最后给一份选型决策矩阵,你能直接拿去给团队做评估。版本基线:K8s 1.30.4,kubeadm 1.30.4,Helm 3.14,操作系统 Ubuntu 22.04 LTS。核心问题kubeadm、二进制、托管集群,本质区别是什么?kubeadm 搭生产集群,证书、etcd、HA 控制面怎么搞才稳?二进制部署在 2024 年还有意义吗?什么场景才值得?托管集群(ACK/EKS/TKE)省了什么、限制了什么?隐性成本在哪?怎么根据团队规模和业务诉求,做出可复用的选型决策?一、原理剖析1.1 三种方式的本质差异先把三种方式的本质讲清楚,不然选型就是拍脑袋。kubeadm:官方提供的集群引导工具。它把初始化控制面、签发证书、配置 kubelet、加入节点这套流程标准化,但不负责基础设施(机器、网络、LB)。本质是半自动引导工具 一份配置。二进制部署:手工下载 apiserver/scheduler/controller-manager/kubelet/kube-proxy 二进制,用 systemd 跑起来,自己配证书、自己写 etcd 集群。本质是手工搭一个 K8s,你能看到每个螺丝。托管集群:云厂商(阿里云 ACK、AWS EKS、腾讯云 TKE)提供控制面,你只管工作节点。控制面的可用性、升级、监控由厂商负责。本质是控制面 as a Service。┌──────────────┬──────────────────┬──────────────────┬──────────────────┐ │ │ kubeadm │ 二进制 │ 托管集群 │ ├──────────────┼──────────────────┼──────────────────┼──────────────────┤ │ 控制面部署 │ 工具自动引导 │ 手工 systemd │ 厂商托管 │ │ etcd │ 可内置/外置 │ 手工搭 │ 厂商托管(隐藏) │ │ 证书管理 │ kubeadm 自动签发 │ 全手工 │ 厂商托管 │ │ 升级 │ kubeadm upgrade │ 手工替换二进制 │ 厂商一键(有限) │ │ 自由度 │ 中(标准路径) │ 极高 │ 低(厂商限制) │ │ 运维成本 │ 中 │ 极高 │ 低 │ │ 故障排查 │ 可查所有组件 │ 可查所有组件 │ 控制面黑盒 │ │ 离线/私有云 │ 支持 │ 支持 │ 不支持(多数) │ │ 起步门槛 │ 中 │ 极高 │ 低 │ └──────────────┴──────────────────┴──────────────────┴──────────────────┘1.2 控制面高可用:为什么是核心难点三种方式都要解决一个问题:控制面高可用。这是生产集群和测试集群最大的分水岭。控制面高可用的核心是 apiserver 的多实例 前置 LB。etcd 也要多实例(推荐 3 或 5 节点,奇数防脑裂)。看下图:端到端 Raft端到端 Raft端到端 Raft外部 LBVIP: 10.0.0.100keepalived/Haproxyapiserver-110.0.0.10:6443apiserver-210.0.0.11:6443apiserver-310.0.0.12:6443etcd-110.0.0.10:2379etcd-210.0.0.11:2379etcd-310.0.0.12:2379scheduler-1leader electionscheduler-2standbycontroller-mgr-1leader electioncontroller-mgr-2standby关键认知:apiserver 无状态,可直接水平扩展,前面挂 LB。etcd 是 Raft 一致性协议,多节点之间需要端到端通信,不能简单 LB。每个 apiserver 连自己的本地 etcd 性能最好。scheduler 和 controller-manager 通过 leader election 选主,多副本只有一个干活,其他 standby。这就是为什么 kubeadm 默认参数带--leader-electtrue。kubelet 和 kube-proxy 连 apiserver 用的是 kubeconfig 里的 server 地址。生产环境这个地址必须是 LB 的 VIP,否则 apiserver 故障切换就没意义。这是 kubeadm HA 部署的核心:--control-plane-endpoint指向 LB。1.3 证书体系:K8s 的信任链证书管理是生产集群的另一大坑。K8s 控制面有一堆证书,搞不清楚就升级就出事。CA(pki/ca.crt / ca.key)──┬─ apiserver serving cert ├─ apiserver-kubelet-client cert ├─ front-proxy-ca ─── front-proxy-client ├─ etcd CA(pki/etcd/ca.crt) │ ├─ etcd server cert │ ├─ etcd peer cert │ └─ etcd healthcheck-client ├─ apiserver-etcd-client cert └─ sa.key / sa.pub(签名 ServiceAccount Token)记住几点:CA 有效期 10 年,但签发的组件证书默认只有 1 年。1 年到期不续,集群就挂。kubeadm 1.20 支持自动续期(kubelet 触发),但控制面静态 Pod 的证书需要 kubeadm 主动续。升级前必查证书有效期:kubeadm certs check-expiration。续期命令:kubeadm certs renew all。续完必须重启控制面静态 Pod(apiserver/scheduler/controller-manager),否则用的还是老证书。生产环境强烈建议把根 CA 离线备份,CA 丢了整个集群的信任链就断了。二、实战操作2.1 环境准备(3 控制面 3 工作节点)这次我们搭一个真正的 HA 集群:3 台 master 3 台 worker 外部 LB。角色主机名IP配置LBlb-0110.0.0.1002C4Gmasterk8s-master-110.0.0.114C8G 100G SSD(etcd)masterk8s-master-210.0.0.124C8G 100G SSDmasterk8s-master-310.0.0.134C8G 100G SSDworkerk8s-worker-110.0.0.218C16Gworkerk8s-worker-210.0.0.228C16Gworkerk8s-worker-310.0.0.238C16GVIP 用 keepalived 漂在 LB 上:10.0.0.100:6443。2.2 搭建 HA 负载均衡(keepalived haproxy)apiserver 前置 LB,生产推荐 keepalived haproxy(也可以用云厂商 SLB,更省心)。在两台 LB 机器上:# 两台 LB 都执行sudoapt-getinstall-ykeepalived haproxy# 配置 haproxy(两台配置一样)sudocat/etc/haproxy/haproxy.cfgEOF global log /dev/log local0 maxconn 4096 defaults log global mode tcp option tcplog timeout connect 5s timeout client 30s timeout server 30s frontend k8s-api bind *:6443 mode tcp default_backend k8s-api backend k8s-api mode tcp option tcp-check balance roundrobin server k8s-master-1 10.0.0.11:6443 check inter 5s fall 3 rise 2 server k8s-master-2 10.0.0.12:6443 check inter 5s fall 3 rise 2 server k8s-master-3 10.0.0.13:6443 check inter 5s fall 3 rise 2 EOF# 主 LB 配置 keepalivedsudocat/etc/keepalived/keepalived.confEOF global_defs { router_id K8S_LB } vrrp_script check_haproxy { script pidof haproxy interval 2 weight -20 } vrrp_instance VI_1 { state MASTER # 备机写 BACKUP interface eth0 virtual_router_id 51 priority 100 # 备机写 90 advert_int 1 authentication { auth_type PASS auth_pass K8sHaPwd2024 } virtual_ipaddress { 10.0.0.100/24 } track_script { check_haproxy } } EOFsudosystemctlenable--nowhaproxy keepalived# 验证 VIPipaddr show eth0|grep10.0.0.1002.3 kubeadm 初始化第一个控制面所有节点先做基础准备(参考上一篇的环境准备步骤):关 swap、装 containerd、装 kubeadm/kubelet/kubectl。然后开始初始化。# 在 k8s-master-1 上执行# 1. 预下载镜像(避免 init 时拉镜像慢)sudokubeadm config images pull\--image-repositoryregistry.k8s.io\--kubernetes-versionv1.30.4# 2. 生成初始化配置catkubeadm-init.yamlEOF apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration bootstrapTokens: - groups: - system:bootstrappers:kubeadm:default-node-token token: abcdef.0123456789abcdef ttl: 24h usages: - signing - authentication nodeRegistration: criSocket: unix:///run/containerd/containerd.sock name: k8s-master-1 taints: [] --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.30.4 imageRepository: registry.k8s.io controlPlaneEndpoint: 10.0.0.100:6443 apiServer: certSANs: - 10.0.0.100 - k8s-api.example.com - 127.0.0.1 extraArgs: audit-log-path: /var/log/kubernetes/audit/audit.log audit-log-maxage: 30 audit-log-maxbackup: 10 audit-log-maxsize: 100 feature-gates: DynamicResourceAllocationtrue extraVolumes: - name: audit hostPath: /var/log/kubernetes/audit mountPath: /var/log/kubernetes/audit readOnly: false pathType: DirectoryOrCreate controllerManager: extraArgs: cluster-signing-duration: 87600h horizontal-pod-autoscaler-sync-period: 15s node-monitor-grace-period: 40s scheduler: extraArgs: profile: kubernetes.io/v1.30 etcd: local: dataDir: /var/lib/etcd extraArgs: auto-compaction-mode: periodic auto-compaction-retention: 5h quota-backend-bytes: 8589934592 heartbeat-interval: 500 election-timeout: 5000 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration maxPods: 110 kubeReserved: cpu: 500m memory: 1Gi ephemeral-storage: 1Gi systemReserved: cpu: 500m memory: 1Gi evictionHard: memory.available: 500Mi nodefs.available: 10% imagefs.available: 10% imageGCHighThresholdPercent: 85 imageGCLowThresholdPercent: 80 EOF# 3. 创建审计目录sudomkdir-p/var/log/kubernetes/audit# 4. 初始化(注意 --upload-certs 让其他 master 加入时自动同步证书)sudokubeadm init--configkubeadm-init.yaml --upload-certs|teekubeadm-init.log# 输出里会有两条关键命令:# (a) 加入新控制面节点# (b) 加入工作节点# 一定要保存好 kubeadm-init.log# 5. 配置 kubectlmkdir-p$HOME/.kubesudocp-i/etc/kubernetes/admin.conf$HOME/.kube/configsudochown$(id-u):$(id-g)$HOME/.kube/config2.4 加入其余控制面和工作节点# 在 k8s-master-2 / k8s-master-3 上执行(从 init 日志拿命令,形如:)sudokubeadmjoin10.0.0.100:6443\--tokenabcdef.0123456789abcdef\--discovery-token-ca-cert-hash sha256:hash\--control-plane\--certificate-keycertificate-key# 在 k8s-worker-1/2/3 上执行sudokubeadmjoin10.0.0.100:6443\--tokenabcdef.0123456789abcdef\--discovery-token-ca-cert-hash sha256:hash注意--control-plane标志:加 master 用,会拉起控制面静态 Pod;加 worker 不用。--certificate-key是 init 时--upload-certs生成的,有效期 2 小时,过期可以kubeadm init phase upload-certs --upload-certs重新生成。2.5 安装 CNI 和验证# 装 Calico(下一篇文章会详细讲 CNI 选型)kubectl apply-fhttps://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/calico.yaml# 验证所有节点 Readykubectl get nodes-owide# NAME STATUS ROLES AGE VERSION# k8s-master-1 Ready control-plane 10m v1.30.4# k8s-master-2 Ready control-plane 8m v1.30.4# k8s-master-3 Ready control-plane 8m v1.30.4# k8s-worker-1 Ready none 6m v1.30.4# k8s-worker-2 Ready none 6m v1.30.4# k8s-worker-3 Ready none 6m v1.30.4# 验证所有 Pod Runningkubectl get pods-A# 验证 etcd 集群成员sudoETCDCTL_API3etcdctl--endpointshttps://127.0.0.1:2379\--cacert/etc/kubernetes/pki/etcd/ca.crt\--cert/etc/kubernetes/pki/etcd/healthcheck-client.crt\--key/etc/kubernetes/pki/etcd/healthcheck-client.key\member list-wtable2.6 证书管理与续期生产必须掌握的运维动作:# 1. 检查证书有效期(日常巡检必备)sudokubeadm certs check-expiration# 2. 续期所有证书(到期前 30 天做)sudokubeadm certs renew all# 3. 续期后必须重启静态 Pod(否则不生效)# kubeadm 1.30 可以这样重启:sudomkdir-p/etc/kubernetes/manifests-backupsudomv/etc/kubernetes/manifests/*.yaml /etc/kubernetes/manifests-backup/# 等 30 秒让 kubelet 把 Pod 停掉sleep30sudomv/etc/kubernetes/manifests-backup/*.yaml /etc/kubernetes/manifests/# 验证kubectl-nkube-system get pods|grep-Eapiserver|controller|scheduler# 4. 更新 kubelet 用的 admin.conf(本地 kubectl 配置)sudocp/etc/kubernetes/admin.conf$HOME/.kube/configsudochown$(id-u):$(id-g)$HOME/.kube/config# 5. 备份 CA(每季度做一次,放离线介质)sudotar-czvfk8s-ca-backup-$(date%Y%m%d).tar.gz /etc/kubernetes/pki/ca.crt /etc/kubernetes/pki/ca.key /etc/kubernetes/pki/sa.key /etc/kubernetes/pki/sa.pub /etc/kubernetes/pki/etcd/ca.crt /etc/kubernetes/pki/etcd/ca.key# 把 tar 包存到离线介质,CA 是集群信任链的根,丢了等于集群报废2.7 集群升级流程(1.30.4 → 1.31.x)升级是生产最容易出事的操作,流程必须严格:# 1. 先升级 kubeadm(所有 master)sudoapt-mark unhold kubeadmsudoapt-getupdatesudoapt-getinstall-ykubeadm1.31.x-1.1sudoapt-mark hold kubeadm# 2. 验证 kubeadm 版本kubeadm version# 3. 升级第一个 master 的控制面sudokubeadm upgrade apply v1.31.x# 4. 升级其他 master(注意是 upgrade node,不是 apply)sudokubeadm upgradenode# 5. 腾空节点(逐个 master 操作)kubectl cordon k8s-master-1 kubectl drain k8s-master-1 --ignore-daemonsets --delete-emptydir-data# 6. 升级 kubelet 和 kubectlsudoapt-mark unhold kubelet kubectlsudoapt-getinstall-ykubelet1.31.x-1.1kubectl1.31.x-1.1sudoapt-mark hold kubelet kubectlsudosystemctl daemon-reloadsudosystemctl restart kubelet# 7. 恢复调度kubectl uncordon k8s-master-1# 8. 同样流程升级 worker 节点(kubeadm upgrade node 升级 kubelet)# 9. 升级 CNI(很多事故是升级 K8s 忘了升级 CNI 导致的)# 10. 验证:kubectl get nodes 看 kubelet 版本经验:升级务必先在测试集群走一遍完整流程,记录每步耗时和坑。生产升级分批,一个节点升完观察 30 分钟再升下一个。永远不要在生产直接跳两个 minor 版本(虽然官方允许,但 CNI/CSI 兼容性容易翻车)。三、踩坑与排查坑 1:kubeadm init 失败,报[ERROR Swap]: running with swap on is not supported现象:kubeadm init直接报错退出。原因:K8s 1.30 仍然不支持 swap(实验性支持需手动开)。新装的 Ubuntu 默认开了 swap 分区。解决:sudoswapoff-asudosed-i/swap/d/etc/fstab# 重启验证free-h# Swap 行必须全是 0坑 2:join 失败,报token invalid或discovery-token-ca-cert-hash不匹配现象:worker 节点 join 时报 token 失效或 hash 对不上。原因:kubeadm init 时生成的 token 默认 24 小时过期;CA cert hash 是基于 CA 证书算的,如果重装过集群 hash 会变。解决:# 在 master 上重新生成 tokensudokubeadm token create --print-join-command# 这条命令直接打印可用的完整 join 命令# 如果还想要 control-plane 的 certificate-keysudokubeadm init phase upload-certs --upload-certs# 输出的 key 用在 --certificate-key 参数坑 3:HA 集群 LB 切换后,kubelet 连不上 apiserver现象:LB 主备切换后,部分节点 kubelet 报connection refused,Pod 状态异常。原因:kubelet 连 apiserver 用的是 kubeconfig 里的 server 地址(VIP)。如果 keepalived 切换 VIP 慢(10s),kubelet 短期内连不上,影响 Pod 状态上报和心跳。排查与解决:# 1. 看 kubelet 日志sudojournalctl-ukubelet--since5 min ago|grep-iapiserver# 2. 测 VIP 连通性nc-zv10.0.0.1006443# 3. 优化 keepalived 切换速度# vrrp_script interval 调小到 1s,fallback 检查脚本要快# 不要用复杂脚本,简单的 pidof/ping 即可# 4. 给 kubelet 加重试参数(/var/lib/kubelet/kubeadm-flags.env)KUBELET_KUBEADM_ARGS--resolv-conf/run/systemd/resolve/resolv.conf --kubeconfig/var/lib/kubelet/kubeconfig --config/var/lib/kubelet/config.yaml --v2根治:云上用云厂商 SLB(自带健康检查 私网 VIP),比自建 keepalived 稳得多。坑 4:etcd 集群磁盘满,整个集群不可写现象:apiserver 报etcdserver: mvcc: database space exceeded,所有写操作失败。原因:etcd 默认配额 2GB(我们的配置里改成 8GB),超过会进入只读模式。常见于大量 watch 事件、CRD 滥用、没有压缩。解决:# 1. 紧急压缩(在任一 etcd 节点)exportETCDCTL_API3etcdctl--endpointshttps://10.0.0.11:2379,https://10.0.0.12:2379,https://10.0.0.13:2379\--cacert/etc/kubernetes/pki/etcd/ca.crt\--cert/etc/kubernetes/pki/etcd/peer.crt\--key/etc/kubernetes/pki/etcd/peer.key\compact$(etcdctl... endpoint status-wjson|jq.[].Status.header.revision)# 2. 撤销告警etcdctl... alarm disarm# 3. 碎片整理(每个节点单独做,逐个做避免同时不可用)etcdctl...--endpointshttps://10.0.0.11:2379 defrag# 等 30s 再做下一个节点etcdctl...--endpointshttps://10.0.0.12:2379 defrag# 4. 长期:开启 auto-compaction(已在配置里加)# auto-compaction-modeperiodic, auto-compaction-retention5h坑 5:托管集群看似便宜,账单爆炸现象:某团队用 ACK Pro 托管集群,起步 3 个 worker,月账单 2000 元。业务起来后扩到 20 个 worker,账单变 1.5 万,其中托管控制面费 工作节点 流量费 监控费,远超预算。原因:托管集群的隐性成本:控制面管理费:按集群收(ACK Pro ~240 元/月,EKS $0.10/小时)。工作节点:云主机费,比自建贵(包了镜像、运行时)。流量费:跨可用区、公网流量,生产很容易爆。托管组件费:云监控、日志服务、安全扫描,默认开,按量收费。快照/备份费:云盘快照按容量收。解决:选型阶段就把 TCO(总拥有成本)算清楚,不要只看控制面费。具体对比见下一节。四、最佳实践与选型决策矩阵4.1 托管集群选型决策表维度阿里云 ACKAWS EKS腾讯云 TKE自建 kubeadm控制面费用Pro 0.84 元/时$0.10/时0.64 元/时0(自担机器)起步门槛极低,5 分钟出集群低极低中,需 K8s 经验私有云/离线标准版可私有部署不支持可私有部署完全支持控制面可观测有限(Pro 给指标)有限有限完全可观测升级自由度厂商节奏厂商节奏厂商节奏完全自主CNI 选择Terway/Flannel/CalicoVPC CNI/Calico/CiliumTKE CNI/Calico任意厂商绑定中(StorageClass/Ingress)高(VPC CNI)中无适合规模中小到大型大型中小到大型任意合规/金融场景可(专有版)一般可强4.2 选型决策矩阵按这个顺序问自己:是否在公有云?否 → 自建(kubeadm 或二进制)。是 → 看 2。是否需要私有云/离线?是 → 自建。否 → 看 3。团队 K8s 运维能力?弱(无专职 SRE) → 托管。中 → 托管或 kubeadm。强 → 看 4。集群规模 100 节点?是 → 托管(控制面免运维)或 kubeadm HA。否 → 看 5。特殊定制需求(如自研调度器、特殊网络)?是 → 二进制或 kubeadm。否 → 托管。具体场景对照:创业团队/小公司(50 节点):公有云托管集群。控制面省心,把人力投到业务。中型公司(50-500 节点):托管 自建混合。核心生产托管,测试/特殊业务自建。大型公司(500 节点):多区域 kubeadm HA 自研运维平台。规模大到托管费不划算。金融/政企(合规优先):私有云 kubeadm HA,严格控制组件版本和审计。特殊场景(自研调度器/网络):二进制部署,深度定制。4.3 二进制部署什么时候还值得老实说,2024 年 99% 的场景不需要二进制。但有几种情况它仍有价值:学习/教学:想彻底搞懂每个组件怎么跑,二进制是最佳路径。深度定制:比如自研 scheduler 插件需要特定编译参数,或要改 kubelet 源码。极端精简:嵌入式/IoT 场景,只要 kubelet apiserver,不要 controller-manager。历史包袱:已有二进制集群,迁移成本高于维护成本。否则,kubeadm 是更优选择——它本质上也是把二进制部署流程标准化了,该看到的日志、该调的参数都能调,还省了证书管理的心。4.4 生产集群通用清单不论用哪种方式,生产集群都要满足:控制面 ≥ 3 副本,apiserver 前置 LBetcd ≥ 3 节点,独立 SSD,定期 defrag证书自动续期或监控告警( 30 天告警)etcd 定期备份(至少每天一次,跨地域存)控制面组件全部监控,Prometheus Grafana升级有完整 SOP,先测试再生产,分批节点开机自动加入集群(或自动伸缩)网络插件明确选型,CNI/NetworkPolicy 策略清晰日志收集(apiserver audit kubelet 业务日志)故障演练:定期 kill apiserver/etcd,验证 HA 有效性五、小结三种搭建方式没有银弹:kubeadm 是标准化半自动,适合大多数自建场景;二进制是完全手工,只在深度定制或学习场景有价值;托管集群是控制面 as a Service,适合人力紧张、不想自担控制面风险的团队。kubeadm HA 的核心是三个东西:控制面 endpoint 指向 LB、etcd 多节点 独立 SSD、证书体系持续运维。这三个搞稳了,集群的基础就稳了。证书管理是新手最容易忽视、生产最容易翻车的点。务必建立巡检机制:kubeadm certs check-expiration每月跑一次, 30 天告警,续期后重启静态 Pod。CA 离线备份是最后一道保险。托管集群的隐性成本必须算清:控制面费只是冰山一角,流量费、监控费、托管组件费才是大头。选型时算 TCO,不要只看单价。被厂商绑定后迁移成本极高,核心业务建议保留自建能力。思考题你的团队当前规模(节点数、SRE 人数、业务合规要求),应该选哪种搭建方式?列出你的决策依据。如果今天让你为一家 200 人、50 节点的中型公司设计 K8s 落地方案,你会推荐托管还是自建?多集群策略怎么设计?延伸阅读kubeadm 官方文档:https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/高可用拓扑选择:https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/ha-topology/证书管理:https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-certs/ACK 产品文档:https://help.aliyun.com/product/85222.htmlEKS 用户指南:https://docs.aws.amazon.com/eks/