Kubernetes服务发现

一开始学 K8s 的时候,我总觉得服务发现不就是给 Pod 整个固定入口吗?能有多难?结果越学越懵:

  • 为什么 Pod 的 IP,不管在哪个节点,都能直接访问?

  • 为什么我自己搭的集群,创建 LoadBalancer Service,IP 一直是 pending?

  • 为什么有了 Service,还要搞个 Ingress?

  • 还有 Headless Service,这玩意儿到底是干嘛的?


一、最基础也最容易忽略:Pod 的跨节点网络,到底是怎么通的?

这是我第一个搞不懂的点:我之前以为,Pod 是跑在节点上的,那不同节点的 Pod,要访问的话,是不是要做 NAT?要做端口转发? 结果 K8s 说,不用,你直接用 Pod 的 IP 就能访问,不管它在哪个节点。

这到底是怎么做到的?

原来 K8s 搞了个 "扁平网络"

K8s 设计了一个扁平化的网络模型:整个集群的所有 Pod,都在同一个大的网络里,每个 Pod 都有一个唯一的、整个集群内都能路由的 IP,没有 NAT,没有端口转换,你直接用这个 IP,就能访问到它。

那这个网络是怎么实现的?K8s 自己没做,而是搞了个标准接口,叫CNI(容器网络接口),把具体的实现,交给第三方插件,比如我们常见的 Flannel、Calico。

跨节点通信的两种实现

不同节点的 Pod,怎么通信?有两种完全不同的实现方式:

  1. Overlay 网络(比如 Flannel) : 这个我一开始死活搞不懂,后来才想明白,它就是在节点的物理网络之上,又建了一条虚拟的隧道。 比如我节点 A 的 Pod,要给节点 B 的 Pod 发数据,它会把原来的 Pod 数据包,再包一层节点的数据包,然后通过物理网络,把这个包发到节点 B,节点 B 收到之后,把外层的包拆掉,把数据交给自己的 Pod。

    你可以把它理解成,在普通的高速公路之上,又建了一条专门给 Pod 用的虚拟高速路,不管你原来的物理网络是什么样的,这条虚拟路都能通。

  2. 路由模式(比如 Calico): 这个就更直接了,它不搞隧道,也不做封包,而是直接给集群的路由表加规则:它会告诉每个节点,"如果要去某个 Pod 的 IP 段,你就把数据包发给对应的节点就行"。 这样一来,数据包直接用原生的 IP 路由转发,不用封包解包,性能比 Overlay 要高很多。

搞懂了这个,我才明白,原来 K8s 的网络,不是什么黑科技,就是靠这些插件,帮我们把整个集群的 Pod,都放到了同一个大网络里,这才是后面所有服务发现的基础。


二、kube-proxy 的两种模式,为什么大集群一定要用 IPVS?

iptables 模式:小集群够用,大集群会 "卡"

iptables 是默认的模式,它的逻辑很简单:

  • 你创建一个 Service,kube-proxy 就给它生成一条 iptables 规则,按顺序存到规则列表里

  • 当有请求过来,iptables 就从列表的第一条开始,一条条找,直到找到匹配的规则,然后把请求转发到 Pod

这个模式,小集群的时候,规则少,找起来很快,没什么问题。 但是如果你的集群很大,有上万个 Service,那规则列表就会变得超级长,每次请求,都要从头遍历所有规则,规则越多,找的速度就越慢,请求的延迟就会越来越高。

IPVS 模式:专门为大规模集群设计的负载均衡

IPVS 就不一样了,它是 Linux 内核里,专门为四层负载均衡做的模块:

  • 它存规则的时候,不是用顺序列表,而是用哈希表

  • 哈希表的好处是,你可以直接通过 Service 的 IP + 端口,一下就定位到对应的规则,不管有多少规则,这个定位的时间都是固定的

也就是说,哪怕你有上万个 Service,IPVS 的转发速度,都不会变慢,这就是为什么大集群一定要用 IPVS 模式的原因。

这两个模式,不是功能不一样,是处理请求的方式不一样,小集群用哪个都无所谓,大集群一定要用 IPVS,不然性能会崩。


三、Headless Service:这玩意儿到底是干嘛的?

一开始我看到 Headless Service:Service 不就是给个固定入口吗?你把 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 的功能,用起来和云厂商的一模一样。 它有两个组件:

  1. Controller:负责管 IP,你提前告诉它你有哪些可用的 IP,它会给每个 LoadBalancer Service 分配一个

  2. Speaker:负责管网络,告诉集群外的网络,这个 IP 在哪个节点,把流量接过来,转发到 Pod

用了 MetalLB 之后,我自己的私有集群,也能正常用 LoadBalancer 了,再也不会 pending 了,这个坑,我踩了整整一天才搞懂。


五、最后一个疑问:为什么有了 Service,还要 Ingress?

有 NodePort、LoadBalancer 了,为什么还要搞个 Ingress?

直到开始部署多个服务,才明白:

  • 用 NodePort 的话,每个服务要开一个不同的端口,我有 10 个服务,就要记 10 个端口,太麻烦了

  • 用 LoadBalancer 的话,每个服务要一个独立的公网 IP,10 个服务就要 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 的这些知识点,都是层层递进的:

  1. 先搞懂基础的 Pod 网络,知道为什么 Pod 的 IP 能直接访问

  2. 然后搞懂 Service 是怎么解决 PodIP 不稳定的问题的

  3. 然后搞懂不同的 Service 类型,分别解决什么问题

  4. 最后搞懂 Ingress,怎么用一个 IP 管理所有的服务

只要把这些难点一个个啃透,你就会发现,原来 K8s 的服务发现,不是一堆乱七八糟的概念,而是一套完整的、解决问题的方案,从基础的网络,到服务的入口,到多服务的管理,都给你考虑到了。

相关推荐
七七powerful2 小时前
K8s 工具安装文档 — Harbor + ArgoCD
容器·kubernetes·argocd
立莹Sir2 小时前
云原生实战:从零搭建企业级K8s环境
云原生·容器·kubernetes
立莹Sir2 小时前
云原生全解析:从概念到实践,Java技术栈如何拥抱云原生时代
java·开发语言·云原生
Skilce2 小时前
K8S部署
linux·运维·服务器·容器·kubernetes
张3232 小时前
kubernetes Pod难点
云原生·容器·kubernetes
qq2439201613 小时前
ubuntu搭建k8s 1.35版本
云原生·容器·kubernetes
cyber_两只龙宝3 小时前
【Oracle】Oracle之DQL中SELECT的基础使用
linux·运维·服务器·数据库·云原生·oracle
悠悠121383 小时前
K8s持久化存储深度解析:PV、PVC、StorageClass三剑客的生产实战
云原生·容器·kubernetes