Skip to content

第6章 Kubernetes 部署交付(实施版核心章之一) ​

对应简历:技能第5条「熟练掌握 Docker 与 Kubernetes 集群部署(kubeadm 单 / 多 master 高可用,Nginx + Keepalived VIP 漂移),具备 ARM 国产化与 x86 云环境双实践」/ 项目一职责「负责 Kubernetes 集群部署与高可用改造:统信 UOS(ARM)环境完成 Docker 与 kubeadm 部署,通过 Nginx + Keepalived(VIP 漂移 + 健康检查)升级为多 master 高可用并完成故障转移测试」/ 项目二职责「完成 containerd 运行时标准化、kubeadm 三副本控制平面与 Flannel 网络部署,沉淀 9 项生产避坑清单」

实施版定位:本章按"怎么把一套 K8s 集群装出来、改高可用、验收到能交付"组织——kubeadm 全流程、多 master 改造、证书、ARM 差异、高可用入口全量保留;集群内部原理(调度细节/QoS/探针/存储)压缩为 6.6–6.8 三节"交付够用版",答案仍是完整口语段,深度细节回查运维版第 6 章。术语约定:Kubernetes(以下简称 K8s,正文简写)。内网 IP 一律脱敏为 <VIP>、<master01-ip> 等占位符。

6.1 kubeadm 部署全流程:六阶段(含 kubeadm-config.yaml) 【必背】 ​

标准回答("你们集群怎么部署的"标准答案,六阶段顺序讲): 我的部署流程六个阶段按序讲,两个项目(银行 ARM 内网、移动云 x86 云上)都是这套骨架。阶段〇全节点初始化:关防火墙和 swap → 加载 overlay、br_netfilter 模块 → sysctl 配 ip_forward、bridge-nf-call-iptables、swappiness=0 → chrony 时间同步(证书校验对时间敏感,这是避坑清单第 9 条的教训)→ 装容器运行时(银行是 Docker 18.09 aarch64 静态包,移动云是 containerd 1.7.8 加三个关键配置,见第 5 章 5.7)→ 装 kubelet/kubeadm/kubectl 指定版本(银行 v1.17.3 受 BCS 配套约束、移动云 v1.28.2 自主选型)。

阶段一控制平面:master01 写 kubeadm-config.yaml(controlPlaneEndpoint 指向统一入口、certSANs 包含 VIP 和全部 master)→ kubeadm init --config --upload-certs → 配 kubectl → master02/03 带 --control-plane 加入。阶段二 CNI,顺序铁律:必须在 worker 之前——apply Flannel,DaemonSet 的 init 和主容器镜像全局替换成内网仓库地址;不先装 CNI,worker 加入即 NotReady,九成 NotReady 是 CNI 镜像拉取问题。

阶段三 worker 加入:kubeadm token create --print-join-command 生成命令批量执行。阶段四健康验证:get nodes 全 Ready、kube-system Pod 全 Running、etcd 副本 healthy。阶段五配套:监控(第 7 章)和可视化管控。部署顺序本身就是交付物——我把六阶段连同命令写成部署手册的章节结构,新环境照着重放就能复制。 实操模板(kubeadm-config.yaml,移动云项目真实结构脱敏):

yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: <master01-ip>
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///var/run/containerd/containerd.sock
  name: k8s-master-01
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.28.2
controlPlaneEndpoint: "<VIP>:6443"          # 统一入口(CLB 或 LB VIP)
imageRepository: registry.aliyuncs.com/google_containers
networking:
  serviceSubnet: 10.96.0.0/12
  podSubnet: 10.244.0.0/16
  dnsDomain: cluster.local
apiServer:
  certSANs:                                 # 证书必须含 VIP + 全部 master IP/主机名
    - "<VIP>"
    - "<master01-ip>"
    - "<master02-ip>"
    - "<master03-ip>"
etcd:
  local:
    dataDir: /var/lib/etcd
    extraArgs:
      auto-compaction-retention: "1"
      quota-backend-bytes: "8589934592"
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd                       # 与 containerd 的 SystemdCgroup 对齐
maxPods: 110

追问链:

  • 迭问1:为什么要先装 CNI 再加 worker? → 答:因为 kubelet 判断节点 Ready 依赖 CNI 就绪——网络插件没起来,节点的 Pod 网络没配置,kubelet 上报的就是 NotReady。而 CNI 本身是一组 DaemonSet Pod,它的镜像在内网拉不到就永远起不来,形成死锁:节点 NotReady 是因为 CNI 没好,CNI 没好是因为镜像拉不下来。我先在测试环境踩过"节点都加完了才装 CNI"的弯路,一批 Pod 起不来,从此把"先 CNI 后 worker"写进部署顺序——顺序错误在 K8s 交付里不是效率问题,是能不能装通的问题。
  • 迭问2:token 过期了怎么加节点? → 答:分两种节点。加 worker:kubeadm token create --print-join-command 生成新命令直接执行;discovery 的 CA 哈希丢了就用 openssl 从 ca.crt 算出来自己拼命令。加 master 多一步:master01 执行 kubeadm init phase upload-certs --upload-certs 拿一次性 certificate-key,join 命令加 --control-plane --certificate-key。我把这两组命令存在部署手册的"扩容"一节,加节点时直接复制——交付文档的完整性就体现在这些"低频但关键"的操作上。

6.2 单 master → 多 master 高可用改造(银行项目核心题,必背全文) 【必背】 ​

