235|Kubernetes 核心对象:Pod、Deployment、StatefulSet、Service、Ingress

DevOps、容器与云原生约 21 分钟阅读12,099 字符
知识库文档 236 / 251

阅读时间:约 45 分钟 | 难度:中高级 适用读者:Java 后端工程师、DevOps 工程师、架构师 前置知识:Docker 基础、YAML 语法、Linux 网络基础


一、引言:为什么需要理解 Kubernetes 核心对象

Kubernetes(K8s)已经成为容器编排的事实标准。无论你用的是阿里云 ACK、腾讯云 TKE 还是自建集群,K8s 都在你的应用下面默默运转。很多 Java 工程师对 K8s 的理解停留在"能部署就行"——但面试官真正想考察的,是你对声明式 API 设计哲学的理解,以及这些核心对象在生产环境中的坑和最佳实践。

Kubernetes 的核心对象体系可以概括为五个层次:

文章配图
文章配图

  • Pod:最小的调度和运行单元
  • Deployment:无状态应用的声明式部署控制器
  • StatefulSet:有状态应用的部署控制器
  • Service:服务发现与负载均衡抽象
  • Ingress:七层 HTTP/HTTPS 路由入口

本文从设计原理出发,结合源码级细节和生产实践,深入剖析这五个核心对象。


二、Pod:最小调度单元

2.1 Pod 是什么,为什么不是容器

很多人刚接触 K8s 时会问:既然 Docker 已经能运行容器了,为什么还要套一层 Pod?

Pod 是 Kubernetes 的最小调度单元,不是最小运行单元。 容器仍然是最小运行单元,但 K8s 不会直接调度单个容器——它调度的最小粒度是 Pod。

设计 Pod 有两个核心原因:

原因一:解决"紧密耦合"容器的协作问题。 假设你有一个 Java 应用容器和一个日志收集 sidecar 容器(如 Filebeat),它们需要共享文件系统、共享网络命名空间。如果直接调度两个独立容器,编排它们在同一台机器上运行、共享资源会非常复杂。Pod 把这种"一组需要紧密协作的容器"抽象为一个原子调度单元。

原因二:简化容器运行时接口(CRI)。 K8s 只调度 Pod,不调度容器。这意味 K8s 只需要定义 Pod 的接口,底层可以用 Docker、containerd、CRI-O 等任意容器运行时,只要它们能创建 Pod 就行。

2.2 Pod 内部的网络与存储模型

Pod 内部的所有容器共享两个关键资源:

文章配图
文章配图

共享网络命名空间(Network Namespace):

  • 同一个 Pod 内的容器共享同一个 IP 地址和端口空间
  • 容器之间通过 localhost 通信,无需 service 发现
  • 这意味着 Pod 内容器不能绑定相同端口

共享存储卷(Volume):

  • Pod 内所有容器可以挂载同一个 Volume
  • 这是 sidecar 模式(日志收集、代理注入)的物理基础
code
# 一个典型的 Pod 内部结构
Pod IP: 10.244.1.5
├── pause 容器(基础设施容器,持有网络命名空间)
├── app 容器(Java 应用,端口 8080)
│   └── 挂载 /app/logs → Volume: app-logs
└── filebeat 容器(日志收集,端口 5044)
    └── 挂载 /app/logs → Volume: app-logs(同一个卷)

2.3 pause 容器:Pod 的"地基"

每个 Pod 启动时,kubelet 会先创建一个 pause 容器(也叫 infra 容器)。这个容器极其轻量,只做一件事——持有 Pod 的网络命名空间和 IPC 命名空间。

Pod 内的其他容器启动时,会 --net=container:pause 加入 pause 容器的命名空间。这意味着:

  • pause 容器挂了 = Pod 网络命名空间没了 = 整个 Pod 需要重建
  • pause 容器是 Pod 生命周期的"锚点"

2.4 Pod 生命周期

Pod 生命周期有五个关键阶段:

文章配图
文章配图

