我一直在尝试找到一种方法来在一个命名空间中定义一个服务,该命名空间链接到另一个命名空间中运行的Pod。我知道在namespaceA
中运行的Pod中的容器可以通过在集群DNS中将其引用为serviceX
来访问namespaceB
中定义的serviceX.namespaceB.svc.cluster.local
,但我宁愿不让容器内的代码需要知道serviceX
的位置。也就是说,我希望代码只是查找serviceX
然后才能访问它。
Kubernetes documentation表明这是可能的。它表示您在没有选择器的情况下定义服务的原因之一是您希望将服务指向另一个名称空间或另一个集群中的服务。
这告诉我,我应该:
serviceX
中定义namespaceA
服务,没有选择器(因为我想要选择的POD不在namespaceA
中)。serviceX
中定义一个服务(我也称为namespaceB
),然后namespaceA
中定义一个Endpoints对象,指向serviceX
中的namespaceB
。这是我无法完成的第三步。
首先,我尝试以这种方式定义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
这似乎是合乎逻辑的方法,显然是targetRef
的用途。但是,这导致了一个错误,即ip
阵列中的addresses
字段是强制性的。因此,我的下一个尝试是将一个固定的ClusterIP地址分配给serviceX
中的namespaceB
,并将其放入IP字段中(注意service_cluster_ip_range
配置为192.168.0.0/16
,192.168.1.1
被指定为serviceX
中namespaceB
的ClusterIP; serviceX
中的namespaceA
是在192.168.0.0/16
子网上自动分配了不同的ClusterIP):
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
这被接受,但在serviceX
访问namespaceA
没有被转发到namespaceB
的Pod - 他们超时了。看看iptables设置,看起来它必须做两次NAT预路由来完成它。
我唯一能找到的工作 - 但不是一个令人满意的解决方案 - 是在serviceX
中查找提供namespaceB
的Pod的实际IP地址,并将该地址放在namespaceA
的Endpoints对象中。当然,这并不令人满意,因为Pod IP地址可能会随着时间而改变。这就是服务IP需要解决的问题。
那么,有没有办法满足文档的承诺,我可以将一个命名空间中的服务指向运行在不同命名空间中的服务?
一位评论者质疑你为什么要这样做 - 这是一个对我有意义的用例,至少:
假设您有一个多租户系统,该系统还包括可在租户之间共享的通用数据访问功能。现在想象一下,这种数据访问功能与常见的API有不同的风格,但性能特征不同。有些租户可以访问其中一个,其他租户可以访问另一个。
每个租户的pod都在自己的命名空间中运行,但每个人都需要访问其中一个常见的数据访问服务,这些服务必然位于另一个命名空间中(因为它可以被多个租户访问)。但是,如果他们的订阅更改为访问性能更高的服务,您不希望租户必须更改其代码。
一个潜在的解决方案(我能想到的最干净的解决方案,如果它只能工作)是在每个租户的数据访问服务的命名空间中包含一个服务定义,每个服务定义都配置用于适当的端点。此服务定义将配置为指向每个租户有权使用的正确数据访问服务。
我偶然发现了同样的问题并找到了一个不需要任何静态ip配置的好解决方案:
你可以通过它的DNS name(如你所提到的)访问服务:servicename.namespace.svc.cluster.local
您可以使用该DNS名称在another namespace via a local service中引用它:
kind: Service
apiVersion: v1
metadata:
name: service-y
namespace: namespace-a
spec:
type: ExternalName
externalName: service-x.namespace-b.svc.cluster.local
ports:
- port: 80
这样做很简单
如果您想将其用作主机并想要解决它
它会像:servicename.namespacename.svc.cluster.local
这会将请求发送到你提到的命名空间内的特定服务。
对于前者
kind: Service
apiVersion: v1
metadata:
name: service
spec:
type: ExternalName
externalName: <servicename>.<namespace>.svc.cluster.local
这里用适当的值替换<servicename>
和<namespace>
。
在kubernetes中,名称空间用于创建虚拟环境,但所有名称空间都相互连接。
您可以通过在比命名空间服务更高的层上部署某些内容来实现此目的,例如服务负载均衡器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来说有点乱。