原文链接#

前言#

这份 GitHub 仓库是一份覆盖面相当广的 K8s 实践手册,从集群基础配置、安全、网络到 GitOps 都有涉及,对于想系统梳理 K8s 知识体系的工程师来说很有参考价值。

但该仓库最后更新停留在较早期的内容,遗漏了 K8s 近几个版本中已经稳定(或接近稳定)的重要特性,最典型的就是 Gateway API。本文以仓库内容为骨架,加入我自己的实践理解,同时补充这些新特性,力求给出一份更完整的参考。


一、集群基础:规划要在最前面#

网络 CIDR 规划#

集群建好之后最难改的就是网络。在创建集群之前必须认真计算三块地址段:

  • 节点子网:根据最大节点数预留,留足余量
  • Pod CIDR:每个节点 maxPods 数量决定每个节点需要多大的子网块。例如每节点最多 110 个 Pod,则每节点需要 /25(128 个地址),256 个节点就需要 /17
  • Service CIDR:独立规划,避免与内网路由冲突

对于托管 K8s(GKE/EKS/AKS),云厂商通常会额外保留部分地址,规划时要读清楚文档,留出 1.5~2 倍余量。

私有集群#

节点和 API Server 不应暴露在公网。控制面通过私有端点访问,节点出流量经过 NAT 网关。这一点在公有云环境下几乎是标配,但在自建集群(K3s/kubeadm)里容易被忽视。

Infrastructure as Code#

所有集群配置(节点池、网络、IAM)都应该用 Terraform / Pulumi 管理,而不是在控制台手点。没有 IaC 的集群,两周后就没人知道某个配置是怎么来的。


二、安全#

Pod Security Standards 替代 PSP#

Pod Security Policy 在 K8s 1.21 被废弃,1.25 彻底移除。现在的替代方案是 Pod Security Admission(PSA),通过 Namespace 标签控制:

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

三个级别:

级别 适用场景
privileged 系统组件、CNI 插件
baseline 普通业务,防止明显的提权
restricted 严格硬化,生产推荐

restricted 模式要求:非 root 运行、只读根文件系统、drop all capabilities、禁止 hostPath、禁止 hostNetwork/hostPID 等。

业务容器的 SecurityContext 示例:

securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  capabilities:
    drop: ["ALL"]

RBAC:最小权限原则#