阶段 含义 常见原因
Pending Pod 已创建但容器未启动 调度中、镜像拉取中、资源不足
Running 至少有一个容器在运行 正常运行状态
Succeeded 所有容器正常退出(exit 0) Job/CronJob 完成
Failed 所有容器终止,至少一个异常退出 应用 crash、OOMKilled
Unknown 无法获取 Pod 状态 节点失联、kubelet 异常

除此之外,Pod 还有 Conditions 字段,比 Phase 更细粒度:

  • PodScheduled:是否已调度到节点
  • ContainersReady:所有容器是否就绪
  • Initialized:所有 init 容器是否完成
  • Ready:Pod 是否可接受流量

2.5 Init 容器与 Sidecar 容器

K8s 1.28 之前,只有两种容器类型:init 容器和普通容器。1.28 引入了 sidecar 容器(带 restartPolicy: Always 的 init 容器),解决了 sidecar 启动顺序问题。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-sidecar
spec:
  initContainers:
  - name: init-db
    image: busybox
    command: ['sh', '-c', 'until nc -z db 3306; do sleep 2; done']
  - name: sidecar-proxy
    image: envoyproxy/envoy
    restartPolicy: Always  # 1.28+ sidecar 特性
  containers:
  - name: app
    image: my-java-app:latest

Init 容器:按顺序执行,全部成功后才会启动普通容器。用于数据库就绪检查、配置文件生成等初始化任务。

Sidecar 容器(1.28+):与普通容器并行运行,但在普通容器之前启动。用于服务网格代理、日志收集等需要在应用启动前就绪的场景。

2.6 探针:让 Pod 真正"可用"

Pod 的 Running 状态不等于"可以接受流量"。K8s 提供三种探针来精确控制 Pod 的就绪和存活:

探针类型 作用 失败后果 典型用途
livenessProbe 判断容器是否存活 重启容器 死锁检测、内存泄漏恢复
readinessProbe 判断容器是否可接受流量 从 Service 摘除 依赖就绪检查、预热完成
startupProbe 判断容器是否启动完成 阻塞其他探针 启动慢的应用保护

关键配置参数:

yaml
livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 30   # 启动后等待时间
  periodSeconds: 10          # 探测间隔
  timeoutSeconds: 5          # 超时时间
  failureThreshold: 3        # 失败多少次才判定为失败
  successThreshold: 1        # 成功多少次才判定为成功

生产环境最佳实践:

  • readinessProbe 应检查关键依赖(数据库连接池、缓存连接),而不仅仅是 HTTP 200
  • livenessProbe 应足够轻量,避免依赖外部服务(外部服务挂了会导致级联重启)
  • 启动慢的应用(如 Spring Boot)一定要设置 startupProbe,否则 initialDelaySeconds 过长会导致就绪延迟,过短会导致提前被 kill

2.7 资源限制与 QoS

Pod 的 CPU 和内存通过 requests 和 limits 控制:

yaml
resources:
  requests:
    memory: "512Mi"
    cpu: "500m"
  limits:
    memory: "1Gi"
    cpu: "1000m"
  • requests:调度器用来决定 Pod 放在哪个节点上(保证的基本资源量)
  • limits:容器能使用的资源上限

根据 requests 和 limits 的关系,Pod 分为三个 QoS 等级:

QoS 等级 条件 OOM 优先级
Guaranteed requests == limits(所有容器) 最低被杀
Burstable requests < limits 中等被杀
BestEffort 未设置 requests 和 limits 最先被杀

生产环境建议:核心服务设为 Guaranteed,非关键服务设为 Burstable。永远不要用 BestEffort。


三、Deployment:声明式部署与滚动更新

3.1 从 ReplicaSet 到 Deployment

要理解 Deployment,先理解 ReplicaSet:

ReplicaSet:保证指定数量的 Pod 副本始终运行。它是"命令式"的——你告诉它"保持 3 个副本",它就持续 reconcile 到 3 个。

