云原生

Kubernetes 上线前的 20 项检查清单

2026-07-23 #发布治理#Kubernetes#运维#生产实践#SRE

“清单已勾选”不等于“系统可上线”。一份有效的 Kubernetes 上线清单,必须同时写清检查对象、执行方法、通过标准、失败动作和证据保存位置。

本文以一个运行在 prod 命名空间、由 Deployment 管理的无状态 HTTP API 为例。示例变量需要替换成实际值,并先在非生产环境验证。数据库迁移、消息消费、定时任务和有状态工作负载还需要增加各自的专项检查。

使用方式:先建立发布上下文

开始前固定本次发布对象,避免后续命令误查其他环境:

1
2
3
export NS=prod
export APP=order-api
export MANIFEST_DIR=deploy/overlays/prod

在变更单或流水线中记录:

字段 示例 作用
需求或缺陷编号 REQ-1842 串联代码、镜像和发布记录
Git 提交 完整 commit SHA 确认构建源代码
镜像 仓库地址加 digest 保证实际运行内容不可变
目标环境 集群、命名空间、工作负载 防止操作错环境
观察窗口 发布后 30 分钟 明确何时才能宣布完成
回滚目标 上一镜像 digest 避免故障时临时寻找版本
停止条件 错误率、延迟、不可用副本阈值 让回滚决策可执行

下面 20 项不是平均用力。核心交易链路、外部入口和包含数据变更的服务,应设置更严格的门槛。

一、变更、制品与配置

1. 变更范围和依赖已经说明

上线前必须知道“改了什么”,也要知道“没有改什么”。至少列出 API、配置、数据库、消息格式、定时任务、Ingress、Service 和下游依赖的变化。

通过标准: 需求、提交、镜像和部署对象可以相互追溯;高风险变化有负责人、验证步骤和停止条件;共享基础设施明确归属,不在应用发布中顺手重建。

1
2
git show --stat --oneline HEAD
git diff --name-status <last-release-tag>...HEAD

如果本次只更新应用镜像,就不要同时修改平台团队维护的 Ingress、证书或集群级策略。发布范围越小,失败定位和回滚越可靠。

2. 镜像使用不可变 digest

latest、stable 或普通版本标签都可能被重新指向。Deployment 应保存镜像 digest,标签只用于方便人阅读。

1
image: registry.example.com/team/order-api@sha256:<64-hex-digest>

通过标准: digest 对应本次 Git 提交;镜像已经完成漏洞、恶意软件和敏感信息扫描;基础镜像来源可追溯;生产 Deployment 不依赖可变标签。

1
2
kubectl -n "$NS" get deployment "$APP" \
-o jsonpath='{.spec.template.spec.containers[*].image}{"\n"}'

镜像扫描结果要保留在制品或流水线记录中。发现高危漏洞时,应根据是否可利用、是否暴露和是否存在补丁作出可审计决策,而不是只看漏洞数量。

3. 清单通过服务端校验,并审阅真实差异

本地 YAML 能解析,不代表 API Server 接受,也不代表 Admission Policy 会放行。先做服务端 dry-run,再查看差异:

1
2
kubectl apply --server-side --dry-run=server -f "$MANIFEST_DIR"
kubectl diff -f "$MANIFEST_DIR"

kubectl diff 在“存在差异”时通常返回退出码 1,流水线需要区分“有预期差异”和“命令执行失败”:

1
2
3
4
5
6
7
8
set +e
kubectl diff -f "$MANIFEST_DIR"
diff_rc=$?
set -e

if [ "$diff_rc" -gt 1 ]; then
exit "$diff_rc"
fi

通过标准: dry-run 成功;diff 只有本次批准的字段;没有意外删除对象、修改 selector、替换 Service ClusterIP 或覆盖平台托管资源。

4. 配置与 Secret 已校验

