Kubernetes最佳实践:生产环境配置规范

Kubernetes 最佳实践完全指南:生产环境配置规范

摘要:Kubernetes 在生产环境的稳定运行不是"装上就跑"——它需要从 Pod 资源配置、安全上下文、网络隔离、自动扩缩容、故障容忍、可观测性等多维度系统化规范。本文基于 Kubernetes 1.28+(覆盖 1.30 / 1.31),结合 CNCF、阿里云、字节跳动等大厂的实战经验,给出一份可直接落地的生产配置清单,每个资源都给出完整 YAML 示例 + 关键字段解释 + 常见坑点。

适用版本:Kubernetes 1.28 - 1.31(GA) 读者:SRE / 平台工程师 / 后端开发


一、生产环境配置核心原则

在开始看 YAML 之前,先记住这几条贯穿全文的原则:

  1. 声明式 + 版本化:所有 YAML 全部进 Git,走 GitOps(Argo CD / Flux)
  2. 最小权限:每个 ServiceAccount 只给能用到的 RBAC
  3. 资源可观测:每个 Pod 必须有 requests/limits,否则被驱逐都不知道为什么
  4. 故障自愈:Deployment 用 HPA + PDB + 多副本,避免单点
  5. 安全默认:Pod 跑非 root、只读文件系统、禁止特权升级
  6. 可声明验证:用 kube-linter / conftest / OPA 在 CI 阶段拦截错误配置

二、Pod 配置规范(核心)

2.1 一个生产可用的 Pod 模板

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
  namespace: prod
  labels:
    app: backend
    version: v1.2.3
    tier: api
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9090"
    prometheus.io/path: "/metrics"
spec:
  # 安全上下文(整个 Pod 生效)
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    fsGroupChangePolicy: OnRootMismatch
    seccompProfile:
      type: RuntimeDefault
  # 服务账号
  serviceAccountName: backend-sa
  # 自动挂载(避免意外泄露 token)
  automountServiceAccountToken: false
  # 域名配置(k8s 默认)
  dnsPolicy: ClusterFirst
  dnsConfig:
    options:
      - name: ndots
        value: "2"
  # 优雅停机
  terminationGracePeriodSeconds: 30
  # 调度约束
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          podAffinityTerm:
            labelSelector:
              matchLabels:
                app: backend
            topologyKey: kubernetes.io/hostname
  containers:
    - name: app
      image: registry.example.com/team/app:v1.2.3
      imagePullPolicy: IfNotPresent
      # 容器级安全
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        runAsNonRoot: true
        runAsUser: 1000
        capabilities:
          drop: ["ALL"]
      # 端口定义
      ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        - name: metrics
          containerPort: 9090
          protocol: TCP
      # 资源配置(**必填**)
      resources:
        requests:
          cpu: "100m"
          memory: "128Mi"
          ephemeral-storage: "500Mi"
        limits:
          cpu: "500m"
          memory: "256Mi"
          ephemeral-storage: "1Gi"
      # 探针(**三探针必填**)
      startupProbe:
        httpGet:
          path: /healthz
          port: http
        initialDelaySeconds: 10
        periodSeconds: 5
        failureThreshold: 30
      livenessProbe:
        httpGet:
          path: /healthz
          port: http
        periodSeconds: 10
        timeoutSeconds: 3
        failureThreshold: 3
      readinessProbe:
        httpGet:
          path: /ready
          port: http
        periodSeconds: 5
        timeoutSeconds: 2
        failureThreshold: 2
      # 环境变量
      env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: TZ
          value: "Asia/Shanghai"
      # 数据卷
      volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: cache
          mountPath: /app/cache
  volumes:
    - name: tmp
      emptyDir: {}
    - name: cache
      emptyDir:
        sizeLimit: 1Gi

2.2 关键字段详解