Deployment:在 ReplicaSet 之上加了声明式更新能力。你声明"我的应用应该是 my-app:v2 这个版本",Deployment 负责通过创建新 ReplicaSet、逐步替换 Pod 来完成更新。

Deployment 的核心设计模式是控制器模式(Controller Pattern):声明期望状态 → 控制器持续 reconcile 实际状态 → 趋近期望状态。

3.2 Deployment 的核心字段

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3                    # 期望副本数
  revisionHistoryLimit: 10       # 保留的历史 ReplicaSet 数量
  strategy:
    type: RollingUpdate          # 更新策略
    rollingUpdate:
      maxSurge: 1                # 滚动更新时最多超出几个 Pod
      maxUnavailable: 0          # 滚动更新时最多几个 Pod 不可用
  selector:
    matchLabels:
      app: order-service
  template:                      # Pod 模板
    metadata:
      labels:
        app: order-service
        version: v2
    spec:
      containers:
      - name: app
        image: order-service:v2
        ports:
        - containerPort: 8080

3.3 滚动更新机制详解

Deployment 的滚动更新是面试高频考点。核心流程如下:

文章配图
文章配图

步骤分解:

  1. 用户更新 Deployment(如修改 image tag)
  2. Deployment Controller 创建新 ReplicaSet(RS-v2),replicas=0
  3. 逐步扩容新 RS、缩容旧 RS,步长由 maxSurge 和 maxUnavailable 控制
  4. 新 Pod 通过 readinessProbe 后,才摘除旧 Pod
  5. 旧 RS 缩容到 0,保留(数量由 revisionHistoryLimit 决定)用于回滚

maxSurge 和 maxUnavailable 的作用:

code
假设 replicas = 3, maxSurge = 1, maxUnavailable = 0:
Step 1: RS-v1=3, RS-v2=0 → 创建 1 个新 Pod
Step 2: RS-v1=3, RS-v2=1 → 新 Pod Ready → 删除 1 个旧 Pod
Step 3: RS-v1=2, RS-v2=1 → 创建 1 个新 Pod
Step 4: RS-v1=2, RS-v2=2 → 新 Pod Ready → 删除 1 个旧 Pod
... 依此类推

maxUnavailable=0 的含义:滚动更新期间,可用 Pod 数始终 ≥ replicas(即 3 个)。这是生产环境推荐设置——宁可慢一点,也不能损失可用性。

3.4 回滚机制

bash
# 查看历史版本
kubectl rollout history deployment/order-service

# 回滚到上一版本
kubectl rollout undo deployment/order-service

# 回滚到指定版本
kubectl rollout undo deployment/order-service --to-revision=3

回滚本质上是把旧 ReplicaSet 重新扩容,把当前 ReplicaSet 缩容。因为旧 RS 的 Pod 模板保留了完整的配置,所以回滚是可靠的。

重要:回滚只回滚 Pod 模板(image、env、resources 等),不会回滚关联的 Service、ConfigMap、Ingress 等资源——这些需要单独管理。

3.5 蓝绿部署与金丝雀发布

Deployment 原生只支持滚动更新。蓝绿部署和金丝雀发布需要额外工具或模式:

蓝绿部署:准备两套完整的 Deployment(blue 和 green),通过切换 Service 的 selector 来切换流量。优点是瞬间切换,缺点是资源翻倍。

金丝雀发布:新版本只部署少量 Pod(如 1 个),配合 Ingress 或 Service Mesh(如 Istio)按比例引流。如果新版本正常,逐步扩大比例。


四、StatefulSet:有状态应用管理

4.1 为什么需要 StatefulSet

Deployment 管理的 Pod 是无状态的、可互换的。Pod 名称随机(如 order-service-7d8f9b6c-abcde),挂了就换一个新的,IP 也会变。