检查必填键、值格式、环境差异和引用对象是否存在。Secret 的 base64 只是编码,不是加密;不要把明文或解码结果打印进 CI 日志。

1
2
3
4
kubectl -n "$NS" get configmap "${APP}-config" -o name
kubectl -n "$NS" get secret "${APP}-secret" -o name
kubectl -n "$NS" get deployment "$APP" \
-o jsonpath='{.spec.template.spec.containers[*].envFrom}{"\n"}'

通过标准: 配置有 schema 或启动校验;Secret 来自受控系统并有轮换方案;Pod 只读取所需 Secret;配置变化能触发受控滚动更新,例如使用带内容哈希的 ConfigMap 名称。

不要在同一个 ConfigMap 名称下静默替换关键配置,又期待所有 Pod 同时生效。明确配置版本,才能复现和回滚。

5. 数据库和消息契约向前、向后兼容

Deployment 回滚只能恢复应用镜像,不能自动撤销已经执行的数据库迁移或已发布的消息。推荐使用“扩展—迁移—收缩”:

  1. 先增加新字段、表或兼容消费逻辑;
  2. 部署能同时理解新旧结构的应用;
  3. 回填数据并验证;
  4. 所有旧版本退出观察窗口后,再删除旧字段。

通过标准: 新旧版本可在滚动期间共存;迁移可重复执行或能识别已完成状态;大表变更评估锁等待和耗时;消息消费者能处理版本差异;失败后有恢复而不只是 rollout undo。

二、资源、容量与调度

6. requests 与 limits 来自观测数据

调度器主要依据 requests 放置 Pod;内存超过 limit 可能触发 OOM,CPU limit 可能带来节流。不要机械套用固定比例。

1
2
3
kubectl -n "$NS" get deployment "$APP" \
-o jsonpath='{range .spec.template.spec.containers[*]}{.name}{" requests="}{.resources.requests}{" limits="}{.resources.limits}{"\n"}{end}'
kubectl -n "$NS" top pod -l "app=$APP" --containers

通过标准: 每个业务容器都有 requests;内存 limit 留有峰值和运行时开销;CPU 策略经过压测;命名空间 ResourceQuota 和节点可分配资源能容纳滚动发布时的额外副本。

建议同时看过去 7~30 天的 P50、P95、峰值和 OOM/节流指标。只看某一时刻的 kubectl top 容易低估批处理、促销和缓存重建峰值。

7. 副本数和扩缩容策略覆盖故障场景

需要高可用的服务通常不能只运行一个副本。使用 HPA 时,目标利用率依赖资源 requests,缺失 requests 会让部分指标无法正确计算。

1
2
3
kubectl -n "$NS" get deployment "$APP" \
-o custom-columns='NAME:.metadata.name,DESIRED:.spec.replicas,READY:.status.readyReplicas'
kubectl -n "$NS" get hpa "$APP" 2>/dev/null || true

通过标准: 单个 Pod 或单个节点失效后仍能维持最低容量;HPA 的最小、最大副本和稳定窗口有压测依据;节点池能在扩容时间内提供资源;下游连接池不会因副本增加被打满。

8. 副本跨故障域分布

多个副本落在同一节点,不能抵御节点故障。优先使用拓扑分布约束表达节点或可用区分布:

1
2
3
4
5
6
7
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: order-api
1
2
kubectl -n "$NS" get pod -l "app=$APP" \
-o custom-columns='POD:.metadata.name,NODE:.spec.nodeName,READY:.status.containerStatuses[*].ready'

通过标准: labelSelector 与 Pod 标签一致;故障域标签在节点上真实存在;约束不会在节点不足时让全部新 Pod Pending;关键副本不会集中到单节点。

9. PDB 与维护目标一致

PodDisruptionBudget 约束通过 Eviction API 发起的自愿中断,例如部分节点维护;它不能阻止节点宕机,也不直接控制 Deployment 的滚动更新。

