一、前置背景为什么需要Service遗留问题上一章学习的Deployment/RS等控制器可以实现Pod的多副本管理但存在致命缺陷Pod是临时资源重建后IP必然变化无法通过固定IP访问若用Nginx反向代理Pod需手动维护upstream中的Pod IPPod死亡后Nginx 7层探测会停止转发到该Pod但新Pod的IP不会自动加入upstream需手动/脚本修改Nginx配置、重载才能恢复运维成本极高Service的解决方案Service通过标签选择器匹配Pod 自动维护后端端点 固定访问入口实现前后端解耦匹配同时满足「标签匹配」「Pod就绪Ready通过就绪探针」的PodPod扩缩容、重建、就绪状态变化时Service自动更新后端Pod列表上层应用只需访问Service的固定VIP/域名完全感知不到Pod的变化二、核心组件kube-proxy的代理模式迭代每个Node运行kube-proxy进程负责将Service的负载均衡规则落地到节点共三代迭代模式K8s版本模式工作原理优缺点教材实操验证v1.0userspace1. kube-proxy监听APIServer的Service变化2. 修改节点iptables规则将流量转发到kube-proxy进程3. kube-proxy代理请求到后端Pod❌ 性能极差kube-proxy参与流量转发请求量大时成为瓶颈-v1.1默认iptables1. kube-proxy监听APIServer的Service变化2.仅将负载均衡规则写入节点iptables不参与流量转发3. 流量直接由iptables转发到后端Pod✅ 性能好无流量代理规则同步开销低❌ 规则规模超1万条时性能下降仅支持随机、轮询等简单算法新建Service后执行ipvsadm -Ln无规则说明当前是iptables模式v1.8ipvs1. kube-proxy监听APIServer的Service变化2. 将负载均衡规则写入内核级LVSipvs3. 流量由ipvs转发到后端Pod✅ 性能最优专业四层负载均衡支持rr/wrr/lc/wlc等10算法规则规模大时性能稳定❌ 依赖内核ipvs模块部分环境默认未开启切换后ipvsadm -Ln可见Service对应的负载均衡规则【⚠️疑难点】为什么K8s的ipvs默认用NAT模式而非DR/TUNipvs有三种工作模式K8s选择NAT的核心原因模式优点缺点K8s适配性NAT支持Service端口和Pod端口不一致兼容性强入站/出站流量都经过ipvs调度✅ 每个节点的ipvs规则仅给本节点的Pod使用单节点流量压力小兼容性优先级高于性能DR回程流量不经过ipvs性能更高需修改ARP响应规则且Service端口必须和Pod端口一致❌ 灵活性差不符合K8s端口映射的需求TUN支持跨公网组建集群需对报文二次封装性能低❌ K8s集群内通信无需跨公网 overhead过高【实操】切换kube-proxy为ipvs模式修改kube-proxy的ConfigMap注意在kube-system命名空间kubectl edit configmap kube-proxy -n kube-system # 找到mode字段修改为ipvs默认空值为iptables mode: ipvs删除kube-system命名空间下所有kube-proxy Pod触发重建加载新配置# 通过标签筛选kube-proxy Pod kubectl get pod -n kube-system -l k8s-appkube-proxy # 删除所有kube-proxy PodDS控制器会自动重建 kubectl delete pod -n kube-system -l k8s-appkube-proxy验证节点执行ipvsadm -Ln能看到Service对应的ipvs规则即成功。 踩坑提示kube-proxy的ConfigMap修改后不会自动生效必须删除Pod重建才能加载新配置若节点未加载ipvs内核模块会导致kube-proxy启动失败。kubectl edit的校验机制kubectl edit修改资源时K8s会校验配置合法性若修改的值超出字段定义范围如滚动更新最大副本数设为非法值保存时会自动弹回不允许保存错误配置错误修改会缓存到/tmp/kubectl-edit-xxxx.yaml方便排查问题三、Service工作模式4种类型3.1 组件协同流程用户通过kubectl提交Service配置到APIServerAPIServer将配置持久化到etcd每个节点的kube-proxy监听APIServer的Service变化kube-proxy将Service规则同步到本节点的iptables/ipvsCoreDNS自动为Service生成DNS记录供集群内Pod解析3.2 ClusterIP默认类型仅集群内访问模式结构分配仅集群内部可访问的虚拟IPClusterIP后端匹配符合条件的Pod实现集群内负载均衡底层默认使用ipvs的NAT模式支持Service端口和Pod端口不一致完整资源清单# Deployment配置带就绪探针Service依赖该探针 apiVersion: apps/v1 kind: Deployment metadata: name: myapp-clusterip-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: myapp release: stabel svc: clusterip template: metadata: labels: app: myapp release: stabel env: test svc: clusterip spec: containers: - name: myapp-container image: hb.reg.com/library/myapp:1.0 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 readinessProbe: # 就绪探针检测/index1.html是否存在 httpGet: path: /index1.html port: 80 initialDelaySeconds: 5 periodSeconds: 5 --- # ClusterIP Service配置 apiVersion: v1 kind: Service metadata: name: myapp-clusterip namespace: default spec: type: ClusterIP selector: # 标签选择器和Pod标签做子集匹配 app: myapp release: stabel svc: clusterip ports: - name: http port: 80 # Service暴露的集群内访问端口 targetPort: 80 # 后端Pod的容器端口核心特性教材实操案例1就绪探针强依赖Service匹配Pod必须同时满足两个条件标签匹配 Pod处于Ready状态缺一不可初始创建Pod时就绪探针检测/index1.html不存在Pod状态为Ready: 0/1此时查看Service的ipvs规则无后端Podipvsadm -Ln | grep 10.3.124.142 # 仅显示集群IP无Real Server进入Pod创建index1.htmldate /usr/local/nginx/html/index1.html就绪探针通过后Pod变为Ready: 1/1Service自动将Pod加入后端ipvs规则出现对应Real Server此时访问Service正常。2内置DNS解析Service创建后自动生成集群内唯一的DNS记录格式为service-name.namespace.svc.cluster.local集群内Pod默认DNS指向CoreDNS的VIP默认10.0.0.10可直接解析验证方法# 用dig命令验证解析 dig A myapp-clusterip.default.svc.cluster.local 10.0.0.10 # 在Pod内访问域名 kubectl exec -it pod-demo -- wget http://myapp-clusterip.default.svc.cluster.local/hostname.html3internalTrafficPolicy集群内流量策略K8s 1.26稳定控制集群内流量如何访问ClusterIP教材明确两种取值的差异取值说明适用场景教材实操结果Cluster默认集群内任意节点、任意Pod都可以通过ClusterIP/域名访问Service通用场景节点上执行curl ClusterIP、Pod内访问域名均正常Local仅集群内的Pod可以通过域名访问节点上无法通过ClusterIP访问且无本地Pod时丢包仅集群内访问减少跨节点转发开销提升性能节点执行curl ClusterIP提示Connection refusedPod内访问域名正常 官方建议若Service仅需要被集群内Pod访问强烈建议设置为Local降低性能开销。4会话保持IPVS持久化连接默认Service是轮询负载若需将同一客户端的请求固定到同一个Pod可开启会话保持spec: sessionAffinity: ClientIP # 默认是None关闭会话保持 sessionAffinityConfig: clientIP: timeoutSeconds: 10800 # 超时时间默认10800秒3小时范围1-86400秒原理基于客户端IP绑定超时时间内同一IP的请求转发到同一Pod验证开启后ipvsadm -Ln可见规则后增加persistent 10800标识踩坑会话保持仅在ipvs模式下生效iptables模式下不生效3.3 NodePort对外暴露服务客户端 | http://192.168.24.10:30540 v [NodePort 30540] -- 每台节点都开同一个端口 | v [kube-proxy] -- iptables/IPVS 规则匹配 | DNAT: NodeIP:30540 - ClusterIP:80 v [Service ClusterIP:80] -- 根据 selector 选 Pod | round-robin 负载均衡 v [Pod 1 / Pod 2 / Pod 3] -- nginx 处理请求模式结构在ClusterIP的基础上在每个节点的物理网卡上绑定一个静态端口NodePort外部可以通过NodeIP:NodePort访问集群内的Service默认端口范围30000-32767可通过kube-apiserver的--service-node-port-range参数修改如设置为12000-22000完整资源清单# Deployment配置 apiVersion: apps/v1 kind: Deployment metadata: name: myapp-nodeport-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: myapp release: stabel svc: nodeport template: metadata: labels: app: myapp release: stabel env: test svc: nodeport spec: containers: - name: myapp-container image: hb.reg.com/library/myapp:1.0 ports: - name: http containerPort: 80 --- # NodePort Service配置 apiVersion: v1 kind: Service metadata: name: myapp-nodeport namespace: default spec: type: NodePort selector: app: myapp release: stabel svc: nodeport ports: - name: http port: 80 # 集群内访问的Service端口 targetPort: 80 # Pod端口 nodePort: 30010 # 节点暴露的端口不指定会自动分配核心特性实操案例完全兼容ClusterIP的所有能力集群内可以通过ClusterIP、域名访问每个节点的所有可用网卡都会绑定NodePort的ipvs规则因此任意节点的IP:NodePort都可以访问# 所有节点都可查到30010端口的规则 ipvsadm -Ln | grep 30010 # 浏览器访问任意节点IP:30010均可正常响应externalTrafficPolicy外部流量策略重点区分取值说明优缺点适用场景Cluster默认外部流量可以被转发到任意节点的Pod会做SNAT丢失客户端源IP可用性高即使访问的节点没有Pod副本也能转发通用场景Local外部流量只能转发到当前节点的Pod没有本地Pod则丢包保留客户端源IP性能好保留源IP但节点没有Pod时访问失败配合DaemonSet使用每个节点都有Pod副本的场景⚠️ 易混点区分教材特别强调策略控制范围作用internalTrafficPolicy集群内访问ClusterIP控制节点/ Pod是否能通过ClusterIP访问ServiceexternalTrafficPolicy集群外访问NodePort控制外部流量是否保留源IP、是否跨节点转发生产建议NodePort本身无高可用能力生产环境需在集群外部额外部署负载均衡器Nginx、F5、云LB将流量转发到所有节点的NodePort避免单节点故障。3.4 LoadBalancer仅云厂商可用仅在云厂商环境可用依托云供应商的负载均衡能力自动创建外部LB将流量转发到NodePort私有云环境一般不用用「NodePort 自建LB」替代阿里云资源清单示例apiVersion: v1 kind: Service metadata: name: my-nginx-svc namespace: default annotations: service.beta.kubernetes.io/alibaba-cloud-loadbalancer-id: ${YOUR_LB_ID} service.beta.kubernetes.io/alicloud-loadbalancer-force-override-listeners: true spec: type: LoadBalancer selector: app: nginx ports: - port: 80 targetPort: 803.5 ExternalName集群外服务映射模式结构特殊的Service类型不涉及kube-proxy、ipvs仅通过CoreDNS实现CNAME域名别名用于将集群外的服务映射到集群内让Pod可以通过固定的Service名称访问外部服务不需要Label Selector、不需要端口映射也可自定义端口完整资源清单案例案例1映射外部百度域名apiVersion: v1 kind: Service metadata: name: my-service-1 namespace: default spec: type: ExternalName externalName: www.baidu.com验证Pod内执行ping my-service-1.default.svc.cluster.local解析结果为百度的IP限制仅集群内Pod可访问外部客户端不会使用集群的CoreDNS因此无法通过该Service访问外部服务案例2映射外部MySQL数据库生产常用apiVersion: v1 kind: Service metadata: name: mysql-service namespace: default spec: type: ExternalName externalName: mysql.prod.company.com # 外部MySQL域名 ports: - name: mysql port: 3306 targetPort: 3306应用配置时只需填写jdbc:mysql://mysql-service:3306/dbname无需关心外部MySQL的真实地址外部MySQL地址变化时仅需修改Service的externalName字段所有Pod无需修改配置实现解耦【⚠️疑难点】ExternalName和其他类型的本质区别类型实现方式能力ClusterIP/NodePort/LoadBalancerkube-proxy ipvs四层负载均衡感知后端健康状态ExternalNameCoreDNS CNAME仅域名别名无负载均衡能力不感知后端健康状态四、EndpointSliceService的底层实现Service的后端Pod列表由EndpointSlice维护替代了旧版的Endpoints资源支持更大规模单Slice支持1000个端点单Service支持多个Slice的端点管理。4.1 自动关联带Label Selector的Service默认场景当Service配置了spec.selector时Service控制器自动监听符合标签的Pod自动创建同名的EndpointSlice包含所有Ready状态的Pod的IP、端口kube-proxy将EndpointSlice中的端点同步到本节点的ipvs规则Pod就绪状态变化、IP变化、扩缩容时EndpointSlice自动更新无需人工干预实操验证创建带selector的Service后自动生成同名EndpointSlicekubectl get endpointslice # 输出可见myapp-clusterip对应的EndpointSlice包含就绪Pod的IPPod就绪状态变化时EndpointSlice自动更新当一个Pod创建index1.html就绪后EndpointSlice自动加入该Pod的IPPod未就绪时自动移除。4.2 手动关联不带Label Selector的Service特殊场景当Service没有配置spec.selector时如访问集群外服务、非Pod端点Service不会自动创建EndpointSlice需用户手动创建EndpointSlice通过标签kubernetes.io/service-name关联到对应的Servicekube-proxy将手动创建的EndpointSlice同步到ipvs规则手动创建EndpointSlice完整示例apiVersion: discovery.k8s.io/v1 kind: EndpointSlice metadata: name: my-service-noselector-1 labels: # 必须关联Service的名称否则Service无法识别该Slice kubernetes.io/service-name: my-service-noselector addressType: IPv4 ports: - name: http protocol: TCP port: 80 endpoints: - addresses: - 192.168.10.12 # 后端服务IP可以是集群外IP - addresses: - 192.168.10.13【⚠️教材严格限制】手动EndpointSlice的IP规范端点IP不能是以下类型否则kube-proxy不支持本地回环地址127.0.0.0/8、::1/128链路本地地址169.254.0.0/16、fe80::/64其他K8s Service的ClusterIP五、常见问题问题排查思路依据Pod Running但Service访问不通检查Pod是否Ready就绪探针是否通过只有Ready的Pod才会被加入EndpointSlice就绪探针未通过时Service无后端端点Service会话保持不生效确认sessionAffinity设置为ClientIP且集群使用ipvs模式iptables模式不支持会话保持NodePort外部访问不通1. 检查externalTrafficPolicy是否为Cluster2. 检查节点防火墙是否放行NodePort端口3. 检查Pod是否ReadyLocal模式下无本地Pod会丢包防火墙拦截端口ExternalName解析失败1. 确认CoreDNS正常运行2. 检查Pod的/etc/resolv.conf是否指向集群CoreDNS IP默认10.0.0.10外部客户端不使用集群CoreDNS无法解析修改kube-proxy后不生效修改ConfigMap后必须删除kube-proxy Pod重建才能加载新配置kube-proxy配置热加载机制修改Service后不生效确认修改的是正确的NamespaceService修改后EndpointSlice自动更新最多延迟10秒Service控制器同步机制Local模式ClusterIP节点访问不通internalTrafficPolicy: Local时仅Pod内可通过域名访问节点上无法通过ClusterIP访问官方流量策略定义六、实操命令速查操作命令创建ClusterIP Servicekubectl create svc clusterip myapp --tcp80:80查看所有Servicekubectl get svc -A查看Service详情kubectl describe svc svc-name查看EndpointSlicekubectl get endpointslice -A查看ipvs规则ipvsadm -Ln修改Service配置kubectl edit svc svc-name查看Service的yaml定义kubectl get svc svc-name -o yaml验证Service DNS解析kubectl exec -it pod-name -- nslookup svc-name.ns.svc.cluster.local修改kube-proxy为ipvs模式kubectl edit configmap kube-proxy -n kube-system修改mode为ipvs后删除kube-proxy Pod查看kube-proxy日志kubectl logs -n kube-system kube-proxy-pod-name七、本章重点总结Service的核心是解耦Pod的动态变化和上层访问避免Pod重建导致的访问失效集群内访问优先用ClusterIP开启internalTrafficPolicy: Local提升性能对外暴露优先用NodePort外部LB避免直接用NodePort暴露公网云环境可使用LoadBalancer访问集群外服务优先用ExternalName减少配置耦合EndpointSlice是Service的底层数据载体理解它才能真正搞懂Service的工作原理所有Service的流量转发能力最终依赖kube-proxy落地到节点的iptables/ipvs规则