自建的 k8s 多节点集群,如何提供一个统一的对外 IP?

假设,我私有自建了一个 k8s 集群,集群有 10 个 node

然后我部署了一个 api 接口的 pod,我希望暴露这个 pod,让集群外的人,可以随意调用这个 api

这个时候,我有一个问题,调用者需要通过 ip+port 的方式访问,port 好说。但是 ip 填什么?

因为有 10 个 node,就有 10 个 ip,而且 pod 可以被任意调度到某个 node

这个时候,client 如何调用这个 api? 从 client 的角度,总得有一个固定 ip 吧?


这个时候,是不是要用「负载均衡」?

如果是的话,那么这个 「负载均衡」 部署在哪里?

  • 如果部署在 k8s 外面,那么问题依旧。
  • 如果部署在 k8s 集群内,作为一个 pod 存在,那么问题来了。「负载均衡」自己的 ip 就会随着部署在不同 node 上而变化,问题依旧了。
阅读 4.5k
2 个回答

填任意一个 Node 的 IP 即可。

如果你希望流量可以均匀的负载到每个 Node 上,那么你需要再套一层负载均衡器。那么方案无外乎三种:

  1. 你可以自己搭建一个负载均衡服务器,后面是你这 10 个 Node,把流量转发到对应的端口上。然后对外提供服务时填此负载均衡器的 IP 即可。
  2. 如果你在公有云上,也可以选择 k8s 内置的 LoadBalance 功能,此功能与云厂商提供的 LB 服务相结合。那么你对外提供服务时填的是云厂商的 LB 的地址。
  3. 如果你觉得单纯 IP 不好记,还是域名更好记,那么就是再开一个 k8s Ingress。同样地,你需要前面有一个负载均衡器,然后你把你的域名解析到此负载均衡器上,对外提供服务时填写该域名即可。

P.S. 其实你不要把 k8s 想得太复杂了,本质上就是你搭建了一个有 10 个节点的分布式集群,现在这个集群对外提供服务、需要一个所谓的统一的 Endpoint 该怎么办?没有 k8s 之前怎么办,现在依然怎么办。


【针对评论区的补充】

这里面有几个东西都起到负载均衡的作用了,所以你可能有点儿混淆。

首先先明确负载均衡的作用,它就是把一组什么玩意儿(一组里可能只有 1 个,但它也是一组),对外以一个统一入口的形式提供服务,访问这一个入口,流量会基于某种规则调度到后面的玩意儿上。这个“玩意儿”是啥,还需要具体问题具体分析,下面会讲到。

第一个是我们原本通常意义上所说的负载均衡,在有和没有 k8s 之前指的都是一回事儿。这里的负载均衡,是为了把公网流量均匀地负载到每台主机上。所以它当然是独立于你的集群存在的。这里你可以自己搭一台服务器,通过简单的反向代理来实现负载均衡。它实际就是原答案里我说的第一条。

这是“负载均衡到主机”,此时你的主机是一开始提到的那个“一组什么玩意儿”。

第二个是 k8s 的 External Load Balancer,直译过来叫外部负载均衡器。它实际就是原答案里我说的第二条。跟第一条没有什么实质性的区别,仅仅是跟公有云相结合的、所以能利用到一些公有云本身提供的能力,省得你自己搭费劲儿而已。

这是“负载均衡到 Node”,此时 Node 是一开始提到的那个“一组什么玩意儿”。但一般来说,你一台主机上只会跑一个 k8s node。除非是测试环境你没那么多真实主机可以用,只能迫不得已在一台主机上运行好几个 node 以实现伪集群。否则大部分语境下“负载均衡到主机”跟“负载均衡到 Node”是一个意思。

以上这两种,都是存在于 k8s 外部的负载均衡。只是一个是你自己搭的、你自己手动维护管理,一个是公有云提供的、你可以通过 k8s 维护管理。

第三个是 k8s 的 Service,它本身就有负载均衡的能力。你一个 Service 下可能会运行多个 Pod 对吧?那么请求实际会进到哪个 Pod 里,这是 Service 本身的负载均衡的能力。它是通过 kube-proxy 组件实现的。

这是“负载均衡到 Pod”,此时 Pod 是一开始提到的那个“一组什么玩意儿”。

第四个就是 k8s 的 Ingress 了,实质它就是个 nginx。因为是 nginx,所以它有七层负载均衡的能力。比如你可以配置 example.com/a/* 路径下的请求都转发给 Service A、 example.com/b/* 路径下的请求都转发给 Service B、 example.com/c/* 路径下的请求都转发给某一个特定的 Pod,这都可以。

这是“负载均衡到 Service/Pod”,此时 ServicePod 是一开始提到的哪个“一组什么玩意儿”。

以上这两种,都是存在于 k8s 内部的负载均衡。

这里面只有前两个你二选一即可,剩下的都是在各自范围实现的负载均衡,彼此并不重叠也不冲突。

回到原题,你需要的是第一种或第二种的负载均衡器。

原本没有k8s集群时,各个Node之间是独立的,组成集群后,k8s集群当作一个由多个Node组成的整体。k8s把分布在各个节点上的pod的网络合并成一个虚拟的大内网,即每个pod都在集群的内网中有一个唯一的IP地址。

如果是通过service/ingress作为流量入口,在收到请求时,最终会把请求转发给工作负载(pod),pod可能会被k8s调度到集群内的不同节点,集群内部,service就是一个负载均衡。

service有三种类型,默认的clusterIP,这个模式下,创建一个负载均衡器使用的是集群内部的IP,这种负载均衡只能在集群内部使用。

第二个是LoadBalancer,当使用公有云的集群,这种模式,正常情况下会有服务商提供一个更上层的负载均衡器,并提供一个公网IP x.x.x.x 。云服务商会将目的地址是x.x.x.x的流量转发到你的公有云的所有Node节点上。

第三个是NodePort,如果是自己建的私有集群,如果想要让服务从外部访问,只能选择这个模式。这个模式,调度器会在每个节点中都监听一个端口,每个节点都会把来自这个端口的请求负载到集群内部的pod中。

通过这个 Service 的负载机制,加上全局互通唯一的pod IP 就能够实现: 不管以哪个Node作为流量的入口,都能将请求转发到位于不同节点的POD上。


如果是自建的k8s集群,就只能选择NodePort暴露对外的服务。

这个时候可以只把一个节点暴露给外部,就可以只通过这一个节点访问集群内位于其他节点上的POD。

如果只使用一个节点,可能会有问题:

1. 这一个节点可能会有性能瓶颈,转发可能会出现问题
2. 这一个节点出现问题,会导致集群内所有的服务都不可用,就失去了“集群”冗余的作用。

为了解决上面这个问题,就是在集群上层再增加一层负载均衡,把请求转发到集群内的各个节点上。

这里如果是 LoadBalancer 类型的 service ,其实可以忽略上面这个这个集群外负载均衡的方案,因为在使用loadBalancer类型的服务时,云服务商已经帮你做了负载均衡了,云服务商会把流量转发到集群内的各个节点上,而不是单一节点。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进