位于另一个命名空间的服务

问题描述:

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

I have been trying to find a way to define a service in one namespace that links to a Pod running in another namespace. I know that containers in a Pod running in namespaceA can access serviceX defined in namespaceB by referencing it in the cluster DNS as serviceX.namespaceB.svc.cluster.local, but I would rather not have the code inside the container need to know about the location of serviceX. That is, I want the code to just lookup serviceX and then be able to access it.

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

The Kubernetes documentation suggests that this is possible. It says that one of the reasons that you would define a service without a selector is that You want to point your service to a service in another Namespace or on another cluster.

这表明我应该:

  1. namespaceA 中定义一个 serviceX 服务,没有选择器(因为我要选择的 POD 不在 namespaceA 中).
  2. namespaceB中定义一个服务(我也称它为serviceX),然后
  3. namespaceA 中定义一个 Endpoints 对象以指向 namespaceB 中的 serviceX.
  1. Define a serviceX service in namespaceA, without a selector (since the POD I want to select isn't in namespaceA).
  2. Define a service (which I also called serviceX) in namespaceB, and then
  3. Define an Endpoints object in namespaceA to point to serviceX in namespaceB.

这是我无法完成的第三步.

It is this third step that I have not been able to accomplish.

首先,我尝试以这种方式定义 Endpoints 对象:

First, I tried defining the Endpoints object this way:

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

这似乎是合乎逻辑的方法,显然targetRef 的用途.但是,这导致了一个错误,指出 addresses 数组中的 ip 字段是必需的.所以,我的下一个尝试是为 namespaceB 中的 serviceX 分配一个固定的 ClusterIP 地址,并将其放在 IP 字段中(注意 service_cluster_ip_range配置为192.168.0.0/16192.168.1.1被分配为namespaceBserviceX的ClusterIP; namespaceA 中的 serviceX192.168.0.0/16 子网上被自动分配了一个不同的 ClusterIP):

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 预路由才能完成此操作.

That was accepted, but accesses to serviceX in namespaceA did not get forwarded to the Pod in namespaceB - they timed out. Looking at the iptables setup, it looks like it would have had to do NAT pre-routing twice to accomplish that.

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

The only thing I did find that worked - but is not a satisfactory solution - is to lookup the actual IP address of the Pod providing serviceX in namespaceB and put that address in the Endpoints object in namespaceA. That isn't satisfactory, of course, because the Pod IP address may change over time. That's the problem service IPs are there to solve.

那么,有没有办法满足文档中的承诺,即我可以将一个命名空间中的服务指向运行在不同命名空间中的服务?

So, is there a way to meet what seems to be the promise of the documentation that I can point a service in one namespace to a service running in a different namespace?

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

A commenter questioned why you would want to do this - here is a use case that makes sense to me, at least:

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

Say you have a multi-tenant system, which also includes a common data-access function that can be shared between tenants. Now imagine that there are different flavors of this data-access function with common APIs, but different performance characteristics. Some tenants get access to one of them, other tenants have access to another one.

每个租户的 pod 都在自己的命名空间中运行,但是每个租户都需要访问这些公共数据访问服务之一,该服务必然位于另一个命名空间中(因为它被多个租户访问).但是,您不希望租户在订阅更改以访问更高性能的服务时必须更改其代码.

Each tenant's pods run in their own namespaces, but each one needs to access one of these common data-access services, which will necessarily be in another namespace (since it is accessed by multiple tenants). But, you wouldn't want the tenant to have to change their code if their subscription changes to access the higher-performing service.

一个潜在的解决方案(我能想到的最干净的解决方案,如果它有效的话)是在每个租户的命名空间中为数据访问服务包含一个服务定义,每个服务定义都针对适当的端点进行配置.此服务定义将配置为指向每个租户有权使用的正确数据访问服务.

A potential solution (the cleanest one I can think of, if only it worked) is to include a service definition in each tenant's namespace for the data-access service, with each one configured for the appropriate endpoint. This service definition would be configured to point to the proper data-access service each tenant is entitled to use.

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

I stumbled over the same issue and found a nice solution which does not need any static ip configuration:

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

You can access a service via it's DNS name (as mentioned by you): servicename.namespace.svc.cluster.local

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

You can use that DNS name to reference it in another namespace via a local service:

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