Kubernetes 模式
面向生产环境的 Kubernetes 模式,用于可靠地部署、管理和调试工作负载。
何时激活
- 编写 Kubernetes 清单(Deployment、Service、Ingress、Job)
- 配置资源请求/限制、存活/就绪探针
- 设置 RBAC、命名空间或 ServiceAccount
- 管理 Kubernetes 中的配置与密钥
- 调试 CrashLoopBackOff、OOMKilled、Pending Pods 或镜像拉取错误
- 配置 HPA(水平 Pod 自动缩放器)或 PodDisruptionBudget
- 审查 Kubernetes YAML 的安全性与正确性
使用场景
与上面的何时激活相同。此别名遵循仓库技能格式的约定。每当您编写、审查或调试 Kubernetes YAML 和工作负载时,请使用此技能。
工作原理
本技能提供可直接复制粘贴的生产级 YAML 模式和按任务组织的 kubectl 调试命令:
- Deployment 模板 — 一个完全配置的生产级 Deployment,包含安全上下文、滚动更新策略、三种探针类型、资源限制以及来自 ConfigMap/Secret 的环境变量注入。
- 探针 — 启动探针 vs 存活探针 vs 就绪探针的决策表,包含正确的
failureThreshold × periodSeconds计算公式。 - Service 与 Ingress — ClusterIP、LoadBalancer 和带 TLS 的 Ingress 模式,附带 cert-manager 注解。
- ConfigMap 与 Secret —
envFrom、文件挂载和外部密钥指导。 - 资源管理 — 按工作负载类型(Web API、JVM、Worker、Sidecar)的请求与限制经验法则。
- RBAC — 最小权限 ServiceAccount → Role → RoleBinding 链。
- HPA 与 PDB — 自动缩放和节点排空安全配置。
- Job 与 CronJob — 一次性与周期性工作负载模式,包含正确的
restartPolicy。 - kubectl 速查表 — 日志、执行、回滚、端口转发、试运行和常见错误诊断命令。
- 反模式与检查清单 — 哪些不该做,以及安全/可靠性/可观测性检查清单。
示例
请参阅以下各节获取完整的可运行示例。快速参考:
| 任务 | 跳转至 |
|---|---|
| 完整的生产级 Deployment YAML | 核心工作负载模式 |
| 探针配置 | 探针 |
| RBAC 最小权限设置 | RBAC |
| 调试 CrashLoopBackOff | kubectl 调试速查表 |
| 自动缩放 | HPA |
核心工作负载模式
Deployment — 生产模板
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-namespace
labels:
app: my-app
version: "1.0.0"
spec:
replicas: 3
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新期间允许额外 1 个 Pod
maxUnavailable: 0 # 永远不低于期望数量
template:
metadata:
labels:
app: my-app
version: "1.0.0"
spec:
# Pod 级别的安全上下文
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
# 优雅关闭
terminationGracePeriodSeconds: 30
containers:
- name: my-app
image: ghcr.io/org/my-app:1.0.0 # 永远不要使用 :latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
protocol: TCP
# 需要同时设置资源请求和限制
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
# 容器安全上下文
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# 探针(请参阅下面的探针部分)
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 0
periodSeconds: 30
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 2
# 从 ConfigMap 和 Secret 获取环境变量
envFrom:
- configMapRef:
name: my-app-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-app-secrets
key: db-password
# 当 readOnlyRootFilesystem: true 时需要可写 tmp 目录
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
探针 — 存活、就绪、启动
理解何时使用每种探针至关重要:
| 探针 | 失败操作 | 用途 |
|---|---|---|
startupProbe | 杀死容器(如果启动缓慢) | 启动慢的应用(JVM、Python) |
livenessProbe | 重启容器 | 死锁 / 挂起进程检测 |
readinessProbe | 从 Service 端点中移除 | 临时不可用(数据库重连) |
# 正确模式:startupProbe 涵盖慢启动,
# 然后 liveness/readiness 接管
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30 # 30 * 5s = 150s 最大启动时间
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 30
failureThreshold: 3 # 3 * 30s = 90s 后重启
readinessProbe:
httpGet:
path: /ready # 单独的端点:检查 DB、缓存等
port: 8080
periodSeconds: 10
failureThreshold: 2
# 错误:使用 initialDelaySeconds 而不使用 startupProbe
# 如果应用需要 60 秒启动,应该使用 startupProbe
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60 # 坏:任意等待,存在竞态条件
Service 与 Ingress
Service 类型
# ClusterIP(默认)— 仅限内部
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: my-namespace
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
# LoadBalancer — 外部流量(云提供商)
spec:
type: LoadBalancer
ports:
- port: 443
targetPort: 8080
带 TLS 的 Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
namespace: my-namespace
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: my-app-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
ConfigMap 与 Secret
ConfigMap — 非敏感配置
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
namespace: my-namespace
data:
LOG_LEVEL: "info"
APP_ENV: "production"
MAX_CONNECTIONS: "100"
# 对于复杂配置,挂载为文件
app.yaml: |
server:
port: 8080
timeout: 30s
# 将 ConfigMap 挂载为文件
volumes:
- name: config
configMap:
name: my-app-config
items:
- key: app.yaml
path: app.yaml
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
Secret — 敏感数据
# 从字面值创建 Secret(CLI,然后存储在 Vault/SOPS 中)
kubectl create secret generic my-app-secrets \
--from-literal=db-password='s3cr3t' \
--namespace=my-namespace \
--dry-run=client -o yaml | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: my-app-secrets
namespace: my-namespace
type: Opaque
# 值是 base64 编码的(不是加密的 — 对于真正的加密,请使用 Sealed Secrets 或 ESO)
data:
db-password: czNjcjN0 # 's3cr3t' 的 base64
重要提示: raw Kubernetes Secret 仅被 base64 编码,除非集群配置了加密,否则不会静态加密。对于生产环境,请使用 Sealed Secrets 或 External Secrets Operator。
资源请求与限制
resources:
requests: # 调度器使用此值放置 Pod
cpu: "100m" # 100 毫核 = 0.1 CPU
memory: "128Mi"
limits: # 超过此值时容器会被杀死/节流
cpu: "500m"
memory: "256Mi"
经验法则:
| 工作负载类型 | CPU 请求 | 内存请求 | 说明 |
|---|---|---|---|
| Web API | 100–250m | 128–256Mi | 设置限制为请求的 2-4 倍 |
| Worker/消费者 | 250–500m | 256–512Mi | 为可预测性设置内存限制 = 请求 |
| JVM 应用 | 500m–1 | 512Mi–2Gi | 在 -Xmx 之上为 JVM 开销预留空间 |
| Sidecar | 10–50m | 32–64Mi | 保持最小 |
# 错误:没有请求或限制 — 不可预测的调度、OOM 驱逐
containers:
- name: app
image: myapp:latest
# 缺少 resources: {} — 在生产环境中很危险
# 错误:有限制但没有请求 — 请求默认为限制,过度预留容量
resources:
limits:
cpu: "2"
memory: "1Gi"
# 缺少请求 — 将默认为限制值
RBAC — 角色与 ServiceAccount
最小权限原则
根据应用是否调用 Kubernetes API,有两种模式:
模式 A — 应用不需要 Kubernetes API(大多数应用)
在 ServiceAccount 上禁用令牌自动挂载。不需要 Role/RoleBinding。
# ServiceAccount 禁用令牌 — 最安全的默认值
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-namespace
automountServiceAccountToken: false # 不会将 K8s API 令牌注入 Pod
# 在 Deployment 中引用 — 无令牌,无 API 访问
spec:
template:
spec:
serviceAccountName: my-app-sa
automountServiceAccountToken: false # 双重保险:也在 Pod 级别设置
模式 B — 应用需要 Kubernetes API(Operator、控制器、配置观察器)
启用令牌并仅授予实际需要的权限。
# 1. ServiceAccount — 为此 SA 启用令牌
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-namespace
automountServiceAccountToken: true # 需要令牌:应用调用 K8s API
# 2. Role — 仅授予应用所需权限(命名空间作用域)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: my-app-role
namespace: my-namespace
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"] # 只读,特定资源
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["my-app-secrets"] # 按名称限制到特定 Secret
verbs: ["get"]
# 3. 将 Role 绑定到 ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-rolebinding
namespace: my-namespace
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: my-namespace
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: my-app-role
# 4. 在 Deployment 中引用 SA
spec:
template:
spec:
serviceAccountName: my-app-sa
# automountServiceAccountToken 默认从 SA 继承为 true — 令牌被注入
水平 Pod 自动缩放器 (HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
namespace: my-namespace
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2 # 对于 HA,始终至少 2 个
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # 当平均 CPU > 70% 时扩容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
HPA 要求所有容器都设置
resources.requests— 它将利用率计算为当前值 / 请求值。
PodDisruptionBudget (PDB)
防止在节点排空或滚动更新期间过多 Pod 下线:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
namespace: my-namespace
spec:
minAvailable: 2 # 或使用 maxUnavailable: 1
selector:
matchLabels:
app: my-app
命名空间与多租户
# 创建带有资源配额的命名空间
kubectl create namespace my-namespace
# 应用 ResourceQuota 以限制命名空间消耗
kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
name: my-namespace-quota
namespace: my-namespace
spec:
hard:
requests.cpu: "4"
requests.memory: 4Gi
limits.cpu: "8"
limits.memory: 8Gi
pods: "20"
EOF
Job 与 CronJob
# 一次性 Job(数据库迁移、数据处理)
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
namespace: my-namespace
spec:
backoffLimit: 3 # 失败后最多重试 3 次
ttlSecondsAfterFinished: 3600 # 1 小时后自动删除
template:
spec:
restartPolicy: OnFailure # 对于 Job 必须为 Never(不是 Always)
containers:
- name: migrate
image: ghcr.io/org/my-app:1.0.0
command: ["python", "manage.py", "migrate"]
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
memory: "512Mi"
# 周期性 CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: cleanup
namespace: my-namespace
spec:
schedule: "0 2 * * *" # 每天凌晨 2 点
concurrencyPolicy: Forbid # 不允许同时运行
startingDeadlineSeconds: 100
successfulJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: cleanup
image: alpine:3.18
command: ["sh", "-c", "find /tmp -mtime +7 -delete"]
kubectl 调试速查表
Pod 诊断
# 查看 Pod 事件
kubectl describe pod <pod-name> -n <namespace>
# 实时流式日志
kubectl logs -f <pod-name> -n <namespace>
# 多容器 Pod 的日志
kubectl logs -f <pod-name> -c <container-name> -n <namespace>
# 检查资源使用情况
kubectl top pod <pod-name> -n <namespace>
# 进入正在运行的容器
kubectl exec -it <pod-name> -n <namespace> -- /bin/sh
# 端口转发到 Pod
kubectl port-forward pod/<pod-name> 8080:8080 -n <namespace>
Deployment 管理
# 回滚到上一版本
kubectl rollout undo deployment/<name> -n <namespace>
# 回滚到特定修订版
kubectl rollout undo deployment/<name> --to-revision=2 -n <namespace>
# 查看滚动更新历史
kubectl rollout history deployment/<name> -n <namespace>
# 手动扩缩
kubectl scale deployment/<name> --replicas=5 -n <namespace>
YAML 验证
# 试运行 — 不执行实际更改,仅验证
kubectl apply -f manifest.yaml --dry-run=client -o yaml
# 针对现有资源验证
kubectl diff -f manifest.yaml
# 针对服务器端架构验证
kubectl apply -f manifest.yaml --dry-run=server -o yaml
Secret 与 ConfigMap 操作
# 解码 Secret
kubectl get secret <name> -n <namespace> -o jsonpath='{.data.password}' | base64 -d
# 从文件创建 ConfigMap
kubectl create configmap <name> --from-file=config.yaml -n <namespace>
集群状态
# 节点状况
kubectl get nodes -o wide
# 显示资源和标签
kubectl get pods -n <namespace> --show-labels -o wide
# 后接输出格式
kubectl get pods -n <namespace> -o yaml
kubectl get pods -n <namespace> -o jsonpath='{.items[*].metadata.name}'
反模式与检查清单
要避免的事情(反模式)
| ❌ 不要 | 原因 | ✅ 应该 |
|---|---|---|
使用 :latest 镜像标签 | 不可重现的部署 | 使用固定语义版本标签 |
设置 initialDelaySeconds 而不是使用 startupProbe | 竞态条件;如果应用启动更快则延迟浪费 | 使用 startupProbe 并移除 initialDelaySeconds |
| 使用默认令牌挂载(应用不使用 API) | 不必要的 API 暴露 | 在 ServiceAccount 上设置 automountServiceAccountToken: false |
省略 resources.requests | 不可预测的调度 + OOM 风险 | 始终为所有容器设置请求 |
给所有容器设置宽泛的 securityContext.capabilities | 不必要的权限 | drop: ["ALL"] 并逐个添加所需权限 |
从 Ingress 移除 pathType | 某些 Ingress 控制器会拒绝 | 明确定义 pathType: Prefix |
忽略 readOnlyRootFilesystem 和 Pod 安全策略 | 容器逃逸风险 | 使用 Pod 安全准入;设置 readOnlyRootFilesystem: true 并挂载 emptyDir |
生产检查清单
在部署到生产环境之前,验证此清单:
安全性
- 使用非 root 用户,禁用权限提升
- 只读根文件系统(需要时挂载
emptyDir) - ServiceAccount 令牌已禁用(除非应用调用 K8s API)
- 所有 Secret 已从 Vault/ESO 正确同步
- 网络策略限制入口/出口流量
可靠性
- 定义了所有三个探针(启动、存活、就绪)
- PodDisruptionBudget 已配置(
minAvailable: 2) - 资源请求/限制已设置
- 设置
terminationGracePeriodSeconds(通常 ≥ 30s) - HPA 已配置(CPU 和内存)
- Deployment 使用滚动更新策略(
maxSurge= 1,maxUnavailable= 0)
可观测性
- 应用以结构化格式(JSON)记录日志
-
readinessProbe检查后端依赖项(数据库、缓存) - Prometheus / metrics 端点已暴露
- 就绪/存活端点具有合理的
failureThreshold × periodSeconds计算
操作
- 镜像标签是固定的,不是
:latest - 命名空间使用 ResourceQuota
- 考虑节点污点和容忍
- 环境变量从 ConfigMap/Secret 注入,而非硬编码
- 测试了回滚过程
附录:文件挂载示例
ConfigMap 挂载为 YAML 配置文件
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: my-namespace
data:
# 挂载为 /etc/app/settings.yaml
settings.yaml: |
server:
port: 8080
database:
max_connections: 100
timeout: 30s
logging:
level: info
format: json
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: my-app
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
volumes:
- name: config
configMap:
name: app-config
Secret 挂载为文件
apiVersion: v1
kind: Secret
metadata:
name: app-tls
namespace: my-namespace
type: kubernetes.io/tls
data:
tls.crt: <base64-编码的证书>
tls.key: <base64-编码的密钥>
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: my-app
volumeMounts:
- name: tls
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls
secret:
secretName: app-tls
附录 B:环境变量注入模式
组合使用 ConfigMap 和 Secret
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: my-namespace
data:
APP_ENV: "production"
LOG_LEVEL: "warn"
# DB_PASSWORD 在 Secret 中,不在 ConfigMap 中
---
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: my-namespace
type: Opaque
data:
DB_PASSWORD: c3VwZXJzZWNyZXQ=
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: my-app
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets