Kubernetes服务发现

张开发
2026/4/13 19:38:16 15 分钟阅读

分享文章

Kubernetes服务发现
一开始学 K8s 的时候我总觉得服务发现不就是给 Pod 整个固定入口吗能有多难结果越学越懵为什么 Pod 的 IP不管在哪个节点都能直接访问为什么我自己搭的集群创建 LoadBalancer ServiceIP 一直是 pending为什么有了 Service还要搞个 Ingress还有 Headless Service这玩意儿到底是干嘛的一、最基础也最容易忽略Pod 的跨节点网络到底是怎么通的这是我第一个搞不懂的点我之前以为Pod 是跑在节点上的那不同节点的 Pod要访问的话是不是要做 NAT要做端口转发 结果 K8s 说不用你直接用 Pod 的 IP 就能访问不管它在哪个节点。这到底是怎么做到的原来 K8s 搞了个 “扁平网络”K8s 设计了一个扁平化的网络模型整个集群的所有 Pod都在同一个大的网络里每个 Pod 都有一个唯一的、整个集群内都能路由的 IP没有 NAT没有端口转换你直接用这个 IP就能访问到它。那这个网络是怎么实现的K8s 自己没做而是搞了个标准接口叫CNI容器网络接口把具体的实现交给第三方插件比如我们常见的 Flannel、Calico。跨节点通信的两种实现不同节点的 Pod怎么通信有两种完全不同的实现方式Overlay 网络比如 Flannel 这个我一开始死活搞不懂后来才想明白它就是在节点的物理网络之上又建了一条虚拟的隧道。 比如我节点 A 的 Pod要给节点 B 的 Pod 发数据它会把原来的 Pod 数据包再包一层节点的数据包然后通过物理网络把这个包发到节点 B节点 B 收到之后把外层的包拆掉把数据交给自己的 Pod。你可以把它理解成在普通的高速公路之上又建了一条专门给 Pod 用的虚拟高速路不管你原来的物理网络是什么样的这条虚拟路都能通。路由模式比如 Calico 这个就更直接了它不搞隧道也不做封包而是直接给集群的路由表加规则它会告诉每个节点“如果要去某个 Pod 的 IP 段你就把数据包发给对应的节点就行”。 这样一来数据包直接用原生的 IP 路由转发不用封包解包性能比 Overlay 要高很多。搞懂了这个我才明白原来 K8s 的网络不是什么黑科技就是靠这些插件帮我们把整个集群的 Pod都放到了同一个大网络里这才是后面所有服务发现的基础。二、kube-proxy 的两种模式为什么大集群一定要用 IPVSiptables 模式小集群够用大集群会 “卡”iptables 是默认的模式它的逻辑很简单你创建一个 Servicekube-proxy 就给它生成一条 iptables 规则按顺序存到规则列表里当有请求过来iptables 就从列表的第一条开始一条条找直到找到匹配的规则然后把请求转发到 Pod这个模式小集群的时候规则少找起来很快没什么问题。 但是如果你的集群很大有上万个 Service那规则列表就会变得超级长每次请求都要从头遍历所有规则规则越多找的速度就越慢请求的延迟就会越来越高。IPVS 模式专门为大规模集群设计的负载均衡IPVS 就不一样了它是 Linux 内核里专门为四层负载均衡做的模块它存规则的时候不是用顺序列表而是用哈希表哈希表的好处是你可以直接通过 Service 的 IP 端口一下就定位到对应的规则不管有多少规则这个定位的时间都是固定的也就是说哪怕你有上万个 ServiceIPVS 的转发速度都不会变慢这就是为什么大集群一定要用 IPVS 模式的原因。这两个模式不是功能不一样是处理请求的方式不一样小集群用哪个都无所谓大集群一定要用 IPVS不然性能会崩。三、Headless Service这玩意儿到底是干嘛的一开始我看到 Headless ServiceService 不就是给个固定入口吗你把 ClusterIP 设为 None那还叫 Service 吗普通 Service 解决不了的问题普通的 ClusterIP Service是做负载均衡的你访问 Service 的名字它会给你返回一个虚拟 IP然后把请求随机转发到某个 Pod。 但是有状态应用比如 MySQL 的主从集群每个 Pod 的身份都不一样我要访问的不是 “随便一个 MySQL Pod”我要访问的是 “主节点的那个 Pod”我要直接连到具体的某个 Pod而不是被负载均衡到随机的 Pod。这时候普通的 Service 就没用了因为你没法通过它定位到具体的 Pod。Headless Service给每个 Pod 一个稳定的域名Headless Service 就是解决这个问题的它没有统一的 ClusterIP你把clusterIP设为 None当你给 StatefulSet 绑定了 Headless Service 之后StatefulSet 会给每个 Pod生成一个独立的 DNS 记录pod-name.service-name.namespace.svc.cluster.local这些域名是永久稳定的不管 Pod 的 IP 怎么变只要 Pod 的名字不变这个域名就永远指向它就算 Pod 重建了DNS 记录会自动更新域名还是能用。这样一来MySQL 的从节点只需要配置主节点的域名mysql-0.mysql-svc就永远能找到主节点就算主节点重建了也不用改配置完美解决了有状态应用的网络标识问题。Headless Service不是没用的东西是专门给有状态应用准备的这是我之前完全没搞懂的点。四、最坑的一个问题LoadBalancer 的 pending到底怎么解决我自己用虚拟机搭了个 K8s 集群然后创建了一个 LoadBalancer 类型的 Service结果等了半天EXTERNAL-IP一直是pending查了半天都不知道为什么。原来云厂商的 LoadBalancer是要花钱的LoadBalancer 这个类型一开始是给云厂商设计的 当你在阿里云、AWS 这些云平台上创建 LoadBalancer Service 的时候K8s 会自动调用云厂商的 API帮你创建一个云负载均衡实例给你分配一个公网 IP整个过程都是自动的。但是你自己搭的私有集群没有云厂商的这些服务K8s 就懵了它不知道要去哪里给你申请公网 IP也不知道要怎么创建负载均衡所以就只能把 IP 一直卡在 pending。私有集群的救星MetalLB那私有集群就不能用 LoadBalancer 了吗当然不是MetalLB 就是干这个的 它模拟了云厂商的负载均衡能力给你的私有集群也能提供 LoadBalancer 的功能用起来和云厂商的一模一样。 它有两个组件Controller负责管 IP你提前告诉它你有哪些可用的 IP它会给每个 LoadBalancer Service 分配一个Speaker负责管网络告诉集群外的网络这个 IP 在哪个节点把流量接过来转发到 Pod用了 MetalLB 之后我自己的私有集群也能正常用 LoadBalancer 了再也不会 pending 了这个坑我踩了整整一天才搞懂。五、最后一个疑问为什么有了 Service还要 Ingress有 NodePort、LoadBalancer 了为什么还要搞个 Ingress直到开始部署多个服务才明白用 NodePort 的话每个服务要开一个不同的端口我有 10 个服务就要记 10 个端口太麻烦了用 LoadBalancer 的话每个服务要一个独立的公网 IP10 个服务就要 10 个 IP成本太高了而 Ingress就是解决这个问题的它可以让你用一个公网 IP管理所有的服务通过域名或者路径来区分www.test.com的请求转发到 A 服务api.test.com的请求转发到 B 服务test.com/a的请求转发到 A 服务test.com/b的转发到 B 服务这样一来我只需要一个 IP就能管理所有的服务不用记一堆端口也不用买一堆 IP太方便了。而且 Ingress 还能帮你处理 HTTPS 的证书做限流、鉴权这些高级功能这些都是 Service 做不到的。总结K8s 的这些知识点都是层层递进的先搞懂基础的 Pod 网络知道为什么 Pod 的 IP 能直接访问然后搞懂 Service 是怎么解决 PodIP 不稳定的问题的然后搞懂不同的 Service 类型分别解决什么问题最后搞懂 Ingress怎么用一个 IP 管理所有的服务只要把这些难点一个个啃透你就会发现原来 K8s 的服务发现不是一堆乱七八糟的概念而是一套完整的、解决问题的方案从基础的网络到服务的入口到多服务的管理都给你考虑到了。

更多文章