但有状态应用(数据库、消息队列、分布式存储)需要:

  • 稳定的网络标识:每个 Pod 有固定的 DNS 名称
  • 稳定的持久化存储:Pod 重建后仍挂载同一个 Volume
  • 有序的部署和扩缩容:按 0→1→2 顺序启动,按 2→1→0 顺序缩容
  • 有序的滚动更新:按 2→1→0 顺序更新

StatefulSet 就是为满足这些需求设计的。

4.2 StatefulSet 的核心特性

yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: kafka
spec:
  serviceName: kafka-headless   # 必须关联一个 Headless Service
  replicas: 3
  podManagementPolicy: OrderedReady  # 或 Parallel
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0               # 分区更新
  volumeClaimTemplates:          # 为每个 Pod 独立创建 PVC
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 100Gi
  selector:
    matchLabels:
      app: kafka
  template:
    metadata:
      labels:
        app: kafka
    spec:
      containers:
      - name: kafka
        image: confluentinc/cp-kafka:7.5.0

四个核心特性:

文章配图
文章配图

1. 稳定的网络标识 每个 Pod 有固定名称:{statefulset-name}-{ordinal},如 kafka-0、kafka-1、kafka-2。 配合 Headless Service,每个 Pod 有独立的 DNS 记录:

code
kafka-0.kafka-headless.default.svc.cluster.local
kafka-1.kafka-headless.default.svc.cluster.local
kafka-2.kafka-headless.default.svc.cluster.local

2. 稳定的持久化存储volumeClaimTemplates 为每个 Pod 创建独立的 PVC。Pod 重建后,同名 PVC 重新绑定——数据不丢失。PVC 的生命周期独立于 Pod,即使 StatefulSet 被删除,PVC 也不会自动删除。

3. 有序部署(podManagementPolicy: OrderedReady)

  • 启动:严格按 kafka-0 → kafka-1 → kafka-2 顺序,每个 Pod 必须 Ready 后才能启动下一个
  • 缩容:严格按 kafka-2 → kafka-1 → kafka-0 顺序,每个 Pod 必须完全停止后才能缩容下一个
  • 这保证了 Kafka、ZooKeeper 等集群的 leader 选举不会混乱

4. 有序滚动更新

  • 默认从最大序号开始更新,倒序进行
  • partition 字段可以实现金丝雀更新:如果 partition=2,则只有序号 ≥2 的 Pod 更新(即 kafka-2),kafka-0 和 kafka-1 不变

4.3 Headless Service 的作用

StatefulSet 必须关联一个 clusterIP: None 的 Headless Service。它与普通 Service 的区别:

特性 普通 Service Headless Service
Cluster IP 分配一个虚拟 IP 无 Cluster IP
DNS 解析 返回 Cluster IP 返回所有 Pod IP
用途 负载均衡 服务发现(每个 Pod 独立访问)

对于 Kafka 这样的应用,客户端需要知道每个 broker 的地址——Headless Service 让客户端通过 DNS 发现所有 Pod。

4.4 StatefulSet vs Deployment 选型

场景 选择 原因
无状态 API 服务 Deployment 快速扩缩容,Pod 可互换
数据库、消息队列 StatefulSet 需要稳定网络标识和持久存储
缓存集群(Redis Cluster) StatefulSet 节点间需要互相识别
定时任务 CronJob 一次性或周期性执行
每个节点一个 Pod DaemonSet 日志收集、监控 agent

五、Service:服务发现与负载均衡

5.1 Service 的本质

Service 是 Kubernetes 中最容易被误解的对象。很多人以为 Service 是一个"代理"或"负载均衡器",但实际上:

Service 是一个虚拟的、逻辑上的服务抽象——它本身不处理任何流量。 Service 本质上是一组 iptables/IPVS 规则的集合,这些规则由 kube-proxy 在节点上维护。

当你创建一个 Service 时:

  1. kube-proxy 在节点上添加 iptables/IPVS 规则
  2. 流量到达节点后,规则将流量转发到 Service 选择的后端 Pod
  3. Service 本身没有进程、不监听端口、不转发流量

5.2 四种 Service 类型