字段为什么必填
resources.requests调度器用,没填可能被调度到资源不够的节点
resources.limits防止单 Pod 吃光节点资源
securityContext.runAsNonRoot防容器内提权
readOnlyRootFilesystem: true容器文件系统只读,攻击者无法写二进制
startupProbe慢启动应用避免被 liveness 误杀
livenessProbe死循环必须重启,但别用错路径导致重启风暴
readinessProbe控制 Service 后端流量,避免启动期收到请求
terminationGracePeriodSeconds优雅停机时长,配合 preStop hook 处理 in-flight 请求
imagePullPolicy: IfNotPresent减少每次重启拉镜像的网络开销

坑点 1livenessProbe 的 path 必须真的存在且返回 200。如果健康检查挂,应用就被杀;恢复需要重新拉镜像启动,时间长。慢启动应用必须用 startupProbe 给宽限时间。 坑点 2resources.requestslimits 都填的话,QoS 类是 Burstable;想让 Pod 享受 Guaranteed(最稳定),requests == limitsBestEffort(无 requests)会被节点压力时最先驱逐

三、Deployment 规范

3.1 多副本 + 滚动更新 + 历史保留

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-api
  namespace: prod
  labels:
    app: backend
spec:
  replicas: 3
  revisionHistoryLimit: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%             # 允许超出 replicas 25%
      maxUnavailable: 0          # 滚动期间零停机
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
        version: v1.2.3
    spec:
      # ... 同 Pod 模板部分,省略

3.2 关键字段

  • revisionHistoryLimit: 10:保留 10 个历史版本,方便回滚
  • maxSurge + maxUnavailable:滚动策略参数,生产建议 maxUnavailable: 0(不中断服务)
  • strategy.type: RollingUpdate:默认即可;金丝雀用蓝绿或 Argo Rollouts
  • selector.matchLabels 必须稳定:改 selector 等于创建新 Deployment

四、Service 规范

4.1 ClusterIP + 注解

apiVersion: v1
kind: Service
metadata:
  name: backend-api
  namespace: prod
  labels:
    app: backend
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9090"
spec:
  type: ClusterIP
  selector:
    app: backend
  ports:
    - name: http
      port: 80
      targetPort: http         # 引用 Pod 端口名
      protocol: TCP
    - name: metrics
      port: 9090
      targetPort: metrics
      protocol: TCP
  # k8s 1.30+ 可启用 trafficDistribution(v1.31 GA)
  # internalTrafficPolicy: Cluster

4.2 Service 类型选型

类型用途
ClusterIP集群内部访问(默认)
NodePort简单暴露,端口 30000-32767(开发测试用)
LoadBalancer云厂商集成,自动创建 LB(生产推荐)
ExternalNameCNAME 别名,访问外部服务

坑点 3:生产不要用 NodePort 直接对外——它会暴露到所有节点 IP,安全风险大。NodePort 之上套一层 Ingress / Gateway API。

五、HPA 自动扩缩容

5.1 基于 CPU/Memory 的 HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: backend-api-hpa
  namespace: prod
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: backend-api
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300   # 缩容缓冲 5 分钟
      policies:
        - type: Percent
          value: 50
          periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0     # 扩容立即响应
      policies:
        - type: Percent
          value: 100
          periodSeconds: 30

5.2 自定义指标 + KEDA

生产环境光 CPU/Memory 不够。推荐用 KEDA(事件驱动扩缩):

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: backend-api-keda
  namespace: prod
spec:
  scaleTargetRef:
    name: backend-api
  minReplicaCount: 3
  maxReplicaCount: 100
  triggers:
    - type: prometheus
      metadata:
        serverAddress: http://prometheus.monitoring:9090
        metricName: http_requests_per_second
        query: |
          sum(rate(http_requests_total{namespace="prod",service="backend-api"}[1m]))
        threshold: "1000"

坑点 4:HPA 需要 metrics-server(或 KEDA Prometheus adapter),否则 kubectl get hpa 显示 <unknown>/70%。生产前先 kubectl top nodes 验证 metrics-server 工作。

六、PVC 持久化存储

6.1 动态 PVC + StorageClass

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ssd-retain
provisioner: kubernetes.io/aws-ebs   # 或 alicloud-disk / ceph-csi
parameters:
  type: gp3
  fsType: ext4
