我一直试图找到一种方法,在一个名称空间中定义一个服务,该服务链接到另一个名称空间中运行的Pod。我知道运行在namespaceA中的Pod中的容器可以通过在集群DNS中将其引用为serviceX.namespaceB.svc.cluster.local来访问namespaceB中定义的serviceX,但我宁愿容器内的代码不需要知道serviceX的位置。也就是说,我希望代码只查找serviceX,然后能够访问它。

Kubernetes的文档表明这是可能的。它说,定义不使用选择器的服务的原因之一是您希望将服务指向另一个命名空间或另一个集群上的服务。

这让我觉得我应该:

在namespaceA中定义一个serviceX服务,没有选择器(因为我想选择的POD不在namespaceA中)。 在namespaceB中定义一个服务(我也称之为serviceX),然后 在namespaceA中定义端点对象,以指向namespaceB中的serviceX。

这第三步是我没能完成的。

首先,我尝试这样定义Endpoints对象:

kind: Endpoints
apiVersion: v1
metadata:
  name: serviceX
  namespace: namespaceA
subsets:
  - addresses:
      - targetRef:
          kind: Service
          namespace: namespaceB
          name: serviceX
          apiVersion: v1
    ports:
      - name: http
        port: 3000

That seemed the logical approach, and obviously what the targetRef was for. But, this led to an error saying that the ip field in the addresses array was mandatory. So, my next try was to assign a fixed ClusterIP address to serviceX in namespaceB, and put that in the IP field (note that the service_cluster_ip_range is configured as 192.168.0.0/16, and 192.168.1.1 was assigned as the ClusterIP for serviceX in namespaceB; serviceX in namespaceA was auto assigned a different ClusterIP on the 192.168.0.0/16 subnet):

kind: Endpoints
apiVersion: v1
metadata:
  name: serviceX
  namespace: namespaceA
subsets:
  - addresses:
        - ip: 192.168.1.1
          targetRef:
            kind: Service
            namespace: namespaceB
            name: serviceX
            apiVersion: v1
    ports:
      - name: http
        port: 3000

这是可以接受的,但是对namespaceA中的serviceX的访问没有转发到namespaceB中的Pod—它们超时了。看一下iptables设置,它似乎必须执行两次NAT预路由才能实现这一点。

我发现唯一有效的方法(但不是一个令人满意的解决方案)是在namespaceB中查找提供serviceX的Pod的实际IP地址,并将该地址放在namespaceA中的Endpoints对象中。当然,这并不令人满意,因为Pod IP地址可能会随着时间的推移而改变。这就是服务ip要解决的问题。

那么,是否有一种方法可以满足文档的承诺,即我可以将一个名称空间中的服务指向在另一个名称空间中运行的服务?

一个评论者质疑你为什么要这样做-这里有一个用例,至少对我来说是有意义的:

假设您有一个多租户系统,该系统还包括一个可在租户之间共享的公共数据访问功能。现在想象一下,这个数据访问函数有不同的风格,使用通用api,但性能特征不同。一些租户可以访问其中一个,其他租户可以访问另一个。

每个租户的pod在各自的名称空间中运行,但每个pod都需要访问其中一个公共数据访问服务,该服务必须位于另一个名称空间中(因为它由多个租户访问)。但是,如果租户的订阅更改以访问性能更高的服务,则不希望租户必须更改其代码。

一个潜在的解决方案(我能想到的最干净的解决方案,如果它能工作的话)是在每个租户的名称空间中包含数据访问服务的服务定义,并为适当的端点配置每个服务定义。该服务定义将被配置为指向每个租户有权使用的正确数据访问服务。


当前回答

您可以通过在比命名空间服务更高的层部署一些东西来实现这一点,比如服务loadbalancer https://github.com/kubernetes/contrib/tree/master/service-loadbalancer。如果你想将它限制在单个名称空间,使用"——namespace=ns"参数(默认为所有名称空间:https://github.com/kubernetes/contrib/blob/master/service-loadbalancer/service_loadbalancer.go#L715)。这对L7来说很有效,但对L4来说有点乱。

其他回答

在花了一些时间尝试在EKS中实现这一点之后,我找到了可能在将来对其他人有用的解决方案。

由于EKS不支持外部名称,解决方案是在每个有服务的命名空间中创建入口,并通过为每个Ingress添加IngressGroups的注释,使所有入口使用相同的负载均衡器,如下所示:

alb.ingress.kubernetes.io / group.name:我的团队

欲了解更多详情,请访问此链接并搜索: 使用IngressGroups在多个Ingress资源之间共享应用程序负载均衡器

您可以通过在比命名空间服务更高的层部署一些东西来实现这一点,比如服务loadbalancer https://github.com/kubernetes/contrib/tree/master/service-loadbalancer。如果你想将它限制在单个名称空间,使用"——namespace=ns"参数(默认为所有名称空间:https://github.com/kubernetes/contrib/blob/master/service-loadbalancer/service_loadbalancer.go#L715)。这对L7来说很有效,但对L4来说有点乱。

我偶然发现了同样的问题,并找到了一个很好的解决方案,不需要任何静态ip配置:

您可以通过服务的DNS名称(如您所述)访问服务:servicename.namespace.svc.cluster.local

您可以使用该DNS名称通过本地服务在另一个命名空间中引用它:

kind: Service
apiVersion: v1
metadata:
  name: service-y
  namespace: namespace-a
spec:
  type: ExternalName
  externalName: service-y.namespace-b.svc.cluster.local
  ports:
  - port: 80

要在两个不同的命名空间中访问服务,你可以使用这样的url:

HTTP://<your-service-name>.<namespace-with-that-service>.svc.cluster.local

列出你可以使用的所有命名空间:

kubectl get namespace

对于该名称空间中的服务,您可以简单地使用:

kubectl get services -n <namespace-name>

这对你有帮助。

使用headless服务跨多个名称空间访问服务

实际上,我发现headless服务可以从多个名称空间访问,而不需要做很多事情。如果你想采用无头服务方式,那么按以下方式转换你的服务:

apiVersion: v1
kind: Service
metadata:
  name: nfs-server-svc
  namespace: nfs-server
spec:
  clusterIP: None
  ports:
  - name: nfs
    port: 2049
    targetPort: nfs
    protocol: TCP
  - name: rpcbind
    port: 111
    targetPort: rpcbind
  - name: mountd
    port: 20048
    targetPort: mountd
  selector:
    app: nfs-server-app
  type: ClusterIP

现在您可以ssh到任何命名空间中的任何容器并ping <service.name>.<namespace>或<service.name>.<namespace>.svc或<service.name>.<namespace>.svc.cluster.local。他们都解决了。例子:

kubectl -n nfs-server exec -it my-web-deployment-68976cb578-f9v8t -- bash
bash-5.1# ping nfs-server-svc-svc.nfs-server.svc.cluster.local
PING uoe-api-v6-nfs-svc.uoe-api-v6.svc (10.244.2.176): 56 data bytes
64 bytes from 10.244.2.176: seq=0 ttl=62 time=0.877 ms
64 bytes from 10.244.2.176: seq=1 ttl=62 time=0.983 ms
64 bytes from 10.244.2.176: seq=2 ttl=62 time=0.956 ms
^C
--- nfs-server-svc-svc.nfs-server.svc.cluster.local ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.877/0.938/0.983 ms
bash-5.1#

nfs-server-svc-svc.nfs-server.svc.cluster.local

注意:我使用默认设置的法兰绒网络插件,k8s版本v1.24.4,并确保你的容器安装了ping。