
kubernetes 集群装到生产机上绕不开一个问题用什么方式装。kubeadm 一行命令能起一个能跑的集群托管服务点几下鼠标就有但真到机房不能出公网、镜像仓库要自己搭、交付时要写清楚每个组件怎么起来的、端口是谁监听、证书是谁签的场景二进制部署 k8s 集群几乎是唯一能交代得清楚的路子。而整套二进制集群里最先要立起来的不是 kubelet是 etcd 数据库集群和 master 控制面——etcd 存着集群里所有状态的唯一真相master 节点上的 kube-apiserver、kube-controller-manager、kube-scheduler 是大脑。这两块不通后面所有工作节点都是白搭。这篇就把上篇的活儿干完从零把三节点 etcd 数据库集群拉起来再把 master 控制面组件一个个跑起来最后用 kubectl 打通第一条请求。中段会附上完整的配置文件、参数解释和踩坑记录照着抄能跑改着用能懂。1. 手搓二进制集群这件事到底图什么1.1 三条部署路径的取舍逻辑把部署方式摊开看其实就是三种kubeadm、纯二进制、托管。它们不是谁替代谁的关系适用的场景差得挺远。kubeadm 的好处是快它把证书签发、静态 Pod 清单、etcd 部署、kubelet 配置全包了一条kubeadm init出来就是能用的集群。但它同时把这些东西藏起来了证书放在/etc/kubernetes/pki组件以静态 Pod 形式跑你要改一个 apiserver 启动参数得去改/etc/kubernetes/manifests/kube-apiserver.yaml再让 kubelet 自己去重启容器。理解成本反而变高了而且 etcd 默认是跑在 master 上的容器里的和宿主机耦得很紧。托管就更好理解了控制面根本不在你手上你只管节点和负载。省心是真省心代价是网络模型、审计日志、版本节奏全跟着平台走某些行业里连控制面在哪儿都说不清楚合规那一关就过不去。纯二进制是三条路里最笨的也是最透明的。每个组件就是一个二进制文件一个 systemd unit一份配置文件日志走 journald 或者独立日志文件什么时候启动、监听哪个端口、读了哪些证书systemctl status和ss -lntp一查就明白。出问题的时候你不是在猜容器里发生了什么而是直接看进程。这就是它在一些场景下不可替代的原因——不是因为它先进是因为它可控、可审计、可复现。维度kubeadm纯二进制托管服务上手速度快半小时出集群慢一天起步最快分钟级组件可见性中静态 Pod 形式高进程级可见低基本黑盒参数定制粒度中受 kubeadm 配置约束高任意参数低升级方式kubeadm upgrade分阶段手工替换二进制逐台滚动平台自动证书管理自动有 renew 子命令全手工需要自己规划平台负责适合场景自建集群、测试环境离线机房、强审计要求、教学拆解快速上线业务注意一个细节二进制部署并不是更稳定的部署方式。稳定性取决于你的运维能力不取决于安装方式。同一套二进制集群参数配错一样会隔三差五挂。别把安装方式当成质量保证。1.2 二进制部署的代价到底在哪很多人只看到二进制部署可控的好处忽略了它真正贵的地方。我梳理下来代价集中在三块。第一块是证书。kubeadm 有一整套证书生命周期管理二进制没有。你得自己算好每张证书的用途、CN、O、SAN 列表、有效期然后自己记着什么时候该轮换。apiserver 证书、etcd 证书、kubelet 客户端证书、service account 密钥、聚合层的 front-proxy 证书一套下来十几张少一张或者 hosts 少一个 IP症状就是连接超时或者 x509 报错。这块是新手翻车最多的地方。第二块是版本对齐。控制面组件之间有小版本兼容窗口kube-apiserver 可以和 kubelet 差两个小版本但 skew 策略有明确限制。二进制部署下你得自己盯着升级的时候 kube-apiserver 先升、controller-manager 和 scheduler 跟着、kubelet 最后顺序错了会出现组件之间协议不匹配的问题。第三块是备份。etcd 是唯一真相来源二进制部署下你如果不做定时快照一次误删或者磁盘写满就可能把整个集群的状态送走。etcdctl snapshot save这行命令必须写进定时任务里而且要定期演练恢复不然备份等于没有。这三块不是不能做是要提前想清楚。我在实际项目里见过太多集群装起来了但没人知道证书什么时候过期的情况最后在某个周末集体翻车。1.3 上篇要交付的东西把范围说清楚避免看完一半发现少东西。这一篇的交付物是三台机器组成的 etcd 数据库集群启用双向 TLS能通过etcdctl endpoint health和member list校验能正常读写一套完整的 PKI 证书包含根 CA、etcd 服务端/客户端证书、apiserver 服务端证书、controller-manager 与 scheduler 的客户端证书、admin 管理证书、service account 密钥对、聚合层 front-proxy 证书第一台 master 上的 kube-apiserver、kube-controller-manager、kube-scheduler 三个进程以 systemd 方式常驻端口正常监听一个能用的 admin kubeconfigkubectl get componentstatuses1.19 之后这个命令已经废弃实际用kubectl get --raw /healthz和kubectl api-resources能拿到正常响应。不包含的部分kubelet、kube-proxy、容器运行时、CNI 网络插件、CoreDNS、高可用 VIP。这些是下篇的活。上篇结束时集群是控制面活着但没有任何节点的状态kubectl get nodes会是空的这是正常的别慌。2. 集群拓扑与地址规划先把账算清楚2.1 节点角色与硬件底线我这里用一个三节点的例子三个节点既跑 etcd 又跑 master 控制面。这是小规模自建集群最常见的形态省机器但要接受一个现实etcd 对磁盘 IO 极其敏感和控制面混部的时候apiserver 的高频写入会和 etcd 的 WAL 写抢 IO。地址规划如下全是内网段实际替换成你自己的主机名IP角色说明k8s-m1192.168.10.11etcd-1 master首个控制面后续 kubectl 默认指向它k8s-m2192.168.10.12etcd-2 master控制面副本k8s-m3192.168.10.13etcd-3 master控制面副本硬件底线我给个明确数字每个节点 4 核 8G 起步系统盘 100Getcd 数据盘单独挂一块 SSD。如果只能给一块盘至少保证 etcd 数据目录不和日志目录同盘高频争抢。etcd 的官方建议是磁盘 fsync 延迟 p99 小于 10ms机械盘基本做不到跑起来会看到 leader 频繁切换、apply 延迟飙升。这一条我在生产上吃过亏一开始用普通云盘etcd 的backend_commit_duration_seconds长期在 100ms 以上整个集群的 API 响应都跟着抖。2.2 网段规划的三个硬约束网段这块二进制部署比 kubeadm 自由但自由意味着你得自己保证不冲突。三个网段必须提前定死中途改的代价是重建集群。第一个是节点网段192.168.10.0/24这个没什么好说的机器本身的地址。第二个是 Service 网段我定10.0.0.0/24。这里有个坑apiserver 会自动占用这个网段的第一个 IP 作为 kubernetes Service 的 ClusterIP也就是10.0.0.1。你在 apiserver 证书的 hosts 里必须把这个 IP 加进去否则集群内部通过 Service 名访问 apiserver 时会证书校验失败。另外/24只给 256 个地址减去网络地址、广播地址、网关和一个被占用的.1实际可用两百多个。中小集群够用如果你预计 Service 数量会上千直接开/1610.96.0.0/12也是常见选择看你的 Pod 网段怎么划。第三个是 Pod 网段我定10.244.0.0/16每个节点分一个/24也就是单节点最多 254 个 Pod。这个数字是算出来的/16切成/24一共 256 个节点槽位。如果你的集群规模会超过这个数要么把节点掩码改成/23要么把 Pod 网段开到/14。注意Service 网段和 Pod 网段绝对不能重叠也不能和节点物理网段重叠。我见过一次事故节点网段是10.244.1.0/24Pod 网段也是10.244.0.0/16结果那台节点上的 Pod 全部无法通信排查了大半天才发现是路由冲突。还有一个容易被忘的如果机器上装了 Docker默认会创建docker0网桥占172.17.0.1/16。如果你把 Service 网段或者 Pod 网段划到172.17.0.0/16里必然冲突。检查方法很简单ip route看一眼有没有172.17.0.0/16 dev docker0有的话换网段或者干脆把 Docker 卸掉只用 containerd。2.3 etcd 集群规模与法定人数计算etcd 用的是 Raft 协议写操作需要过半数节点确认才算提交。这就决定了它的节点数只能是奇数而且容错能力和节点数的关系不是线性的。计算公式是容忍故障数 (N - 1) / 2法定人数 N / 2 1向下取整后再加一。节点数法定人数容忍故障评价110只能做测试无任何容错321生产最小规模推荐532规模较大时使用写延迟会上升743基本没必要Raft 心跳开销明显3 节点是绝大多数场景的正确答案。它容忍一台机器完全宕机写性能还能接受。5 节点看着更稳但每次写都要等 3 个节点确认跨机房部署时延迟会成倍放大得不偿失。7 节点就更没必要了Raft 的日志复制开销和节点数正相关超过 5 个之后收益极低。还有个细节节点数定下来之后initial-cluster里的成员列表就必须严格一致三台机器上的配置除了各自的ETCD_NAME、监听地址、对外通告地址之外其他必须完全一样。任何一个字符不一致集群就组不起来。3. 操作系统底座别让后续问题从这一步埋下3.1 内核参数、swap 与文件句柄这一节看着枯燥但跳过的人后面都会回来补。所有节点都要做一条都不能少。swap 必须关掉。kubelet 默认不允许在开启 swap 的机器上启动虽然新版本有failSwapOn: false的选项但生产上没人这么干因为内存交换会让 Pod 的延迟完全不可预测。关法是两步先swapoff -a临时关再注释掉/etc/fstab里所有的 swap 行永久关。只做第一步的话重启之后又回来了。内核模块要加载br_netfilter和overlay。前者决定了网桥的流量会不会经过 iptableskube-proxy 的 Service 转发依赖它后者是 overlayfs 存储驱动的基础containerd 和 Docker 都要用。写进/etc/modules-load.d/k8s.conf开机自动加载。sysctl 参数至少这几个cat /etc/sysctl.d/k8s.conf EOF net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 net.ipv4.tcp_tw_recycle 0 fs.file-max 1048576 fs.inotify.max_user_watches 524288 fs.inotify.max_user_instances 8192 vm.swappiness 0 EOF sysctl --systemip_forward不用解释IP 转发不开跨节点通信直接废。inotify那两个值是给 kubelet 和容器运行时用的Pod 一多默认的 8192 个实例上限会被打穿症状是日志里出现too many open files或者 inotify watch 添加失败。fs.file-max和LimitNOFILE一起配systemd unit 里也要写两处都要。3.2 时间同步这件事不能省时间同步是所有分布式系统的隐形地基。etcd 的 Raft 用租约做 leader 选举TLS 证书有生效时间和过期时间service account token 有有效期任何一处时间偏差过大都会出问题。我遇到过最典型的一次某台节点的时钟慢了两天kubelet 一直报certificate has expired or is not yet valid但证书本身查下来完全正常。后来date一看才发现问题。配置上所有节点必须指向同一个时间源允许误差控制在 100ms 内。做法是装 chrony 或者 ntpd指向机房里最近的时间服务器。检查命令是chronyc sources -v或者ntpq -p看偏移量那一列如果长期在几百毫秒以上说明网络或者服务器有问题得先解决再继续。提示虚拟机环境下时间漂移比物理机严重得多尤其是被挂起恢复过的虚拟机。部署前先timedatectl看一眼 NTP synchronized 是不是 yes不是的话先把这块弄好能省后面无数排查时间。3.3 目录与用户约定目录结构我习惯这样定后面所有配置文件都往这套路径上放好处是换机器的时候能直接复制粘贴路径用途/opt/etcd/binetcd 二进制和 etcdctl/etc/etcdetcd 配置文件、证书/var/lib/etcdetcd 数据目录建议独立磁盘/opt/kubernetes/bin控制面二进制/etc/kubernetes控制面配置、证书、kubeconfig/var/log/kubernetes组件日志如果不用 journald证书目录单独说一下。/etc/kubernetes/pki存所有证书权限设成600属主 root。根 CA 的私钥ca-key.pem是最敏感的东西拿到它就能签任何证书理论上可以伪造一个 apiserver。生产环境里建议把这个文件从 master 上移走离线保管签证书的时候再临时放回来。日常运维根本用不到它。再补一句关于用户所有控制面进程都用 root 跑别折腾新建用户。原因很简单10250、6443 这些端口都小于 1024非 root 用户需要额外配 capabilities而且证书文件权限管理会更麻烦。用 root 不是好实践但在这种基础设施层面它是最不容易出错的。4. PKI 证书体系整个集群的地基4.1 证书清单与用途先把清单列出来签证书的时候照着表核别漏。证书CNO组织用途ca.pem / ca-key.pemkubernetes-根 CA签发所有其他证书etcd.pem / etcd-key.pemetcd-etcd 服务端和 peer 双向认证apiserver.pemkube-apiserver-apiserver 对外提供 HTTPSapiserver-kubelet-client.pemkube-apiserversystem:mastersapiserver 访问 kubelet 时的客户端证书front-proxy-client.pemfront-proxy-client-聚合层代理客户端证书controller-manager.pemsystem:kube-controller-manager-controller-manager 访问 apiserverscheduler.pemsystem:kube-scheduler-scheduler 访问 apiserveradmin.pemadminsystem:masters集群管理员kubectl 用sa.key / sa.pub--service account token 签名密钥对这里面 O 字段Organization在 Kubernetes 里被当作用户组使用。system:masters是内置的超级管理员组绑定到 cluster-admin拥有全部权限。system:kube-controller-manager和system:kube-scheduler是内置的系统用户名各自有预置的 RBAC 权限。这些名字是硬编码的写错一个字母就变成普通用户什么都干不了而且报错信息通常很隐晦——只会告诉你forbidden不会告诉你因为你的 CN 拼错了。4.2 用 cfssl 生成根 CAcfssl 用起来比 openssl 顺手配置文件是 JSON可读性好。装法很简单三个二进制cfssl、cfssljson、cfssl-certinfo扔进/usr/local/bin并加执行权限。先写 CA 的签名策略这份配置决定了后面所有证书的默认有效期和用途{ signing: { default: { expiry: 87600h }, profiles: { kubernetes: { usages: [ signing, key encipherment, server auth, client auth ], expiry: 87600h } } } }87600h是十年。这个时长在安全审计里算偏长但 Kubernetes 生态里 kubeadm 默认也是一样的量级主要是为了避免证书轮换带来的运维负担。如果你的合规要求更严改成8760h一年也完全可以只是要建好轮换的提醒机制别到期了才想起来。然后写 CA 的 CSR{ CN: kubernetes, key: { algo: rsa, size: 2048 }, names: [ { C: CN, L: Beijing, ST: Beijing, O: k8s, OU: system } ] }生成根 CAcfssl gencert -initca ca-csr.json | cfssljson -bare ca执行完会得到ca.pem和ca-key.pem。ca-key.pem立刻挪走离线保管或者至少chmod 600并记录在册。接下来所有证书都用ca.pem和ca-key.pem签发用 profile 名字kubernetes。4.3 etcd 与 apiserver 证书的 hosts 字段这是整套流程里最容易出错的一步。hosts字段决定了证书的 SANSubject Alternative Name列表也就是说——客户端用什么地址访问服务那个地址就必须在列表里。少一个连上之后立刻断开报x509: certificate is valid for X, not Y。etcd 证书的 hosts{ CN: etcd, hosts: [ 127.0.0.1, localhost, 192.168.10.11, 192.168.10.12, 192.168.10.13 ], key: { algo: rsa, size: 2048 }, names: [ { C: CN, L: Beijing, ST: Beijing, O: etcd, OU: system } ] }三台机器共用同一张证书也没问题因为 hosts 里包含了全部三个 IP。如果你想让每台机器用独立的证书更严谨就分别签三张每张 hosts 只放本机 IP 加 127.0.0.1CN 分别写 etcd-1、etcd-2、etcd-3。两种做法都能跑前者省事后者便于按节点吊销。apiserver 证书的 hosts 要求更多一个都不能少{ CN: kube-apiserver, hosts: [ 127.0.0.1, localhost, kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster.local, 192.168.10.11, 192.168.10.12, 192.168.10.13, 10.0.0.1 ], key: { algo: rsa, size: 2048 }, names: [ { C: CN, L: Beijing, ST: Beijing, O: k8s, OU: system } ] }逐个解释一下为什么这些都要kubernetes、kubernetes.default、kubernetes.default.svc、kubernetes.default.svc.cluster.local是集群内部访问 apiserver 的四个标准域名Pod 里的应用最常见的就是用https://kubernetes.default.svc访问 API。10.0.0.1是这个域名的解析结果也就是 Service 网段的第一个地址。少任何一个Pod 里调用 API 就会失败。注意如果你后面要上高可用前面挂一个 VIP 比如192.168.10.100这个 VIP 也必须写进 hosts。补证书是可以的重新签发一次再分发到三台机器但正在跑的 apiserver 需要重启才能加载新证书这个窗口要挑在业务低峰。签发命令是统一的格式cfssl gencert \ -caca.pem -ca-keyca-key.pem \ -configca-config.json -profilekubernetes \ etcd-csr.json | cfssljson -bare etcd其他几张证书同理改一下文件名和 JSON 就行。签发完记得把*.pem和*-key.pem分发到三台机器的/etc/kubernetes/pki和/etc/etcd下权限统一600。4.4 service account 密钥与聚合层证书service account 的密钥对不是 X.509 证书是一对 RSA 密钥。apiserver 用私钥给 service account token 签名其他组件用公钥验签。openssl genrsa -out sa.key 2048 openssl rsa -in sa.key -pubout -out sa.pub chmod 600 sa.key私钥只需要放在 apiserver 所在的节点上公钥要分发到需要验证 token 的组件apiserver、controller-manager。这里有个历史遗留问题老版本里 service account token 是密钥直接加密的新版本1.20 之后改成了基于 TokenRequest 的短期 token配合--service-account-issuer参数使用安全模型完全不同。不管哪个版本这对密钥都得签好别用弱长度2048 是底线。front-proxy 证书是给聚合层用的。metrics-server、custom metrics API 这类组件通过 APIService 注册到 apiserver 上请求进来之后 apiserver 会反向代理到这些组件的 Service这个代理过程需要一张客户端证书来标识身份。不签这张证书的后果是metrics-server 装好了但kubectl top一直 500报x509: certificate signed by unknown authority或者unable to handle the request。这个坑我踩过当时排查了很久才定位到是聚合层证书没配。签法和前面一样CSR 里 CN 写front-proxy-client不需要 hosts{ CN: front-proxy-client, key: { algo: rsa, size: 2048 }, names: [{ C: CN, L: Beijing, ST: Beijing, O: k8s }] }5. etcd 集群二进制部署实录5.1 安装与目录布局先在一个节点上做做完验证配置文件再复制到另外两台改三个参数就行。mkdir -p /opt/etcd/bin /etc/etcd /var/lib/etcd cd /tmp tar -xzf etcd-v3.5.12-linux-amd64.tar.gz cp etcd-v3.5.12-linux-amd64/etcd /opt/etcd/bin/ cp etcd-v3.5.12-linux-amd64/etcdctl /opt/etcd/bin/ chmod x /opt/etcd/bin/* /opt/etcd/bin/etcd --version版本上3.5 系列是目前主流3.4 已经停止维护。3.5 有几个值得知道的特性默认开启--force-new-cluster的保护、支持--config-file读 YAML、压缩策略更合理。3.6 也在推进但生产上我建议 3.5 的最新小版本等 3.6 生态跟上了再考虑。数据目录单独挂盘这一步别省。如果你按前面说的挂了独立 SSDmount到/var/lib/etcd上。没条件的话至少要保证/var/lib/etcd不和/var/log同一个分区被日志写满。etcd 磁盘满了会直接进入只读模式整个集群的写操作全部失败这个故障现象非常吓人——所有创建、更新、删除操作都报etcdserver: mvcc: database space exceeded看起来像整个集群挂了本质只是磁盘满了。5.2 配置文件逐参数解释/etc/etcd/etcd.conf的内容这是第一台的# 成员名称三台必须不同 ETCD_NAMEetcd-1 # 数据目录 ETCD_DATA_DIR/var/lib/etcd/default.etcd # peer 端口集群内部通信 ETCD_LISTEN_PEER_URLShttps://192.168.10.11:2380 ETCD_INITIAL_ADVERTISE_PEER_URLShttps://192.168.10.11:2380 # client 端口对外提供读写 ETCD_LISTEN_CLIENT_URLShttps://192.168.10.11:2379,https://127.0.0.1:2379 ETCD_ADVERTISE_CLIENT_URLShttps://192.168.10.11:2379 # 集群成员列表三台完全一致 ETCD_INITIAL_CLUSTERetcd-1https://192.168.10.11:2380,etcd-2https://192.168.10.12:2380,etcd-3https://192.168.10.13:2380 ETCD_INITIAL_CLUSTER_STATEnew ETCD_INITIAL_CLUSTER_TOKENetcd-cluster-k8s # 客户端证书校验 ETCD_CERT_FILE/etc/etcd/pki/etcd.pem ETCD_KEY_FILE/etc/etcd/pki/etcd-key.pem ETCD_TRUSTED_CA_FILE/etc/etcd/pki/ca.pem ETCD_CLIENT_CERT_AUTHtrue # peer 双向认证 ETCD_PEER_CERT_FILE/etc/etcd/pki/etcd.pem ETCD_PEER_KEY_FILE/etc/etcd/pki/etcd-key.pem ETCD_PEER_TRUSTED_CA_FILE/etc/etcd/pki/ca.pem ETCD_PEER_CLIENT_CERT_AUTHtrue # 日志级别 ETCD_LOG_LEVELinfo几个参数值得单独说。ETCD_INITIAL_CLUSTER_STATE在集群首次启动时必须是new之后重启保持不变也没问题因为 etcd 会先读数据目录里的成员信息但如果数据目录被清空过而状态还是new就会出现重复创建成员的问题。这个坑后面排查章节会展开。ETCD_INITIAL_CLUSTER_TOKEN是用来区分集群的标识多套集群共用同一批机器或者同一份配置模板时token 不一样能防止串台。ETCD_LISTEN_CLIENT_URLS我把127.0.0.1也加上了方便在节点上本地用 etcdctl 排查不用每次都带--endpoints。对外通告地址ETCD_ADVERTISE_CLIENT_URLS只写本机实际 IP不要写 127.0.0.1否则其他客户端拿到这个地址之后连不上。ETCD_CLIENT_CERT_AUTHtrue打开了客户端证书强制校验意味着任何没带证书的请求都会被拒绝。这是 etcd 数据安全的基本要求生产上不要关。关掉之后任何人只要网络可达就能读写整个集群的状态相当于把 Kubernetes 的全部 Secret 明文暴露出去。5.3 systemd 托管与三节点启动unit 文件/usr/lib/systemd/system/etcd.service[Unit] DescriptionEtcd Server Afternetwork.target Afternetwork-online.target Wantsnetwork-online.target Documentationhttps://example.internal/etcd [Service] Typenotify WorkingDirectory/var/lib/etcd/ EnvironmentFile-/etc/etcd/etcd.conf ExecStart/opt/etcd/bin/etcd Restarton-failure RestartSec5 LimitNOFILE65536 [Install] WantedBymulti-user.targetTypenotify配合 etcd 的 sd_notify 支持systemd 能准确知道服务什么时候真正就绪systemctl start会阻塞到 etcd 完成启动。这个细节对自动化脚本很重要用Typesimple的话命令立刻返回但 etcd 可能还在初始化后续操作会偶发失败。EnvironmentFile前面加个-表示文件不存在也不报错这是为了防止配置文件的路径写错导致 unit 直接起不来看不到真正的原因。LimitNOFILE65536必须加。etcd 每个客户端连接都占一个文件描述符apiserver 三副本加上各种 watch 连接默认的 1024 很容易打满症状是too many open files。配置完成之后三台机器依次执行systemctl daemon-reload systemctl enable etcd systemctl start etcd启动顺序无所谓Raft 协议会自动完成选举。三台都起来之后任意一台上看日志journalctl -u etcd -f正常的话会看到一条published {Name:etcd-1 ClusterID:xxx} successfully以及ready to serve client requests。如果只有一台起来它会一直打印waiting for the other members这是正常的等另外两台起来就自动组集群。5.4 集群健康校验与验证写入先看成员列表export ETCDCTL_API3 etcdctl \ --endpointshttps://192.168.10.11:2379,https://192.168.10.12:2379,https://192.168.10.13:2379 \ --cacert/etc/etcd/pki/ca.pem \ --cert/etc/etcd/pki/etcd.pem \ --key/etc/etcd/pki/etcd-key.pem \ member list -w table正常输出三行每行的IS LEADER只有一个是 trueIS HEALTHY全是 true。3.5 版本的 etcdctl 已经内置了 v3 APIETCDCTL_API其实可以不设但写上没坏处兼容老脚本。再看健康状态etcdctl --endpoints... --cacert... --cert... --key... endpoint health --cluster etcdctl --endpoints... --cacert... --cert... --key... endpoint status --cluster -w tableendpoint status会输出每个成员的版本、数据大小、raft 索引、raft 任期。这里有个很实用的判断技巧如果某个节点的 raft index 明显落后于 leader说明它的复制跟不上可能是磁盘慢或者网络抖动。如果持续落后超过几十万条基本可以确定这块盘不行。最后做一次写入验证etcdctl --endpoints... --cacert... --cert... --key... put /test/hello world etcdctl --endpoints... --cacert... --cert... --key... get /test/hello读到了就说明集群彻底通了。顺手把测试 key 删掉etcdctl del /test/hello。提示如果endpoint health报context deadline exceeded但member list正常先检查是不是某台机器的防火墙挡了 2379/2380。CentOS 系默认开着 firewalldfirewall-cmd --list-ports看一眼就知道。6. master 控制面组件部署6.1 kube-apiserver 启动参数逐个拆apiserver 是整个集群的入口所有组件、所有 kubectl、所有 Pod 里的应用最终都是打到它身上。参数配错的代价最大所以这一节我写得细一点。先看 unit 文件/usr/lib/systemd/system/kube-apiserver.service[Unit] DescriptionKubernetes API Server Afternetwork.target Afteretcd.service Wantsnetwork-online.target [Service] EnvironmentFile-/etc/kubernetes/apiserver.conf ExecStart/opt/kubernetes/bin/kube-apiserver $KUBE_APISERVER_ARGS Restarton-failure RestartSec5 LimitNOFILE65536 [Install] WantedBymulti-user.target把参数放到EnvironmentFile里的好处是改参数不用碰 unit 文件daemon-reload都省了直接systemctl restart就生效。/etc/kubernetes/apiserver.conf的核心内容KUBE_APISERVER_ARGS\ --advertise-address192.168.10.11 \ --bind-address192.168.10.11 \ --secure-port6443 \ --allow-privilegedtrue \ --authorization-modeNode,RBAC \ --enable-admission-pluginsNodeRestriction \ --enable-bootstrap-token-authtrue \ --client-ca-file/etc/kubernetes/pki/ca.pem \ --tls-cert-file/etc/kubernetes/pki/apiserver.pem \ --tls-private-key-file/etc/kubernetes/pki/apiserver-key.pem \ --kubelet-client-certificate/etc/kubernetes/pki/apiserver-kubelet-client.pem \ --kubelet-client-key/etc/kubernetes/pki/apiserver-kubelet-client-key.pem \ --kubelet-preferred-address-typesInternalIP,Hostname,InternalDNS,ExternalDNS \ --etcd-servershttps://192.168.10.11:2379,https://192.168.10.12:2379,https://192.168.10.13:2379 \ --etcd-cafile/etc/kubernetes/pki/ca.pem \ --etcd-certfile/etc/kubernetes/pki/etcd.pem \ --etcd-keyfile/etc/kubernetes/pki/etcd-key.pem \ --service-cluster-ip-range10.0.0.0/24 \ --service-node-port-range30000-32767 \ --service-account-key-file/etc/kubernetes/pki/sa.pub \ --service-account-signing-key-file/etc/kubernetes/pki/sa.key \ --service-account-issuerhttps://kubernetes.default.svc.cluster.local \ --api-audienceshttps://kubernetes.default.svc.cluster.local \ --requestheader-client-ca-file/etc/kubernetes/pki/ca.pem \ --requestheader-allowed-namesfront-proxy-client \ --requestheader-extra-headers-prefixX-Remote-Extra- \ --requestheader-group-headersX-Remote-Group \ --requestheader-username-headersX-Remote-User \ --proxy-client-cert-file/etc/kubernetes/pki/front-proxy-client.pem \ --proxy-client-key-file/etc/kubernetes/pki/front-proxy-client-key.pem \ --apiserver-count3 \ --endpoint-reconciler-typelease \ --event-ttl1h \ --v2 \ --logtostderrtrue几组参数分类说。--advertise-address和--bind-address决定 apiserver 在哪里监听、向其他组件通告什么地址。三台机器各写自己的 IP。如果写成0.0.0.0监听通告还是得写实际 IP否则其他组件拿到0.0.0.0没法连。省略--advertise-address时 apiserver 会自动探测但探测结果在多网卡机器上往往不是你想要的所以显式写死更稳。--authorization-modeNode,RBAC两个模式都要。RBAC 负责常规的权限控制Node 模式是给 kubelet 用的特殊授权器它允许 kubelet 只操作自己节点上的资源比如读取自己节点上的 Pod、写自己的 Node 状态、写自己的 Event。少了 Node 模式kubelet 启动时会不停报 403日志里能刷出几万行。--enable-admission-pluginsNodeRestriction配合 Node 授权模式使用作用是限制 kubelet 只能修改自己 Node 对象上的特定字段比如标签、注解、状态不能随便改别人的。这是安全加固的必选项。--enable-bootstrap-token-authtrue是给 kubelet TLS 引导用的下篇会用到。现在开着不生效但提前开好省得回头改。--endpoint-reconciler-typelease是新版本推荐的 endpoint 更新机制用 Lease 对象代替直接改 Endpoints减少 apiserver 和 etcd 之间的写压力。三副本以上集群强烈建议开。--apiserver-count3要和实际的 apiserver 副本数一致。这个值影响的是kubernetesService 的 Endpoints 数量分配写错了会出现 Service 的可用端点少于实际副本的情况。聚合层那五个--requestheader-*和--proxy-client-*参数是给 metrics-server 这类扩展 API 用的。这套配置的逻辑是apiserver 收到发往 APIService 的请求用front-proxy-client证书作为客户端身份反向代理过去扩展组件那边验证这个证书和请求头里的用户信息。--requestheader-client-ca-file我用的是同一个根 CA--requestheader-allowed-names限定只有 CN 为front-proxy-client的证书才被接受。如果不配这套metrics-server 装上去之后kubectl top nodes会一直报错而且报错信息跟证书八竿子打不着容易让人往错误方向排查。6.2 kubeconfig 的生成与身份绑定apiserver 起来之后其他组件和 kubectl 要通过它。Kubernetes 的客户端认证方式有三种客户端证书、bearer token、静态 token 文件。控制面组件统一用客户端证书 kubeconfig 文件。生成 admin 的 kubeconfig这是你日常运维用的最高权限账号cd /etc/kubernetes/pki kubectl config set-cluster kubernetes \ --certificate-authorityca.pem \ --embed-certstrue \ --serverhttps://192.168.10.11:6443 \ --kubeconfig/etc/kubernetes/admin.kubeconfig kubectl config set-credentials admin \ --client-certificateadmin.pem \ --client-keyadmin-key.pem \ --embed-certstrue \ --kubeconfig/etc/kubernetes/admin.kubeconfig kubectl config set-context kubernetes \ --clusterkubernetes \ --useradmin \ --kubeconfig/etc/kubernetes/admin.kubeconfig kubectl config use-context kubernetes \ --kubeconfig/etc/kubernetes/admin.kubeconfig--embed-certstrue会把证书内容直接塞进 kubeconfig 文件里而不是引用路径。这样 kubeconfig 是自包含的拷到别的机器上直接能用不用连带分发证书。代价是文件变大而且证书更新之后要重新生成 kubeconfig。controller-manager 和 scheduler 的 kubeconfig 生成方式完全一样只是 CN 换成system:kube-controller-manager和system:kube-scheduler文件名换一下。签这两张证书的时候 O 字段留空因为它们的权限来自用户名而不是组。一个细节kubeconfig 里--server指向的是本机 IP如果 apiserver 挂了controller-manager 会一直重试。这没问题但如果你有三台 apiserver最好在前端挂一个 VIPkubeconfig 指向 VIP这样任何一台挂了都不影响连接。VIP 的搭建不在本篇范围内先按单点写。6.3 kube-controller-manager 与 kube-scheduler这两个组件是控制器模式的执行者。controller-manager 是一堆控制器的集合Deployment、ReplicaSet、Node、Endpoint、ServiceAccount 等等scheduler 负责把 Pod 分配到节点上。kube-controller-manager的核心参数--bind-address127.0.0.1 --kubeconfig/etc/kubernetes/kube-controller-manager.kubeconfig --authentication-kubeconfig/etc/kubernetes/kube-controller-manager.kubeconfig --authorization-kubeconfig/etc/kubernetes/kube-controller-manager.kubeconfig --leader-electtrue --use-service-account-credentialstrue --cluster-namekubernetes --cluster-cidr10.244.0.0/16 --node-cidr-mask-size24 --allocate-node-cidrstrue --service-cluster-ip-range10.0.0.0/24 --cluster-signing-cert-file/etc/kubernetes/pki/ca.pem --cluster-signing-key-file/etc/kubernetes/pki/ca-key.pem --cluster-signing-duration87600h --root-ca-file/etc/kubernetes/pki/ca.pem --service-account-private-key-file/etc/kubernetes/pki/sa