文章配图
文章配图

类型 访问范围 Cluster IP 实现方式 典型场景
ClusterIP 集群内部 分配 iptables/IPVS 微服务间通信
NodePort 节点 IP + 端口 分配 iptables + 节点端口 开发测试、简单暴露
LoadBalancer 外部 分配 云厂商 LB 生产环境外部访问
ExternalName 不适用 无 DNS CNAME 外部服务映射

ClusterIP 流量路径(iptables 模式):

code
请求 → Service ClusterIP:80
  → iptables 随机选择:
    → Pod-A 10.244.1.5:8080
    → Pod-B 10.244.2.3:8080
    → Pod-C 10.244.1.7:8080

5.3 kube-proxy 的三种模式

kube-proxy 是 Service 的底层实现引擎,有三种工作模式:

模式 原理 性能 特性
userspace 请求到 kube-proxy 用户态进程转发 最差 已废弃
iptables 内核态 iptables 规则转发 中等 规则数多时性能下降
IPVS 内核态 IPVS 负载均衡 最优 支持多种调度算法

IPVS 模式的优势:

  • 支持 rr(轮询)、lc(最少连接)、dh(目标哈希)、sh(源哈希)等调度算法
  • 规则数量多时性能远优于 iptables(iptables 是 O(n) 链式匹配,IPVS 是 O(1) 哈希查找)
  • 生产环境强烈推荐使用 IPVS 模式

5.4 Service 与 Endpoints

Service 通过 selector 匹配 Pod 的 labels,自动创建 Endpoints 对象:

yaml
# Service 自动创建的 Endpoints
apiVersion: v1
kind: Endpoints
metadata:
  name: order-service
subsets:
- addresses:
  - ip: 10.244.1.5
  - ip: 10.244.2.3
  - ip: 10.244.1.7
  ports:
  - port: 8080

Endpoints 的更新机制:

  • kube-controller-manager 中的 Endpoints Controller 持续 watch Pod 变化
  • Pod 的 readinessProbe 失败 → Endpoints 摘除该 Pod IP
  • 这就是为什么 readinessProbe 失败后 Pod 不会重启,但会被 Service 摘除

EndpointSlice(1.21+ GA): 当 Service 后端有大量 Pod 时,单个 Endpoints 对象会变得巨大(每次更新都要全量传输)。EndpointSlice 将 Endpoints 拆分为多个小对象,每个默认最多 100 个 endpoint,显著提升扩展性。

5.5 无 Selector 的 Service

Service 可以不设置 selector,手动创建 Endpoints:

yaml
apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  ports:
  - port: 3306
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-db
subsets:
- addresses:
  - ip: 192.168.1.100      # 集群外部的数据库 IP
  ports:
  - port: 3306

用途:将集群外部的服务(如自建数据库、第三方 API)映射为集群内的 Service,让应用代码无需知道外部地址。


六、Ingress:七层路由与流量管理

6.1 Ingress 的工作原理

Service 解决的是四层(TCP/UDP)的流量路由问题。但在 HTTP/HTTPS 层面,我们需要更细粒度的控制:

  • 根据域名路由:api.example.com → api-service,web.example.com → web-service
  • 根据路径路由:/api/v1 → v1-service,/api/v2 → v2-service
  • TLS 终止、限流、重写、跨域等七层功能

这就是 Ingress 的职责。

文章配图
文章配图

6.2 Ingress 的核心组件

Ingress 不是一个独立工作的对象——它需要三个组件配合:

组件 职责 示例
Ingress 资源 声明路由规则(域名、路径、TLS) YAML 配置
Ingress Controller 实际处理流量的控制器 Pod Nginx Ingress、Traefik、Istio Gateway
LoadBalancer Service 将外部流量导入 Ingress Controller 云厂商 LB 或 NodePort
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.example.com
    secretName: api-tls-secret
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /orders
        pathType: Prefix
        backend:
          service:
            name: order-service
            port:
              number: 8080
      - path: /users
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 8080

