第一次用二进制方式把控制面拉起来的那天,我在 etcd 的启动日志里泡了整整一个下午。表面上所有文件都摆好了,证书也签了,systemd 单元也写了,systemctl start返回成功,可etcdctl endpoint health就是不给面子,一直提示连接超时。后来才发现是配置里listen-client-urls写成了127.0.0.1,而健康检查走的是节点 IP——一个字符的差别,卡掉半天。这件事让我对"二进制部署"有了很实在的认识:它不难,但它对细节的容忍度极低,任何一处偷懒都会在后面某个环节以完全不相干的形式爆出来。
这篇东西讲的就是这套流程的上半段:用二进制方式部署 Kubernetes 集群的 etcd 数据库集群和 master 控制面节点。适合那些已经不满足于kubeadm init一行命令完事、想真正搞清楚控制面每个组件怎么起、参数怎么给、证书怎么串起来的人。如果你只是想快速跑个测试集群,kubeadm 更合适;但如果你要交付一套能长期维护、出问题能自己定位的环境,二进制这条路值得走一遍。下面这些内容都是我在实际环境里反复趟出来的,包含完整的配置样例、启动顺序、健康核验方法,以及几个我踩过并且知道很多人也会踩的坑。
1. 二进制部署的取舍:多花三倍时间到底换来了什么
1.1 kubeadm 与二进制两条路的真实差距
很多人对二进制部署的第一印象是"老派""折腾",这个判断不算错,但只说对了一半。kubeadm 把控制面组件全部容器化,用静态 Pod 的方式跑在 kubelet 下面,好处是升级方便、依赖少、一条命令起集群;代价是当 apiserver 起不来的时候,你要先确认容器运行时是否正常、静态 Pod 的 manifest 是否被正确读取、kubelet 的 cgroup driver 是否匹配,排查链条被拉长了三到四层。
二进制部署则是把 apiserver、controller-manager、scheduler、etcd 全部作为宿主机上的 systemd 服务运行。任何一个组件挂了,systemctl status直接告诉你退出码,journalctl -u直接给你完整日志,中间没有任何抽象层。这一点在故障定位时价值极高——你看到的错误就是组件本身吐出来的错误,不需要在容器层和宿主层之间来回猜。
另一个差别在可控性。二进制方式下,每个组件的启动参数都明明白白写在 service 文件里,你想改--audit-log-path、想加--enable-admission-plugins、想调--kube-api-qps,改一行重启即可,不用去想 kubeadm 的配置对象怎么嵌套。反过来,kubeadm 的配置虽然有ClusterConfiguration这个入口,但并不是所有参数都能透传,遇到需要深度定制的场景,最后还是要落到静态 Pod 的 manifest 上手工改,那时候你面对的复杂度其实和二进制差不多了,还多了一层"这个改动会不会被 kubeadm upgrade 覆盖"的顾虑。
我的实际体会是:学习路径上,二进制跑一遍能让你对控制面的理解从"知道有这么几个组件"变成"知道它们怎么互相找对方";生产路径上,如果你的环境需要精细的审计、定制化的准入控制、或者有明确的合规要求要逐项核对启动参数,二进制更省心。
1.2 这套集群的组件清单与调用链路
在动手之前,先把要部署的东西列清楚。这套架构里,上半段涉及的组件有四个:
| 组件 | 角色 | 部署形态 | 默认端口 |
|---|---|---|---|
| etcd | 集群唯一数据源,存所有对象状态 | systemd 服务,三节点集群 | 2379(客户端)/2380(对等) |
| kube-apiserver | 唯一读写 etcd 的入口,所有请求的关口 | systemd 服务,可多实例 | 6443 |
| kube-controller-manager | 控制器循环,负责"期望状态"落地 | systemd 服务,多实例选主 | 10257(指标) |
| kube-scheduler | 调度决策,决定 Pod 落到哪个节点 | systemd 服务,多实例选主 | 10259(指标) |
调用链路是单向的、清晰的:kubectl或其他客户端 → apiserver(认证、鉴权、准入)→ etcd。controller-manager 和 scheduler 都是 apiserver 的客户端,它们通过 watch 机制感知对象变化,把决策结果写回 apiserver,再由 apiserver 落到 etcd。
这里面有一条很多人一开始会搞混的规则:只有 apiserver 能直连 etcd,其他组件一律不允许。controller-manager 想读数据?走 apiserver。scheduler 想更新 Pod 的绑定信息?走 apiserver。这条规则保证了 etcd 的写入路径只有一条,所有校验逻辑都集中在一个地方。所以你在配置 etcd 的证书时,客户端证书只需要给 apiserver 用,不需要给其他组件各签一份。
另一个必须提前想清楚的是高可用拓扑。上半段虽然是"master 节点和 etcd 集群",但你要在建之前就决定:etcd 是独立部署还是和 master 混部?三台机器还是五台?我的建议是etcd 独立部署,即使机器不够也要在逻辑上分开——etcd 对磁盘 IO 极其敏感,一旦和 apiserver 抢同一块盘,写入延迟上来之后整个集群都会变慢,表现出来就是"kubectl 卡""Pod 创建慢",而你去查 apiserver 日志又什么都查不到。混部在小规模测试环境可以接受,正式环境尽量避开。
1.3 动手前必须钉死的环境基线
二进制部署失败的原因里,有一大半跟"环境没对齐"有关,跟技术本身无关。下面这几项我会在动手前逐条核对,缺一项都不往下走。
操作系统与内核参数。关闭 swap 是硬性要求,swapoff -a只是临时生效,要改/etc/fstab注释掉 swap 行。同时确认br_netfilter和overlay两个内核模块已加载,net.bridge.bridge-nf-call-iptables和net.ipv4.ip_forward两个参数设为 1。这两项不设置,后面网络插件装上去 Pod 之间也通不了,而且症状很迷惑——同节点能通、跨节点不通。
时间同步。这一条被严重低估。etcd 的 Raft 协议依赖时间来判断心跳和选举超时,节点间时间偏差过大时会频繁触发重新选举,表现是集群反复在"健康"和"不健康"之间跳。所有节点必须配置同一个时间源,偏差控制在毫秒级。
主机名与 hosts 解析。每台机器设置唯一主机名,并且在/etc/hosts里写全所有节点的 IP 与主机名映射。不要依赖 DNS,集群刚起来的时候 DNS 往往还没就绪,证书里的 SAN 又需要主机名可解析。
防火墙与端口。要么在测试环境直接关掉,要么精确放行 2379、2380、6443、10257、10259 以及后续 kubelet 用的 10250。我倾向于后者,因为直接关防火墙容易掩盖端口配置错误,等到上生产再开就一堆问题。
版本一致性。所有节点的 kube-apiserver、kube-controller-manager、kube-scheduler、kubectl 版本必须完全一致,小版本号都不能差。etcd 版本要选 apiserver 官方兼容矩阵里支持的版本,不要图新。
2. etcd 集群:证书、配置与三节点握手的关键链路
2.1 etcd 在这套架构里到底存了什么
先把这个说清楚,后面配证书的时候才知道为什么这么配。etcd 是一个分布式的键值存储,Kubernetes 把整个集群的"期望状态"和"实际状态"全部存在里面——每一个 Deployment、每一个 Service、每一个 ConfigMap、每一个 Secret,包括节点的心跳信息,都是 etcd 里的一个 key。key 的命名规则大致是/registry/<资源类型>/<命名空间>/<对象名>,比如/registry/deployments/default/nginx。
这意味着两件事。第一,etcd 挂了,集群就失忆了。已经跑着的 Pod 可能还能继续跑,因为容器是运行时在管,但任何声明式操作都会失败,controller-manager 也无法感知变化。第二,etcd 的性能直接决定集群的性能上限。Kubernetes 对 etcd 的写入是相当频繁的,节点心跳、状态更新、控制器回写,量大且持续。所以 etcd 的磁盘要用 SSD,不要用网络存储,也不要在上面跑别的重 IO 服务。
etcd 集群的部署数量有个硬性规则:必须是奇数,推荐 3 个或 5 个。因为 Raft 协议需要多数派(quorum)才能达成一致,3 节点容忍 1 个故障,5 节点容忍 2 个。2 节点和 4 节点没有任何意义,容错能力和少一个的奇数节点一样,但可用性风险更高——4 节点挂 2 个就完全不可用了,3 节点挂 1 个还能活。这一点在选择机器数量时就要定下来,中途扩容要重新配置集群成员,很麻烦。
2.2 自签 CA 与证书签发顺序的讲究
Kubernetes 全链路是 TLS 的,证书签发是二进制部署里最容易出错也最值得认真对待的一步。整个上半段涉及的证书关系可以用一句话概括:一个自签的根 CA,下面挂两套独立的中间体系——etcd 一套,kubernetes 一套。
分开的理由是隔离。etcd 的 CA 只用来签发 etcd 服务端证书、etcd 对等证书和 apiserver 访问 etcd 用的客户端证书;kubernetes 的 CA 用来签发 apiserver 服务端证书、各组件访问 apiserver 的客户端证书、以及后续 ServiceAccount 的密钥。两套 CA 不交叉,任何一套泄露都不会波及另一套。
具体要签的证书清单如下:
| 证书 | 用途 | 关键 SAN |
|---|---|---|
| etcd 服务端证书 | etcd 对外提供服务的身份 | 所有 etcd 节点 IP、127.0.0.1、主机名 |
| etcd 对等证书 | 节点之间 2380 端口通信 | 同上 |
| apiserver 访问 etcd 的客户端证书 | apiserver 作为 etcd 客户端 | 无(只需 CN) |
| apiserver 服务端证书 | apiserver 对外的身份 | 所有 master IP、VIP、kubernetes、kubernetes.default、kubernetes.default.svc、kubernetes.default.svc.cluster.local、10.0.0.1 |
最后那个10.0.0.1是 Service 网段的第一个地址,也就是集群内部kubernetes服务的 ClusterIP。这个地址必须出现在 apiserver 证书的 SAN 里,否则集群内部的组件(比如 CoreDNS、kube-proxy)通过 Service 名访问 apiserver 时会报证书不匹配。我见过不止一次因为漏了这一项,导致 CoreDNS 起来了但一直 CrashLoopBackOff,日志里只有一句含糊的x509: certificate is valid for ...。
签发工具上,cfssl和openssl都行,cfssl的 JSON 模板更直观,适合批量。下面是我常用的 etcd 服务端证书签发配置:
{ "CN": "etcd", "key": { "algo": "rsa", "size": 2048 }, "hosts": [ "127.0.0.1", "192.168.10.11", "192.168.10.12", "192.168.10.13", "etcd-1", "etcd-2", "etcd-3" ], "names": [ { "C": "CN", "L": "Beijing", "O": "etcd", "OU": "Etcd Security", "ST": "Beijing" } ] }签发命令大概是这个样子:
cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ etcd-server-csr.json | cfssljson -bare etcd-server打完会得到etcd-server.pem(证书)、etcd-server-key.pem(私钥)、etcd-server.csr(请求文件,可删)。三台机器用同一份证书是可以的,因为 SAN 里已经包含了所有节点。如果你更看重隔离性,也可以每台单独签,把 hosts 里只保留自己的 IP 和主机名。
提示:证书有效期建议给到 10 年。Kubernetes 组件的证书过期是个非常隐蔽的故障源——某天早上集群突然全部不可用,日志里才看到
certificate has expired。与其每年提心吊胆,不如一开始就给足期限,并且把签发脚本和到期日记在交接文档里。
2.3 配置文件与 systemd 单元的落地细节
etcd 的配置可以走命令行参数,也可以走 YAML 配置文件。三节点集群参数不少,我强烈建议走 YAML,一是可读性好,二是改的时候不容易漏。下面这份是节点一上的样例:
name: etcd-1>[Unit] Description=Etcd Server After=network.target After=network-online.target Wants=network-online.target [Service] Type=notify WorkingDirectory=/var/lib/etcd/ ExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd.yaml Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.targetType=notify是 etcd 特别推荐的方式,etcd 启动完成后会主动通知 systemd,这样systemctl start的返回时机才是准确的——用simple的话命令会在进程刚起来时就返回,你以为好了,其实还没选主完成。LimitNOFILE调大是必须的,apiserver 的 watch 连接会占用大量文件描述符,默认的 1024 很容易打满。
三台机器上的配置文件只有name、listen-*、advertise-*三组字段不同,initial-cluster必须完全一致。这一点经常被写错——有人图省事,每台机器的initial-cluster里只写自己,结果三台各自成了独立集群,怎么都连不上。
2.4 集群拉起之后的健康核验
启动顺序上,三台机器几乎同时systemctl start etcd即可。Raft 会自己完成选举,先启动的节点会一直重试连接其他成员,直到凑够多数派。全部启动后,用下面的命令核验:
ETCDCTL_API=3 etcdctl \ --endpoints=https://192.168.10.11:2379,https://192.168.10.12:2379,https://192.168.10.13:2379 \ --cacert=/etc/etcd/ssl/ca.pem \ --cert=/etc/etcd/ssl/etcd-server.pem \ --key=/etc/etcd/ssl/etcd-server-key.pem \ endpoint health --cluster三个节点都返回is healthy: true才算过。如果只有一部分健康,先看endpoint status:
ETCDCTL_API=3 etcdctl \ --endpoints=https://192.168.10.11:2379 \ --cacert=/etc/etcd/ssl/ca.pem \ --cert=/etc/etcd/ssl/etcd-server.pem \ --key=/etc/etcd/ssl/etcd-server-key.pem \ endpoint status --cluster -w table这个表格里最重要的两列是IS LEADER和RAFT INDEX。领导者必须唯一,如果出现两个 leader,说明脑裂了,通常是网络分区或者initial-cluster-token冲突。RAFT INDEX三个节点的值应该接近,如果某个节点明显落后,说明它没跟上复制,可能是磁盘慢或者网络丢包。
还有一个容易被忽略的检查:etcdctl member list里的成员数量和initial-cluster里写的一致。我曾经遇到过一个环境,之前部署失败过没清理干净,>/usr/local/bin/kube-apiserver \ --etcd-servers=https://192.168.10.11:2379,https://192.168.10.12:2379,https://192.168.10.13:2379 \ --etcd-cafile=/etc/kubernetes/pki/etcd/ca.pem \ --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.pem \ --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client-key.pem \ --secure-port=6443 \ --bind-address=192.168.10.11 \ --advertise-address=192.168.10.11 \ --service-cluster-ip-range=10.0.0.0/24 \ --service-node-port-range=30000-32767 \ --tls-cert-file=/etc/kubernetes/pki/apiserver.pem \ --tls-private-key-file=/etc/kubernetes/pki/apiserver-key.pem \ --client-ca-file=/etc/kubernetes/pki/ca.pem \ --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.pem \ --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client-key.pem \ --service-account-key-file=/etc/kubernetes/pki/sa.pub \ --service-account-signing-key-file=/etc/kubernetes/pki/sa.key \ --service-account-issuer=https://kubernetes.default.svc.cluster.local \ --authorization-mode=Node,RBAC \ --enable-admission-plugins=NodeRestriction \ --allow-privileged=true \ --v=2
这份参数里藏着几个必须解释清楚的细节。
--service-cluster-ip-range一旦确定就不能改。这是 Service 的虚拟 IP 段,已经创建的 Service 的 ClusterIP 就从这里面分配。改了之后旧 Service 全部失效,而且 etcd 里残留的对象会导致 apiserver 起来后各种报错。所以这个网段要在动手前规划好,通常用/24或/16,不要和节点网段、Pod 网段重叠。
--service-account-*三个参数是配套的,缺一不可。sa.key是私钥,用来签发 ServiceAccount 的 token;sa.pub是公钥,apiserver 用它验证 token;issuer是签发者标识,必须和 token 里的iss声明一致。这三个在较新版本里缺失会直接拒绝启动,因为 ServiceAccount 的 token 机制改成了 projected volume 形式,必须走这套签名体系。
--authorization-mode=Node,RBAC的顺序有讲究。授权时按顺序匹配,Node 模式在前意味着 kubelet 发出的请求会优先按节点身份校验,这样 kubelet 只能操作与自己节点相关的对象,权限边界更清晰。如果只用 RBAC,kubelet 的权限就得靠一堆 RoleBinding 来兜,容易配宽。
--advertise-address必须写本机的真实 IP。这个地址会被写进kubernetes服务的 Endpoints,其他组件和节点都通过它连 apiserver。写成0.0.0.0或者127.0.0.1都会导致集群内部连不通。
apiserver 起不来的最常见原因是证书问题。--etcd-certfile指向的证书如果 SAN 不对、或者 CA 不匹配,日志里会给x509: certificate signed by unknown authority。另一个高频原因是 etcd 的客户端证书 CN 写错了——etcd 那边如果配了client-cert-auth: true但没配--client-cert-auth-ca,或者证书链条不完整,握手会失败。
3.2 controller-manager 与 scheduler 的选主配置
这两个组件在单 master 环境下简单,在多 master 环境下必须配选主,否则多个实例会同时干活,产生冲突——比如两个 controller-manager 同时认为某个 Pod 需要重建,就会重复创建。
选主的三个参数是固定的:
--leader-elect=true \ --leader-elect-lease-duration=15s \ --leader-elect-renew-deadline=10s \ --leader-elect-retry-period=2s这几个值的默认值在多 master 环境下通常够用,但如果你的环境网络延迟较大,lease-duration可以适当放大到 20s 或 30s,避免频繁切换。注意renew-deadline必须小于lease-duration,retry-period必须小于renew-deadline,这个大小关系配错了组件会直接拒绝启动。
controller-manager 还有一个必须配的参数是--cluster-cidr,它要和后面网络插件的 Pod 网段一致。如果这个值没配或者配错,节点上的 PodCIDR 分配会出问题,表现是节点 NotReady 或者 Pod 一直 Pending。
scheduler 的参数相对简单,主要就是 kubeconfig 和选主。不过有一点值得注意:scheduler 的默认调度策略在较新版本里已经全部移到配置对象里,命令行参数只保留少量。如果你需要自定义调度器配置(比如调整节点打分权重),要走--config指向一个 KubeSchedulerConfiguration 文件。
这两个组件的 systemd 单元都要配After=kube-apiserver.service,但不要配Requires。因为 apiserver 重启的时候,controller-manager 和 scheduler 会自动重连,不需要跟着重启;配了Requires反而会导致 apiserver 短暂重启时把这两个也一起带下去,恢复时间变长。
3.3 kubeconfig 与 kubectl 的落地
kubectl 本身不依赖网络,它读的是 kubeconfig 文件。二进制部署里通常要生成三份 kubeconfig:admin 用的、controller-manager 用的、scheduler 用的。三份里的客户端证书不同,权限也不同——admin 证书的 CN 是admin,O 是system:masters,拥有全量权限;另外两个的 CN 分别是system:kube-controller-manager和system:kube-scheduler,靠内置的 ClusterRoleBinding 拿到对应权限。
生成方式可以用kubectl config set-cluster那一套命令:
kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/pki/ca.pem \ --embed-certs=true \ --server=https://192.168.10.11:6443 \ --kubeconfig=/root/.kube/config kubectl config set-credentials admin \ --client-certificate=/etc/kubernetes/pki/admin.pem \ --client-key=/etc/kubernetes/pki/admin-key.pem \ --embed-certs=true \ --kubeconfig=/root/.kube/config kubectl config set-context kubernetes \ --cluster=kubernetes \ --user=admin \ --kubeconfig=/root/.kube/config kubectl config use-context kubernetes --kubeconfig=/root/.kube/config--embed-certs=true这个参数建议加上,它把证书内容直接嵌进 kubeconfig 文件里,这样 kubeconfig 就是个自包含的文件,拷到别的机器上就能用,不用再同步证书。代价是文件体积大一点,而且证书轮换后要重新生成。
三份 kubeconfig 里的 server 地址,如果配置了 VIP(比如用 keepalived + haproxy 做了 6443 的负载均衡),就写 VIP;如果是纯测试环境单点,写单个 master 的 IP。写 VIP 的好处是 apiserver 单点故障时客户端不用改配置,坏处是 VIP 本身成了新的单点,需要额外保证它的高可用。
kubectl 配好之后,第一件事是kubectl get componentstatuses——虽然这个命令在新版本里已经废弃,但仍然能反映基本状态。更有用的是kubectl get --raw /healthz,返回ok说明 apiserver 到 etcd 的链路是通的。再执行kubectl api-resources能列出所有资源类型,说明整个 API 层是完整的。
4. 排错实录:二进制部署里最常翻车的六个点
4.1 证书 SAN 漏写导致的神秘连接失败
这是我在二进制部署里遇到次数最多、也最浪费时间的一类问题。症状通常是:apiserver 起来了,curl -k https://IP:6443/healthz返回 ok,但 kubectl 用域名或 VIP 访问就失败,报x509: certificate is valid for 192.168.10.11, not 192.168.10.100。
根因很直接:证书的 SAN 列表里没有你要访问的那个地址。签发时 hosts 只写了节点 IP,没写 VIP、没写kubernetes.default.svc。浏览器和 curl 加-k会忽略这个错误,所以很多人第一次调试时用curl -k验证通过了就以为没问题,等到真正用 kubectl 才暴露。
排查方法很简单,直接用 openssl 看证书内容:
openssl x509 -in /etc/kubernetes/pki/apiserver.pem -noout -text | grep -A 1 "Subject Alternative Name"这条命令会列出证书里所有的 SAN。对照一下你要访问的地址在不在里面,不在就重签。
需要加进 apiserver 证书 SAN 的完整清单是:所有 master 节点 IP、VIP(如果用了)、所有 master 主机名、kubernetes、kubernetes.default、kubernetes.default.svc、kubernetes.default.svc.cluster.local、10.0.0.1(Service 网段第一个 IP)。etcd 证书的 SAN 则是:所有 etcd 节点 IP、127.0.0.1、所有 etcd 主机名。
注意:重签证书后,所有依赖这份证书的组件都要重启。apiserver 重启会短暂中断服务,多 master 环境下用滚动重启,一个一个来。
4.2 etcd 启动即退出:数据目录权限与残留状态
systemctl start etcd后立刻failed,journalctl -u etcd里只有一两行就没了。这种情况九成是数据目录的问题。
第一种可能是权限。etcd 以什么用户运行,>kubectl get componentstatuses 2>/dev/null; \ kubectl get --raw /healthz; echo; \ kubectl get --raw /livez; echo; \ kubectl get --raw /readyz; echo
/healthz是老接口,/livez和/readyz是新版的分层检查。三个都返回 ok,说明 apiserver 自身的状态、到 etcd 的连接、内部 informer 缓存都是正常的。如果/readyz不 ok,可以加?verbose参数看具体是哪一项失败:
kubectl get --raw '/readyz?verbose'它会逐项列出检查结果,比如[+]etcd ok、[+]poststarthook/... ok,哪一项挂了非常明确。
etcd 侧的核验再跑一遍endpoint health --cluster,确认三个节点都健康、leader 唯一、raft index 接近。这两项都过了,上半段就算交付了。
5.2 交付前值得跑完的检查清单
下面这份清单是我每次部署后都会走一遍的,列出来供参考:
| 检查项 | 命令 | 期望结果 |
|---|---|---|
| etcd 三节点健康 | etcdctl endpoint health --cluster | 全部 true |
| etcd 成员数 | etcdctl member list | 3 个,无幽灵成员 |
| apiserver 健康 | kubectl get --raw /readyz | ok |
| 组件选主状态 | kubectl get leases -n kube-system | 各组件有且仅有一个 holder |
| 证书有效期 | openssl x509 -noout -dates -in ... | 剩余时间充足 |
| 端口连通性 | 从每台机器nc -zv全部关键端口 | 全部通 |
| 时间偏差 | 各节点date对比 | 偏差 < 500ms |
kubectl get leases -n kube-system这个检查特别值得加进去。它能看到 controller-manager、scheduler、以及后续加入的 kubelet 的选主情况。如果某个组件的 lease 显示 last renew 时间很旧,说明那个实例已经挂了但还没被清理,多 master 环境下这种情况会导致操作被路由到已经失效的实例上。
另外提一句,etcd 的定期备份要在上半段就配好,不要等集群跑起来再补。etcdctl snapshot save配合定时任务,每天一份,保留七天,这是最低要求。备份文件要验证过能恢复,没验证过的备份等于没有备份——我吃过这个亏,真出事的时候才发现备份文件是空的。
上半段的东西到这里就齐了:etcd 集群活着、apiserver 通着、controller-manager 和 scheduler 在选主、kubectl 能正常下发请求。下半段要接的是 worker 节点:kubelet 怎么配、containerd 怎么起、kube-proxy 和网络插件怎么落地、节点怎么加入集群。那部分坑同样不少,尤其是容器运行时和 cgroup driver 的匹配问题,可以单独写一篇。