几个容易踩的坑:

  1. 不要用 cluster-admin——即使是管理脚本也不行,用有限权限的 ServiceAccount
  2. 命名空间 Role 优先于 ClusterRole——只有真正需要跨命名空间权限时才用 ClusterRole
  3. 每个应用独立 ServiceAccount——不要共用 default SA
  4. 定期审计kubectl auth can-i --list --as system:serviceaccount:<ns>:<sa>
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: app-reader
rules:
- apiGroups: [""]
  resources: ["pods", "configmaps"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-reader-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: my-app
  namespace: production
roleRef:
  kind: Role
  name: app-reader
  apiGroup: rbac.authorization.k8s.io

仓库未覆盖:ValidatingAdmissionPolicy(CEL 策略,K8s 1.30 GA)#

ValidatingAdmissionPolicy 用 CEL(Common Expression Language)表达式直接在 API Server 层做校验,不再需要部署 Webhook 服务器(OPA Gatekeeper / Kyverno),显著降低复杂度。

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: require-resource-limits
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups: ["apps"]
      apiVersions: ["v1"]
      operations: ["CREATE", "UPDATE"]
      resources: ["deployments"]
  validations:
  - expression: >
      object.spec.template.spec.containers.all(c,
        has(c.resources) &&
        has(c.resources.limits) &&
        has(c.resources.limits.memory)
      )      
    message: "所有容器必须设置 memory limits"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: require-resource-limits-binding
spec:
  policyName: require-resource-limits
  validationActions: [Deny]
  matchResources:
    namespaceSelector:
      matchLabels:
        env: production

对于简单策略(禁止特权容器、强制 label、强制资源限制),VAP + CEL 比引入 Kyverno/OPA 轻量得多。复杂策略才考虑 Webhook 方案。


三、Pod 配置#

三种探针#

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 15
  periodSeconds: 20
  failureThreshold: 3

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10
  failureThreshold: 3

startupProbe:          # 启动慢的应用(ML 模型、遗留系统)
  httpGet:
    path: /healthz
    port: 8080
  failureThreshold: 30   # 30 * 10s = 5 分钟最大启动时间
  periodSeconds: 10
  • liveness 失败 → 重启容器。不要把依赖外部服务的检查放进来,否则外部抖动会引发雪崩重启
  • readiness 失败 → 从 Endpoints 移除,不接收流量。用于应用预热完成前的流量隔离
  • startup 专为慢启动设计,期间 liveness/readiness 暂停

资源请求与限制#

resources:
  requests:
    memory: "256Mi"
    cpu: "100m"
  limits:
    memory: "256Mi"   # memory 设置等于 request(不可压缩资源)
    # cpu 不设 limit(可压缩,设 limit 会导致不必要的 throttle)

用 VPA 的推荐模式(updateMode: "Off")来验证初始值:

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: my-app
  updatePolicy:
    updateMode: "Off"   # 只给建议,不自动修改

四、扩缩容与调度#

HPA 目标值计算#

原始公式:

目标利用率 = (1 - 安全余量) / (1 + 预期增长率)

举例:memory limit 100%,保留 15% 安全余量,考虑 45% 流量增长:

(1 - 0.15) / (1 + 0.45) = 0.85 / 1.45 ≈ 58%

HPA 配置:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 58
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300   # 防止频繁缩容
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60

Pod Disruption Budget#

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: my-app-pdb
spec:
  minAvailable: 2     # 或 maxUnavailable: 1
  selector:
    matchLabels:
      app: my-app

PDB 保护自愿中断(节点排空、升级),对于 crash / OOM 无效。多副本部署必须配置。

反亲和性 + 跨可用区分布#

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchLabels:
          app: my-app
      topologyKey: kubernetes.io/hostname   # 同节点不调度
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
      podAffinityTerm:
        labelSelector:
          matchLabels:
            app: my-app
        topologyKey: topology.kubernetes.io/zone  # 尽量跨 AZ

五、部署策略#

Rolling Update vs Recreate#

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1         # 最多比期望多 1 个 Pod
    maxUnavailable: 0   # 不允许不可用(零停机)

Recreate 策略适合有状态应用不能多版本并存的场景,代价是有停机窗口。

Canary 发布#

手动简单实现:用两个 Deployment 共享同一个 Service selector,通过副本比例控制流量。

v1: 9 replicas → 90% 流量
v2: 1 replica  → 10% 流量

生产建议使用 Argo Rollouts 或配合 Gateway API 的流量权重分割(见第七节)。


六、优雅关闭#

K8s 终止流程:

Pod 进入 Terminating
    ↓
执行 preStop Hook
    ↓
发送 SIGTERM
    ↓
等待 terminationGracePeriodSeconds(默认 30s)
    ↓
发送 SIGKILL
spec:
  terminationGracePeriodSeconds: 60
  containers:
  - name: app
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh", "-c", "sleep 5"]  # 给 LB 时间摘除该 Pod

preStopsleep 5 是为了等负载均衡器(Ingress/Service)完成摘除,避免新请求还在进来时应用已经停止接收。应用本身需要捕获 SIGTERM 做连接排空。


七、网络#

Network Policy:默认拒绝,显式放行#

# 默认拒绝所有入站
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}
  policyTypes: [Ingress]
---
# 只允许同命名空间的 frontend 访问 backend 的 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - port: 8080

CNI 需要支持 NetworkPolicy(Calico、Cilium、Weave Net),Flannel 默认不支持。

仓库未覆盖:Gateway API(独立版本号,v1.0 已在 2023-10 GA)#

这是本文重点补充内容。