6.3 pathType 的三种模式

pathType 匹配规则 示例
Prefix 路径前缀匹配,/foo 匹配 /foo、/foo/bar 最常用
Exact 精确匹配,/foo 只匹配 /foo 健康检查端点
ImplementationSpecific 由 Ingress Controller 决定 取决于具体实现

6.4 Ingress Controller 的演进:Gateway API

Ingress 的局限性在复杂场景下逐渐暴露:

  • 不同 Ingress Controller 的 annotation 互不兼容——迁移成本高
  • 跨命名空间路由、流量分割、A/B 测试等高级功能需要特定 controller 支持
  • 缺乏角色分离(平台管理员 vs 应用开发者)

Gateway API(K8s SIG-Network 推出的下一代入口网关标准)解决了这些问题:

yaml
# Gateway API 示例
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: order-route
spec:
  parentRefs:
  - name: app-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /orders
    backendRefs:
    - name: order-service-v1
      port: 8080
      weight: 90          # 90% 流量到 v1
    - name: order-service-v2
      port: 8080
      weight: 10          # 10% 流量到 v2(金丝雀)

Ingress vs Gateway API 对比:

维度 Ingress Gateway API
标准化 弱(依赖 annotation) 强(原生字段)
角色分离 无 GatewayClass/ Gateway/ Route 三层
流量管理 基础路由 权重、镜像、header match
跨命名空间 不支持 支持
生态成熟度 非常成熟 快速发展中

七、五个核心对象的协作关系

这五个对象不是各自孤立的——它们组合在一起,构成了 Kubernetes 应用部署的完整体系:

文章配图
文章配图

code
外部用户
  │
  ▼
Ingress (根据域名/路径路由)
  │
  ▼
Service (负载均衡到 Pod)
  │
  ├──▶ Pod (Deployment 管理)
  │     ├── Pod-7d8f (无状态,可替换)
  │     ├── Pod-9a3c (无状态,可替换)
  │     └── Pod-2b5f (无状态,可替换)
  │
  └──▶ Pod (StatefulSet 管理)
        ├── db-0 (有状态,稳定标识)
        ├── db-1 (有状态,稳定标识)
        └── db-2 (有状态,稳定标识)

关键协作规则:

  1. Deployment/StatefulSet → Pod:创建和管理 Pod 的生命周期
  2. Pod → Service:通过 labels 被 Service 的 selector 选中
  3. Service → Ingress:作为 Ingress 的 backend
  4. StatefulSet → Headless Service:必须关联 Headless Service 以提供稳定的 DNS

八、实战案例:一个典型的微服务部署

假设我们要在 K8s 上部署一个订单系统,包含以下组件:

  • order-service(无状态 API):3 副本,滚动更新
  • mysql(数据库):1 副本 StatefulSet,持久化存储
  • redis(缓存):3 副本 StatefulSet,集群模式
  • nginx(Ingress):入口路由,TLS 终止

完整部署架构:

code
                       ┌──────────────┐
                       │  Cloud LB    │
                       └──────┬───────┘
                              │
                       ┌──────▼───────┐
                       │ Nginx Ingress│  (Ingress Controller)
                       │  Controller  │
                       └──────┬───────┘
                              │
              ┌───────────────┼───────────────┐
              │               │               │
     ┌────────▼─────┐  ┌─────▼──────┐  ┌─────▼──────┐
     │ order-svc    │  │ user-svc   │  │ gateway    │
     │ (ClusterIP)  │  │ (ClusterIP)│  │ (ClusterIP)│
     └──────┬───────┘  └─────┬──────┘  └─────┬──────┘
            │                │                │
     ┌──────▼───────┐  ┌─────▼──────┐  ┌─────▼──────┐
     │ order-deploy │  │ user-deploy│  │ gateway    │
     │  (Deploy)    │  │  (Deploy)  │  │ (Deploy)   │
     └──────┬───────┘  └────────────┘  └────────────┘
            │
     ┌──────┼──────────────────┐
     │      │                  │
