Kubernetes
Kubernetes 把应用的运行要求保存为 API 对象:使用哪个镜像、维持多少副本、分配什么资源、从哪里读取配置、怎样判断可以接收请求。控制器持续比较这些要求和集群现状,创建或调整相应资源。应用团队维护声明,集群负责调度和执行;故障排查则沿着声明对象找到具体的节点、容器和网络路径。
从声明对象到第一个可访问的服务
集群中各组件怎样交接
控制面
├─ API Server:认证、授权、准入、API 对象的读写入口
├─ etcd:保存集群 API 数据
├─ Scheduler:为尚未调度的 Pod 选择节点
└─ Controller Manager:运行副本、节点等控制循环
工作节点
├─ kubelet:根据分配给本节点的 Pod 管理容器并报告状态
├─ CRI 运行时:例如 containerd,负责镜像与容器
├─ CNI 网络插件:配置 Pod 网络
├─ Service 数据面:kube-proxy,或插件提供的替代实现
└─ CSI 存储插件:按驱动能力挂载、管理存储客户端把 Deployment 交给 API Server 后,Deployment 控制器创建或调整 ReplicaSet,ReplicaSet 控制器再维持 Pod 数量。Scheduler 写入调度结果,目标节点的 kubelet 调用运行时启动容器。它们通过 API 状态协作,整个过程是异步的;一次请求返回成功,后面仍可能因资源不足、拉取镜像失败或探针失败而停下。
来源:Kubernetes Components,官方文档以 CC BY 4.0 提供。下图的实线串起发布执行步骤和客户端请求流向,虚线表示状态驱动的数据面配置。
Service 的业务流量不需要逐次经过 API Server;EndpointSlice 也不是请求经过的代理服务器,它描述后端地址和状态,供数据面更新转发规则。
Linux 上准备独立实验集群
单机 Compose 适合少量服务的集中部署;需要多节点调度、服务发现和统一资源控制时,Kubernetes 更有价值,但同时增加控制面、网络与存储的维护工作。本地使用 kind,生产可选择托管集群或自行维护 kubeadm 集群。kind 的节点是同一宿主上的容器,适合应用行为测试,无法提供物理机级故障隔离。
以下操作在 Linux x86-64 的 Bash 终端执行。宿主普通用户需要使用 Docker 的权限,这类权限本身接近宿主管理员权限;kind 会创建特权节点容器。业务容器单独使用非 root 身份。给 Docker 环境预留约 4 CPU、8 GiB 内存和足够的镜像磁盘空间,这是两节点小实验的起步配置,实际资源占用以本机为准。
下载 Kubernetes 实验包,解压后进入含有 kind.yaml 的目录。安装的二进制只放到当前目录:
mkdir -p bin
curl -q --fail-with-body -L \
https://kind.sigs.k8s.io/dl/v0.33.0/kind-linux-amd64 -o bin/kind
curl -q --fail-with-body -L \
https://github.com/kubernetes-sigs/kind/releases/download/v0.33.0/kind-linux-amd64.sha256sum \
-o kind.sha256sum
printf '%s bin/kind\n' "$(awk '{print $1}' kind.sha256sum)" | sha256sum -c -
curl -q --fail-with-body -L \
https://dl.k8s.io/release/v1.36.4/bin/linux/amd64/kubectl -o bin/kubectl
curl -q --fail-with-body -L \
https://dl.k8s.io/release/v1.36.4/bin/linux/amd64/kubectl.sha256 -o kubectl.sha256
printf '%s bin/kubectl\n' "$(tr -d '\r\n' < kubectl.sha256)" | sha256sum -c -
chmod +x bin/kind bin/kubectl
export PATH="$PWD/bin:$PATH"
kind version
kubectl version --client
docker info两个校验都应显示 OK,任一下载或校验失败就停止,不执行文件。ARM64 使用对应官方资产,不能只改可执行文件名。下载方式见 kind 起步说明和 kubectl Linux 安装。
实验固定 Kubernetes 1.36.4;kind 节点使用该版本发布清单中的摘要。选择集群补丁和升级线时再检查 Kubernetes 发行与维护信息,不要把演示版本当作永久推荐。
kind.yaml:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker创建前用 kind get clusters 确认没有名为 ops7 的已有集群;若有,换一个新名字并同步后续 context,不能删除来路不明的集群。
export KUBECONFIG="$PWD/kubeconfig"
kind create cluster --name ops7 --config kind.yaml \
--image kindest/node:v1.36.4@sha256:099e049362a1526b2db71494e1947aae99bd16290d7c895f2b7ea312e3cbfaed \
--kubeconfig "$KUBECONFIG" --wait 180s
chmod 600 "$KUBECONFIG"
kubectl --context kind-ops7 get nodes
kubectl --context kind-ops7 get pods -n kube-system预期两个节点为 Ready,CoreDNS 可运行。kubeconfig 包含认证材料,不加入 Git,也不贴到问题截图。显式 --kubeconfig 使 kind 只操作该文件,不合并日常集群配置。后续终端定义一个仅供实验使用的短函数:
k() { kubectl --kubeconfig "$KUBECONFIG" --context kind-ops7 "$@"; }
k config current-context输出应为 kind-ops7。每个新终端都需重新设置本实验的 KUBECONFIG 和函数,不能凭提示符猜当前集群。
用 Deployment 运行两个副本
Pod 把紧密协作的容器放在同一个调度单元中,共享网络地址和可声明的卷。Pod 重建会获得新身份,IP 也可能变化。长期运行的无状态应用用 Deployment 管理,避免直接创建一个无控制器的 Pod 后把恢复寄托于人工操作。
实验包 app.yaml 包含 Namespace、ConfigMap、Deployment 和 Service。Deployment 的核心关系如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: k8s-lab
spec:
replicas: 2
selector:
matchLabels: {app: web}
template:
metadata:
labels: {app: web}
spec:
containers:
- name: web
image: nginx:1.30.4-alpine
ports:
- {name: http, containerPort: 8080}apiVersion 与 kind 选择 API 和资源类型,metadata 标识对象,spec 写期望配置。查询运行中的对象时,还会看到控制器维护的 status,其中描述当前副本、条件和失败原因。修改 spec.replicas 是提出扩缩容要求,只有后续 status 和实际 Pod 都符合要求,才完成了这次调整。
selector 必须匹配 Pod 模板标签,控制器靠它识别副本。containerPort 是端口声明,应用仍须实际监听 0.0.0.0:8080。完整清单还配置了探针、只读根文件系统和资源限制,不要只复制上面片段替代整个实验文件。
镜像先下载到 Docker,再导出本机架构的归档并加载进 kind 各节点的运行时。下面的 --platform 需要支持该选项的 Docker 版本(API 1.48+):
docker pull nginx:1.30.4-alpine
docker image save --platform linux/amd64 -o nginx-amd64.tar nginx:1.30.4-alpine
kind load image-archive nginx-amd64.tar --name ops7
k apply -f app.yaml
k -n k8s-lab rollout status deployment/web --timeout=120s
k -n k8s-lab get deployment,replicaset,pod -l app=web
k -n k8s-lab get deployment webapp=web 标签在 Pod 和 ReplicaSet 上,Deployment 自身未加该标签,因此最后单独查询它。预期 Deployment 的 Ready 为 2/2。若出现 ImagePullBackOff,先确认加载成功、清单镜像名一致以及 imagePullPolicy: IfNotPresent,再查 Pod 事件。
直接使用 kind load docker-image 在 Docker containerd 镜像存储环境可能遇到 content digest ... not found,因为导出内容缺少其他架构的数据。上述单平台归档是 kind 已知问题给出的定向处理方法,不需要切换宿主镜像存储。ARM64 节点改成对应架构,旧 Docker 无此导出选项时先按平台升级工具或从完整的受控镜像归档导入。
完整镜像配置直接执行 Nginx,UID/GID 为 101,关闭提权并移除 capabilities;配置从只读 ConfigMap 挂载,pid 和临时文件放进独立 emptyDir。这避免为写日志或临时文件开放整个根目录。
k -n k8s-lab exec deployment/web -- id
k -n k8s-lab exec deployment/web -- nginx -t
k -n k8s-lab logs deployment/web --tail=20身份输出应含 uid=101,配置测试成功,访问日志输出到标准输出。容器运行身份与 kind 节点容器的特权身份是两层不同的权限。
从集群 DNS 访问 Service
Service 给一组可变化的 Pod 提供稳定的发现入口。它通过标签选择后端,并把自己的端口映射到容器端口:
apiVersion: v1
kind: Service
metadata:
name: web
namespace: k8s-lab
spec:
selector: {app: web}
ports:
- {name: http, port: 80, targetPort: http}这里 80 是 Service 端口,http 是 Pod 中指向 8080 的命名端口。名称解析使用集群 DNS:
k -n k8s-lab get service web
k -n k8s-lab get endpointslice -l kubernetes.io/service-name=web
k -n k8s-lab exec deployment/web -- \
wget -qO- http://web.k8s-lab.svc.cluster.local/预期响应 k8s-v1。这次请求由 Pod 内发出,使用集群 DNS 和 Service 转发。默认集群域为 cluster.local;自建集群若使用其他域,按实际配置替换。Service 与 DNS 细节见 Service。
本机临时检查应用可以建立端口转发,在一个终端保持运行:
k -n k8s-lab port-forward --address 127.0.0.1 service/web 18080:80另一个本机终端执行:
curl -q --noproxy '*' --fail-with-body http://127.0.0.1:18080/预期仍为 k8s-v1。port-forward 会选取一个 Pod,经 API 通道转发到它,主要用于调试;它没有走正常 Service 虚拟 IP 转发,也没有验证入口负载均衡。结束时在转发终端按 Ctrl+C。
应用的网络、配置和数据怎样组织
工作负载类型决定恢复方式
Deployment 的副本可以互相替代。数据库节点、每台机器上的采集器和一次性数据任务,则需要不同的控制方式:
| 对象 | 适合的运行模式 | 应用仍需解决什么 |
|---|---|---|
| Deployment | 可替换的 HTTP、RPC、消费服务副本 | 会话外置、重复消费、优雅关闭 |
| StatefulSet | 稳定序号、网络身份、每副本独立存储 | 数据复制、选主、备份和应用级一致性 |
| DaemonSet | 每个符合条件的节点部署采集器或网络组件 | 节点权限、资源开销和升级兼容 |
| Job | 执行到完成的批处理 | 幂等、重试、副作用与结束清理 |
| CronJob | 定时创建 Job | 时区、错过调度、并发与重复执行 |
StatefulSet 会让 db-0 重建后继续使用相应身份和卷,但把三个数据库容器放进去不会自动产生复制协议。Job 的 Pod 也可能重试;写库、发消息等操作需要自己的去重键。CronJob 可以设置 concurrencyPolicy: Forbid 避免同一 CronJob 的任务重叠,但任务本身仍应容忍重复调度。各类控制器的完整字段见 Workloads。
Pod 内的普通 init container 顺序执行且须成功退出,适合准备文件或检查局部初始化;长期辅助进程可用 sidecar。不要把外部数据库短暂不可达写成永远失败的初始化或存活判断,使所有应用副本同时失去服务能力。
稳定地址、外部入口与网络策略
常用 Service 类型的区别:
| 类型 | 地址和用途 |
|---|---|
| ClusterIP | 集群内部虚拟地址;默认类型 |
| NodePort | 在节点端口提供入口;是否能从外部到达还受路由和防火墙限制 |
| LoadBalancer | 请求实现方提供外部负载均衡;本地集群没有对应实现时地址可能一直 Pending |
| ExternalName | DNS CNAME 别名,不建立代理转发 |
| Headless | clusterIP: None,用于直接发现后端地址,常配 StatefulSet |
从集群外按域名访问 HTTP 服务,还需要入口控制器。Ingress 是路由声明,控制器读取它并配置实际代理。Gateway API 把基础设施与应用路由分为 GatewayClass、Gateway、HTTPRoute 等对象,适合更明确地分配入口维护与路由配置权限。API 对象创建后,必须检查实现方报告的 Accepted、Programmed、ResolvedRefs 等状态,并从真实入口发送请求。Gateway API列出了实现与能力范围。
社区 Ingress NGINX 已退役,不应作为新集群的默认安装路线。这一变化针对该控制器项目,不代表 Kubernetes Ingress API 或 Nginx 软件本身同时退役;既有集群应按官方退役说明选择维护中的替代实现,迁移时比较注解、超时、路径改写、TLS 和来源地址语义。
入口请求的关系可以拆成:
域名 DNS → 外部负载均衡或入口地址 → Gateway/Ingress 实现
↓
Service 对应的就绪后端
↓
Pod 应用端口TLS 可以在入口终止,也可以继续加密到应用,证书和应用信任配置分别处理。入口 404 常要看 host/path 规则;503 要继续看是否有可用后端;连接超时则先定位哪个 TCP 跳点失败。域名和证书分别参见域名与 DNS、SSL / 证书。
Namespace 用于对象命名、权限和配额管理,不会自动封锁跨 namespace 通信。NetworkPolicy 则选择 Pod,声明允许的 ingress/egress;策略叠加按允许规则并集计算,且依赖网络插件实现。kind 默认网络不能作为策略生效的验证环境。采用支持策略的 CNI 后,应分别从允许和禁止的来源请求目标,并检查 DNS、监控、入口健康检查的必要通路。NetworkPolicy 语义还规定了来源 selector 与 IP 条件的组合方式。
配置进入进程后何时生效
ConfigMap 保存非敏感配置,Secret 保存密码、令牌、证书等敏感材料。它们都可以经环境变量或卷交给应用,但生效过程不同:
| 注入方式 | 修改对象后发生什么 |
|---|---|
| 环境变量 | 已启动进程继续使用旧值,需要重建 Pod |
| 普通 ConfigMap/Secret 卷 | kubelet 最终更新投影文件,应用还需重新读取 |
subPath 挂载 | 不接收后续投影更新,需要重建 Pod |
| 外部配置服务 | 取决于客户端刷新、缓存和失败策略 |
实验使用 subPath 挂载 nginx.conf。修改 ConfigMap 后执行 rollout restart,新 Pod 才会加载新文件。即使采用普通投影卷,Nginx 也需要配置检查与 reload;文件变化不会自动让所有应用重新解释配置。ConfigMap 文档说明了这些更新条件。
Spring Boot 服务通常将环境名称、数据库地址与 Secret 引用放到 Pod 模板中,以下是容器配置片段:
env:
- name: SPRING_PROFILES_ACTIVE
value: test
- name: SPRING_DATASOURCE_URL
value: jdbc:postgresql://db:5432/app
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: app-db
key: passwordapp-db 必须存在于同一 namespace。地址、驱动、账户权限与连接池配置仍由应用部署负责,连接方式见 Spring Boot 部署。用文件管理多项 Spring 配置时,可以把目录挂载到 /config/ 并配置 Spring 的额外配置位置;不要在镜像内固化生产密码。
Secret 中的 base64 只是编码。限制读取 Secret 的 RBAC,开启并验证集群存储加密,控制 CI 渲染日志;有权创建 Pod 的身份往往也能通过挂载读取该 namespace 的 Secret,需要共同设计权限。Secret 文档说明了存储和访问风险。
卷的生命周期与一次重建实验
容器可写层随容器重建丢失,emptyDir 随 Pod 生命周期存在,同一 Pod 内容器重启可保留,Pod 删除后被清理。需要跨 Pod 保存数据时,使用 PVC 申请存储:
Pod 的 volume 引用 PVC
↓
PVC:容量、访问模式、StorageClass
↓ 绑定
PV:集群中的存储资源
↓
CSI / 存储实现:云盘、网络存储、本地目录……StorageClass 指定动态供应的驱动和参数。ReadWriteOnce 通常限制为单节点读写,并非天然单 Pod 互斥;需要单 Pod 访问时检查驱动与 ReadWriteOncePod 支持。PV 的 Delete 回收策略可能随 PVC 删除而删除底层数据,Retain 则留下需要管理员处理的存储。Persistent Volumes给出了绑定、访问和回收规则。
kind 提供的默认本地存储适合下面的重建实验,不能抵御宿主或集群删除。先检查供应器:
k get storageclass
k apply -f storage.yaml
k -n k8s-lab wait --for=condition=Ready pod/writer --timeout=120s
k -n k8s-lab exec writer -- sh -c 'printf "saved-value\n" > /data/value'
k -n k8s-lab exec writer -- cat /data/value
k -n k8s-lab delete pod writer --wait=true
k apply -f storage.yaml
k -n k8s-lab wait --for=condition=Ready pod/writer --timeout=120s
k -n k8s-lab exec writer -- cat /data/value两次都应读到 saved-value。中间只删除 writer Pod,PVC 保留,因此新的 Pod 挂回同一数据。若 PVC 为 Pending,describe pvc scratch 查看是没有默认 StorageClass、容量不足还是等待首个消费者;使用 WaitForFirstConsumer 的存储在 Pod 参与调度后才绑定是正常流程。
数据库生产备份仍需数据库自己的快照、日志或一致性机制;卷存在只能说明文件仍在,不能据此判断数据库事务恢复完成。
调度、健康检查和滚动更新
requests 决定放得下,limits 约束运行消耗
Scheduler 按资源请求和节点可分配量选择节点,不按照监控上“此刻 CPU 很闲”直接放置。实验请求 50m CPU / 32Mi,上限 500m / 128Mi,仅适用于小型 Nginx 演示。Java 服务应根据堆、元空间、线程栈、直接内存和其他 native 内存一起估算容器预算。
CPU 超过限制通常被节流;内存压力可能触发 OOM 终止。requests 过大会让 Pod Pending,过小又会使调度器低估承载压力。需要同时看请求总额、运行用量与限额事件,不能只调一个数字。容器资源管理描述了调度与运行时的不同作用。
k describe node ops7-worker
k -n k8s-lab get pods -o wide
k top nodes
k -n k8s-lab top podstop 依赖 Metrics Server;裸 kind 未安装时出现 Metrics API 不可用,应先接入受支持的指标组件,不把它当成节点宕机。HPA 再基于指标调整副本,例如 CPU 利用率目标需要合理的 CPU requests。增加副本后仍可能缺节点,节点自动扩容又由单独组件执行。HPA说明了指标、控制周期和稳定窗口。
硬性 nodeSelector/亲和性要求缩小候选节点;taint 排斥不匹配的 Pod,toleration 只提供容忍能力,不保证调度到该节点。跨节点、跨可用区分散副本可使用 topologySpreadConstraints,但硬性分散在容量不足时也可能阻塞发布。应给滚动新增副本和节点维护留出容量,而不是把正常运行资源用满。
startup、readiness、liveness 分别影响什么
| 探针 | 失败后的主要动作 | 适合判断 |
|---|---|---|
| startup | 超过阈值后重启容器;成功前不执行其 readiness/liveness | 慢启动是否完成 |
| readiness | 将 Pod 标记为未就绪,正常服务端点停止接收新流量 | 是否适合接收当前请求 |
| liveness | 达到阈值后重启容器 | 进程是否进入无法自行恢复的状态 |
演示中三者分别请求 /live、/ready、/live。startup 的 2s × 30 给启动留下约一分钟容忍期。Spring Boot 可以采用 Actuator 的 liveness/readiness health group,并确保探针访问的端口和业务端口故障关系明确。不要让共享数据库暂时失败触发所有副本的 liveness 重启。
Readiness 状态传播有延迟;已经建立的连接也不会因 Ready 改变而自动完成业务处理。优雅关闭还要让应用停止接新任务、完成在途请求,并给 terminationGracePeriodSeconds 留足时间。探针配置与失败阈值见探针配置手册;端点的 ready/serving/terminating 条件见 EndpointSlice。
坏 readiness 怎样让发布停在中间
实验 Deployment 使用:
replicas: 2
progressDeadlineSeconds: 60
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0更新 Pod 模板后,控制器为新模板建立 ReplicaSet。它可以先增加一个副本,只有新副本可用后才逐步减少旧副本。终止中的 Pod 还可能短暂占用资源,因此容量不能简单按 replicas + maxSurge 作为绝对峰值。
只在实验 namespace 注入错误探针,其他配置保持不变:
k -n k8s-lab patch deployment web --type=strategic --patch-file bad-readiness.yaml
k -n k8s-lab rollout status deployment/web --timeout=90s
k -n k8s-lab get rs,pods -l app=web
k -n k8s-lab describe deployment web
k -n k8s-lab get endpointslice -l kubernetes.io/service-name=web -o yaml预期 rollout 非零退出,错误为超过进度期限或等待超时;新 Pod 的 /missing 返回 404,Ready 为 false。两个旧 Pod 仍可用,新 ReplicaSet 有一个无法就绪的副本。这个状态解释了 maxUnavailable: 0 的作用,也说明 Deployment 遇到进度超时后需要操作者处理,它不会自行选择历史版本自动回滚。Deployment 更新规则定义了这些条件。
先检查新 Pod 的 describe 和日志确认确实是 readiness 404,而非拉取失败。再查看历史,恢复错误更新之前的模板:
k -n k8s-lab rollout history deployment/web
k -n k8s-lab rollout undo deployment/web
k -n k8s-lab rollout status deployment/web --timeout=120s
k -n k8s-lab exec deployment/web -- wget -qO- http://web/恢复后应再次返回 k8s-v1,并回到两个 Ready 副本。实际多次变更时用 --to-revision=<已确认的编号> 明确目标,不盲选“上一个”。
Deployment revision 保存的是 Pod 模板,不包含所有外部 ConfigMap、Secret、数据库状态,也不负责恢复 Service 改动。应用镜像、配置版本与数据兼容关系要一起记录。多个清单需要打包成受控的应用版本时可使用 Helm。
最小权限与维护中断
应用无需 Kubernetes API 时设置 automountServiceAccountToken: false。需要 API 时给独立 ServiceAccount 绑定最小 Role,明确资源和动词;发布者和应用运行身份分别管理。namespace 级 Role 不应为部署一个 HTTP 服务而拥有集群管理员权限。
k auth can-i create deployments -n k8s-lab
k auth can-i get secrets -n k8s-lab
k auth can-i create clusterroleskind 默认实验身份权限较大,上述命令用于看清当前权限,并不建立隔离。生产环境用平台发放的受限身份重新验证。Pod Security Admission 可限制特权、宿主挂载等配置,RBAC 管 API 操作,NetworkPolicy 管网络访问,三者互相补充。RBAC 实践解释了可创建工作负载与可读取凭据之间的关联。
PDB 约束通过 Eviction API 发起的自愿中断,例如 drain。它不会阻止节点突然失效,也不替代 Deployment 自己的滚动策略。仅一个可调度 worker 的实验集群无法验证维护时的多节点承载能力,不能强行忽略 PDB 来制造“无损维护”。
集群运行维护与分层故障处理
自建集群需要哪些真实准备
托管集群由服务商承担约定范围内的控制面运行,自建则需要维护 API 高可用、etcd、证书、节点运行时、CNI、CSI 和升级。生产控制面通常配置多个故障域中的成员和稳定 API 入口;etcd 多数派丢失会影响对象变更,已有容器可能继续运行,但重新调度、扩缩容和配置更新将受影响。
kubeadm 初始化不会替你安装所有依赖。Ubuntu/Debian 从受信的 apt 仓库安装运行时和 Kubernetes 软件包,Rocky/AlmaLinux/RHEL 从受信的 dnf 仓库安装;Kubernetes 包仓库按 minor 分开,补丁版本在变更时明确选择并锁定。完整发行版步骤分别查安装 kubeadm和容器运行时,先在官方页面切换到目标集群版本,不要把 containerd 1.x 的插件键直接抄到 2.x。
在已经分配给集群的 Linux 节点上,管理员先只读确认:
uname -m
free -h
swapon --show
timedatectl status
ip route
sysctl net.ipv4.ip_forward
containerd --version
sudo systemctl status containerd --no-pager
sudo crictl --runtime-endpoint unix:///run/containerd/containerd.sock info
kubeadm version
kubelet --version命令不存在先完成安装;CRI 无响应先修服务或 socket,不继续 init。内核模块、转发与防火墙按 CNI 的网络模式配置;kubelet 和运行时使用兼容的 cgroup 驱动。swap 采用经过设计的策略,新手基线通常禁用,不能在已有业务主机上随意关闭或修改 fstab。主机网段、Pod CIDR、Service CIDR、VPN 路由应无冲突。
kubeadm config print init-defaults 可生成与本机版本匹配的模板。管理员编辑其中的节点地址、稳定控制面入口、CRI socket、Kubernetes 版本、镜像仓库与网络,再进行校验和镜像检查:
kubeadm config print init-defaults > kubeadm-config.yaml
# 在编辑器中按节点规划修改后执行
kubeadm config validate --config kubeadm-config.yaml
kubeadm config images list --config kubeadm-config.yaml
sudo kubeadm config images pull --config kubeadm-config.yaml首次初始化、受控分发管理员配置、安装兼容的 CNI、生成短期 join 凭据、加入 worker、验证跨节点网络,是创建 kubeadm 集群中的连续步骤。安装 CNI 时选择明确版本及其支持的 Kubernetes 线,审阅它创建的 RBAC/CRD 和 Pod/Service 网段,使用保存的清单或 Chart;不能仅执行一个来源浮动的远程 YAML。
节点 Ready 后,还要分别验证不同节点间 Pod 请求、Service/DNS、入口、存储挂载和策略允许/拒绝。缺网络或存储插件时,应用 YAML 的成功创建会掩盖后续不可运行状态。
内网与离线集群怎样准备镜像
需要准备的不只有业务镜像:Kubernetes 控制面、pause、CoreDNS、etcd、CNI、CSI、入口控制器和监控组件都可能拉取独立镜像。联网受控环境先固定版本和摘要,列出完整依赖,再同步到内网仓库或导出归档;节点需要信任仓库 CA 并获得正确凭据。
kind 使用 kind load docker-image 或 kind load image-archive。普通 containerd 节点必须导入实际 CRI 使用的存储和 namespace,不能在管理机执行 docker load 后就期待所有 worker 看见镜像。离线包还要包含架构匹配的软件包、Chart、CRD、配置和校验文件,见离线部署。
HTTP 代理、镜像仓库代理与 Pod 网络代理分别配置;NO_PROXY 要包含 API 地址及需要直连的域名/IP 范围,并按客户端实际支持格式确认。遇到证书错误先修 CA 信任,避免把禁用 TLS 校验写成长期方案。
节点升级、备份和恢复次序
升级前核对 API 弃用、kubectl/kubelet/control-plane 版本偏差、CNI/CSI/入口兼容,以及现有资源是否仍能提交。kubeadm 按官方支持顺序逐 minor 升级,先控制面,再逐节点 kubelet;不是全机同时升级软件包。kubeadm 升级手册给出具体版本步骤。
具备其他可用节点、业务副本和维护授权后,才对选定 worker 执行以下模板。NODE 必须由节点清单确定:
kubectl --context "$CTX" get node "$NODE"
kubectl --context "$CTX" cordon "$NODE"
kubectl --context "$CTX" drain "$NODE" --ignore-daemonsets --timeout=10mCTX 是已核实的目标集群 context。drain 被 PDB、未管理 Pod 或本地数据阻止时,先确认业务和数据迁移方案,不直接加 --force 或 --delete-emptydir-data。完成该节点的运行时/kubelet 升级并检查 Ready、组件日志和业务后再执行 uncordon,然后处理下一节点。安全 drain说明了中断与清理行为。
恢复材料分开保存:API 数据的 etcd 快照、PKI/加密配置和集群配置、应用的外部数据库与卷备份、镜像和声明文件。etcd 快照不包含 PVC 中的业务文件;只保存 Git YAML 也无法重建数据库。快照必须加密并限制访问,因为其中可能含 Secret。按etcd 运维恢复时,应在隔离环境先恢复控制面,再恢复依赖和应用数据,最后恢复入口并验证实际业务。不要把 kubectl get all 当作完整集群备份,它不会列出全部资源种类。
从第一条异常找到下一步
在目标 namespace 内先取摘要,不立即删除异常 Pod,否则可能失去上一轮容器日志:
k -n k8s-lab get pods -o wide
k -n k8s-lab get events --sort-by=.metadata.creationTimestamp
k -n k8s-lab describe pod "$POD"
k -n k8s-lab logs "$POD" -c web --tail=100
k -n k8s-lab logs "$POD" -c web --previous --tail=100POD 从前一条 Pod 清单选择。--previous 读取同一 Pod 中上一轮终止容器日志,没有上一轮时会报错,这是日志不存在而非新的应用故障。
| 现象 | 第一证据 | 判断与处理 |
|---|---|---|
| Pending / FailedScheduling | Pod Events、节点 Allocatable、PVC | 请求资源、亲和性、taint 或存储阻塞;修对应约束后观察调度 |
| ImagePullBackOff | Events 中具体拉取错误 | 区分不存在、无权限、DNS、CA;修镜像名/凭据/节点网络后等待重试 |
| CrashLoopBackOff | previous 日志、lastState、退出码 | 修启动参数、配置或程序错误,再更新模板;反复重启不能修程序 |
| OOMKilled | lastState、内存曲线与限制 | 先控制负载或恢复稳定版本,再分析泄漏/内存预算 |
| Running 但不 Ready | probe Events、探针响应、应用日志 | 区分错误路径、超时、真实依赖失败;修复后检查端点恢复 |
| Service 不通 | selector、EndpointSlice、端口、Pod 内请求 | 从就绪后端向 DNS/数据面扩展,不用 port-forward 成功替代 Service 验证 |
| Evicted / NodeNotReady | Node Conditions、节点磁盘和 kubelet 日志 | 处理磁盘、内存、inode 或节点连通,再恢复调度 |
EndpointSlice 为空先查 selector;有地址但 ready: false 先查 readiness;端点正确而只有跨节点失败,再查 CNI 路由、MTU、防火墙与策略。生产监控同时保留节点资源、容器重启、调度失败、不可用副本、API 延迟、etcd 存储以及应用延迟和错误率。日志要集中采集并设置保留期,节点轮转日志不是长期故障档案。
结束本地实验
如需继续 Helm 实验,保留本地 kind 集群和镜像。完成后先关闭 port-forward,确认这里只存在可丢弃实验数据:
k -n k8s-lab get deployment,pod,pvc
k get nodes
kind delete cluster --name ops7 --kubeconfig "$KUBECONFIG"
kind get clusters该命令只删除 ops7 本地集群,但同时会删除它的节点容器及本地卷数据。确认集群不再列出;临时 kubeconfig 含认证材料,按本机文件管理方式删除这一明确文件,不清理其他集群配置。
权威资料与规范地址
客户端、集群与对象
- Kubernetes 发行信息:https://kubernetes.io/releases/
- 组件:https://kubernetes.io/docs/concepts/overview/components/
- kind 起步:https://kind.sigs.k8s.io/docs/user/quick-start/
- kind 已知问题:https://kind.sigs.k8s.io/docs/user/known-issues/
- kind 0.33.0 节点镜像:https://github.com/kubernetes-sigs/kind/releases/tag/v0.33.0
- kubectl Linux:https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
- 工作负载:https://kubernetes.io/docs/concepts/workloads/
- Deployment:https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
网络、配置和存储
- Service:https://kubernetes.io/docs/concepts/services-networking/service/
- EndpointSlice:https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/
- Gateway API:https://gateway-api.sigs.k8s.io/
- Ingress NGINX 退役:https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/
- NetworkPolicy:https://kubernetes.io/docs/concepts/services-networking/network-policies/
- ConfigMap:https://kubernetes.io/docs/concepts/configuration/configmap/
- Secret:https://kubernetes.io/docs/concepts/configuration/secret/
- PersistentVolume:https://kubernetes.io/docs/concepts/storage/persistent-volumes/
资源、权限与运行维护
- 容器资源:https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
- HPA:https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/
- 探针:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
- RBAC:https://kubernetes.io/docs/concepts/security/rbac-good-practices/
- 安装 kubeadm:https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
- 容器运行时:https://kubernetes.io/docs/setup/production-environment/container-runtimes/
- 创建集群:https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
- 升级集群:https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/
- drain:https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/
- etcd:https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/