为什么 Ingress 不够用?

Ingress API 设计于 2015 年,职责模糊、扩展性差,所有高级功能(流量权重、Header 改写、TLS 终结策略)都靠 Annotation 实现,不同 Ingress Controller 的 Annotation 互不兼容,配置无法移植。

Gateway API 的三层模型

GatewayClass(基础设施层,由平台团队管理)
    └── Gateway(网关实例,由平台/运维团队管理)
            └── HTTPRoute / GRPCRoute / TCPRoute(路由规则,由开发团队管理)

这种分层设计把「提供网关能力」和「使用网关能力」清晰解耦,符合大型团队的权责划分。

基本配置示例

# 1. GatewayClass:声明用哪个实现(Cilium / Envoy Gateway / Istio / Kong 等)
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: cilium
spec:
  controllerName: io.cilium/gateway-controller

---
# 2. Gateway:实例化一个 HTTPS 网关
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: prod-gateway
  namespace: infra
spec:
  gatewayClassName: cilium
  listeners:
  - name: https
    port: 443
    protocol: HTTPS
    tls:
      mode: Terminate
      certificateRefs:
      - name: prod-tls-cert
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            gateway-access: allowed

---
# 3. HTTPRoute:由业务团队在自己的命名空间管理
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app-route
  namespace: production
spec:
  parentRefs:
  - name: prod-gateway
    namespace: infra
  hostnames: ["api.example.com"]
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api/v1
    backendRefs:
    - name: my-app-svc
      port: 8080

流量权重分割(Canary 发布)

Gateway API 原生支持,不需要额外 Annotation:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-route
  namespace: production
spec:
  parentRefs:
  - name: prod-gateway
    namespace: infra
  rules:
  - backendRefs:
    - name: my-app-v1
      port: 8080
      weight: 90
    - name: my-app-v2
      port: 8080
      weight: 10

Header/Path 改写

rules:
- matches:
  - path:
      type: PathPrefix
      value: /legacy
  filters:
  - type: URLRewrite
    urlRewrite:
      path:
        type: ReplacePrefixMatch
        replacePrefixMatch: /api
  backendRefs:
  - name: backend-svc
    port: 8080

与 Ingress 对比总结

维度 Ingress Gateway API
标准化程度 低,依赖 Annotation 高,规范内置
权限分层 GatewayClass / Gateway / Route 三层
流量权重 Annotation(不可移植) 原生 weight 字段
协议支持 HTTP/HTTPS HTTP、GRPC、TCP、UDP、TLS
成熟度 稳定但不再扩展新能力 v1.0 已 GA(2023-10),当前 v1.5.x

目前支持 Gateway API 的实现:Cilium、Envoy Gateway、Istio、Kong、Traefik、HAProxy Ingress 等,主流控制器已全面跟进。


八、Secrets 管理#

方案 复杂度 适用场景
Kubernetes Secrets(明文 base64) 开发环境,必须配合 etcd 加密
Sealed Secrets GitOps 场景,加密存 Git
External Secrets Operator 对接 AWS Secrets Manager / GCP Secret Manager
HashiCorp Vault 企业级,多集群,动态凭证