┌────▼──┐ ┌─▼──────┐  ┌───────▼──┐
│ MySQL │ │ Redis  │  │ RocketMQ │
│  STS  │ │  STS   │  │   STS    │
│(1副本)│ │(3副本) │  │ (2副本)  │
└───────┘ └────────┘  └──────────┘

关键配置要点:

  1. Deployment 的 readinessProbe:检查数据库连接、Redis 连接是否就绪,而不仅仅是 HTTP 200
  2. StatefulSet 的 podManagementPolicy:数据库用 OrderedReady,缓存集群可用 Parallel 加速启动
  3. Service 的 sessionAffinity:如果应用使用了 session sticky,需要设置 sessionAffinity: ClientIP
  4. Ingress 的 CORS 配置:通过 annotation 配置跨域,而不是在应用代码中配置

九、常见生产环境问题与排查思路

9.1 Pod 一直 Pending

原因排查优先级:

  1. kubectl describe pod 查看 Events
  2. 最常见原因:资源不足(CPU/内存不够)、PVC 未绑定、节点有 taint/toleration 不匹配
  3. 镜像拉取失败(ImagePullBackOff)——检查镜像仓库权限和镜像 tag 是否正确

9.2 Service 访问不到 Pod

排查步骤:

  1. 确认 Service 的 selector 和 Pod 的 labels 是否匹配(kubectl get endpoints 检查)
  2. 确认 Pod 的 readinessProbe 是否通过(kubectl get pods -o wide 查看 READY 列)
  3. 确认 kube-proxy 规则是否生效(进节点 iptables -t nat -L | grep <service-name>)
  4. 确认网络插件(CNI)是否正常工作

9.3 Deployment 滚动更新卡住

常见原因:

  • 新 Pod 的 readinessProbe 一直失败 → 卡在"等待新 Pod ready"
  • maxUnavailable=0 且 maxSurge=0 → 没有余量,无法更新
  • 新 Pod 无法调度(资源不足)→ 卡在 Pending

9.4 StatefulSet Pod 无法启动

排查重点:

  • PVC 是否绑定成功(kubectl get pvc)
  • 前一个序号的 Pod 是否 Ready(OrderedReady 策略下,Pod-1 必须等 Pod-0 Ready)
  • 存储后端是否正常(CSI driver 问题)

十、面试问答

以下问题从面试官视角设计,考察对 Kubernetes 核心对象的深入理解。先尝试自己回答,再对照参考答案。

Q1:Service 的 ClusterIP 模式是如何实现负载均衡的?流量真的经过 ClusterIP 这个虚拟 IP 吗?

面试官意图:考察对 kube-proxy 原理的理解,区分"逻辑抽象"和"物理实现"。

参考答案:流量不经过 ClusterIP 这个虚拟 IP——它只是一个逻辑标识。实际上,kube-proxy 在节点上维护 iptables/IPVS 规则,这些规则将目标地址为 ClusterIP 的流量随机转发到后端 Pod 的 IP。以 iptables 模式为例:每个 Service 创建一条 iptables 链,链中为每个后端 Pod 创建一条规则,用 statistic 模块实现随机选择。这本质上是 DNAT(目标地址转换)——流量到达节点后,内核将目标地址从 ClusterIP 改为 Pod IP,然后走正常的网络栈转发。

Q2:Deployment 的滚动更新过程中,如果新 Pod 的 readinessProbe 一直失败,会发生什么?旧 Pod 会被删除吗?

面试官意图:考察对滚动更新安全机制的深入理解。

参考答案:不会。Deployment Controller 的逻辑是:先创建新 Pod → 等待新 Pod Ready → 再删除旧 Pod。如果新 Pod 的 readinessProbe 一直失败,Controller 会一直等待(不会删除旧 Pod),最终滚动更新超时,但旧 Pod 完好无损。这就是 Deployment 的"安全失败"设计——宁可更新失败,也不影响线上服务。排查方法是 kubectl describe deployment 查看 Conditions,以及 kubectl logs 查看新 Pod 的启动日志。

