K8s 1.36.4 集群运行手册(RUNBOOK)¶
面向接手日常运维与值班的人:拿着就能做,每条操作都给了完整命令与预期输出。 本手册回答"这个集群平时怎么用、怎么查、怎么救";"这次变更改了什么、怎么退"见
k8s_136_cluster_deploy/docs/SOP.md。同一事实只写一处,SOP 需要时链接到本文。
目录¶
路径约定:本文命令中的文件路径均相对项目根目录(即本工程所在目录
k8s_ins/)书写; 涉及系统文件(如/etc/kubernetes/...)时使用绝对路径,因为那是命令本身的参数。
1. 服务概览¶
| 项 | 说明 |
|---|---|
| 用途 | 内部 Kubernetes 学习/测试集群,运行自建工作负载 |
| 版本 | Kubernetes v1.36.4,容器运行时 containerd 2.3.x |
| 拓扑 | 单控制面:k8s-master-01;工作节点 k8s-node-01/02/03 |
| 控制面入口 | VIP 192.168.31.8:16443(haproxy → apiserver 6443),VIP 由 keepalived 持有 |
| 网络 | Pod 10.244.0.0/16,Service 10.96.0.0/12(Calico 3.32.2,Tigera Operator,IPIPCrossSubnet) |
| 附加组件 | metrics-server v0.9.0(kubectl top)、Headlamp v0.45.0(Web 控制台) |
| 已下线 | k8s-master-02/03 虚拟机(关机、autostart 禁用、磁盘保留);见 SOP 5.10 可一键扩回 |
| 依赖 | 全部虚拟机位于同一台 KVM 宿主机;宿主机或存储故障将同时影响所有节点 |
| 业务影响 | 控制面不可用时:已有 Pod 继续运行,但无法创建/修改/删除任何资源,kubectl 全部失败 |
| 重要性等级 | 学习环境(非生产);无 SLA,但故障会导致实验中断 |
组件清单:
| 组件 | 位置 | 作用 |
|---|---|---|
| kube-apiserver / etcd / controller-manager / scheduler | k8s-master-01(静态 Pod,kube-system) |
控制面四件套 |
| etcd | 数据目录 /var/lib/etcd(独立盘 /dev/vdb,10G xfs) |
集群唯一数据存储 |
| keepalived + haproxy | k8s-master-01 |
提供 VIP 192.168.31.8:16443 |
| kubelet / containerd | 全部 4 台在线节点 | 节点代理与运行时 |
| calico-node / typha / kube-controllers | calico-system |
CNI 与网络策略 |
| metrics-server | kube-system |
资源指标(kubectl top) |
| Headlamp | kube-system |
Web 控制台(ClusterIP,需端口转发访问) |
2. 接入与凭据¶
| 项 | 值 / 位置 |
|---|---|
| 管理入口 | 本机(dell-R620)已装 kubectl v1.36.4,直接 kubectl 即可(context:kubernetes-admin@kubernetes) |
| 集群侧 kubeconfig | 各 master 的 /etc/kubernetes/admin.conf;root 的 /root/.kube/config |
| 节点 SSH | root@192.168.31.10–.15,端口 22,密钥认证(口令/密钥路径见配置 config/deploy.yaml,不入本文档) |
| keepalived 口令 | cluster.keepalived_auth_pass(k8s_136_cluster_deploy/config/deploy.yaml) |
| haproxy stats 账号 | cluster.haproxy_stats_user / haproxy_stats_password(同文件),仅监听 127.0.0.1:9000 |
| 集群凭据查询 | grep -E 'keepalived_auth_pass\|haproxy_stats' k8s_136_cluster_deploy/config/deploy.yaml(路径均相对项目根目录,下同) |
客户端接入示例(在任意能连到 VIP 的机器上):
mkdir -p ~/.kube
scp root@192.168.31.10:/etc/kubernetes/admin.conf ~/.kube/config
sed -i 's#https://192.168.31.8:16443#https://192.168.31.8:16443#' ~/.kube/config # 确认 server 指向 VIP
chmod 600 ~/.kube/config
kubectl get nodes # 预期:4 行,均 Ready
Headlamp 控制台访问(当前为 ClusterIP,不对外暴露):
ssh -L 8080:127.0.0.1:8080 root@192.168.31.10 # 本机开隧道(保持不关)
# 另开终端,在 master-01 上执行:
kubectl -n kube-system port-forward svc/headlamp 8080:80
# 浏览器访问 http://localhost:8080
3. 日常速查表¶
| 目的 | 命令 | 预期 |
|---|---|---|
| 看节点 | kubectl get nodes -o wide |
4 行全 Ready |
| 看异常 Pod | kubectl get pods -A \| grep -Ev 'Running\|Completed' |
无输出 |
| 看资源用量 | kubectl top nodes |
输出 CPU/内存使用 |
| 看控制面健康 | kubectl get --raw='/readyz?verbose' |
末尾 readyz check passed |
| 看 etcd 状态 | ssh root@192.168.31.10 "crictl exec \$(crictl ps --name etcd -q \| head -1) etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key endpoint status -w table" |
IS LEADER=true、IS HEALTHY=true |
| 看 kubelet 日志 | ssh root@192.168.31.10 "journalctl -u kubelet --since '30 min ago' -p err" |
无新增错误 |
| 看 Pod 日志 | kubectl -n <ns> logs <pod> --tail=100 --since=30m |
— |
| 备份 etcd | 见 第 6 章 | 生成 .db 快照 |
| 扩回 3 控制面 | 见 SOP 5.10 | etcd 成员 1→3 |
| 证书到期检查 | ssh root@192.168.31.10 "kubeadm certs check-expiration" |
各证书 RESIDUAL TIME 充足 |
维护铁律:当前 etcd 只有 1 个成员,没有冗余。任何时刻只操作一台节点,不要并行重启/关机; 关闭
k8s-master-01就等于整个控制面下线。
4. 日常操作¶
4.1 状态检查¶
# 集群整体
kubectl get nodes -o wide # 预期:4 行 Ready
kubectl get pods -A -o wide | grep -Ev 'Running|Completed' # 预期:无输出
kubectl get --raw='/readyz?verbose' | tail -3 # 预期:readyz check passed
# 控制面四件套(均为静态 Pod,位于 kube-system)
kubectl -n kube-system get pods -l tier=control-plane # 预期:etcd / kube-apiserver / controller-manager / scheduler 各 1 个 Running
# CNI
kubectl get tigerastatus # 预期:各行 Available=True
kubectl -n calico-system get pods # 预期:calico-node×4、typha、kube-controllers 全 Running
# 宿主机层(以 master-01 为例)
ssh root@192.168.31.10 "systemctl is-active kubelet containerd keepalived haproxy"
# 预期:active active active active
输出不符时:节点非 Ready 见 8.2;Pod 异常见 8.3; 控制面 Pod 缺失见 8.1。
4.2 重启组件¶
# 重启单个节点的 kubelet(安全,先确认该节点不是唯一控制面)
ssh root@192.168.31.13 "systemctl restart kubelet && systemctl is-active kubelet" # 预期:active
# 重启工作节点上的容器运行时(高危:该节点所有容器会被重启)
ssh root@192.168.31.13 "systemctl restart containerd" # 预期:重启后 Pod 自动重建
# 重启控制面某组件(高危):静态 Pod 由 kubelet 管理,移动 manifest 即可重建
ssh root@192.168.31.10 "mv /etc/kubernetes/manifests/kube-scheduler.yaml /tmp/ && sleep 20 && mv /tmp/kube-scheduler.yaml /etc/kubernetes/manifests/"
# 预期:20 秒内 kube-scheduler 新 Pod 出现并 Running
不要用
systemctl restart kube-apiserver之类的命令——控制面组件由 kubelet 以静态 Pod 方式托管。
4.3 扩缩容¶
# 控制面缩容到单 master(双向工具,幂等;必须先看预演)
cd k8s_136_cluster_deploy # 相对项目根目录
.venv/bin/python control_plane_scale.py --config config/deploy.yaml --expand --dry-run
.venv/bin/python control_plane_scale.py --config config/deploy.yaml --expand # 扩回 3 master
# 工作节点扩容(新节点需已具备 containerd/kubelet,或先跑部署工程完成基线)
ssh root@192.168.31.10 "kubeadm token create --print-join-command" # 取 join 命令
ssh root@<新节点IP> "<上一步输出的 kubeadm join 命令>"
kubectl get nodes # 预期:新节点出现并转为 Ready
4.4 查看日志¶
# 控制面组件(静态 Pod 日志)
kubectl -n kube-system logs etcd-k8s-master-01 --tail=50
kubectl -n kube-system logs kube-apiserver-k8s-master-01 --tail=50
# 仅看最近 30 分钟的错误
kubectl -n kube-system logs kube-apiserver-k8s-master-01 --since=30m | grep -Ei 'error|fail|timeout|deadline'
# 节点代理日志(journald)
ssh root@192.168.31.10 "journalctl -u kubelet --since '1 hour ago' | tail -50"
ssh root@192.168.31.10 "journalctl -u containerd --since '1 hour ago' -p err | tail -30"
# CNI
kubectl -n calico-system logs -l k8s-app=calico-node --tail=50 --since=30m
4.5 进入容器与主机排障¶
# 进入 Pod
kubectl -n <ns> exec -it <pod> -- sh
# 节点上的容器运行时排查(需先装 cri-tools,已装)
ssh root@192.168.31.13 "crictl ps -a | head -20" # 容器清单(含已退出)
ssh root@192.168.31.13 "crictl logs <container-id> | tail -50"
ssh root@192.168.31.13 "crictl inspect <container-id> | grep -A3 exitCode"
5. 配置管理¶
| 配置 | 位置 | 修改方式 | 生效方式 |
|---|---|---|---|
| 部署工程参数 | k8s_136_cluster_deploy/config/deploy.yaml |
编辑后重跑工程(幂等) | 重跑 main.py,仅差异项被变更 |
| kubelet 参数 | /var/lib/kubelet/config.yaml(各节点) |
不要手改,由 kubeadm 生成 | systemctl restart kubelet |
| 控制面参数 | /etc/kubernetes/manifests/*.yaml |
编辑后 kubelet 自动重建该静态 Pod | 保存即生效(约 20–60 秒) |
| 集群级配置 | ConfigMap kubeadm-config(kube-system) |
kubectl -n kube-system edit cm kubeadm-config |
影响后续 kubeadm join/upgrade |
| keepalived | /etc/keepalived/keepalived.conf(master) |
手工编辑或由工程重跑覆盖 | systemctl restart keepalived |
| haproxy | /etc/haproxy/haproxy.cfg(master) |
同上 | systemctl restart haproxy |
| SELinux(haproxy 入口依赖) | 策略存储:semanage port -l \| grep 16443、getsebool haproxy_connect_any |
semanage port -a -t http_port_t -p tcp 16443、setsebool -P haproxy_connect_any on |
立即生效并随重启保留(-P 持久)。Enforcing 下缺这两项会让 haproxy 启动失败(第 8 章场景 1) |
| Calico | CRD:kubectl get installation.operator.tigera.io default -o yaml |
kubectl edit(改 IPPool 等) |
Operator 自动 reconcile |
易错点:
baseline.disable_selinux已于 2026-09-29 由true改为false(原值会在重跑时执行setenforce 0并把/etc/selinux/config写成SELINUX=disabled,见k8s-deploy-yaml-selinux-20260929/SOP.md)。若改回true再重跑,本机 haproxy 等依赖 SELinux 策略的服务会失去约束基础- 重跑本工程会按
config/deploy.yaml中全部role=master节点重写 haproxy 后端列表——当前定义 3 台,其中.11/.12虚机已关闭,故重跑会把已被手工清理掉的后端写回(_write为哈希比对后覆盖);缩容/扩回工具control_plane_scale.py不写此文件,不受影响 - 改
config/deploy.yaml里的 VIP / 网卡 / 节点 IP 后重跑,会触发 keepalived/haproxy 重写与重启(高危,会短暂中断 VIP) - Calico 的
ipPools[0].cidr不要随意改;改池会导致已有 Pod IP 失效(需重建 Pod) kubeadm-config里若残留过期ClusterStatus,会让kubeadm join --control-plane误判成员已存在——扩回工具已自动处理
6. 备份与恢复¶
6.1 备份策略¶
| 对象 | 方式 | 位置 |
|---|---|---|
| etcd 数据 | etcdctl snapshot save |
远端 /var/lib/etcd/snapshot_*.db,再下载到工程 backups/<时间戳>/ |
| 宿主配置 | 部署/变更前自动打包 | 各节点 /root/k8s_backup_<时间戳>/host_config.tgz |
| 覆盖前的单文件 | 自动 .bak.<时间戳> |
原文件同目录(工程 --rollback 依赖此约定) |
完整台账与清理时机见 k8s_136_cluster_deploy/backup/README.md。
6.2 手工备份 etcd(随时可做,只读)¶
ssh root@192.168.31.10 'cid=$(crictl ps --name etcd -q | head -1); \
crictl exec "$cid" etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /var/lib/etcd/snapshot_manual_$(date +%Y%m%d_%H%M%S).db'
# 预期:输出 "Snapshot saved at /var/lib/etcd/snapshot_manual_<时间戳>.db"
ssh root@192.168.31.10 "ls -lh /var/lib/etcd/snapshot_manual_*.db | tail -3"
6.3 恢复 etcd(高危,须停机,影响整个控制面)¶
# 1) 停控制面(移开 etcd 与 apiserver 的静态 Pod 清单)
ssh root@192.168.31.10 "mkdir -p /tmp/manifests_off && \
mv /etc/kubernetes/manifests/etcd.yaml /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/manifests_off/"
ssh root@192.168.31.10 "crictl pods | grep -c etcd || true" # 预期:0(已停)
# 2) 备份现有数据目录并执行恢复
ssh root@192.168.31.10 "mv /var/lib/etcd /var/lib/etcd.bak.$(date +%Y%m%d-%H%M%S)"
ssh root@192.168.31.10 'cid=$(crictl ps -a --name etcd -q | head -1); \
crictl exec "$cid" etcdctl snapshot restore /var/lib/etcd/<快照文件> --data-dir /var/lib/etcd'
# 预期:输出 "2026-xx-xx ... restored snapshot" 且无报错
# 3) 恢复控制面
ssh root@192.168.31.10 "mv /tmp/manifests_off/*.yaml /etc/kubernetes/manifests/"
kubectl get --raw='/readyz' # 预期:ok
kubectl get nodes # 预期:4 行
恢复后验证:kubectl get nodes、kubectl get pods -A | grep -v Running、kubectl get --raw='/readyz?verbose'。
注意:快照是某一时点的数据,恢复后该时点之后的所有变更(含新建 Pod/Service)会丢失。
演练记录:本环境尚未做过完整的快照恢复演练(待补)。
7. 监控与告警¶
| 项 | 查看方式 | 关注阈值 |
|---|---|---|
| 节点资源 | kubectl top nodes |
CPU/内存持续 > 80% |
| Pod 资源 | kubectl top pods -A --sort-by=memory \| head |
单 Pod 接近 limit |
| etcd 数据量 | ssh root@192.168.31.10 "du -sh /var/lib/etcd" |
当前 ~300MB;后端配额默认 2GiB |
| etcd 盘水位 | ssh root@192.168.31.10 "df -h /var/lib/etcd" |
> 80% 需处理(盘仅 10G) |
| 证书余期 | ssh root@192.168.31.10 "kubeadm certs check-expiration" |
余期 < 90 天需续期 |
| 节点 Ready | kubectl get nodes |
出现非 Ready 立即处置 |
| 控制面可用 | curl -sk https://192.168.31.8:16443/healthz |
非 ok 立即处置 |
| 宿主机资源 | free -h; nproc(宿主机) |
内存/CPU 紧张会影响全部节点 |
当前未接入任何监控告警系统(无 Prometheus/Grafana/Altertingmanager)。Headlamp 仅提供只读的控制台视图。
如需监控,可沿用 monitoring_upgrade/ 目录中的 kube-prometheus-stack 工程(本次未部署)。
8. 故障处置¶
场景 1:kubectl 无法连接 apiserver¶
症状:kubectl get nodes 报 Unable to connect to the server / TLS handshake timeout /
context deadline exceeded / connect: connection refused / EOF;
curl -sk https://192.168.31.8:16443/healthz 不通。
诊断
# 1) VIP 是否存在(在 master-01 上执行)
ssh root@192.168.31.10 "ip -4 -o addr show | grep 192.168.31.8" # 预期:有 ens2 上的一条 /32
# 2) 控制面静态 Pod 是否在跑
ssh root@192.168.31.10 "crictl ps --name etcd,kube-apiserver" # 预期:两个容器 Running
# 3) apiserver 最近是否 Fatal 退出(关键证据)
ssh root@192.168.31.10 "journalctl -u kubelet --since '30 min ago' | grep -Ei 'apiserver|etcd|Fatal' | tail -20"
# 4) etcd 自身状态与成员数
ssh root@192.168.31.10 'cid=$(crictl ps -a --name etcd -q | head -1); crictl logs "$cid" 2>&1 | tail -30'
# 预期:无 "lost the lead" / "failed to commit" / "is starting a new election"
# 5) 入口代理 haproxy 与其 SELinux 约束(宿主机重启后最易踩:SELinux 拦截会让 haproxy 起不来)
ssh root@192.168.31.10 "systemctl is-active haproxy; journalctl -u haproxy --since '-30min' --no-pager | tail -5"
# 预期:active。若为 failed 且含 "cannot bind socket (Permission denied) for [0.0.0.0:16443]" → 见下方 SELinux 分支
ssh root@192.168.31.10 "getenforce; grep 'type=AVC' /var/log/audit/audit.log | grep 'comm=\"haproxy\"' | tail -5"
# 预期:Enforcing 且无 haproxy 的 AVC;出现 name_bind / name_connect 拒绝即为根因
处置(按诊断结果分支)
- 若 etcd 报反复选举 /
no route to host:说明成员数不足(曾有 3 成员,现单成员则不会发生)。 历史对策:启动其他 master 虚机恢复多数派,再重启本机 kubelet 触发静态 Pod 重建:bash sudo virsh start k8s-master-02 # 宿主机上执行 ssh root@192.168.31.10 "systemctl restart kubelet" # 触发静态 Pod 重建 - 若 apiserver 容器不存在(kubelet 未拉起):
bash ssh root@192.168.31.10 "ls /etc/kubernetes/manifests/ && systemctl restart kubelet" - 若 VIP 丢失:
bash ssh root@192.168.31.10 "systemctl restart keepalived haproxy && ip -4 -o addr show | grep 192.168.31.8" - 若 haproxy 为 failed,且日志含
cannot bind socket (Permission denied) for [0.0.0.0:16443]: SELinux 拦截。Enforcing下haproxy_t既不能绑 16443,也不能连后端 6443;restart无效,须补策略(持久):bash ssh root@192.168.31.10 "dnf -q install -y policycoreutils-python-utils" # 提供 semanage(缺失时) ssh root@192.168.31.10 "semanage port -a -t http_port_t -p tcp 16443" # 允许绑定入口端口 ssh root@192.168.31.10 "setsebool -P haproxy_connect_any on" # 允许连接后端 6443 ssh root@192.168.31.10 "systemctl restart haproxy"回滚:semanage port -d -t http_port_t -p tcp 16443与setsebool -P haproxy_connect_any off。
验证:curl -sk https://192.168.31.8:16443/healthz 返回 ok,且 kubectl get nodes 正常。
完整根因与证据见 k8s_136_cluster_deploy/docs/INCIDENT-20260916-etcd-quorum-lost.md(etcd quorum 场景)
与 k8s-restart-16443-selinux-20260929/SOP.md(SELinux 场景)。
场景 2:节点 NotReady¶
症状:kubectl get nodes 中某节点 NotReady;其上 Pod 变为 Unknown/停止调度。
诊断
NODE_IP=192.168.31.13 # 换成故障节点 IP
kubectl describe node k8s-node-01 | grep -A5 Conditions # 看 Ready 条件与 Reason
ssh root@$NODE_IP "systemctl is-active kubelet containerd" # 预期:active active
ssh root@$NODE_IP "journalctl -u kubelet --since '30 min ago' -p err | tail -20"
ssh root@$NODE_IP "df -h /var/lib/kubelet; free -m" # 磁盘满或内存耗尽会导致 NotReady
ssh root@$NODE_IP "crictl ps | wc -l" # containerd 是否有响应
处置
- kubelet 未运行 →
ssh root@$NODE_IP "systemctl restart kubelet",等 30 秒复查 - 磁盘/内存耗尽 → 清理空间或重启节点(先
kubectl drain迁走业务):bash kubectl drain k8s-node-01 --ignore-daemonsets --delete-emptydir-data # 高危:会驱逐该节点业务 Pod ssh root@$NODE_IP "reboot" kubectl uncordon k8s-node-01 - containerd 无响应 →
ssh root@$NODE_IP "systemctl restart containerd"
验证:kubectl get nodes 中该节点回到 Ready;kubectl get pods -A -o wide | grep <node> 无异常。
场景 3:Pod 无法调度或持续 Pending¶
症状:kubectl get pods -A 出现 Pending;describe 事件里有 Insufficient cpu/memory 或
0/4 nodes are available。
诊断
kubectl -n <ns> describe pod <pod> | tail -20 # 看 Events 段的失败原因
kubectl describe nodes | grep -A5 "Allocated resources" # 看各节点已分配资源
kubectl get nodes --show-labels | grep -v control-plane # 确认可调度节点
kubectl get pods -A --field-selector status.phase=Pending # 全局 Pending 清单
处置
- 资源不足 → 扩容节点或调低 requests;确认工作节点未被 cordon(
kubectl get nodes若含SchedulingDisabled,用kubectl uncordon <node>) - 污点阻拦 →
kubectl describe node <node> | grep Taints,必要时kubectl taint nodes <node> <key>- - 镜像拉取失败(
ImagePullBackOff)→ 见场景 5
验证:Pod 转为 Running(或 Completed),kubectl get pods -A | grep -v Running 无输出。
场景 4:Pod 之间网络不通¶
症状:跨节点 Pod 无法互访;kubectl exec 内 ping/curl 超时;DNS 解析失败。
诊断
kubectl get tigerastatus # 预期:Available=True(若 False 即 CNI 异常)
kubectl -n calico-system get pods -o wide | grep -v Running # 是否有 calico-node 未 Running
kubectl get ippools.crd.projectcalico.org -o yaml | grep cidr # 预期:10.244.0.0/16
kubectl -n calico-system logs -l k8s-app=calico-node --tail=50 --since=30m | grep -Ei 'error|fail'
# DNS 专项
kubectl -n kube-system get pods -l k8s-app=kube-dns # 预期:coredns 2 个 Running
kubectl run dnstest --rm -it --image=registry.k8s.io/e2e-test-images/agnhost:2.45 -- \
/agnhost dns-check kubernetes.default # 预期:解析出 ClusterIP
处置
calico-node异常 →kubectl -n calico-system rollout restart daemonset/calico-node- IPPool 与实际不符 → 不要随意改池,需评估后重建受影响 Pod
- CoreDNS 异常 →
kubectl -n kube-system rollout restart deploy/coredns,并检查其上游 (kubectl -n kube-system get cm coredns -o yaml | grep forward,本环境上游为192.168.31.2) - 节点间隧道异常(IPIP 模式)→ 确认
ipip模块已加载:ssh root@<node> "lsmod | grep ipip"
验证:kubectl run 的临时 Pod 能解析并连通 kubernetes.default;业务 Pod 互访恢复。
场景 5:镜像拉取失败¶
症状:Pod 处于 ImagePullBackOff / ErrImagePull。
诊断
kubectl -n <ns> describe pod <pod> | grep -A3 Events # 看具体错误:超时 / 401 / not found
# 在目标节点上直接测拉取
ssh root@192.168.31.13 "crictl pull <镜像地址>" # 预期:成功;失败会给出明确原因
# 外网通路(本环境经旁路网关访问海外)
ssh root@192.168.31.13 "curl -sI --max-time 20 -o /dev/null -w '%{http_code}\n' https://quay.io/v2/" # 预期:401
处置
- 网络不通 → 检查节点 DNS(应为
192.168.31.2)与旁路通路;本环境海外直连带宽仅 300–450KB/s,大镜像需等待 - 镜像地址错 → 用
kubectl set image或改清单后重新 apply - 私有仓库需凭据 →
kubectl -n <ns> create secret docker-registry ...并在 Pod 中引用
验证:Pod 转为 Running;ssh root@<node> "crictl images | grep <镜像>" 能看到镜像。
9. 安全与权限¶
| 项 | 说明 |
|---|---|
| 认证 | kubeconfig 中的客户端证书(kubernetes-admin,属 system:masters 组) |
| 授权 | 集群内置 RBAC;未启用审计日志与 PodSecurity 强制策略 |
| 证书位置 | /etc/kubernetes/pki/(CA、apiserver、etcd、front-proxy 等),权限 644/600,仅 root 可读私钥 |
| 证书有效期 | 非 CA 证书 1 年、CA 10 年(kubeadm 显式设置);到期前须续期 |
| kubeconfig | /etc/kubernetes/admin.conf(权限 600),含集群管理员权限——等同集群 root,勿外传 |
| haproxy stats | 仅监听 127.0.0.1:9000 且启用认证 |
| SELinux / firewalld | 已禁用(学习环境取舍),见 SOP 第 8 章风险表 |
| SSH | root 密钥登录(口令为空时不启用密码登录),密钥由使用者保管 |
证书续期(到期前 90 天执行,高危:会重启控制面组件):
ssh root@192.168.31.10 "kubeadm certs check-expiration" # 先看余期
ssh root@192.168.31.10 "kubeadm certs renew all" # 续期
ssh root@192.168.31.10 "systemctl restart kubelet" # 重建静态 Pod 使新证书生效
ssh root@192.168.31.10 "kubeadm certs check-expiration" # 复查余期
# 注意:续期后需同步更新 ~/.kube/config(客户端证书也变了)
ssh root@192.168.31.10 "cat /etc/kubernetes/admin.conf" > ~/.kube/config && chmod 600 ~/.kube/config
10. 容量与性能¶
当前水位基线(2026-09-16 实测):
| 项 | 数值 |
|---|---|
| 在线节点 | 4 台:master-01(4 vCPU / 8GiB)、node-01/02/03(各 8 vCPU / 16GiB) |
| 控制面用量 | CPU ~0.5 核、内存 ~0.9–1.6 GiB |
| etcd 数据目录 | /var/lib/etcd(独立盘 10G xfs),当前占用约 300MB |
| etcd 后端配额 | 默认 2 GiB(超限即只读告警) |
| 宿主机 | 32 vCPU / 117 GiB,已用约 18 GiB |
| 海外拉取带宽 | 300–450 KB/s(大镜像需耐心) |
扩容触发条件与操作
- 工作负载资源不足 → 新增工作节点(见 4.3)
- 需要控制面冗余 →
control_plane_scale.py --expand扩回 3 master(etcd 恢复 3 成员) - etcd 盘使用率 > 80% → 先确认是否为历史快照堆积(
ls -lh /var/lib/etcd/snapshot_*.db), 清理旧快照;仍不足则需扩容盘(高危,须停控制面) - 宿主机资源紧张 → 全部节点都在这台宿主机上,扩容应先在宿主机加资源
已知瓶颈
- 单控制面:
k8s-master-01故障即控制面全停(无冗余) - 单宿主机:宿主机宕机则 4 台虚机同时不可用
- etcd 单成员:数据无副本,仅依赖快照备份
- etcd 盘仅 10G:快照堆积会挤占空间
- 海外镜像拉取慢:大镜像首次拉取耗时长
11. 附录¶
术语表¶
| 术语 | 含义 |
|---|---|
| VIP | 虚拟 IP 192.168.31.8,控制面统一入口,由 keepalived 持有 |
| quorum | etcd 多数派;单成员集群 quorum=1,无冗余 |
| 静态 Pod | 由 kubelet 直接从 /etc/kubernetes/manifests/ 托管的 Pod(控制面四件套) |
| 母机 | 执行部署与集群操作的节点,本环境为 k8s-master-01 |
相关文档与交付物¶
| 文档 | 用途 |
|---|---|
k8s_136_cluster_deploy/docs/SOP.md |
部署与缩容的变更档案(怎么改、怎么验、怎么退) |
k8s_136_cluster_deploy/docs/INCIDENT-20260916-etcd-quorum-lost.md |
etcd 失多数派故障复盘 |
k8s_136_cluster_deploy/backup/README.md |
备份台账(含清理时机) |
k8s_136_cluster_deploy/config/deploy.yaml.example |
全部可配置项与注释 |
修订记录¶
| 日期 | 修改人 | 变更内容 |
|---|---|---|
| 2026-09-16 | 运维(AI 辅助) | 初版:集群由 3 master HA 缩容为单 master,按 writing-ops-change-sop 规范补建本手册 |
来源:
/home/rain/Documents/k8s_ins/k8s_136_cluster_deploy/docs/RUNBOOK.md| 最后更新:2026-09-29 20:54(由build/sync.py同步,请勿在站点侧修改)