首页 / 资讯中心 / 文章详情

Kubernetes服务暴露全解析:ipvs模式与五种Service类型实战

Kubernetes服务暴露全解析:ipvs模式与五种Service类型实战 ★ FEATURED ARTICLE
1. 服务暴露全景图先搞清楚这五种“入口”各自解决什么问题说到 kubernetes 集群的服务暴露我见过不少新手上来就对着 YAML 抄抄完发现 Pod 是起来了、Service 也建了但外面怎么都访问不到一脸懵。其实 k8s 里 Service 的玩法就那几种——ClusterIP、NodePort、Headless、LoadBalancer、ExternalName再加上 kube-proxy 底层走的是 ipvs 还是 iptables排列组合下来确实容易让人绕晕。这篇文章我就以运维实操的角度把这五种服务类型从头到尾捋一遍重点讲 ipvs 模式下 ClusterIP 是怎么干活的、NodePort 怎么把端口暴露到宿主机、Headless 为什么不需要 ClusterIP、LoadBalancer 在自建集群里怎么落地以及 ExternalName 这个“轻量代理”到底怎么用。先说个大概的结论方便后面展开ClusterIP 是默认类型给你一个集群内部可达的虚拟 IPNodePort 是在 ClusterIP 基础上把端口映射到每个节点的宿主机LoadBalancer 又是在 NodePort 基础上接了一个外部负载均衡器分配一个外部 IPHeadless 是不分配 ClusterIP让 DNS 直接返回后端 Pod 的真实地址ExternalName 最特殊它连 Pod 都不管只是把 Service 名解析到外部域名。这五个类型从内到外层层递进搞懂它们之间的关系比死记命令有用得多。那 ipvs 在这里面扮演什么角色ipvs 是 kube-proxy 的转发引擎之一负责把访问 ClusterIP 的流量分发给后端 Pod。在比较新的集群版本里kube-proxy 默认可能还是 iptables 模式但 ipvs 模式在性能和扩展性上明显更优尤其 Pod 数量上去了之后iptables 规则链会变得又长又慢而 ipvs 用的哈希表天然就是为大规模负载均衡设计的。下面我会一步步演示怎么把集群切到 ipvs 模式然后逐个实操五种服务类型。这篇文章适合谁看你已经搭好了 k8s 集群不管是用 kubeadm 还是二进制方式知道 Pod、Deployment 大概是什么东西但一直没搞明白 Service 的几种暴露方式有什么区别。适合正在做集群运维、排障、或者要给业务做网络方案选型的人。看完之后你至少能回答这几个问题什么时候用 NodePort、什么时候上 LoadBalancer、Headless 到底解决什么问题、ExternalName 和 Ingress 有什么区别。2. 为什么我优先把 kube-proxy 切成 ipvs 模式2.1 三种转发模式的历史包袱先把底层机制讲透。kube-proxy 从诞生到现在经历过 userspace、iptables、ipvs 三种模式。userspace 模式是最古老的所有 Service 流量先在用户态经过 kube-proxy 进程再转给后端 Pod性能差且容易成为瓶颈现在基本没人用了。iptables 模式是目前最常见的默认模式它利用 Linux 内核的 iptables 规则做 DNAT把发往 ClusterIP 的包改写目标地址后交给后端 Pod。iptables 模式的缺点是规则数量会随着 Service 和 Endpoint 的数量线性增长一个几百个 Service 的集群可能产生上万条 iptables 规则每次新建 Service 都要遍历更新规则CPU 消耗大匹配效率也逐级下降。ipvs 模式完全不一样。ipvsIP Virtual Server是内核自带的传输层负载均衡模块工作在 Linux 内核的 netfilter 框架的 INPUT 钩子之后、FORWARD 之前专门干四层分发这件事。它背后用的是哈希表而不是线性匹配列表所以 Service 数量再多查找的时间复杂度也是常数级。而且 ipvs 支持多种调度算法比如轮询 rr、最少连接 lc、源地址哈希 sh 等比 iptables 那种纯随机转发灵活得多。我自己的经验是集群规模小比如几十个节点、百来个服务的时候iptables 和 ipvs 用起来差别不大但一旦负载上来、Service 数量超过两百iptables 的规则更新就会出现秒级延迟Pod 滚动更新时连接也有概率被丢弃。换成 ipvs 之后明显省心。2.2 内核模块与依赖工具准备切 ipvs 模式之前确认节点内核加载了必要模块。ipvs 不是某个单一模块而是一组常用的是 ip_vs、ip_vs_rr、ip_vs_wrr、ip_vs_lc、ip_vs_sh、ip_vs_wlc 等。还有 netfilter 的扩展模块 nf_conntrack_ipv4老内核或 nf_conntrack新内核。可以直接用一个脚本把需要的模块都拉起来cat /etc/modules-load.d/ipvs.conf EOF ip_vs ip_vs_rr ip_vs_wrr ip_vs_lc ip_vs_wlc ip_vs_sh ip_vs_mh ip_vs_sed ip_vs_nq nf_conntrack EOF systemctl enable --now systemd-modules-load.service加载之后用lsmod | grep ip_vs验证能看到 ip_vs 等一行行输出就说明内核侧准备好了。另外还需要安装 ipset 和 ipvsadm 管理工具# CentOS / RHEL yum install -y ipset ipvsadm # Ubuntu / Debian apt install -y ipset ipvsadmipvsadm 是查看和管理 ipvs 规则的工具ipset 则是 kube-proxy 用来组织 IP 集合的底层组件。装好之后ipvsadm -Ln如果输出空列表或者提示没有权限说明命令本身没问题只是还没有规则而已。2.3 kube-proxy 切换配置实操如果你是用 kubeadm 安装的集群kube-proxy 是以 DaemonSet 方式跑在 kube-system 命名空间下的 ConfigMap 来管理的。修改方式如下kubectl edit configmap -n kube-system kube-proxy找到config.conf里的mode字段默认通常是空字符串或把它改成ipvsapiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: ipvs ipvs: scheduler: rr然后滚动重启 kube-proxy Pod让配置生效kubectl rollout restart -n kube-system daemonset kube-proxy这里有个细节ipvs.scheduler我填的是rr轮询这是最基本的调度策略大多数场景够用。如果后端 Pod 性能不一致可以考虑lc最少连接或者wlc加权最少连接kube-proxy 支持的调度算法都映射自 Linux 内核 ipvs 的实现可以在ipvsadm -Ln输出里看到每个 Service 实际用的调度算法。验证是否真的切到 ipvs 模式看两个地方。第一个是 kube-proxy 日志kubectl logs -n kube-system -l k8s-appkube-proxy | grep -i Using ipvs正常会输出类似Using ipvs Proxier的日志。第二个更直观随便找一个已存在的 Service访问一次它的 ClusterIP然后看节点上是否生成了对应的 ipvs 规则ipvsadm -LnIP 172.16.x.x 开头的、后面跟着一堆 Pod IP 的规则就是 ClusterIP 的转发规则。注意切 ipvs 之前确认所有节点都有 ip_vs 相关模块否则 kube-proxy 会启动失败并报错cant initialize ipvs: IPVS can not be used。如果个别节点模块异常它会自动回退到 iptables 模式但这种情况最好在日志里及时发现。3. ClusterIP 在 ipvs 模式下到底做了什么3.1 一条访问链路拆开看先部署一个最简单的测试应用后面所有 Service 都基于它来做实验kubectl create deployment nginx-demo --imagenginx kubectl scale deployment nginx-demo --replicas3然后创建一个 ClusterIP 类型的 Service# service-clusterip.yaml apiVersion: v1 kind: Service metadata: name: nginx-svc spec: type: ClusterIP selector: app: nginx ports: - port: 80 targetPort: 80应用之后查看kubectl apply -f service-clusterip.yaml kubectl get svc nginx-svc输出类似NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-svc ClusterIP 10.96.66.66 none 80/TCP 5s现在在任意节点上执行ipvsadm -Ln | grep -A 2 10.96.66.66你会看到类似下面的输出TCP 10.96.66.66:80 rr - 10.244.1.2:80 Masq 1 0 0 - 10.244.2.5:80 Masq 1 0 0 - 10.244.3.9:80 Masq 1 0 0这意味着什么kube-proxy 在集群的每个节点上都为这个 ClusterIP 创建了一条 ipvs 虚拟服务器规则对应的后端就是三个 Pod 的 IP。当你在集群内任意一个 Pod 里访问http://10.96.66.66请求会被 DNAT 到其中一个 Pod IP转发算法是 rr也就是轮询。这就是 ClusterIP 通信用户态看到的全部流程Service IP 在集群内可以被所有节点路由因为每台机器都有这条 ipvs 规则到达节点后由内核的 ipvs 模块直接完成负载均衡整个过程不需要经过用户态的进程。3.2 调度算法和会话保持的坑如果你后端 Pod 是无状态的用默认 rr 就够了。但如果你的业务有会话保持需求比如用户登录状态存在某个 Pod 的内存里那就要小心了。ipvs 里处理会话保持有两种方式一种是设置ipvs.timeout让同一个源 IP 的连接在超时时间内都发给同一个后端另一种是给 Service 打注解让 kube-proxy 为它生成的 ipvs 服务启用源地址哈希调度kubectl annotate service nginx-svc \ kubeproxy.kubernetes.io/ipvs-schedulersh执行之后再去看 ipvsadm 输出调度算法会从 rr 变成 sh。这种情况下我用过同一个客户端连续请求会被固定导到同一个 Pod。不过我得提醒一句上面这种按源 IP 哈希的方式只适合内网场景因为它是基于源 IP 做的。如果前面还架着一层 NAT 或者负载均衡器所有客户端的源 IP 都会被改写成同一个哈希就完全失效了。还有一个高频坑是externalTrafficPolicy。默认情况下 Service 是 Cluster 模式也就是说流量可以在任意节点上被转发到任意 Pod即使目标 Pod 不在当前节点也会跨节点转发。这会导致源 IP 被 SNAT 成节点 IP后端应用看到的客户端 IP 就丢了。如果你需要保留真实客户端 IP把 externalTrafficPolicy 设为 Localspec: externalTrafficPolicy: Local这个模式下只有 Pod 所在节点才会把流量接入kube-proxy 只在本机 Pod 之间做负载均衡不做跨节点转发源 IP 得以保留。代价是流量分配不一定会很均匀因为节点上如果没有某个服务的 Pod就不会接收这个服务的流量。3.3 ClusterIP 的局限在哪里ClusterIP 只能解决集群内部的访问问题集群内任意 Pod 可以通过 ClusterIP 访问 Service但集群外部是绝对访问不到这个 IP 的。所以线上业务如果需要从集群外访问就必须在 ClusterIP 之上再叠一层 NodePort 或者 LoadBalancer这正好是下一节的内容。4. NodePort 和 LoadBalancer把服务从“内网”放到“外网”4.1 NodePort 实操与端口范围NodePort 的思路很简单在 ClusterIP 的基础上让 kube-proxy 在每个节点的宿主机上监听一个固定端口访问节点IP:NodePort时流量会被 DNAT 到对应的 ClusterIP再由 ipvs 分发给后端 Pod。可以说 NodePort 是“站在 ClusterIP 肩膀上再加了一层映射”。创建方式# service-nodeport.yaml apiVersion: v1 kind: Service metadata: name: nginx-nodeport spec: type: NodePort selector: app: nginx ports: - port: 80 targetPort: 80 nodePort: 30080kubectl apply 之后查看kubectl get svc nginx-nodeport NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-nodeport NodePort 10.96.88.88 none 80:30080/TCP 10s现在你可以在集群外通过任何一个节点的 IP 加 30080 端口访问这个服务。比如节点 IP 是 192.168.1.10浏览器访问http://192.168.1.10:30080即可。这里有几个必须知道的细节默认端口范围是 30000-32767。这是 kube-apiserver 的--service-node-port-range参数控制的如果业务需要自定义可以在 apiserver 启动参数里改。但建议别改太小避免和现有服务冲突。nodePort 不指定的话kube-proxy 会自动分配一个 30000 以上的随机端口。所以我上面写了nodePort: 30080生产环境有固定端口诉求时建议显式指定。所有节点的同一个 NodePort 都会监听。也就是说任何一个节点都可以用来访问如果你在节点前面挂了负载均衡可以在负载均衡器上把后端指向所有节点的这个端口这其实就是自建 LoadBalancer 的底层原理。NodePort 和 ClusterIP 同时存在。创建 NodePort Service 时ClusterIP 也会被分配集群内仍然可以用 ClusterIP 访问所以 NodePort 只是多了宿主机端口的暴露层。4.2 自建集群里怎么落地 LoadBalancerLoadBalancer 类型的 Service 在云上是直接创建一个云负载均衡实例自动绑定后端节点端口。但在自建机房或者本地虚拟机里没有云厂商提供的 LB 组件创建的 LoadBalancer Service 会一直处于pending状态# service-loadbalancer.yaml apiVersion: v1 kind: Service metadata: name: nginx-lb spec: type: LoadBalancer selector: app: nginx ports: - port: 80 targetPort: 80然后执行kubectl get svcEXTERNAL-IP 永远是pending。这说明集群里缺少一个外部负载均衡器的实现。自建集群里最常见的补位方案是部署开源的负载均衡控制器比如某款基于二层 ARP 或 BGP 实现的负载均衡器组件它在收到 LoadBalancer Service 创建事件后会在自己的地址池里分一个 IP然后通过 ARP 广播或 BGP 路由把这些 VIP 通告出去。配合 ipvs 模式整体转发链路是外部客户端 - VIP - 宿主机 iptables/ipvs - Pod IP非常顺滑。部署流程大概三步第一准备一段和集群节点同网段的空闲 IP这段 IP 以后专门给负载均衡使用第二把组件以 Deployment 方式部署到集群第三配置地址池给 Service 打注解指定要哪个 IP。# lb-ip-pool.yaml apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: first-pool namespace: metallb-system spec: addresses: - 192.168.50.10-192.168.50.20然后 LoadBalancer Service 就可以直接申请到 IPkubectl apply -f service-loadbalancer.yaml kubectl get svc nginx-lb NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-lb LoadBalancer 10.96.99.99 192.168.50.10 80:31925/TCP 15s这时候你在集群外访问http://192.168.50.10就能打到 Nginx 了。注意自建 LoadBalancer 的本质仍然是任务分发到节点再由节点转发到 ClusterIP 后端。所以如果使用外部流量策略 Local要保证负载均衡器把请求只转发到“确实有该 Service 后端 Pod 的节点”否则会出现部分节点通了、部分节点不通的情况。排查的时候先kubectl get endpoints看后端分布再kubectl describe svc看 Events。5. Headless Service不套 ClusterIP 的另类玩法5.1 为什么需要“无头”服务Headless Service 的定义就一行clusterIP: None。它没有 ClusterIPkube-proxy 不会为它创建 ipvs 规则自然也就没有负载均衡能力。那它存在的意义是什么答案是让 DNS 查询直接返回后端每个 Pod 的 IP而不是一个虚拟的 Service IP。举个例子你在 Kubernetes 里用 StatefulSet 部署了一个有状态应用比如数据库集群每个 Pod 的标识是podname-0、podname-1、podname-2它们的 IP 可能随时变化但主机名是稳定的。这时候如果你创建一个 Headless Service并把 selector 指向这些 Pod那么集群内的 DNS 解析service-name.namespace.svc.cluster.local会返回一组 A 记录每条对应一个 Pod 的 IP。应用拿到这些 IP 之后自己决定连谁、怎么重试、怎么做主从选举。为什么不用 ClusterIP因为 StatefulSet 场景下业务往往需要“点对点”地发现并连接某一个特定的 Pod而 ClusterIP 是“把流量平均分给一堆后端”这恰恰是它做不到的——它会给客户端一个假 IP把真实身份藏起来。Headless Service 的 YAML 长这样# service-headless.yaml apiVersion: v1 kind: Service metadata: name: nginx-headless spec: clusterIP: None selector: app: nginx ports: - port: 80 targetPort: 80创建之后查看注意 CLUSTER-IP 列显示的是Nonenginx-headless ClusterIP None none 80/TCP 5s此时ipvsadm -Ln里不会出现这个 Service 对应的规则因为它压根没有虚拟 IP。5.2 Headless 的 DNS 行为实测在集群里起一个测试 Pod用nslookup或者dig看看解析结果kubectl run test-pod --imagebusybox --rm -it -- sh / # nslookup nginx-headless.default.svc.cluster.local Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: nginx-headless.default.svc.cluster.local Address 1: 10.244.1.2 Address 2: 10.244.2.5 Address 3: 10.244.3.9看到没返回的是三个 Pod IP而不是一个虚拟 IP。这个特性最适合的状态ful应用场景就是应用自己维护节点列表比如数据库主从、分片中间件每个实例要直连其他实例而不是经过一层负载均衡——毕竟有状态应用的主备关系不能靠轮询来“平均分配”。还有一个变体如果 Headless Service 的 selector 不匹配任何 Pod或者干脆不写 selector那么 DNS 返回的就不是 Pod IP而是你手动维护的 Endpoints 里的目标地址。这个后面讲 ExternalName 时可以联想思路是一样的Headless 加手动 Endpoints 可以让你把集群外的固定后端也“伪装”成集群内服务而 ExternalName 则更进一步把整个映射交给 DNS 的 CNAME 机制。5.3 什么时候别用 HeadlessHeadless 不适合无状态 Web 服务。如果你对 nginx 这类应用建了个 Headless Service然后让客户端直接选一个 Pod IP 去连那基本等于抛弃了 k8s 的负载均衡、健康检查、滚动更新这些机制Pod 挂了客户端还死盯着旧 IP 不放属于给自己找麻烦。Headless 虽然灵活但它是“把发现机制交给应用”前提是你的应用确实有这个能力。6. ExternalName用 DNS 当“代理”的轻量服务6.1 ExternalName 的机制与配置ExternalName 是 Service 里最特殊的一个类型它不像其他 Service 那样绑定 Pod 或者 Endpoints而是把 Service 的 DNS 名称解析到另一个外部的 DNS 名称。也就是说当你访问my-ext.default.svc.cluster.local时CoreDNS 会给你一个 CNAME 响应指向你配置的外部域名。典型场景有两个一是业务代码里写死了某个内部服务名但现在该服务被迁移到了集群外你不想改代码只需要在集群里建一个 ExternalName 类型的 Service把内部服务名映射到外部地址代码一行不动就能继续工作二是需要把第三方服务的域名“翻译”成集群内的服务名方便统一维护。YAML 示例# service-externalname.yaml apiVersion: v1 kind: Service metadata: name: external-svc spec: type: ExternalName externalName: api.example.com创建后查看NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE external-svc ExternalName none api.example.com none 8s在集群内解析一下/ # nslookup external-svc.default.svc.cluster.local Name: external-svc.default.svc.cluster.local Address 1: api.example.com注意这里的输出行为CoreDNS 会返回一个 CNAME 记录指向api.example.com然后客户端会继续解析api.example.com拿到它的 A 记录。整个链路是集群内 DNS - 外部 DNS完全不需要 kube-proxy 参与所以你在ipvsadm -Ln里永远看不到 ExternalName 类型的规则。6.2 ExternalName 的坑ExternalName 有几个容易踩的坑它不能指定端口。ExternalName 只做 DNS 层面的 CNAME 跳转你没法在 Service 里把port: 80映射成外部的port: 8080。如果外部服务的端口和集群内约定不一致就得在客户端代码里做处理或者额外挂一层反向代理。ExternalName 只支持 DNS 解析不支持 http 路径转发。它是四层“重定向”不是七层代理所以不要指望它做类似/api转到某域名、/web转到另一域名这种事。那是 Ingress 或者网关的职责。外部域名解析失败时客户端报错比较隐蔽。因为 DNS 解析失败发生在使用方而不是 kube-proxy 层排障时容易误以为 Service 没创建成功。可以先在集群内用一个 Pod 手动 nslookup 这个 ExternalName如果解析不通过再去检查外部域名本身。7. 踩坑实录五种服务类型的常见问题与排查技巧7.1 排障速查表下面这张表是我在实际运维里最常用的排障路径把它贴出来现象可能原因排查命令 / 手段Service ClusterIP 无法访问kube-proxy 未切 ipvs规则未生成ipvsadm -Ln看是否有对应规则查 kube-proxy Pod 日志同 Service 不同 Pod 访问到的后端总是同一个调度算法被配置成了 sh源地址哈希kubectl describe svc看注解改回 rrNodePort 端口不通安全组/防火墙未放行或者 nodePort 超出了默认范围先curl 127.0.0.1:nodePort本地验证再检查节点之间连通性LoadBalancer EXTERNAL-IP 一直是 pending集群里没有部署负载均衡控制器或地址池 IP 段耗尽查控制器日志看 IPAddressPool 配置Headless DNS 返回空结果selector 没匹配到 Pod或 Endpoints 为空kubectl get endpoints看后端数kubectl get pods -l appnginx看标签客户端 IP 被 SNAT 掉应用日志看不到真实来源externalTrafficPolicy 默认是 Cluster改成 Local但注意此时流量只在本机转发ExternalName 解析不了外部域名本身不可达或 CoreDNS 缓存异常在 Pod 内手动 nslookup外部域名再查 CoreDNS 日志ipvs 模式启用后 kube-proxy 不断重启节点内核缺少 ip_vs 模块lsmod7.2 几个特别值得注意的细节第一kube-proxy 三种模式的切换并不是“改了配置就完事”。如果集群里有存量的大量 Service切 ipvs 之后要重启 kube-proxy并且把所有旧 iptables 规则清干净。kube-proxy 启动时会在节点上写入一堆 iptables 链比如 KUBE-SERVICES、KUBE-NODEPORTS切回 ipvs 模式后这些链如果残留会影响节点网络。如果发现切了 ipvs 后流量异常手动检查并清空旧的 kube 相关 iptables 链iptables -t nat -L -n | grep KUBE-SERVICES正常情况下 ipvs 模式也会保留少量 iptables 规则比如用于处理 ingress 流量和 UDP 分片的情况但规则数量远少于 iptables 模式。第二NodePort 的端口竞争问题。当你有多个 Service 都显式配置了同一个 nodePortkube-proxy 会直接报错Service 创建会失败。所以生产环境如果对端口有强约束最好在 CI/CD 阶段做一个端口占用检查避免到了集群里才暴露问题。第三LoadBalancer 模式下 Service 的 externalTrafficPolicy 选择。自建 LB 接节点时如果 policy 是 Cluster负载均衡器可以把流量打给任意节点由节点跨节点转发这种模式对 LB 后端配置最友好——后端节点列表随便填。但如果改成 Local就要保证 LB 只把流量打给“后端 Pod 所在节点”否则一个节点上如果没有 PodLB 却把流量打过去就 100% 失败。有些 LB 实现比如自研的 keepalived nginx 方案没法做到动态感知 Pod 分布这种情况下用 Cluster 反而更稳代价就是多一段节点转发延迟。第四Headless Service 配合 StatefulSet 时的 Pod 就绪问题。StatefulSet 里的 Pod 默认是串行启动的只有前一个 Pod Ready下一个才开始建。如果 Headless Service 的 selector 没写对DNS 解析出来缺了某个 Pod应用启动时连不上 Peer就会导致后续 Pod 一直卡住。排查时优先kubectl get endpoints headless-svc确保每个 Pod 都在 Endpoints 列表里。第五ExternalName 和 Ingress 的选择。如果你的目的只是“把集群里的某个域名访问转到外部系统”并且外部系统还区分 http 路径那 ExternalName 是做不到的应该用 Ingress 的rewrite-target或者直接在负载层做 nginx 反代。ExternalName 最大的价值是“零代理纯 DNS”性能损耗为零但功能也确实是零。8. 几条实战心得直接抄作业先说切 ipvs 模式时的一个小细节如果你用的是老版本集群比如 1.18 以前的记得确认内核版本在 4.19 以上太老的内核里 ipvs 的 conntrack 支持不完整长连接会出现偶发超时。现在主流的节点系统内核都够用但如果你的节点还是 CentOS 7 且没更新内核切 ipvs 前最好先跑一遍压测。然后是关于服务类型的选型思路。我一般是这样做的只需要集群内互相访问的用默认 ClusterIP不要额外暴露需要外部访问但没有外部负载均衡条件的先用 NodePort 撑住端口记到 CMDB条件允许、而且希望端口收敛的上 LoadBalancer一个 VIP 对应一个服务比 NodePort 好维护太多有状态应用需要做域名解析、对等发现的用 Headless集群外服务迁移或对接外部系统的优先想到 ExternalName 而不是写死 IP因为 IP 会变域名好歹有个稳定的入口。最后分享一个排查必杀技当 Service 网络怎么都不通的时候不要急着在应用容器里抓包。先分四步第一步在宿主机上curl 127.0.0.1:nodePort验证本机转发链路第二步在集群内的一个普通 Pod 里curl ClusterIP:Port验证集群内路由第三步看ipvsadm -Ln和iptables-save | grep KUBE确认规则生成没问题第四步再看 kube-proxy 日志有没有同步报错。这四步走下来80% 的问题都能定位到具体层级剩下的就是后端 Pod 本身健康检查不过的问题了。这套排查思路我自己用了很久每次都管用。
阅读完成 · 觉得有帮助?
咨询建站