Q3:StatefulSet 的 Pod 挂了,重建后 IP 会变吗?为什么?

面试官意图:考察对 StatefulSet 网络标识机制的理解,以及和 Deployment 的区别。

参考答案:Pod 的 IP 会变,但 DNS 名称不变。StatefulSet 保证的是稳定的网络标识({name}-{ordinal}.{headless-service}.{namespace}.svc.cluster.local),而不是 IP 地址。Pod 重建后,DNS 记录会更新指向新 IP。这就是为什么有状态应用(如 Kafka)应该通过 DNS 名称(kafka-0.kafka-headless)而不是 IP 地址来互相发现——DNS 名称是稳定的,IP 是临时的。这也是为什么 StatefulSet 必须关联 Headless Service——它需要 DNS 来提供稳定的网络标识。

Q4:Ingress 和 Service(LoadBalancer 类型)的区别是什么?什么时候用 Ingress,什么时候用 LoadBalancer Service?

面试官意图:考察对四层和七层负载均衡的理解,以及架构选型能力。

参考答案:核心区别是工作层级:Service(LoadBalancer)工作在四层,只能按 IP:Port 转发,无法按域名/路径路由;Ingress 工作在七层,可以按 HTTP Host/Path 路由,支持 TLS 终止、限流、重写等高级功能。选型:如果只有一个服务需要暴露,用 LoadBalancer Service 最简单;如果多个服务需要共享同一个入口(按域名/路径区分),用 Ingress 可以节省 LB 成本(一个 LB 服务多个应用)。生产环境的标准做法是:一个 Ingress Controller(用 LoadBalancer Service 暴露)+ 多个 Ingress 资源定义路由规则。

Q5:Pod 的 requests 和 limits 有什么区别?如果只设置 limits 不设置 requests 会怎样?

面试官意图:考察对资源管理和 QoS 机制的理解。

参考答案:requests 是调度器用来决定 Pod 放在哪里的"保证资源量",limits 是容器能使用的上限。如果只设置 limits 不设置 requests,K8s 会自动将 requests 设为等于 limits——这意味着这个 Pod 的 QoS 等级是 Guaranteed(最高优先级,最不容易被 OOM Kill)。但这个行为可能不是你想要的:如果 limits 设得很大,requests 也会变得很大,导致调度器找不到足够资源的节点,Pod 一直 Pending。最佳实践:requests 和 limits 分开设置,requests 根据实际使用量设置(加 20% buffer),limits 根据容量规划设置。

Q6:如果你要设计一个零宕机时间的滚动更新方案,需要考虑哪些因素?

面试官意图:考察系统设计能力和对 Kubernetes 机制的综合运用。

参考答案:零宕机滚动更新需要考虑以下方面:

  1. Pod 层面:配置正确的 readinessProbe(确保新 Pod 完全就绪后才接受流量)和 preStop hook(给旧 Pod 足够时间处理完现有请求,建议 sleep 5-10 秒)
  2. Deployment 层面:设置 maxSurge > 0 且 maxUnavailable = 0,确保更新期间可用副本数不减少
  3. Service 层面:确认 publishNotReadyAddresses: false(默认),确保未就绪的 Pod 不会被加入 Service 后端
  4. 应用层面:应用需要处理 SIGTERM 信号,实现优雅关闭(Spring Boot 的 graceful shutdown);应用需要无状态或外部化状态
  5. Ingress 层面:如果有 Ingress,确认 Ingress Controller 的端点更新延迟(通常几秒),避免短暂地将流量路由到已终止的 Pod
  6. 数据库兼容:确保新旧版本应用兼容同一数据库 schema(先执行数据库迁移,再更新应用)

本文完。 下一篇预告:236|Kubernetes 调度器原理:资源调度、亲和性、污点容忍与自定义调度器。