1
2
3
4
5
6
7
8
9
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: order-api
spec:
minAvailable: 2
selector:
matchLabels:
app: order-api

通过标准: PDB selector 能选中目标 Pod;副本数允许满足 minAvailable;节点排空不会永久卡住;维护手册说明何时允许临时调整 PDB,且调整需要审批。

1
2
kubectl -n "$NS" get pdb "$APP"
kubectl -n "$NS" describe pdb "$APP"

三、生命周期与流量

10. startup、readiness、liveness 语义分离

  • startupProbe 保护启动较慢的应用,成功前不会执行其他两类探针;
  • readinessProbe 决定 Pod 是否进入 Service 后端;
  • livenessProbe 只处理进程无法自行恢复的状态,失败会触发重启。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
startupProbe:
httpGet:
path: /startupz
port: http
periodSeconds: 2
failureThreshold: 30
readinessProbe:
httpGet:
path: /readyz
port: http
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 3
livenessProbe:
httpGet:
path: /livez
port: http
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3

通过标准: 最坏启动时间小于 startup 窗口; readiness 能快速摘除不可服务实例;liveness 不依赖易抖动的外部数据库,不会在高负载时制造级联重启;探针端点轻量且不记录噪声日志。

11. SIGTERM 和在途请求可以优雅结束

Pod 终止时,应用应停止接收新请求、等待在途请求完成、提交或终止后台任务,然后在宽限期内退出。

1
terminationGracePeriodSeconds: 30

通过标准: 应用正确处理 SIGTERM;关停超时大于常见请求耗时;消息消费者先停止拉取再处理已获取消息;长连接有关闭策略;强制终止不会留下重复写入或悬挂锁。

可以在非生产环境执行一次真实终止演练,并同时观察客户端错误、Pod Events 和应用日志。仅看到 Pod 最终消失,不足以证明优雅退出成功。

12. 滚动策略与集群余量匹配

1
2
3
4
5
6
7
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
minReadySeconds: 10
progressDeadlineSeconds: 600

通过标准: maxUnavailable 满足可用性目标;maxSurge 所需额外 CPU、内存、IP 和下游连接均有余量;minReadySeconds 能挡住“刚 Ready 就失败”的实例;进度超时符合真实启动时间。

对于三个副本的关键 API,maxUnavailable: 0、maxSurge: 1 是一个常见起点,不是通用答案。容量紧张时,应先扩容节点或分阶段发布,而不是为了让 rollout 继续而降低可用门槛。

13. Service selector、端口与 EndpointSlice 一致

1
2
3
4
kubectl -n "$NS" get service "$APP" -o wide
kubectl -n "$NS" get endpointslice \
-l "kubernetes.io/service-name=$APP" -o wide
kubectl -n "$NS" get pod -l "app=$APP" --show-labels

通过标准: Service selector 选中预期 Pod;targetPort 对应容器端口名称或端口;EndpointSlice 至少包含期望数量的 Ready 地址;从集群内访问 Service 能得到业务级正确响应。

不要只请求 /livez。至少执行一个不会产生副作用的核心读取或契约检查,确认路由、中间件和依赖装配正常。

14. Ingress、TLS、DNS 和入口所有权清晰

1
2
3
kubectl -n "$NS" get ingress -o wide
kubectl -n "$NS" describe ingress "$APP"
kubectl -n "$NS" get certificate 2>/dev/null || true

通过标准: Host、Path 和后端 Service 正确;证书域名与有效期满足要求;HTTP 到 HTTPS 策略符合预期;入口控制器有可用副本;从真实访问网络完成 Host、TLS 和内容验证。

如果 Ingress 由平台团队或独立仓库管理,应用流水线只能读取和验证,不能重复 apply。共享入口的无意覆盖,影响范围通常远大于单个应用。

四、安全边界

15. 容器遵循最小权限

1
2
3
4
5
6
7
8
9
10
11
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: api
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]