标准回答(六步 + 为什么,完整背): 这是我在银行项目完整做过并验证的改造,六步背全。第一步入口层:所有 master 装四层负载均衡——我先后做过 haproxy(frontend bind 16443 加 backend roundrobin 挂三台 6443 带 check 加 1080 stats 页面)和 Nginx(stream 模块 upstream 加 listen 16443)两套方案,后期标准化到 Nginx;监听 16443 是因为负载均衡和其中一台 master 复用一台机器、6443 已被本地 apiserver 占用。配 Keepalived——priority 100/90 拉开差距,vrrp_script 检测 16443 端口真实监听(ss -antp | grep 16443,数不到进程就 exit 1 触发降权),失败 weight -20 触发 VIP 漂移。

第二步导出集群配置:从 kubeadm-config 的 ConfigMap 导出 kubeadm.yaml。第三步补 certSANs:在 apiServer 段追加新 VIP 加三台 master 的主机名和 IP——原证书没有这些 SAN,不更新证书用 VIP 连集群必报 x509(我真实踩过,见 6.3)。第四步重签证书并加入:移走旧 apiserver 证书再 kubeadm init phase certs apiserver --config kubeadm.yaml 重签,杀 apiserver 容器让 kubelet 拉起新证书实例;从 master01 向 02/03 scp pki 目录下的 ca、sa、front-proxy-ca、etcd 的 CA 证书和 admin.conf,join 命令带 --control-plane 以控制面身份加入。

第五步组件配置切入口:kubelet.conf、controller-manager.conf、scheduler.conf、kube-proxy 的 ConfigMap、~/.kube/config 全部改指 VIP:16443,逐个重启组件生效。第六步验证:kubectl get nodes 三 master Ready → 主节点关机实测 VIP 漂移 → curl -k https://<VIP>:16443/version 正常返回 → 组件 Pod 全 Running。 追问链:

  • 迭问1:为什么必须重签证书,直接加节点行不行? → 答:不行,会卡在证书校验。apiserver 证书只对 certSANs 列表里的名字和 IP 有效,kubeadm 的证书只包含 init 时 certSANs 里的内容;新加的 master 和新 VIP 不在证书里,新节点连 apiserver、客户端用 VIP 访问,TLS 校验都过不去。我踩这个坑的现场表现是:curl -k 能通但 kubectl 报 x509——两句话就把问题切割到证书层。所以改造六步里证书是第三步,顺序错了后面全堵——这也是"改集群入口地址必须先想证书"这条规则的出处。
  • 迭问2:改造期间业务受影响吗?怎么控制风险? → 答:存量 Pod 不受影响——业务 Pod 靠节点上的转发规则运行,不依赖 apiserver 常连;受影响的是管理面操作(kubectl、调度)。我的风险控制三条:操作安排在业务低峰的变更窗口并向客户报备;每一步做完验证一次(节点 Ready、组件 Pod Running)再走下一步,把故障半径控制在单步;改造前备份 kubeadm 配置和 pki 目录,出问题能回到改造前状态。这套节奏就是我在第 15 章讲的割接纪律在集群改造上的应用——实施岗的价值之一是把高风险操作拆成低风险步骤。
  • 迭问3:为什么集群版本用 1.17.3 这么老? → 答:这是华为 BCS 边缘自治版 1.2.6 的配套版本约束——BCS 对纳管的 K8s 版本有严格兼容矩阵,生产必须严格按配套表执行,不能自己随意升级。这个经历养成了我的"版本配套意识",而且正好构成对比:移动云项目没有历史约束,按当时稳定版自主选型 v1.28.2——一个约束、一个自主,两个项目放一起讲就是我双实践的差异标签。

6.3 证书体系与 SAN 六步更新(本人真实故障,必背全文) 【必背】 ​

标准回答(五类证书 + 六步更新 + 真实故障): kubeadm 集群的证书分五类要能数出来:① CA 根证书(ca.crt/ca.key),其他证书都由它签发,默认 10 年;② apiserver 证书,默认 1 年有效期——这是到期事故的主力;③ etcd 证书(server/peer/healthcheck-client);④ kubelet 客户端证书;⑤ ServiceAccount 令牌的签名密钥对。运维规范:到期前 30 天 kubeadm certs check-expiration 检查、kubeadm certs renew all 续期,然后重启控制面静态 Pod 和 kubelet——这套流程写进了移动云项目的运维规范,也是我移交给客户的交接文档之一。

SAN(Subject Alternative Name)是实施最相关的概念:apiserver 证书只对 certSANs 列表里的名字和 IP 有效。本人真实故障:银行项目单 master 升级多 master 后,用 VIP 连 apiserver 报 x509——curl -k 能通但 kubectl 报错,说明不是网络是证书校验;openssl x509 -text 查证书发现 SAN 里没有新 VIP 和 master02/03 的 IP。

证书更新六步(完整背):1 导出集群配置成 kubeadm.yaml;2 在 apiServer 段追加 certSANs(新 VIP 加三台 master 的主机名和 IP);3 移走旧证书(mv /etc/kubernetes/pki/apiserver.{crt,key} ~,不移 kubeadm 不会重建)再 kubeadm init phase certs apiserver --config kubeadm.yaml 重签;4 杀掉 apiserver 容器让 kubelet 自动拉起新证书实例;5 openssl x509 -in apiserver.crt -text 验证 SAN 列表;6 回写 kubeadm-config 的 ConfigMap 同步 certSANs,防止下次 renew 再丢。教训一句话:改集群入口地址必须先想证书——移动云项目初始化时就把 CLB 的 VIP 直接写进 certSANs,这个坑再没出现过。 追问链:

  • 迭问1:证书全过期了集群还能用吗? → 答:业务面还行——已运行的 Pod 靠转发规则跑着,不依赖 apiserver;但管理面瘫痪:kubectl 认证失败、kubelet 心跳失败导致节点 NotReady、新的调度和自愈全部停止,等于"业务还在飞,机场塔台断电"。所以必须提前 30 天续期,我把证书有效期检查做成交接后的巡检固定项——kubeadm certs check-expiration 的输出按到期时间排序,最早到期的盯着。
  • 迭问2:怎么把证书管理交接给客户运维? → 答:三样东西:一张证书台账(证书名、所在路径、到期日、续期命令);一份 SOP(check-expiration 怎么看、renew 之后要重启哪些组件、验证什么);一个提醒机制(监控配证书剩余天数告警,到期前 30 天自动报)。我在银行项目交接时就是这么做的——"人记得"靠不住,把记忆交给系统和文档,这是知识转移能落地的关键。