reclaimPolicy: Retain     # 数据安全第一
volumeBindingMode: WaitForFirstConsumer   # 等 Pod 调度再创建卷
allowVolumeExpansion: true
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
  namespace: prod
spec:
  storageClassName: ssd-retain
  accessModes: [ReadWriteOnce]
  resources:
    requests:
      storage: 100Gi

坑点 5reclaimPolicy 默认 Delete,PVC 删除后云盘跟着删——数据没了。生产用 Retain 或手动 Retain+定期备份。

6.2 挂载到 Pod

volumeMounts:
  - name: data
    mountPath: /app/data
volumes:
  - name: data
    persistentVolumeClaim:
      claimName: app-data

七、NetworkPolicy 网络隔离

生产集群必须配 NetworkPolicy——默认所有 Pod 之间互通,是巨大安全风险。

7.1 默认拒绝所有入站

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: prod
spec:
  podSelector: {}      # 匹配所有 Pod
  policyTypes:
    - Ingress
    # 可选:再加 Egress

7.2 允许前端访问后端 API

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-allow-frontend
  namespace: prod
spec:
  podSelector:
    matchLabels:
      app: backend
      tier: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

坑点 6:NetworkPolicy 需要 CNI 插件支持(Calico / Cilium / Weave)。Flannel 不支持(无策略 enforcement)。装集群前先选好 CNI。

八、PodDisruptionBudget 故障容忍

8.1 标准 PDB

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: backend-api-pdb
  namespace: prod
spec:
  minAvailable: 2              # 至少保留 2 个 Pod 可用
  # 或者用百分比
  # minAvailable: 50%
  selector:
    matchLabels:
      app: backend

坑点 7:节点维护、cluster-autoscaler 缩容时,PDB 防止"全部 Pod 被同时驱逐导致服务不可用"。生产必须有 PDB,否则一次节点升级就能让服务雪崩。

九、LimitRange 与 ResourceQuota

9.1 命名空间资源配额

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: prod
spec:
  limits:
    - type: Container
      default:
        cpu: "500m"
        memory: "512Mi"
        ephemeral-storage: "1Gi"
      defaultRequest:
        cpu: "100m"
        memory: "128Mi"
      max:
        cpu: "2"
        memory: "4Gi"
    - type: PersistentVolumeClaim
      max:
        storage: 100Gi
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: prod-quota
  namespace: prod
spec:
  hard:
    pods: "100"
    requests.cpu: "50"
    requests.memory: "100Gi"
    limits.cpu: "100"
    limits.memory: "200Gi"
    persistentvolumeclaims: "50"
    requests.storage: "1Ti"

坑点 8:用 LimitRange.default 给 namespace 加默认值,Pod 没填 requests/limits 也会自动补上。这是防呆设计

十、ServiceAccount 与 RBAC

10.1 最小权限 SA

apiVersion: v1
kind: ServiceAccount
metadata:
  name: backend-sa
  namespace: prod
automountServiceAccountToken: false    # 默认不挂 token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: backend-role
  namespace: prod
