bigywater.tech
Kubernetes · 67

Service 类型:ClusterIP、NodePort、LoadBalancer

Service 的四种类型到底各是什么关系?NodePort 只在有 Pod 的节点开端口吗?LoadBalancer 没有云厂商时是什么状态?ExternalName 有没有负载均衡?这篇在三节点 kind 集群里每种类型都实际访问一遍。

10.96.0.0/16
ClusterIP 来自的 Service CIDR;宿主机不可达
3 个节点都通
NodePort 在所有节点上监听,包括没有后端 Pod 的节点
没有云控制器时 LoadBalancer 永远停在这个状态

ClusterIP:集群内的虚拟地址

实测 · 本机执行输出
  Service s-cip:type=ClusterIP;clusterIP=10.96.84.171(来自 Service CIDR 10.96.0.0/16);port 8080 → targetPort 80
  从 Pod cli 访问 10.96.84.171:8080 → <title>Welcome to nginx!;用 Service DNS s-cip:8080 → <title>Welcome to nginx!
  从节点 worker(有后端的节点)访问 → <title>Welcome to nginx!;从别的节点 control-plane 访问 → <title>Welcome to nginx!(kube-proxy 在每个节点上都写了规则)
  从宿主机(Mac)直接访问 10.96.84.171:8080:不可达(超时)
  直接访问 Pod IP 10.244.1.74:80 → <title>Welcome to nginx!(Pod IP 也通,但 Pod 重建后会变)

Service s-cip 的 clusterIP=10.96.84.171 来自 Service CIDR 10.96.0.0/16,port: 8080 转发到后端的 targetPort: 80。Pod 里用 IP:8080 或 DNS 名 s-cip:8080 都能访问到 nginx;在有后端的 worker 上、没有后端的 control-plane 上访问同样能通——kube-proxy 在每个节点上都写了规则。从宿主机(Mac)直接访问这个 IP 超时:它不在任何网卡上,只是被节点的转发规则识别。直接访问 Pod IP 也通,但 Pod 重建后会变,所以才需要 Service。

NodePort:每个节点都开

实测 · 本机执行输出
  Service s-np:type=NodePort;自动分配 nodePort=32504(范围 30000-32767);同时也有 clusterIP=10.96.102.139(NodePort 是 ClusterIP 的超集)
  从 Pod cli 访问节点 control-plane(172.18.0.3):32504 → <title>Welcome to nginx!(这个节点没有后端 Pod,也能通)
  从 Pod cli 访问节点 worker(172.18.0.2):32504 → <title>Welcome to nginx!(这个节点有后端 Pod)
  从 Pod cli 访问节点 worker2(172.18.0.4):32504 → <title>Welcome to nginx!(这个节点没有后端 Pod,也能通)
  宿主机访问 localhost:32504(kind 没有映射这个端口):不可达
  指定 nodePort: 80:退出码 1;The Service "s-bad" is invalid: spec.ports[0].nodePort: Invalid value: 80: provided port is not in the valid range. The range of valid ports is 30000-32767
  指定 nodePort: 31234:退出码 0;实际 nodePort=31234
  另一个 Service 也要 31234:退出码 1;The Service "s-pin2" is invalid: spec.ports[0].nodePort: Invalid value: 31234: provided port is already allocated

s-np 自动分配了 nodePort=32504(范围 30000-32767),同时也有 ClusterIP——NodePort 是 ClusterIP 的超集。Pod 访问三个节点的 IP:32504 全部成功,包括没有后端 Pod 的 control-plane 和 worker2:每个节点都会转发。指定 nodePort: 80 被拒(provided port is not in the valid range),指定 31234 成功,第二个 Service 再要 31234 则 provided port is already allocated。kind 没有把这些端口映射到宿主机,所以宿主机的 localhost:32504 不通。

LoadBalancer:没有云就 pending

实测 · 本机执行输出
  Service s-lb:type=LoadBalancer;status.loadBalancer={};clusterIP=10.96.244.203;nodePort=31235
  kubectl get svc:s-lb   LoadBalancer   10.96.244.203   <pending>     8080:31235/TCP   8s
  LoadBalancer 创建时同时分配了 nodePort 31235;用它访问 172.18.0.2:31235 → <title>Welcome to nginx!(云上的负载均衡器就是把流量转发到这些 NodePort)
  allocateLoadBalancerNodePorts: false 的 s-lb2:nodePort=None
    s-lb 的事件:(没有——没有云控制器来处理它)

s-lb 的 status.loadBalancer 是空的,EXTERNAL-IP 是 <pending>,也没有任何事件——没有云控制器来处理它。但它同时被分配了 nodePort 31235,用节点 IP:31235 访问能通:云厂商的负载均衡器做的事就是把流量转给这些 NodePort。allocateLoadBalancerNodePorts: false 的 s-lb2 没有 nodePort(用于负载均衡器直连 Pod 的场景)。

ExternalName:只是 DNS

实测 · 本机执行输出
  Service s-ext:clusterIP=None;externalName=example.com
  在 Pod 里 nslookup s-ext:['s-ext.k61.svc.cluster.local\tcanonical name = example.com'];最后解析到 5 个外部地址(不是 Service 的 IP)
  iptables 里有 s-ext 的规则吗:0 条(没有 kube-proxy 规则,不经过 Service 转发)

s-ext 的 clusterIP 是 None,只有 externalName: example.com。在 Pod 里 nslookup s-ext.k61.svc.cluster.local 得到 canonical name = example.com,再解析出外部地址;节点 iptables 里找不到任何 k61/s-ext 的规则(0 条)。它不经过 kube-proxy,不做端口映射也不做负载均衡。

分步演示

Service 类型
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0,节点 IP 在 Docker 网络 172.18.0.0/16);后端是 1 个副本的 nginx:1.27-alpine;客户端是 busybox 的 wget,节点上用 curl;“宿主机访问”用 Mac 上的 curl,3 秒超时;ExternalName 解析的外部地址个数取决于宿主机的 DNS;每种情形各一次
    • 没有验证:真实云上的 LoadBalancer(健康检查、源 IP);MetalLB 等裸机方案;externalIPs;loadBalancerClass;Service 的 IPv6 / 双栈;ClusterIP 的 None(headless)见下一篇;sessionAffinity(后面有专门一篇);Service 的端口分配器如何避免冲突
    京ICP备2026067502号-1京公网安备11010502063487号