6.4 高可用入口:Nginx+Keepalived 自建 vs CLB 托管(双实践对比) 【必背】 ​

标准回答(两方案都做过,对比讲): K8s 控制平面的高可用入口两个方案我都真实做过,这是我有独特发言权的对比题。方案一自建 Nginx + Keepalived(银行内网):Nginx stream 模块做四层代理,upstream 挂三台 master 的 6443,listen 16443 避开本地 apiserver 占用的 6443;Keepalived 管 VIP——主备 priority 100/90,vrrp_script 用脚本检测 16443 端口有没有真实监听(不是检测 keepalived 进程本身——keepalived 活着不代表后端服务活着,检测目标选错,VIP 漂移就失去意义),失败降权触发漂移。优点是完全可控、不依赖云;代价是这套东西本身成了要维护的对象——脑裂防护、心跳检测、两台代理节点的可用性都要自己管。

方案二托管 CLB(移动云):四层 TCP 监听 VIP:6443 转发三台 master,配 TCP 健康检查,异常节点秒级自动摘除。优点是无单点、免运维、配置即生效;代价是依赖云平台。我的选型结论:云上首选托管,内网自建方案在我手里是完备的备选——而且正因为自建方案每个部件都亲手配过,我对 CLB 健康检查的行为理解是带着实感的:什么参数决定摘除速度、摘除后流量怎么走,这套认知反过来帮助我设计验收演练。两个方案的验证动作也一样:制造故障实测——自建方案关主节点看 VIP 秒级漂移,托管方案停一台 master 的 kubelet 看 CLB 秒级摘除,通过 VIP 执行 kubectl 正常返回才算高可用真正成立。 追问链:

  • 迭问1:Keepalived 脑裂怎么防? → 答:脑裂是主备互相看不见但都认为自己是 MASTER,两边都绑 VIP,流量时通时断。我的四道防线(银行项目真实配置):检测脚本盯真实业务端口(16443 有没有监听)而不是 keepalived 进程;主备 priority 拉开(100 对 90)避免抢占抖动;advert_int 1 秒快速感知、VRRP 通告走管理平面交换机保证链路质量;上线前必须演练——主节点直接关机,实测 VIP 漂移加 curl 验证。确认脑裂一条命令:两台分别 ip addr,都绑着 VIP 就是实锤。
  • 迭问2:CLB 演练你具体怎么做的? → 答:四步完整讲:停掉一台 master 的 kubelet 模拟控制平面故障(停 kubelet 不关机,风险可控可恢复)→ 观察 CLB 健康检查把该后端秒级摘除 → 通过 VIP 执行 kubectl get nodes 正常返回,说明剩余两台 master 承载了全部请求、客户端无感 → 恢复 kubelet,控制面组件自动重启、etcd 自动同步重新加入。结论八个字:秒级摘除、无感切换——这是实测结论不是宣传语,演练记录和操作手册一并写进运维规范交了出去。

6.5 ARM 国产化环境差异(银行项目真实四条) 【必背】 ​

标准回答(四点全是实测): ARM 环境部署 K8s 与 x86 的差异我总结四条,全部在银行项目实测过。第一软件源:必须用 aarch64 专用源——yum 的 kubernetes-el7-aarch64 源,默认源里是 x86 包装不上或装错。第二 Docker:当时没有 ARM 仓库版,用的是 aarch64 静态二进制包 docker-18.09.8.tgz 解压安装——解压、拷贝 systemd unit、enable 就能跑,这也是我"静态二进制优先"装机思路的来源:不挑源、版本可控,内网交付特别好用。

第三镜像:所有容器镜像必须 arm64 版本——海外镜像内网拉不到就 docker save/load 离线导入,我写了批量加载脚本(第 5 章 5.5),交付前用 manifest inspect 或 inspect 的 Architecture 字段逐个核对——这在我这里是流程检查项不是口头注意。第四编译类软件:源码装 Redis、Nginx 要确认 ARM 依赖库齐全,缺的 devel 包要从 ARM 源补。不变的方面也要说清:内核参数(ip_forward、bridge-nf-call-iptables)、防火墙、systemd 这些系统层操作与 x86 完全一致——差异集中在"二进制和镜像的架构"这一层。这套总结写进了部署指南的"环境准备"一节,同类 ARM 项目直接复用。 追问链:

  • 迭问1:ARM 上报 manifest 不匹配? → 答:报错是 no matching manifest for linux/arm64/v8,意思是这个镜像 tag 没有构建 arm64 版本。处理三选一:找官方 multi-arch 版本(manifest inspect 看 architectures 列表确认);基于 arm64 基础镜像自己构建;拿已在 ARM 验证过的离线包直接分发。我的流程化做法是交付前做"镜像架构清单"逐项确认后才进分发环节——把偶发事故变成清单检查,是我从踩坑里提炼的资产。
  • 迭问2:统信 UOS / 麒麟 V10 和 CentOS 用起来差别大吗? → 答:部署操作层面差别很小——yum/rpm 体系、systemd、防火墙这些我天天用的东西基本一致,命令习惯平移就能干活;差别集中在三点:软件源要配国产 OS 自己的仓库和 ARM 架构源;部分华为组件(BCS、FusionCompute 工具链)对国产 OS 有专门的适配版本和配套表;安全基线不同(麒麟有自己的加固项)。我在两个国产 OS 上各交付过一年以上,这个"平移无障碍、配套要看表"的判断是一手结论。

6.6 交付够用版一:核心对象与发布回滚 【理解】 ​

标准回答(三分钟讲清"部署应用要会的东西"): 实施岗在 K8s 上部署应用,核心对象我按四样讲。Pod 是最小部署单元,一个或多个共享网络的容器;但我们不直接管 Pod,用控制器管:Deployment 管无状态应用(Web、API),保证副本数、滚动更新和回滚;DaemonSet 保证每个节点恰好一个 Pod(日志采集、监控代理、CNI 都是它);StatefulSet 管有状态应用;Job/CronJob 跑一次性或定时任务——选型口诀"无状态 Deployment、每节点一份 DaemonSet、一次/定时 Job"。

Service 提供稳定访问入口:它通过 label selector 关联一组 Pod,Endpoints 实时维护 Ready 的 Pod IP 列表,kube-proxy 把规则写进 iptables/ipvs——解决"Pod IP 易变"的根本问题。类型四选:ClusterIP 集群内互调、NodePort 每节点开 30000-32767 对外(我项目 Kuboard 用 30088、kube-state-metrics 用 30080)、LoadBalancer 云厂商 LB、ExternalName 映射外部域名。

发布和回滚命令组四条背熟:kubectl set image deploy/web web=新镜像 触发更新 → kubectl rollout status deploy/web 观察进度 → kubectl rollout history deploy/web 看版本列表 → kubectl rollout undo deploy/web --to-revision=2 回滚。回滚秒级的原理:Deployment 每次更新新建一个 ReplicaSet,旧 RS 缩到 0 但保留——回滚就是把旧 RS 副本拉回来,不涉及重建镜像。滚动更新不中断业务靠两根支柱:maxSurge/maxUnavailable 控节奏加 readiness 探针保新 Pod 就绪才接流量。我在移动云业务发布就是这套流程,配合 Kuboard 页面操作底层逻辑相同。 追问链:

  • 迭问1:Service 访问不通怎么查? → 答:四步链:kubectl get ep <svc> 看 Endpoints 列表——空说明 selector 没匹配上 Pod(标签写错)或 Pod 没 Ready,这是最高频原因;exec 进 Pod 侧 ss -lntp 确认应用真的在监听、targetPort 没写错;查节点转发层 kube-proxy 规则有没有生成;查 DNS——nslookup 服务名看 CoreDNS 解析。四层逐层收窄基本无漏网,这套链我在联调期天天用。
  • 迭问2:数据库为什么不上 K8s? → 答:我的三个理由:滚动更新与数据一致性冲突——数据库升级要控制切换时机,不能像无状态应用直接换实例;调度器不感知主从角色,Pod 漂移带来拓扑风险;运维操作粒度不匹配——备份、扩容、参数调整有自己的流程。所以我两个项目数据库都在 K8s 外:银行是 Vastbase 虚拟机集群、移动云用云数据库主备——"有状态核心数据外置"是我能讲清的架构取舍,也是实施岗对客户解释"为什么不都塞进集群"的标准答案。

6.7 交付够用版二:探针、资源与调度(Kuboard 真实用例) 【理解】 ​

标准回答: 三个"够用版"知识点,每个都对应交付动作。探针三种,由 kubelet 发起(不是 master):livenessProbe 失败重启容器,检测"进程活着";readinessProbe 失败从 Endpoints 摘除不接流量,检测"可以接客";startupProbe 专治慢启动应用——Java 冷启动一两分钟,没它 liveness 会在启动期误杀形成重启循环。我的配置习惯:Java 服务 startup 加 readiness 加宽松 liveness 三层。交付时的关联现象:Running 但 0/1 Ready 就是 readiness 没过——查探针路径端口对不对、initialDelaySeconds 太短、还是应用 bind 了 127.0.0.1。

资源两个值:requests 决定"能不能调度上来"(调度器按 requests 算账),limits 决定"跑起来最多用多少"——CPU 超限被限流变慢不死,内存超限直接 OOMKilled 退出码 137,两类后果完全不同。QoS 三级决定驱逐顺序:Guaranteed(request 等于 limit)最后被驱逐,BestEffort(什么都没设)最先。

调度控制手段按精细度:nodeSelector 按标签硬匹配;亲和性反亲和按"已有 Pod 分布"决策(多副本打散);污点加容忍——master 默认带 NoSchedule 污点,这就是普通 Pod 默认不上 master 的原因。本人真实用例是 Kuboard 部署四件套,全部是我配的:nodeSelector 固定调度到 master-01、tolerations 容忍 control-plane 污点、imagePullSecrets 挂私仓凭据、hostPath DirectoryOrCreate 持久化——四件套齐了才是完整的"固定节点 + 私有镜像 + 本地持久化"部署。 追问链:

  • 迭问1:Pod 一直 Pending 怎么快速分诊? → 答:kubectl get pods -o wide 看 NODE 列:NODE 为空是没调度上——describe 看 Events,资源不足、污点不容忍、PVC 没绑定;NODE 有值但 ContainerCreating 是已绑定到节点、卡在 kubelet 侧——拉镜像慢、CNI 配网、挂卷。两个状态对应完全不同的责任层(调度器 vs 节点 kubelet),判断错了排查方向就全错——这是部署期我最先教新同事的一个判断。
  • 迭问2:CrashLoopBackOff 四查? → 答:一查退出码——describe 看 Last State 的 exitCode,137 先怀疑内存 limit 被 OOMKilled;二查崩溃前日志——logs --previous 看最后几行,配置错误、连不上依赖都在这;三查探针——太激进会把启动慢的应用杀进循环;四查启动命令——command 是否覆盖了镜像的 ENTRYPOINT。四个方向覆盖绝大多数场景,退避时间会指数拉长,要抓紧在前几次崩溃时把日志抓下来。