rules:
  - apiGroups: [""]
    resources: ["configmaps", "secrets"]
    resourceNames: ["backend-config"]
    verbs: ["get", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: backend-rb
  namespace: prod
subjects:
  - kind: ServiceAccount
    name: backend-sa
    namespace: prod
roleRef:
  kind: Role
  name: backend-role
  apiGroup: rbac.authorization.k8s.io

10.2 Pod 引用 SA

spec:
  serviceAccountName: backend-sa
  automountServiceAccountToken: true   # 显式开

坑点 9automountServiceAccountToken: false 时 Pod 内不会挂 /var/run/secrets/kubernetes.io/serviceaccount/token——如果应用要调 k8s API 必须显式开。

十一、镜像拉取与镜像策略

11.1 私有仓库凭据

kubectl create secret docker-registry regcred \
    --docker-server=registry.example.com \
    --docker-username=team \
    --docker-password=xxxxx \
    --docker-email=team@example.com \
    -n prod
spec:
  imagePullSecrets:
    - name: regcred
  containers:
    - name: app
      image: registry.example.com/team/app:v1.2.3

11.2 镜像标签规范

标签何时用
固定版本 v1.2.3生产 ✅ 强烈推荐
摘要 @sha256:xxx最安全(不可变)✅ 极致推荐
latest永远不要
stablemaindev仅开发环境

十二、Ingress 入口

12.1 生产 Ingress(nginx-ingress 示例)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: backend-api
  namespace: prod
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.com
      secretName: api-tls
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backend-api
                port:
                  number: 80

坑点 10:Ingress 必须配 ingressClassName,否则 controller 不接管。pathType: PrefixImplementationSpecific 更明确,新版本推荐 PrefixExact

十三、健康检查与可观测性

13.1 三探针必填

探针作用何时失败
startupProbe慢启动保护启动期不健康时不杀
livenessProbe死循环检测重启 Pod
readinessProbe流量控制Service 剔除该 Pod 后端

13.2 Prometheus 注解

metadata:
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9090"
    prometheus.io/path: "/metrics"

13.3 结构化日志

容器日志必须输出 JSON 到 stdout,让 Fluent Bit / Vector 收集:

# Python 示例
import logging, json
class JsonFormatter(logging.Formatter):
    def format(self, record):
        return json.dumps({
            "ts": record.created,
            "level": record.levelname,
            "msg": record.getMessage(),
            "logger": record.name,
        })
logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler()])

十四、命名规范

资源类型规范示例
Namespace<环境>-<业务线>prod-payments, staging-search
Deployment<业务>-<组件>payments-api, payments-worker
Service同 Deploymentpayments-api
ConfigMap<业务>-<用途>payments-config, payments-redis-conf
Secret<业务>-<用途>payments-db-cred
ServiceAccount<业务>-sapayments-sa
PVC<业务>-<用途>payments-data, payments-cache
Ingress<业务>-ingresspayments-ingress

避免

  • 名字带大写或下划线(DNS label 要求小写)
  • 名字带 appsvck8s 等保留字
  • 太长(> 30 字符不利于 kubectl 输出)

十五、生产环境 Checklist

部署一个应用到生产前,逐项打勾:

  • [ ] Pod 有 resources.requestslimits
  • [ ] Pod 有 securityContext.runAsNonRoot: true
  • [ ] Pod 有 readOnlyRootFilesystem: true
  • [ ] Pod 有 livenessProbereadinessProbe(必要时 startupProbe
  • [ ] 镜像用固定 tag 或 sha256 digest,不用 latest
  • [ ] 镜像走私有仓库,配置了 imagePullSecrets
  • [ ] Deployment 有 revisionHistoryLimitmaxUnavailable: 0
  • [ ] Service 端口引用 Pod 端口名(不写死数字)
  • [ ] HPA 配置了 min/max 与缩容缓冲
  • [ ] 有 PDB 防止雪崩
  • [ ] 有 LimitRange 兜底
  • [ ] 有 NetworkPolicy 限制入站
  • [ ] ServiceAccount 是最小权限,绑定了具体 Role
  • [ ] ConfigMap 用 cm 注入,敏感信息走 Secret
  • [ ] 应用输出 JSON 日志到 stdout
  • [ ] 暴露 /metrics 给 Prometheus
  • [ ] YAML 在 Git 里走 GitOps

十六、写在最后

Kubernetes 的"最佳实践"不是一成不变的——它随版本演进、社区经验沉淀在变。但核心原则不会变:

  1. 声明式 + 版本化(YAML in Git)
  2. 最小权限(SA + RBAC + NetworkPolicy)
  3. 资源可观测(requests/limits/三探针/日志)
  4. 故障自愈(HPA + PDB + 多副本)

把这四点做好,集群就稳了一大半。剩下的细节(监控告警、CI/CD、混沌工程、灰度发布)会在后续文章里讲。


参考资源

想看某个具体资源(比如 Gateway API / Argo Rollouts / cert-manager)的实战配置? 评论区告诉我,我单独写一篇。

Kubernetes #运维 #最佳实践 #生产环境 #DevOps

发表回复

后才能评论