通过标准: 不使用 privileged、hostPID、hostNetwork 或不必要的 hostPath;容器以非 root 用户运行;默认丢弃 Linux capabilities;根文件系统只读;确需写入的目录通过受控卷提供。

安全设置需要在镜像和非生产环境先验证。若应用依赖向根目录写缓存,应修正路径或挂载专用临时目录,而不是直接取消所有限制。

16. ServiceAccount 和 RBAC 只授予必要动作

不访问 Kubernetes API 的业务 Pod 通常可以关闭令牌自动挂载:

1
automountServiceAccountToken: false

需要访问 API 时,使用独立 ServiceAccount 和命名空间级 Role,避免复用默认账号或授予 cluster-admin。

1
2
kubectl auth can-i --as="system:serviceaccount:${NS}:${APP}" get configmaps -n "$NS"
kubectl auth can-i --as="system:serviceaccount:${NS}:${APP}" '*' '*' -n "$NS"

通过标准: 第二条检查应返回 no;权限清单与代码真实调用匹配;部署账号与运行时账号分离;临时提权有到期时间和审计记录。

17. NetworkPolicy 已验证为“实际生效”

NetworkPolicy 是否生效取决于集群网络插件。仅创建对象,不代表数据面一定执行了策略。

通过标准: 默认拒绝策略与业务放行策略配套;入口只允许 Ingress Controller 或明确调用方;出口只允许 DNS、数据库和必要外部 API;在测试命名空间分别验证允许与拒绝路径。

1
2
kubectl -n "$NS" get networkpolicy
kubectl -n "$NS" describe networkpolicy

上线前不要直接在生产一次性启用严格 egress。先从流量观测得到真实依赖,补齐 DNS、时间同步、遥测出口和第三方回调,再在隔离环境验证。

五、观测、恢复与发布责任

18. 指标、日志和追踪能回答“是否变差”

最低观测面包括:

  • 流量:请求数、吞吐、队列长度;
  • 错误:5xx、业务失败、依赖错误、超时;
  • 延迟:P50、P95、P99,并按路由或用例分组;
  • 饱和度:CPU 节流、内存、连接池、线程或协程、磁盘和网络;
  • Kubernetes:不可用副本、Pending、重启、OOMKilled、探针失败和 Events。

通过标准: 新版本、Pod、节点和请求 ID 能关联;仪表盘在发布前可访问;关键告警已经路由到当班人员;发布开始前保存同一时间窗口的基线。

19. 回滚与数据恢复已经演练

1
2
kubectl -n "$NS" rollout history deployment/"$APP"
kubectl -n "$NS" rollout status deployment/"$APP" --timeout=10m

发生异常时,先停止继续放量并保存证据。若变更只有兼容的应用镜像,可以回到上一 digest;若包含数据库、缓存结构或消息副作用,按专项恢复方案处理。

通过标准: 上一镜像仍可拉取;配置和 Secret 版本可恢复;备份完成过实际恢复验证;回滚命令、负责人和权限已确认;明确哪些变化不能通过 Deployment 回滚解决。

20. 发布门禁、观察窗口和交接完整

发布不是 kubectl apply 返回零就结束。建议把发布划分为四个状态:

1
2
待发布 → 发布中 → 观察中 → 已完成
└→ 已停止/已回滚

通过标准: 变更单包含开始与结束时间、操作者、审批人、命令退出码、rollout 结果、镜像 digest、指标截图或查询链接、异常记录和最终结论;观察窗口结束前不宣布完成;跨班次发布有明确交接人。

建议至少设置以下停止条件:

  • 新 Pod 在进度截止时间内无法全部 Available;
  • 错误率超过基线和预设阈值;
  • P95/P99 延迟持续恶化;
  • OOM、重启或探针失败持续增加;
  • 核心业务校验失败;
  • 数据迁移或下游依赖出现不可恢复错误。

一份可复用的 Deployment 基线