6.8 交付够用版三:存储与配置注入 【理解】 ​

标准回答: 实施岗最常用的存储就三种卷:hostPath 把宿主机目录挂进 Pod——我部署 Kuboard 就用它(类型 DirectoryOrCreate 自动建目录)持久化数据;configMap 和 secret 把配置注入成文件或环境变量;需要跨节点持久化时上 PV/PVC 三层抽象——PV 是集群级真实存储资源,PVC 是用户的"存储申请单",StorageClass 做动态供给。PV 生命周期 Available → Bound → Released → Retain/Delete;访问模式 RWO 单节点读写、ROX 多节点只读、RWX 多节点读写(要 NFS 这类共享存储)。

Kuboard 为什么用 hostPath 而不是 PV?这是个"存储选型跟着调度策略走"的典型例子:单副本应用加 nodeSelector 固定调度到 master-01,数据天然只在一个节点,hostPath 就是最简单可靠的方案;我做过实测验证——手动删 Pod 触发重建(还在同节点),配置和审计日志都在。哪天要多副本或允许漂移,就必须换 RWX 网络存储。配置注入的高频追问是更新行为:volume 挂载约 1 分钟热更新(应用要自己支持热加载)、环境变量方式不更新要重启 Pod、subPath 挂载不更新——三种必须张口就说,我处理"改了配置不生效"的联调问题第一步就是问"哪种挂载方式"。Secret 补一句关键认知:base64 只是编码不是加密,安全靠 RBAC 控制——以及 imagePullSecrets 这个 Secret 专属用法(第 5 章 5.6)。 追问链:

  • 迭问1:PV 一直 Pending? → 答:describe pvc 看 Events 逐条对:静态供给场景——PVC 的容量和访问模式与现存 PV 匹配不上(要 20G 只有 10G 的 PV、要 RWX 只有 RWO 的本地盘);动态供给场景——StorageClass 名字写错、provisioner 没部署好、底层存储池满了。另外 Released 状态的 PV 不会自动回到 Available,静态模式要手工清理 claimRef——这个细节踩过一次就记住了。

6.9 部署期故障排查:NotReady 全链路与 9 项避坑 【必背】 ​

标准回答(部署期 = 实施岗的故障主场): K8s 部署期最高频的故障是节点 NotReady,我的全链路排障按"心跳链"走。先懂原理:节点状态是 kubelet 定期(默认 10 秒)向 apiserver 上报的,NotReady 的本质是 apiserver 超过宽限期没收到心跳。

排障四步:① kubectl get pods -n kube-flannel 先看 CNI Pod——Flannel Pod ImagePullBackOff 起不来、节点网络没配好,九成 NotReady 是镜像拉取问题(避坑清单第 1 条);② 上节点 systemctl status kubelet 加 journalctl -u kubelet 看心跳断在哪(高频:容器运行时没起来、cgroup 驱动不匹配);③ 网络链路——节点到 apiserver(VIP:6443)连通性、安全组或防火墙放行;④ 证书是否过期(证书失效 kubelet 无法认证,节点也 NotReady)。

我的排查命令树五层背熟:一看(get pods -o wide、get events 排序、top);二描(describe——九成原因在 Events);三日志(logs,CrashLoop 必用 --previous);四进容器(exec 自证环境);五上节点(journalctl -u kubelet、crictl ps/logs、df/free)。方法论一句话:describe 看"K8s 认为发生了什么",logs 看"应用自己说了什么",journalctl 看"节点上发生了什么"——三层互为印证。

移动云项目部署过程我踩出 9 项避坑清单,每条都是"现象→根因→解决→预防"的完整故事:NotReady 镜像、crictl socket 遍历、etcd Pod 命名(etcd-主机名,不带 kube- 前缀)、ClusterRoleBinding 的 roleRef 不可变要删了重建、Grafana GPG 404、promtool 在主包里、node-exporter 无镜像用 apt 二进制替代、命名空间删后重建要按顺序、时间不同步导致认证失败——面试问"印象深刻的故障"任选一两条展开(完整讲法在第 14 章)。 追问链:

  • 迭问1:为什么要做避坑清单而不是踩一个修一个? → 答:因为实施项目是可复制的——同一个坑在下一次交付、下一个环境还会出现。我的做法是每踩一个坑当场记四样东西:现象、定位命令、根因、预防动作,然后写进部署方案对应步骤的前置检查。清单的价值在第二次交付兑现:移动云方案成为公司外包项目标准底座后,新项目照着方案走,9 个坑一个都不用再踩——这就是"把个人踩坑变成组织资产",也是实施岗文档能力的直接证明。
  • 迭问2:客户现场节点 NotReady 但你排查完不是镜像问题,还查什么? → 答:按心跳链继续收敛:kubelet 进程本身(systemctl 加 journalctl)、运行时状态(crictl version 看 containerd 通不通)、节点资源(磁盘满会让 kubelet 失联——df 看一眼)、时间同步(时间漂移导致证书校验失败)、网络策略(云上安全组是否放行 10250/8472 UDP)。我在现场的真实习惯是"先查共性问题再查个性":三台全 NotReady 是共性问题(镜像/网络/入口),单台 NotReady 是个性问题(该节点服务、磁盘、时间)——这个二分能省一半时间。

