跳到正文

K8s 配置解剖图 ​

一句话:K8s 的 YAML 不是配置清单,而是对控制器的指令——每个字段都在改变调度、流量或故障时的行为;写错一个 selector,控制器会忠实执行错误结果。

适用版本:Kubernetes 1.29+,ingress-nginx 1.9+。默认值以官方文档为准。

为什么需要 ​

只记「Deployment 管副本、Service 管入口」不够。事故往往出在字段之间的关系:selector 和 Pod label 不一致导致 Endpoint 为空,maxUnavailable 和 maxSurge 同时为 0 导致发布卡死,HPA 指向的 Deployment 没设 requests 导致 TARGETS 永远是 unknown。

看懂字段的影响,才能在改配置前预判它会动到调度、流量还是生命周期。

核心机制 ​

Deployment:副本、发布与生命周期 ​

yaml
apiVersion: apps/v1
kind: Deployment
metadata: {name: order, namespace: default}
spec:
  replicas: 3
  revisionHistoryLimit: 10
  strategy: {type: RollingUpdate, rollingUpdate: {maxUnavailable: 0, maxSurge: 1}}
  selector: {matchLabels: {app: order}}
  template:
    metadata: {labels: {app: order}}
    spec:
      terminationGracePeriodSeconds: 30
      containers:
        - name: app
          image: nginx:1.27
          ports: [{containerPort: 80}]
          resources: {requests: {cpu: 500m, memory: 512Mi}, limits: {cpu: 1000m, memory: 1Gi}}
          readinessProbe: {httpGet: {path: /, port: 80}, periodSeconds: 5, failureThreshold: 1}
          livenessProbe: {httpGet: {path: /, port: 80}, periodSeconds: 10, failureThreshold: 3}
字段影响
replicas期望副本数;挂了 HPA 后由 HPA 覆盖,手改会被调回去
revisionHistoryLimit保留多少旧 ReplicaSet 供回滚,默认 10(以官方文档为准)
maxUnavailable: 0 + maxSurge: 1先多起一个再停旧的,发布期间容量不降;集群没余量时会 Pending
selector不可变,必须匹配 template 的 labels;写错就管不到 Pod
terminationGracePeriodSeconds删除 Pod 时给进程的退出时间,默认 30s;必须大于收尾耗时
requests / limitsrequests 决定调度,limits 决定限流和 OOM;只写 limits 会按 requests=limits 处理
readinessProbe失败只摘流量,不重启;failureThreshold: 1 适合快速止损
livenessProbe失败会重启容器;阈值太紧会把慢请求误判成死锁

Service:稳定入口与端口映射 ​

yaml
apiVersion: v1
kind: Service
metadata: {name: order, namespace: default}
spec: {type: ClusterIP, selector: {app: order}, ports: [{name: http, port: 80, targetPort: 80}]}

port 是 Service 端口,targetPort 是容器端口,两者可以不同。示例用 nginx 的 / 作健康路径,生产要换成应用真实的健康检查路径。selector 必须和 Pod label 一致,否则 Endpoint 为空,访问直接 connection refused。type 默认 ClusterIP;NodePort 用于调试,LoadBalancer 依赖云厂商。需要稳定 DNS 或客户端负载均衡时用 clusterIP: None(Headless)。

Ingress:七层路由 ​

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: {name: order, namespace: default}
spec:
  ingressClassName: nginx
  rules:
    - host: order.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service: {name: order, port: {number: 80}}

ingressClassName 决定由哪个控制器处理;pathType 的 Prefix 按路径前缀匹配,Exact 要求完全相等。灰度另建一个 Ingress 指向灰度 Service,再通过注解 nginx.ingress.kubernetes.io/canary: "true" 和 canary-weight 控制比例,完整示例见 03-灰度发布与交付平台。

HPA:指标目标与冷却 ​

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: {name: order, namespace: default}
spec:
  scaleTargetRef: {apiVersion: apps/v1, kind: Deployment, name: order}
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - {type: Resource, resource: {name: cpu, target: {type: Utilization, averageUtilization: 50}}}
  behavior: {scaleUp: {stabilizationWindowSeconds: 0}, scaleDown: {stabilizationWindowSeconds: 300}}

averageUtilization 的分母是 Pod 的 requests,没设 requests 就永远算不出利用率。scaleUp 默认 0s、scaleDown 默认 300s(以官方文档为准):扩得快、缩得慢,是为了防抖而不是省资源。

ResourceQuota:命名空间的总量约束 ​

yaml
apiVersion: v1
kind: ResourceQuota
metadata: {name: team-a, namespace: team-a}
spec:
  hard: {requests.cpu: "20", requests.memory: 40Gi, limits.cpu: "40", limits.memory: 80Gi, pods: "50"}

配额汇总命名空间内所有 Pod 的 requests/limits。一旦存在 CPU 或内存配额,新建 Pod 必须显式写 requests/limits,否则会被拒绝;想让团队不写也有默认值,要再配 LimitRange。

最小上手 ​

把上面的对象分别保存为文件,逐个 apply:

bash
kubectl create namespace team-a
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
kubectl apply -f hpa.yaml
kubectl apply -f quota.yaml

kubectl get deploy,svc,ingress,hpa -n default; kubectl get quota -n team-a
kubectl get endpoints order -n default
# → ENDPOINTS 必须有 Pod IP;为空说明 selector 或 readiness 有问题

前提:集群已装 ingress-nginx 和 metrics-server;否则 Ingress 没有 ADDRESS,HPA 的 TARGETS 是 <unknown>。

生产动作 ​

  • apply 前先校验:kubectl apply --dry-run=server -f <file>,服务端会检查字段、配额和准入策略。
  • 改 selector 前先建新对象:Deployment 的 selector 不可变,改它只能删了重建;灰度时用新 Deployment + 新 Service。
  • requests 必填:所有容器写 requests,核心服务 requests = limits;配额命名空间里缺 requests 会直接被拒绝。
  • 探针按 P99 启动时间定:慢启动应用加 startupProbe;不要让 liveness 在启动期反复杀容器。
  • 发布留余量:maxUnavailable: 0 需要集群有余量;容量紧张时先扩容,否则新 Pod 会 Pending。

边界 ​

  • YAML 正确不等于业务正确:控制器只保证对象存在,端口写错、健康检查路径不存在仍会全线 502。
  • 注解依赖控制器:nginx.ingress.kubernetes.io/* 只在 ingress-nginx 生效,换控制器后灰度直接失效。
  • HPA 不是万能弹性:没有 metrics-server、没有 requests、或节点没有余量,三者缺一个都扩不出来。
  • ResourceQuota 会拦住所有 Pod:配额一旦生效,没写 requests/limits 的旧模板会创建失败。
  • namespace 不是隔离边界:配额和 RBAC 才是;网络隔离要靠 NetworkPolicy。

讲清楚 ​

K8s 配置要按「它会影响什么」来读:replicas 和 HPA 影响副本,requests 影响调度,limits 影响限流和 OOM,selector 决定 Service 能不能找到 Pod,探针决定流量摘不摘、容器重不重启。

几个字段之间有硬关系:selector 必须匹配 template labels,HPA 的分母是 requests,ResourceQuota 生效后 Pod 必须写 requests/limits。忽略这些关系,YAML 能 apply 成功,运行时才炸。

生产上先做三件事:apply 前 server-side dry-run,所有容器写 requests,Service 建完立刻查 Endpoints。这三步能挡住大部分配置类故障。

把一门技术讲透到既懂也会用