下面的片段把多项检查放在一起。它是起点,不是可直接复制到所有服务的模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-api
spec:
replicas: 3
minReadySeconds: 10
progressDeadlineSeconds: 600
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: order-api
template:
metadata:
labels:
app: order-api
spec:
serviceAccountName: order-api
automountServiceAccountToken: false
terminationGracePeriodSeconds: 30
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
seccompProfile:
type: RuntimeDefault
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: order-api
containers:
- name: api
image: registry.example.com/team/order-api@sha256:<digest>
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
envFrom:
- configMapRef:
name: order-api-config-<content-hash>
- secretRef:
name: order-api-secret
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
memory: 512Mi
startupProbe:
httpGet:
path: /startupz
port: http
periodSeconds: 2
failureThreshold: 30
readinessProbe:
httpGet:
path: /readyz
port: http
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 3
livenessProbe:
httpGet:
path: /livez
port: http
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
sizeLimit: 128Mi

示例没有设置 CPU limit,是为了强调“按压测和集群策略决定”,不是建议所有服务都取消 CPU limit。资源、探针、宽限期和副本数必须用真实负载验证。

发布当天的执行顺序

发布前

1
2
3
4
kubectl config current-context
kubectl -n "$NS" get deployment "$APP" -o wide
kubectl apply --server-side --dry-run=server -f "$MANIFEST_DIR"
kubectl diff -f "$MANIFEST_DIR"

保存当前 Deployment、镜像 digest、Available 副本和业务指标基线。确认回滚目标仍可从镜像仓库拉取。

发布中

通过受控流水线应用清单,不建议个人终端临时拼接补丁。并行观察:

1
2
3
kubectl -n "$NS" rollout status deployment/"$APP" --timeout=10m
kubectl -n "$NS" get pod -l "app=$APP" -w
kubectl -n "$NS" get events --sort-by=.metadata.creationTimestamp

get pod -w 和 Events 是辅助证据,最终仍要回到业务错误率、延迟和核心校验。

发布后

1
2
3
4
kubectl -n "$NS" get deployment "$APP" \
-o custom-columns='NAME:.metadata.name,READY:.status.readyReplicas,AVAILABLE:.status.availableReplicas,IMAGE:.spec.template.spec.containers[*].image'
kubectl -n "$NS" get endpointslice \
-l "kubernetes.io/service-name=$APP" -o wide

从集群内、Ingress 内网入口和真实用户路径分别验证。至少覆盖一个成功用例、一个预期失败用例、认证、关键依赖和不会产生副作用的读请求。

自动化门禁的分层

阶段 建议自动化内容 失败处理
提交检查 YAML/schema、策略、镜像扫描 阻止合并
环境预检 服务端 dry-run、diff、容量和依赖检查 阻止发布
滚动发布 rollout、Available 副本、Events 停止继续放量
业务校验 API 契约、只读核心用例、入口访问 自动回滚或人工判定
观察窗口 错误率、延迟、饱和度、业务指标 触发停止条件

自动回滚只适用于“失败信号可靠且回滚动作安全”的场景。数据迁移、消息副作用和外部写入通常需要人工确认,不应被一个模糊的 5xx 峰值直接触发破坏性动作。

小结

Kubernetes 上线检查的核心不是记住 20 个对象,而是把一次发布变成可验证、可停止、可恢复的工程流程:镜像能追溯,差异可审阅,容量有依据,探针有语义,权限和网络有边界,指标能判断是否退化,回滚不依赖临场记忆。

第一次落地时,不必追求一次自动化全部项目。先把 20 项写进变更模板,再逐步把可确定的检查搬进 CI;保留需要业务判断的审批点,并持续用事故与演练结果修订阈值。

一手资料

资料复核日期:2026-08-22。示例中的命名空间、标签、资源、超时、镜像和策略均为占位基线,实施前应以目标集群版本、网络插件、Admission Policy 和业务压测结果为准。

评论
分享

评论