6.10 集群验收与交付物(实施版收口) 【理解】 ​

标准回答: 集群装完不等于交付完成,我的验收清单五组:① 节点层——get nodes 全 Ready、节点时间同步毫秒级、磁盘水位正常;② 控制面——kube-system Pod 全 Running、etcd 副本 healthy(etcdctl 带 --cacert --cert --key 三证书连 127.0.0.1:2379 查 endpoint health)、apiserver 经 VIP 可访问;③ 网络层——跨节点 Pod 互通验证(两个节点各起一个 Pod 互 ping)、Service 与 DNS 解析正常(nslookup 服务名);④ 业务层——示例应用部署、滚动更新与回滚演练、探针行为验证;⑤ 高可用——控制面故障切换演练(6.4 的四步)。每组验收结果记录进验收报告,客户签字才算交付闭环——这个流程我在银行项目和移动云项目都完整走过,银行那次是一次通过。

交付物四件套随之交出:部署手册(六阶段加命令,可重放)、验收报告(五组清单逐项结果)、运维规范(证书续期、etcd 快照、故障切换三件 SOP)、培训材料(第 15 章展开)。后续扩容也是实施责任:加 worker 的 join 命令模板、加 master 的证书流程写进手册"扩容"一节——环境可复制、可扩展,是我衡量一次集群交付质量的两个硬指标。 追问链:

  • 迭问1:etcd 备份怎么定的规范? → 答:两层:定期备份——etcdctl snapshot save 带 CA、客户端证书和私钥三个参数,快照文件连同证书保存到集群外的位置(备份放在故障域里等于没备份);变更前备份——凡升级、大改之前必打快照,这是给自己留的后悔药。恢复流程也验证过:snapshot restore 加数据目录参数,配合控制面组件重启。这套连同命令模板都在运维规范文档里移交客户——交付的完整性就体现在"平时用不上、出事能救命"的东西有没有交出去。

6.11 etcd 与 Raft 多数派:为什么奇数副本 【必背】 ​

标准回答(部署架构题的地基,必背): etcd 是 K8s 集群的"账本"——全部状态数据存在这里,靠 Raft 协议保证一致性。Raft 多数派是必须讲透的概念:写请求要获得过半节点确认才算提交,集群的容错能力是"总节点数减一的半数"——3 副本容忍 1 台故障(2/3 仍过半),5 副本容忍 2 台。关键是要算清边际账:4 副本的多数派是 3 台,容错能力跟 3 副本一模一样只容忍 1 台,多花一台机器没换来任何提升——所以 etcd 副本数必须奇数,生产标配 3 或 5,偶数副本是纯浪费。这个"奇数不是迷信、是多数派数学"的道理讲清楚,面试官就知道你是真理解 Raft 而不是背结论。

两个项目我都是 3 master 堆叠式 etcd(etcd 与控制面组件同节点)跨机房/可用区部署,兼顾成本和容灾。故障推演要会:挂 1 台集群正常只是失去冗余;挂 2 台失去 quorum,写请求全部失败,集群只读甚至不可用——3 是"容忍 1 台"的最小可用配置。运维命令背两条:endpoint health 健康检查(etcdctl 带 --cacert --cert --key 三证书连 127.0.0.1:2379,三副本全 healthy 才算过)和 snapshot save 快照备份——快照定期落到集群外保存(备份放在故障域里等于没备份)。踩过的命名坑也记一下:堆叠式 etcd 的 Pod 名是 etcd-<节点主机名>,不带 kube- 前缀——按文档里的名字敲命令报 Pod NotFound,先 get 对齐真实资源名。 实操模板(健康检查与快照,背):

bash
ETCDCTL_API=3 etcdctl --endpoints=https://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 \
  endpoint health                                # 三副本全 healthy 才算过
ETCDCTL_API=3 etcdctl ... snapshot save /backup/etcd-$(date +%F).db   # 快照

追问链:

  • 迭问1:etcd 快照怎么恢复? → 答:五步:停所有节点的 apiserver(静态 Pod 清单移走或停 kubelet)→ 目标节点 etcdctl snapshot restore 到新 dataDir(恢复会重建集群成员信息)→ 改 etcd 静态 Pod 的 dataDir 指向新目录 → 恢复 apiserver 启动 → 验证集群数据完整。规范里快照和证书一起存集群外——恢复能力验证过才算有备份,这条写进我交付的运维规范。

6.12 集群升级与节点维护(交付后的标准动作) 【理解】 ​

标准回答(客户常问"以后怎么升级/维护"): 集群升级我的五步规范:第一步先备份——etcd 快照 snapshot save 落到集群外,升级失败的最终退路就是快照恢复;第二步 kubeadm upgrade plan 看当前版本和可升级目标;第三步先控制面后 worker 逐台滚动——kubeadm upgrade apply 升控制面,然后每台 worker 走"drain → 升 kubelet → 重启 → 验证 → uncordon";第四步每台升级完验证节点 Ready 和业务 Pod 正常再动下一台——把故障半径控制在单节点;第五步升级前确认兼容性——跨版本废弃 API(旧清单 apply 会失败)、镜像与运行时版本配套。铁律是版本跨度:小版本逐级升,绝不跳级。