几个必做项:

  • 开启 etcd 静态加密(EncryptionConfiguration
  • RBAC 严格限制 Secret 读权限
  • Secret 不出现在日志、环境变量 env.value 里(用 valueFrom.secretKeyRef
  • 定期轮换,有审计日志

九、可观测性#

三大支柱#

  • Metrics:Prometheus + Grafana,应用暴露 /metrics(Prometheus 格式)
  • Logs:结构化 JSON 输出到 stdout/stderr,由 Loki / ELK 采集
  • Traces:OpenTelemetry SDK 埋点,Jaeger / Tempo 收集

OpenTelemetry 是现在的标准选择,SDK 和协议层与后端解耦,一套代码可以对接任意兼容 OTLP 的收集器。

# ServiceMonitor 让 Prometheus 自动发现应用 metrics
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app-monitor
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics

仓库未覆盖:原生 Sidecar 容器(K8s 1.29 Beta,1.33 GA)#

传统 Sidecar(如 Envoy、日志代理)和主容器平起平坐,存在启动顺序和生命周期不确定的问题:主容器比 Sidecar 先启动时会连接失败;Job 中主容器退出后 Sidecar 不自动退出导致 Job 永远不结束。

K8s 1.28 以 Alpha 引入、1.29 默认启用、1.33 进入 Stable,通过 initContainersrestartPolicy: Always 声明:

spec:
  initContainers:
  - name: otel-collector          # 原生 Sidecar
    image: otel/opentelemetry-collector:latest
    restartPolicy: Always         # 关键字段
    ports:
    - containerPort: 4317
  containers:
  - name: app
    image: my-app:latest

原生 Sidecar 保证:

  1. 在主容器之前启动并就绪,主容器才会开始运行
  2. 主容器退出时,Sidecar 随之停止(解决 Job 挂住问题)
  3. 重启策略独立,主容器崩溃不影响 Sidecar

十、GitOps#

GitOps 核心原则:Git 是唯一的事实来源。所有集群变更通过 Git PR/commit 触发,控制器持续对比期望状态和实际状态并自动收敛。

# Argo CD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/k8s-manifests
    targetRevision: main
    path: apps/my-app
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true        # 删除 Git 中不存在的资源
      selfHeal: true     # 漂移后自动恢复
    syncOptions:
    - CreateNamespace=true

CI/CD 必做项:

  • 镜像 tag 用 commit SHA,禁止 latest
  • 构建时做漏洞扫描(Trivy / Grype)
  • 镜像来源只允许受信任的 Registry
  • Manifest 变更经过 review,不直接 kubectl apply

十一、生产就绪清单#

在将工作负载推向生产前,逐项检查:

安全

  • Pod Security Standards 已应用(推荐 restricted
  • 容器以非 root 运行,读写权限最小化
  • RBAC 遵循最小权限,无 cluster-admin 滥用
  • Secrets 不以明文存储,etcd 开启加密

配置

  • 所有容器设置了 resources.requests,memory 设置了 limits
  • liveness / readiness probe 已配置,startup probe 按需添加
  • 标准 label 已应用(app.kubernetes.io/*

可用性

  • 副本数 ≥ 2
  • PodDisruptionBudget 已配置
  • Pod 反亲和性规则已配置,跨节点/跨 AZ 分布

扩缩容

  • HPA 已配置并验证目标利用率计算合理
  • VPA 推荐模式已验证资源配置合理

网络

  • Network Policy 默认拒绝,显式开放必要端口
  • 对外暴露的服务使用 Gateway API / Ingress,而非直接 NodePort
  • 服务间通信考虑 mTLS(Service Mesh 或 Cilium Network Policy)

关闭

  • terminationGracePeriodSeconds 已配置
  • preStop hook 或应用内 SIGTERM 处理已实现

可观测性

  • Metrics endpoint 已暴露,ServiceMonitor 已创建
  • 结构化日志输出到 stdout
  • 关键路径有分布式追踪

部署

  • 镜像 tag 为 commit SHA,非 latest
  • CI 镜像扫描已接入
  • GitOps 工具管理所有变更,无手动 kubectl apply

总结#

kubernetes-best-practices 是一份质量不错的参考手册,覆盖了从安全、资源管理到 GitOps 的主要实践。建议补充关注:

  • Gateway API:Ingress 的正式继任者,v1.0 于 2023-10 GA、当前已发布到 v1.5.x,新项目应优先考虑
  • ValidatingAdmissionPolicy:轻量级策略引擎,适合替代简单的 Kyverno/OPA 用例
  • 原生 Sidecar:解决了长期存在的启动顺序和 Job 生命周期问题

K8s 生态演进很快,核心 API 的稳定性和表达能力都在持续提升。定期回顾 CHANGELOGenhancements 是保持知识更新的好习惯。