Pod/Service/Ingress 深度解析
面试官:K8s 中 Pod 是什么?Container 和 Pod 有什么区别?
你:Pod 是 K8s 中最小的调度单元,一个 Pod 可以包含一个或多个容器。同一个 Pod 中的容器共享网络命名空间(同一 IP)和存储卷,是紧密协作的逻辑整体。相比之下,单独的容器之间完全隔离,无法通过 localhost 直接通信。
面试官:Pod 中的多个容器如何通信?为什么需要 Pause 容器?
这个追问很关键,能说清 Pod 网络模型和 Pause 容器作用的候选人,才真正理解了 K8s 的设计哲学。
链式追问一:Pod 核心原理
Section titled “链式追问一:Pod 核心原理”Q1:Pod 中为什么有 Pause 容器?它的作用是什么?必考
Section titled “Q1:Pod 中为什么有 Pause 容器?它的作用是什么?”Pause 容器(Infra 容器)是每个 Pod 的基础设施:
Pod 启动流程:
1. Kubelet 创建 Pod └── 首先启动 Pause 容器(gcr.io/google_containers/pause-amd64:3.2) ├── 创建并持有 Network Namespace ├── 创建并持有 IPC Namespace └── 分配 Pod IP(从节点网段)
2. 用户容器启动 ├── Container A:加入 Pause 的 Network Namespace │ └── 共享同一个 IP、网络栈 └── Container B:加入 Pause 的 Network Namespace └── 通过 localhost:port 互相访问
3. 容器重启场景 ├── 用户容器 A 崩溃重启 │ └── Pause 容器依然运行 → Pod IP 不变 └── 其他容器不受影响,继续通过原 IP 通信Pause 容器的三大作用:
| 作用 | 原理 | 实际意义 |
|---|---|---|
| 持有网络命名空间 | Pause 容器创建网络栈后不退出,生命周期与 Pod 相同 | Pod IP 恒定,容器重启不影响网络 |
| 持有 IPC 命名空间 | 同一 Pod 的容器共享 System V IPC、POSIX 消息队列 | 容器间可通过共享内存通信 |
| 作为僵尸进程回收者 | Pause 容器回收孤儿进程(reaper) | 避免容器内僵尸进程堆积 |
本质一句话:Pause 容器是 Pod 的”基础设施层”,通过持有命名空间确保 Pod 网络和 IPC 环境稳定,让业务容器来了又走而不影响整体网络拓扑。
Q2:Pod 的生命周期和三种探针的区别?必考
Section titled “Q2:Pod 的生命周期和三种探针的区别?”Pod 状态机:
Pod 生命周期状态流转:
Pending(等待调度) ├── 等待调度器分配节点 ├── 镜像拉取中 └── 资源不足无法调度 ↓Running(运行中) ├── 至少一个容器在运行 ├── 可能包含正常容器 + Init 容器 └── 可能正在重启容器 ↓ ┌──┴──┐ ↓ ↓Succeeded Failed(Job 完成) (容器异常退出) └── 所有容器成功退出 └── 至少一个容器以非 0 退出 └── 或被系统 OOM Kill
Unknown(节点通信故障) └── Kubelet 无法上报状态三种探针对比:
| 探针类型 | 检测目的 | 失败后的动作 | 典型配置场景 |
|---|---|---|---|
| livenessProbe(存活探针) | 容器是否还在运行(死锁、死循环) | 重启容器(restartPolicy: Always/OnFailure) | 应用死锁检测、JVM 卡死 |
| readinessProbe(就绪探针) | 容器是否准备好接受流量 | 从 Service Endpoints 移除,不再接收新请求 | 应用启动完成、依赖就绪检测 |
| startupProbe(启动探针) | 慢启动应用是否完成启动 | 阻止其他探针执行,直到启动成功 | 大型应用初始化(数据库迁移、缓存预热) |
探针配置实战:
spec: containers: - name: app image: myapp:1.0
# 启动探针:慢启动应用(如 Java 应用启动需 60s) startupProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 10 # 启动后 10s 开始探测 periodSeconds: 5 # 每 5s 探测一次 failureThreshold: 30 # 允许失败 30 次(150s 内) # 作用:禁用 liveness/readiness 直到启动完成,避免被误判为死锁
# 存活探针:检测死锁、线程池耗尽 livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 periodSeconds: 10 # 每 10s 探测 failureThreshold: 3 # 连续失败 3 次 → 重启容器 successThreshold: 1 timeoutSeconds: 5
# 就绪探针:滚动更新时新 Pod 必须通过 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 20 periodSeconds: 5 # 每 5s 探测 failureThreshold: 3 # 失败 3 次 → 从 Service 摘除 successThreshold: 1 # 成功 1 次 → 加入 Service探针执行时序:
Pod 启动: 0s → 容器启动 10s → startupProbe 开始探测(liveness/readiness 被阻塞) 60s → startupProbe 成功(或 150s 后失败重启) → livenessProbe 和 readinessProbe 开始工作 80s → readinessProbe 成功 → Pod 加入 Service Endpoints → 开始接收流量120s → livenessProbe 检测到死锁(连续失败 3 次) → 容器重启Q3:Resource 的 Request 和 Limit 有什么区别?OOM Kill 的原理是什么?必考
Section titled “Q3:Resource 的 Request 和 Limit 有什么区别?OOM Kill 的原理是什么?”Request vs Limit 对比:
| 维度 | Request(请求) | Limit(限制) |
|---|---|---|
| 作用阶段 | 调度阶段(Scheduler) | 运行阶段(Runtime) |
| 用途 | 节点资源分配依据 | 容器资源使用上限 |
| CPU 行为 | 调度器保证节点剩余资源 ≥ Request | 超过 Limit → CPU 被限速(Throttle) |
| 内存行为 | 调度器保证节点剩余内存 ≥ Request | 超过 Limit → OOM Kill(容器被杀) |
| QoS 等级 | Request = Limit → Guaranteed | Request < Limit → Burstable |
CPU Throttle 和 OOM Kill 原理:
CPU 限速机制(CFS Quota):
容器配置: request.cpu: 500m # 调度时保证分配 0.5 核 limit.cpu: 1000m # 运行时最多使用 1 核
CFS(Completely Fair Scheduler)实现: 1. 每 100ms(CFS 周期)为一个配额周期 2. 容器在 100ms 内最多使用 100ms × 1 核 = 100ms CPU 时间 3. 如果 50ms 内用完 100ms 配额: 容器在剩余 50ms 内被限速(Throttle),等待下一周期
性能影响: - CPU 密集型应用(计算、加密)频繁被 Throttle → 延迟飙升 - 建议:limit ≈ 实际峰值使用量 × 1.5 倍冗余内存 OOM Kill 机制:
容器配置: request.memory: 512Mi limit.memory: 1Gi
OOM 触发流程: 1. 容器内存使用接近 1Gi(如 900Mi) 2. 内核 OOM Killer 扫描进程树 3. 选择 OOM 分数最高的进程(通常是内存占用最大的) 4. 发送 SIGKILL 信号 → 容器被强制终止
OOM 分数计算: oom_score = (进程内存占用 / 总内存) × 1000 + oom_score_adj 容器进程的 oom_score_adj 根据 Limit 自动调整
实战案例: - JVM 堆内存设置 800Mi,但未限制 Metaspace、直接内存 - 容器总内存达到 1Gi → OOM Kill - 解决:JVM 堆 + 非堆内存 ≤ 容器 Limit 的 80%最佳实践配置:
resources: requests: cpu: "500m" # 调度依据:节点剩余资源 ≥ 500m memory: "512Mi" # 调度依据:节点剩余内存 ≥ 512Mi limits: cpu: "1000m" # 运行上限:最多使用 1 核 memory: "1Gi" # 运行上限:超过触发 OOM Kill
# Java 应用 JVM 参数(容器感知)env:- name: JAVA_OPTS value: > -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 # 堆内存 = Limit × 75%(留 25% 给非堆) -XX:InitialRAMPercentage=50.0本质一句话:Request 是调度器的承诺(保证最小资源),Limit 是运行时的红线(超过触发限速或 Kill),两者配合实现资源隔离和超额订阅。
链式追问二:Service 服务发现
Section titled “链式追问二:Service 服务发现”Q4:Service 的四种类型及应用场景?必考
Section titled “Q4:Service 的四种类型及应用场景?”四种 Service 类型对比:
| 类型 | 访问范围 | 实现机制 | 典型场景 | 性能数据 |
|---|---|---|---|---|
| ClusterIP(默认) | 集群内部 | 虚拟 IP(VIP),iptables/ipvs 转发 | 微服务间通信 | 延迟 ~0.5ms(同节点) |
| NodePort | 集群外部(通过节点 IP) | 节点开放端口(30000-32767) | 开发测试环境、临时暴露 | 延迟 ~1-2ms |
| LoadBalancer | 集群外部(云厂商 LB) | 自动创建云负载均衡器(AWS ALB/阿里云 SLB) | 生产环境对外暴露服务 | 延迟 ~5-10ms(经过云 LB) |
| ExternalName | 集群内部访问外部服务 | CoreDNS CNAME 映射 | 访问外部数据库、第三方 API | 无转发 |
配置示例与流量路径:
# ClusterIP:集群内部访问apiVersion: v1kind: Servicemetadata: name: user-servicespec: type: ClusterIP # 默认类型 selector: app: user-service ports: - port: 80 # Service 端口 targetPort: 8080 # Pod 端口ClusterIP 流量路径(iptables 模式):
Client Pod → ClusterIP:80 ↓ iptables DNAT(目标地址转换) ↓ Pod IP:8080(随机选择) ↓ 直接到达 Pod(不经代理)
性能数据: - 单节点 iptables 规则数:10,000 条 Service → iptables 延迟 ~1ms - ipvs 模式(K8s 1.11+):支持 10万+ Service,延迟 <0.5ms# NodePort:通过节点 IP + 端口访问spec: type: NodePort ports: - port: 80 targetPort: 8080 nodePort: 30080 # 每个节点的 30080 端口 → ServiceNodePort 流量路径:
外部客户端 → Node1:30080 ↓ kube-proxy(监听 NodePort) ↓ ClusterIP:80 ↓ Pod IP:8080
注意:即使访问 Node1,Pod 可能调度在 Node2(跨节点转发)# LoadBalancer:云厂商负载均衡器(生产环境)spec: type: LoadBalancer ports: - port: 80 targetPort: 8080 # 云厂商自动分配外部 IP(如 AWS ALB)LoadBalancer 流量路径:
客户端 → 云 LB(公网 IP) ↓ 健康检查 → 只转发到健康节点 ↓ NodePort 或直接到 Pod(取决于云厂商实现)Q5:Service 如何发现 Pod 并实现负载均衡?高频
Section titled “Q5:Service 如何发现 Pod 并实现负载均衡?”服务发现完整流程:
Service → Pod 的服务发现机制:
1. Service 定义 Label Selector ↓2. Endpoints Controller 监听 Pod 变化 └── 匹配 Label 的 Pod IP:Port 写入 Endpoints 对象 ↓3. kube-proxy 监听 Endpoints 变化 └── 更新节点的 iptables/ipvs 规则 ↓4. CoreDNS 解析 Service 名称 └── my-service.default.svc.cluster.local → ClusterIPkube-proxy 的三种模式对比:
| 模式 | 实现原理 | 性能 | 连接数限制 | 适用场景 |
|---|---|---|---|---|
| userspace(已废弃) | 用户态代理,所有流量经过 kube-proxy 进程 | 低(多次上下文切换) | 无限制 | 早期 K8s(<1.2) |
| iptables(默认) | 内核态 DNAT 规则,随机选择 Pod | 高(~0.5ms) | 规则数 O(n²),大集群慢 | 中小集群(<5000 Service) |
| ipvs(推荐) | 内核态 LVS,支持多种调度算法 | 极高(~0.1ms) | 哈希表 O(1),支持 10万+ Service | 大规模集群 |
iptables 模式的负载均衡实现:
# 查看 iptables 规则(简化版)iptables -t nat -L KUBE-SERVICES
# 规则示例:Chain KUBE-SVC-XXX (user-service)├── 33% probability → KUBE-SEP-POD1 # Pod1 IP:8080├── 33% probability → KUBE-SEP-POD2 # Pod2 IP:8080└── 34% probability → KUBE-SEP-POD3 # Pod3 IP:8080ipvs 模式的负载均衡算法:
# kube-proxy 配置(ipvs 模式)apiVersion: kubeproxy.config.k8s.io/v1alpha1kind: KubeProxyConfigurationmode: ipvsipvs: scheduler: rr # 轮询(Round Robin) # 可选算法: # - lc: 最少连接(Least Connections) # - dh: 目标哈希(Destination Hashing) # - sh: 源地址哈希(Source Hashing) # - sed: 最短期望延迟(Shortest Expected Delay)CoreDNS 服务发现:
Service 名称解析规则:
同命名空间: http://user-service:80 └── 解析为 user-service.default.svc.cluster.local
跨命名空间: http://user-service.prod:80 └── 解析为 user-service.prod.svc.cluster.local
完整 FQDN: http://user-service.prod.svc.cluster.local:80 └── 格式:<service>.<namespace>.svc.cluster.local
性能数据: - CoreDNS 单实例 QPS:~10,000+ - 缓存命中延迟:<1ms - 缓存未命中延迟:~5-10ms(需查询上游 DNS)链式追问三:Ingress 七层路由
Section titled “链式追问三:Ingress 七层路由”Q6:Ingress 和 Service 的区别?为什么需要 Ingress?高频
Section titled “Q6:Ingress 和 Service 的区别?为什么需要 Ingress?”四层 vs 七层负载均衡对比:
Service(L4,传输层): ├── 基于 IP + 端口转发 ├── 不理解 HTTP 协议 ├── 无法按域名、路径、Header 路由 └── 每个服务需要一个 LoadBalancer(成本高)
Ingress(L7,应用层): ├── 基于 HTTP 协议(域名、路径、Header) ├── 支持路由规则:api.example.com/api/users → user-service ├── SSL 终止:在 Ingress 处理 HTTPS,后端用 HTTP ├── 多个服务共享一个入口 IP(节省 LoadBalancer 成本) └── 需要安装 Ingress Controller(Nginx/Traefik/Istio Gateway)Ingress 架构图:
客户端请求(HTTPS) ↓云 LoadBalancer(AWS ALB / 阿里云 SLB) ↓Ingress Controller(Nginx Pod) ├── 解析域名:api.example.com ├── 卸载 SSL(HTTPS → HTTP) ├── 匹配路由规则: │ ├── /api/users → user-service:80 │ ├── /api/orders → order-service:80 │ └── /admin → admin-service:80 └── 转发到对应 Service(ClusterIP) ↓ Pod(HTTP)Ingress 配置示例:
apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: api-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: /$2 # URL 重写 nginx.ingress.kubernetes.io/ssl-redirect: "true" # HTTP 重定向到 HTTPSspec: ingressClassName: nginx # 指定 Ingress Controller
# TLS 配置(HTTPS) tls: - hosts: - api.example.com secretName: tls-secret # Kubernetes Secret 存储证书
# 路由规则 rules: - host: api.example.com http: paths: # 路径:/api/users/* → user-service - path: /api/users(/|$)(.*) pathType: Prefix backend: service: name: user-service port: number: 80
# 路径:/api/orders/* → order-service - path: /api/orders(/|$)(.*) pathType: Prefix backend: service: name: order-service port: number: 80Ingress Controller 对比:
| Ingress Controller | 特点 | 性能 | 适用场景 |
|---|---|---|---|
| Nginx Ingress | 成熟稳定、社区活跃、配置灵活 | QPS ~50,000(单实例) | 通用场景、K8s 官方推荐 |
| Traefik | 原生云原生、自动服务发现、配置热更新 | QPS ~30,000 | 微服务架构、动态配置 |
| Istio Gateway | Service Mesh 集成、流量管理、可观测性 | QPS ~20,000(Sidecar 模式) | 服务网格、灰度发布 |
| Kong | 基于 Nginx、API 网关特性(限流、认证) | QPS ~40,000 | API 网关、企业级特性 |
本质一句话:Ingress 是 K8s 的”智能路由器”,用七层协议实现多服务共享入口、域名/路径路由、SSL 卸载,节省云 LoadBalancer 成本并提升灵活性。
Q7:如何基于 Ingress 实现金丝雀发布(灰度发布)?实战
Section titled “Q7:如何基于 Ingress 实现金丝雀发布(灰度发布)?”金丝雀发布场景:将 10% 流量路由到新版本,观察无异常后逐步扩大比例。
方案一:基于权重(Nginx Ingress):
# 稳定版 IngressapiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: app-stablespec: rules: - host: api.example.com http: paths: - path: / backend: service: name: app-service-stable port: number: 80
---# 金丝雀 Ingress(新版本)apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: app-canary annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "10" # 10% 流量到金丝雀版本spec: rules: - host: api.example.com http: paths: - path: / backend: service: name: app-service-canary port: number: 80方案二:基于 Header(内部测试):
metadata: annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: "X-Canary" nginx.ingress.kubernetes.io/canary-by-header-value: "true" # 请求头 X-Canary: true → 路由到金丝雀版本灰度发布流程:
灰度发布步骤(逐步放量):
1. 部署金丝雀版本(Deployment: app-canary) └── replicas: 1(先起 1 个 Pod)
2. 创建金丝雀 Ingress(weight: 10%) └── 监控错误率、延迟(Prometheus + Grafana) ↓3. 观察 30min 无异常 → weight: 30% └── 继续监控 ↓4. 观察 30min 无异常 → weight: 50% └── 继续监控 ↓5. 观察 30min 无异常 → weight: 100% └── 全量切换 ↓6. 删除旧版本 Deployment 和 Ingress实战案例:
场景:电商订单服务 v1.2.0 灰度发布
监控指标(Prometheus): - 错误率:< 0.1%(正常) - P95 延迟:< 200ms(正常) - QPS:稳定在 5000
灰度过程: 10:00 → weight: 10%(金丝雀 QPS: 500) 10:30 → weight: 30%(金丝雀 QPS: 1500) 11:00 → weight: 50%(金丝雀 QPS: 2500) 11:30 → weight: 100%(全量切换) 12:00 → 删除旧版本
异常回滚: 如果金丝雀版本错误率 > 1%: → 立即 weight: 0%(切回稳定版) → 排查问题后重新灰度