节点维护的标准三步走,顺序不能乱:kubectl cordon 节点 先打成不可调度(新 Pod 不再上来)→ kubectl drain 节点 --ignore-daemonsets --delete-emptydir-data 驱逐存量 Pod → 维护操作完成验证 Ready 后 kubectl uncordon 恢复调度。核心思想是"先摘流量再动刀"——重启节点、换内核参数、升级 kubelet 都用这套动作,业务不受影响。这两套动作连同命令模板都写进交付的运维规范——实施岗的完整交付不止"装好",还包括"教会客户以后怎么安全地动它"。 追问链:

  • 迭问1:升级前怎么检查 API 兼容性? → 答:三条路:kubeadm upgrade plan 的输出会列即将废弃/移除的 API;用工具(pluto、kubent)扫描集群里正在使用旧 API 版本的对象;逐个核对第三方组件(CNI、监控)的版本兼容矩阵——它们经常是被忽略的升级阻塞项。我在升级规范里写的是"三个清单":API 废弃清单、组件兼容清单、etcd 快照回退预案——检查完才允许动第一台节点。

6.13 kubectl 高频命令与交付效率习惯 【理解】 ​

标准回答: 我的 kubectl 使用按场景分组背。对象管理:get/describe/delete 基础三件套,get 记住 -o wide(多看节点和 IP)、-o yaml(看完整定义)、--show-labels;apply -f 清单声明式管理是标准姿势(比 edit 可版本化)。排障组(6.9 的命令树):logs/exec/describe/get events。发布运维组:rollout status/history/undo、scale 手动扩缩、set image 改镜像;cordon/uncordon/drain 节点维护三连(6.12)。调试便捷组:kubectl cp 容器和本机互拷文件(排障取日志)、kubectl port-forward svc/web 8080:80 本地端口转发(不上 NodePort 也能本地调试)、kubectl top 资源查看。

效率习惯三个:-n 指定命名空间 或 set-context 切默认 ns,避免全 ns 扫;alias k=kubectl;危险命令(delete)先 --dry-run=client -o yaml 预览。还有一条纪律:apply 和 edit 的选择——生产变更一律走 apply(文件进 git,运行状态和声明文件最终一致),edit 只用于排障期的临时调整且当天补回文件——这是声明式运维的底线。移动云项目日常 80% 操作我在 Kuboard 页面做(简历口径),kubectl 是底层能力和应急通道——两层都熟,apiserver 不可用时的节点级后手还有 crictl。 追问链:

  • 迭问1:kubectl exec 和 crictl exec 什么区别? → 答:kubectl exec 按 Pod 名进入,经 apiserver 转发执行——有 RBAC 权限控制、有审计、跨节点可用,是协作环境的规范入口;crictl exec 按容器 ID 在本节点直接执行——适合节点侧排障和 apiserver 不可用时的"最后手段"(apiserver 挂了想看容器日志,上节点 crictl 还能干活)。这个"控制面失联时的节点级后手"价值,是我强调两个命令都要熟的原因。

6.14 集群内 DNS 与服务名解析(联调够用版) 【理解】 ​

标准回答: 集群内的服务发现靠 DNS:CoreDNS 以 Deployment 跑在 kube-system,kubelet 给每个 Pod 的 resolv.conf 配上集群 DNS。解析规则(FQDN 格式)必须会写:同命名空间短名 web 即可;跨命名空间用 web.other-ns;全限定名 web.default.svc.cluster.local。Headless Service(clusterIP: None)的解析特殊——返回的是后端 Pod IP 列表而不是 VIP,这是 StatefulSet 稳定网络标识的实现基础。

联调排障三板斧:Pod 里 nslookup 服务名、查 CoreDNS Pod 状态和日志、看 resolv.conf 配置——"服务名不通先分是网络不通还是解析不通",tcp 服务用 telnet 服务名加端口直判。CoreDNS 挂了的现象要会推:所有"按名字访问"的流量失败(服务名解析不了),但直接用 ClusterIP 或 Pod IP 的访问完全正常——转发规则在 iptables/ipvs 里不依赖 DNS——判断口径清晰:服务名失败而 IP 直连正常就是 DNS 层问题。实施交付里这条特别有用:客户报"应用连不上数据库",我第一步让他在 Pod 里 nslookup 数据库的 Service 名——一分钟内分清是"配置错名字"还是"网络不通",这是联调期最高效的一个分诊动作。 追问链:

  • 迭问1:hostNetwork 的 Pod 为什么解析不了服务名? → 答:因为共享宿主网络栈的 Pod 默认不注入集群 DNS 配置——它的 resolv.conf 是宿主机的。解法是显式配 dnsPolicy: ClusterFirstWithHostNet——这是用主机网络的 Pod"解析不了服务名"的经典坑,联调期我处理过,一句话讲清原理和解法。

