K8S - 深入解析 Service 与 Ingress - 服务暴露与流量管理
一、引言
在 Kubernetes 的弹性架构中,Pod 是应用的最小部署单元,但其动态特性导致直接访问面临两大挑战:
• IP 不固定:Pod 重启或扩缩容时 IP 地址会变化
• 生命周期短暂:故障恢复或滚动更新时旧 Pod 会被替换
为解决这些问题,Kubernetes 提供两大核心抽象:
Service:
• 服务发现:为动态 Pod 提供稳定访问入口(DNS 名称 + 虚拟 IP)
• 四层负载均衡:自动分发流量至健康 Pod
Ingress:
• 七层路由控制:基于 HTTP/HTTPS 协议实现域名、路径等高级路由规则
• 流量治理:支持 TLS 终止、重定向、金丝雀发布等场景
二、核心原理
2.1 Pod IP 的局限性
Pod IP 的动态性会导致以下问题:
• 服务中断风险:客户端直接依赖 Pod IP 时,Pod 重建后需手动更新配置
• 扩展性瓶颈:手动维护 IP 列表难以应对动态扩缩容场景
2.2 Service 的工作原理
Service 通过 三层抽象模型 解决 Pod 动态性问题,实现稳定服务暴露:
┌──────────────────┐│ Service │ <─── 稳定入口 (DNS/ClusterIP)│ ClusterIP: VIP │└───────┬──────────┘│通过标签选择器 (Label Selector) 关联│┌───────▼──────────┐│ Endpoints │ <─── 动态维护健康 Pod 列表│ Pod IP:Port 列表 │└───────┬──────────┘│┌─────────────┼───────────────┐│ │ │┌─────▼─────┐ ┌─────▼─────┐ ┌───────▼─────┐│ Pod 1 │ │ Pod 2 │ │ Pod 3 │ <─── 实际服务实例│app=backend│ │app=backend │ app=backend │└───────────┘ └───────────┘ └─────────────┘
核心机制解析:
1.ClusterIP(虚拟 IP)
• Kubernetes 为每个 Service 分配一个集群内唯一的虚拟 IP(ClusterIP)。
• 客户端通过该 IP 或 DNS 名称访问服务,如 backend-service.default.svc.cluster.local。
• ClusterIP 作为逻辑抽象层,完全屏蔽后端 Pod IP 的动态变化。
2.标签选择器(Label Selector)
• Service 通过声明式标签(如 app=backend)筛选目标 Pod,未匹配标签的 Pod 不会加入 Endpoints。
• 松耦合设计:允许 Pod 滚动更新(如 app=backend-v2
)时无缝切换,无需修改 Service 配置。
3.Endpoints 动态更新
Endpoints Controller 实时监控 Pod 状态,自动维护 IP:Port 列表:
• Pod 扩缩容:新 Pod 启动时自动加入列表,旧 Pod 终止时移除。
• 健康检查:若 Pod 处于 NotReady
状态,自动从列表剔除。
4.流量分发与负载均衡
kube-proxy 组件:
• iptables/IPVS 模式:基于内核级规则实现高性能四层负载均衡(默认轮询算法)。
• 会话保持(Session Affinity):通过 service.spec.sessionAffinity 配置粘性会话。
客户端无感知:所有流量通过 ClusterIP 或 DNS 入口,屏蔽后端 Pod 变化。
2.3 Service 类型及适用场景
Service通过 服务发现与 负载均衡保障 Pod 的可靠访问,其三种类型在实现方式与适用场景上存在显著差异:
对比解析
1.负载均衡层级
• ClusterIP/NodePort:四层(TCP/UDP),依赖 kube-proxy的 iptables/IPVS规则。
• LoadBalancer:四层(云厂商 LB)或七层(如 AWS ALB 结合 Ingress)。
2.实现组件差异
• ClusterIP:纯 Kubernetes 原生组件(kube-proxy),无外部依赖。
• LoadBalancer:依赖云厂商基础设施(如 AWS ELB、GCP Cloud Load Balancing)。
3.故障域与可用性
• NodePort:单节点故障导致服务不可用(需手动多节点暴露)。
• LoadBalancer:云厂商 LB 自动处理节点故障,支持跨可用区容灾。
4.选型策略
• 内部服务:ClusterIP + DNS 服务发现。
• 生产公网暴露:LoadBalancer(四层入口) + Ingress(七层路由)。
• 临时访问:NodePort + kubectl port-forward(避免直接暴露节点端口)。
2.4 Service 与 DNS 解析
DNS 命名规则
<service-name>.<namespace>.svc.cluster.local
1.访问方式
• 集群内直连:同一命名空间下可通过 http://backend-service:5000访问
• 跨命名空间访问:需使用完整域名
例:http://backend-service.prod.svc.cluster.local:5000
2.解析流程
客户端请求 → CoreDNS 解析 Service 名称 → 返回 ClusterIP → 流量经 kube-proxy 负载均衡至 Pod。
3.最佳实践
• 禁用 IP 硬编码:ClusterIP 可能因 Service 重建而变化,依赖 DNS 名称确保稳定性。
• 跨命名空间显式调用:避免因命名空间默认值导致路由错误。
注意:Service 的 IP 地址在服务部署后由 Kubernetes 自动分配。
2.5 Ingress 的七层流量治理
Ingress 不是具体的流量代理,而是路由规则的声明式抽象,需配合 Ingress Controller(如 Nginx、Traefik)实现功能:
核心能力:
• 基于域名(example.com)或 路径(/api)路由到不同 Service
• 支持 TLS 证书管理、请求限速、跨域配置等高级策略
与 Service 的关系:
• Service:负责四层(TCP/UDP)负载均衡
• Ingress:补充七层(HTTP/HTTPS)逻辑
典型架构路径:
客户端 → Ingress Controller → Service → Pod
三、Service 与 Ingress 实战
3.1 实战环境准备
1.安装必要工具
Mac/Linux 用户# 安装 Docker 并启动服务
brew install --cask docker
open /Applications/Docker.app
# 安装 kubectl 命令行工具
brew install kubectl
# 安装 Kind(本地K8S集群工具)
brew install kind
# 验证安装
docker --version # 预期输出: Docker version 24.x+
kubectl version --client # 预期输出: Client Version: v1.28.x
kind version # 预期输出: kind v0.20.x
Windows 用户
下载Docker Desktop安装 Chocolatey(包管理器):Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
通过 Chocolatey 安装工具:
choco install kind kubernetes-cli
2.创建本地 Kubernetes 集群
# 创建单节点集群(命名为 k8s-lab)
kind create cluster --name k8s-lab# 验证集群状态
kubectl cluster-info
# 预期输出:Kubernetes control plane is running at https://127.0.0.1:xxxxx
3.2 完整实例
1.目的
• 展示 Service如何通过标签关联 Pod,并通过 Endpoints动态更新实现服务发现。
• 通过 Ingress 统一管理前后端外部流量。
2.实例部署链路架构图
┌──────────┐│ 用户请求 │└────┬─────┘│▼┌──────────┐│ Ingress │└────┬─────┘│┌─────────────────┴─────────────────┐│ │▼ ▼
┌───────────────┐ ┌───────────────┐
│ 前端 Service │ │ 后端 Service │
│ (ClusterIP) │ │ (ClusterIP) │
└───────┬───────┘ └───────┬───────┘
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ 前端 Pod │ │ 后端 Pod │
└───────────────┘ └───────────────┘
3.完整步骤
步骤 1:创建命名空间
# 创建命名空间
kubectl create namespace example
步骤2:部署后端服务
镜像: python:3.9(官方镜像,内置简易 HTTP 服务器)backend-deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:name: backendnamespace: example
spec:replicas: 2selector:matchLabels:app: backend # 选择器标签template:metadata:labels:app: backend # 必须与 selector.matchLabels 一致spec:containers:- name: serverimage: python:3.9command: ["python", "-m", "http.server", "5000"]ports:- containerPort: 5000 # 容器实际监听端口
部署与验证
kubectl apply -f backend-deployment.yaml
# 查看 Pod 详细信息(包含 IP 和标签)
kubectl get pods -n example -l app=backend -o wide --show-labels输出示例:
NAME READY STATUS IP NODE LABELS
backend-7766cc5668-abcde 1/1 Running 10.244.0.10 node1 app=backend
backend-7766cc5668-fghij 1/1 Running 10.244.0.11 node2 app=backend
观察点:
• 两个 Pod 的 IP分别为 10.244.0.10和 10.244.0.11。
• 标签app=backend与 Deployment 中定义的标签一致。
步骤3:创建 Service 并验证 Endpoints
Service 为后端 Pod 提供稳定访问入口。
backend-service.yaml
# backend-service.yaml
apiVersion: v1
kind: Service
metadata:name: backend-servicenamespace: example
spec:selector:app: backendports:- protocol: TCPport: 5000 # Service 监听端口targetPort: 5000 # Pod 端口type: ClusterIP # 默认类型,集群内可访问
注:Service 的 selector.app=backend与 Pod 的 labels.app=backend匹配。
部署与验证
kubectl apply -f backend-service.yaml
# 查看 Service 的 ClusterIP 和 Endpoints 信息
kubectl get svc,ep -n example -l app=backend输出示例:
NAME TYPE CLUSTER-IP PORT(S) AGE
backend-service ClusterIP 10.96.56.77 5000/TCP 5s
NAME ENDPOINTS AGE
backend-service 10.244.0.10:5000,10.244.0.11:5000 5s
验证点:
• Endpoints 由 Kubernetes 自动维护,始终记录当前匹配 Pod 的 IP 和端口。
• Endpoints 实时关联的 Pod IP,与 步骤 2中的 Pod IP 完全一致(10.244.0.10和 10.244.0.11)。
• 端口对应关系:Endpoints 端口 5000与 Pod 的 containerPort一致。
步骤 4:部署前端应用并创建 Service
镜像:nginx:1.25
frontend-deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:name: frontendnamespace: example
spec:replicas: 1selector:matchLabels:app: frontendtemplate:metadata:labels:app: frontend spec:containers:- name: nginximage: nginx:1.25ports:- containerPort: 80
frontend-service.yamlapiVersion: v1
kind: Service
metadata:name: frontend-servicenamespace: example
spec:selector:app: frontendports:- protocol: TCPport: 80targetPort: 80type: ClusterIP
部署与验证:
kubectl apply -f frontend-deployment.yaml
kubectl apply -f frontend-service.yaml# 查看前端 Pod 和 Service
kubectl get pods,svc -n example -l app=frontend -o wide
# 输出示例:
# NAME READY STATUS IP NODE
# frontend-5d8f6c6c7d-xyzab 1/1 Running 10.244.0.12 node1# NAME TYPE CLUSTER-IP PORT(S)
# frontend-service ClusterIP 10.96.58.89 80/TCP
步骤5:配置 Ingress 路由前后端流量
example-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: example-ingressnamespace: exampleannotations:nginx.ingress.kubernetes.io/rewrite-target: /
spec:rules:- host: demo.example.comhttp:paths:- path: /pathType: Prefixbackend:service:name: frontend-service # 前端流量port:number: 80- path: /apipathType: Prefixbackend:service:name: backend-service # 后端流量port:number: 5000
部署与验证:
# 安装 Ingress 控制器(如未安装)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml# 部署 Ingress
kubectl apply -f example-ingress.yaml# 获取 Ingress 外部 IP
kubectl get ingress -n example
# 输出示例:
# NAME CLASS HOSTS ADDRESS PORTS
# example-ingress nginx demo.example.com 203.0.113.10 80
步骤6:全链路验证与动态服务发现测试
验证流量全链路(用户 → Ingress → 前端 Service → 前端 Pod → 后端 Service → 后端 Pod)及动态服务发现能力。# 测试1:通过 Ingress 访问前端
curl -H "Host: demo.example.com" http://203.0.113.10
# 预期输出:Nginx 默认欢迎页(说明前端 Service 和 Ingress 路由正常)# 测试2:通过 Ingress 访问后端 API
curl -H "Host: demo.example.com" http://203.0.113.10/api
# 预期输出:Python HTTP 服务器目录列表(说明后端 Service 和 Ingress 路由正常)# 测试3:进入前端 Pod 验证服务发现
kubectl exec -it $(kubectl get pod -n example -l app=frontend -o jsonpath="{.items[0].metadata.name}") -n example -- sh
while true; do wget -q -O- http://backend-service:5000 && sleep 1; done
输出:交替显示不同pod IP
10.244.0.10 - - [时间戳] "GET / HTTP/1.1" 200 -
10.244.0.11 - - [时间戳] "GET / HTTP/1.1" 200 -
按 Ctrl+C退出。
结论:
• Ingress 成功将外部流量路由到前端和后端 Service。
• Service 实现了负载均衡,请求均匀分发到后端 Pod。
步骤7:动态扩缩容与 Endpoints 验证
验证 Pod 扩缩容场景下 Service 的自动服务发现能力。
# 操作1:扩展后端 Pod 数量
kubectl scale deployment backend -n example --replicas=3
# 观察 Endpoints 变化(关键验证点)
watch kubectl get pods,ep -n example -l app=backend -o wide
输出:
# Pod 列表(约 30 秒后)
NAME READY STATUS IP NODE
backend-7766cc5668-abcde 1/1 Running 10.244.0.10 node1
backend-7766cc5668-fghij 1/1 Running 10.244.0.11 node2
backend-7766cc5668-klmno 1/1 Running 10.244.0.13 node1 # 新增 Pod# Endpoints
NAME ENDPOINTS
backend-service 10.244.0.10:5000,10.244.0.11:5000,10.244.0.13:5000
结论:•Service会自动将新 Pod IP(例如:10.244.0.13)加入 Endpoints。•无需人工干预,服务发现机制会即时生效。
步骤8:模拟 Pod 故障与服务自愈
验证 Pod 异常退出后,Service 如何保障业务连续性。# 操作1:手动删除一个后端 Pod
kubectl delete pod backend-7766cc5668-abcde -n example# 观察故障恢复过程(关键验证点)
watch kubectl get pods,ep -n example -l app=backend -o wide
输出:
# Pod 列表(删除后)
NAME READY STATUS IP NODE
backend-7766cc5668-abcde 0/1 Terminating 10.244.0.10 node1 # 正在终止
backend-7766cc5668-fghij 1/1 Running 10.244.0.11 node2
backend-7766cc5668-klmno 1/1 Running 10.244.0.13 node1# 约 30 秒后,新 Pod 启动完成
NAME READY STATUS IP NODE
backend-7766cc5668-fghij 1/1 Running 10.244.0.11 node2
backend-7766cc5668-klmno 1/1 Running 10.244.0.13 node1
backend-7766cc5668-pqrst 1/1 Running 10.244.0.14 node3 # 新 Pod# Endpoints 自动更新
NAME ENDPOINTS
backend-service 10.244.0.11:5000,10.244.0.13:5000,10.244.0.14:5000
结论:
• 旧 Pod (10.244.0.10) 从 Endpoints 移除。
• 新 Pod (10.244.0.14) 自动加入,服务流量无缝切换。
动态更新原理:
• Deployment 控制器确保 Pod 数量符合 replicas: 3。
• Service 持续监听 Pod 变化,实时更新 Endpoints。
步骤9:验证服务连续性
# 再次进入前端 Pod 发起连续请求,观察负载均衡效果
kubectl exec -it $(kubectl get pod -n example -l app=frontend -o jsonpath="{.items[0].metadata.name}") -n example -- sh
while true; do wget -q -O- http://backend-service:5000 && sleep 1; done
输出: 交替显示不同pod IP
10.244.0.11 - - [时间戳] "GET / HTTP/1.1" 200 -
10.244.0.13 - - [时间戳] "GET / HTTP/1.1" 200 -
10.244.0.14 - - [时间戳] "GET / HTTP/1.1" 200 -
结论:
• 业务流量自动迁移至健康 Pod,实现零感知故障恢复。
• 证明 Service 的负载均衡和健康检查机制有效。
3.3 配置注意事项
3.4 总结与生产建议
生产建议:
• 为 Service 添加 readinessProbe,避免将流量路由到未就绪的 Pod。
• 使用 PodDisruptionBudget保障关键业务的最小可用实例数。
• Ingress 配置 TLS 加密(可通过 Cert-Manager 自动管理证书)。
通过本实例,您已掌握 Kubernetes 服务暴露与流量管理的核心机制。
四、总结
4.1 总结
架构回顾:
外部用户 --> Ingress --> 前端 Service --> 前端 Pod--> 后端 Service --> 后端 Pod
组件总结:
1.Pod 动态性验证:直接依赖 Pod IP 不可靠。
2.Service 核心能力:
• 通过稳定的 ClusterIP和 DNS 名称实现服务发现。
• 通过 Endpoints动态更新支持 Pod 扩缩容。
• 通过 负载均衡提升可用性。
3.Ingress 扩展性:实现七层路由与外部流量管理。