6.15 本章案例 ​

  • 【真实项目·银行多 master 改造(STAR)】背景:数币平台 K8s 集群初为单 master(v1.17.3,受华为 BCS 1.2.6 配套约束),业务方要求控制面高可用,单点不可接受。任务:在统信 UOS ARM 环境完成单 master 到三 master 的改造并验证故障转移。行动:入口层用 haproxy 和 Nginx 双方案做 16443 四层代理(先后实施,验证等价后标准化到 Nginx)加 Keepalived VIP 漂移(检测脚本盯 16443 端口、priority 100/90);证书层踩了 x509 报错的坑后按六步更新 certSANs;scp CA 类证书到 master02/03 后以 --control-plane 加入;组件配置统一切到 VIP:16443 并逐个重启生效。结果:3 master + 10 worker 稳定运行,主节点关机实测 VIP 秒级漂移、kubectl 经 VIP 正常返回,支撑数币试点业务;完整方案沉淀为公司标准实施文档,也是我面试里最完整的"高可用实战"故事。
  • 【真实项目·移动云三主集群(STAR)】背景:公司外包业务上线在即,需要生产级云原生底座。任务:资源评估、方案编制、部署实施、验证全流程由我负责。行动:按通用高可用架构规划 7 台 ECS——3 台 2C4G master 跨 3 可用区、3 台 8C16G worker、1 台 4C8G 独立监控节点;CLB 四层 VIP:6443 统一接入加健康检查自动摘除;containerd 1.7.8(SystemdCgroup)加 kubeadm v1.28.2 加 Flannel v0.22.0 VXLAN;部署顺序严格"初始化→控制面→CNI→worker→验证→监控";过程中踩出九项避坑清单并逐项验证解法。结果:控制平面按 99.99% 设计目标交付(三副本加 CLB 秒级摘除,口径为设计目标);资源交付从天级到分钟级;方案成为公司后续外包项目的标准基础设施底座。
  • 【真实项目·控制面故障切换演练】背景:集群宣称高可用必须有实测支撑。任务:验证单 master 故障时业务和管理面无感。行动:停一台 master 的 kubelet 模拟故障 → 观察 CLB 健康检查摘除该后端 → 经 VIP 的 kubectl get nodes 正常返回 → 恢复 kubelet 观察控制面组件自动重启、etcd 自动同步重新加入。结果:切换全程 kubectl 无失败请求,恢复后集群回到三副本健康状态;演练记录和"故障切换操作手册"写进运维规范——"高可用是设计出来的,更是演练出来的"是我从这次得到的信条。
  • 【经典故障·Pod 全量重启资源不足】背景:某平台改一个证书 Secret 触发 200 多个 Pod 同时重启,标准版私有云资源有限,全部 Pending 卡死。处理:先全停释放资源,再按模块分批——每次拉 20 个等 3 分钟观察稳定,分 10 批全部拉起,历时约半小时恢复。根治:后续版本改为滚动更新(每批 10 个)避免重启风暴。方法论:公共配置变更等于全量重启风暴,资源有限的环境必须有分批拉起预案——这个案例讲"变更风险预判"特别有说服力。

6.16 章末自测清单(快问快答) ​

  1. kubeadm 部署六阶段顺序? → 初始化→控制面→CNI→worker→健康验证→监控配套;CNI 必须先于 worker。
  2. 为什么监听 16443? → 负载均衡与 master 复用一台机器,6443 已被本地 apiserver 占用。
  3. 多 master 改造六步? → 入口层负载均衡→导出配置→补 certSANs→重签证书并加入(scp 证书 + --control-plane)→组件配置切 VIP→验证漂移。
  4. 改集群入口第一件事? → 想证书:certSANs 加 VIP/主机名,移走旧证书重新 init phase certs apiserver。
  5. 证书六步的第 3 步为什么要移走旧证书? → 不移走 kubeadm 认为证书已存在不会重建。
  6. 证书默认有效期与续期规范? → apiserver 等默认 1 年;到期前 30 天 kubeadm certs renew all 加重启组件。
  7. 自建与托管入口的选型结论? → 云上首选托管 CLB(无单点免运维);内网自建 Nginx+Keepalived 是完备备选——两方案都实操过。
  8. 防脑裂检测脚本盯什么? → 真实业务端口 16443 的监听,不是 keepalived 进程本身。
  9. ARM 差异四条? → aarch64 专用源、aarch64 静态二进制包、arm64 镜像离线导入、编译类软件补 ARM 依赖。
  10. NotReady 九成的根因? → CNI 镜像拉取失败——先 get pods -n kube-flannel 看 ImagePullBackOff。
  11. 排查命令树五层? → 看(get/events)→描(describe)→日志(logs --previous)→进容器(exec)→上节点(journalctl/crictl)。
  12. Pending 分诊一句话? → NODE 列空是没调度上(调度层);NODE 有值卡 ContainerCreating 是节点 kubelet 层。
  13. CrashLoop 四查? → 退出码(137=OOM)→崩溃前日志→探针是否激进→启动命令覆盖。
  14. Kuboard 四件套? → nodeSelector 固定节点、tolerations 容忍污点、imagePullSecrets、hostPath DirectoryOrCreate。
  15. ConfigMap 三种更新行为? → volume 挂载约 1 分钟热更、环境变量不更新需重启、subPath 不更新。
  16. etcd 为什么奇数副本? → Raft 多数派:4 副本容错与 3 相同,偶数纯浪费;3 容 1 台、5 容 2 台。
  17. etcd 健康检查要点? → etcdctl 带 --cacert --cert --key 三证书连 127.0.0.1:2379 查 endpoint health。
  18. 堆叠式 etcd 的 Pod 名? → etcd-<节点主机名>,不带 kube- 前缀。
  19. 节点维护三步? → cordon(不可调度)→ drain(驱逐)→ 维护后 uncordon;先摘流量再动刀。
  20. 升级铁律? → 先 etcd 快照;先控制面后 worker 逐台滚动;小版本逐级不跳级。
  21. 集群验收五组清单? → 节点层、控制面(etcd health 三证书)、网络层(跨节点 Pod 互通/DNS)、业务层(发布回滚演练)、高可用演练。
  22. etcd 快照命令要点? → snapshot save 带三证书,快照与证书存集群外。
  23. 跨命名空间访问 Service 的写法? → 服务名.命名空间(全限定 …svc.cluster.local);hostNetwork Pod 要配 ClusterFirstWithHostNet。
  24. CoreDNS 挂了的现象与判别? → 服务名全解析失败、IP 直连正常——转发规则不依赖 DNS。
  25. apply 和 edit 的纪律? → 生产变更走 apply(文件进 git),edit 仅排障临时用且当天补回文件。