kubernetes: служба, расположенная в другом пространстве имен
Я пытался найти способ определить службу в одном пространстве имен, которая связана с модулем, работающим в другом пространстве имен. Я знаю, что контейнеры в модуле Pod, работающем в "namespaceA", могут обращаться к "serviceX", определенному в "namespaceB", ссылаясь на него в кластере DNS как "serviceX.namespaceB.svc.cluster.local", но я бы предпочел не иметь код Внутри контейнера нужно знать о местонахождении "serviceX". То есть я хочу, чтобы код просто посмотрел "serviceX" и затем смог получить к нему доступ.
Документация Kubernetes предполагает, что это возможно. В нем говорится, что одна из причин, по которой вы определяете службу без селектора, заключается в том, что "вы хотите указать свою службу для службы в другом пространстве имен или в другом кластере".
Это подсказывает мне, что я должен:
- Определите службу "serviceX" в "namespaceA" без селектора (поскольку POD, который я хочу выбрать, находится не в "namespaceA").
- Определите службу (которую я также назвал "serviceX") в "namespaceB", а затем
- Определите объект Endpoints в "namespaceA", чтобы он указывал на "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" в массиве "address" является обязательным. Итак, моя следующая попытка состояла в том, чтобы назначить фиксированный адрес ClusterIP для "serviceX" в "namespaceB" и поместить его в поле IP (обратите внимание, что service_cluster_ip_range настроен как 192.168.0.0/16, а 192.168.1.1 был назначен как ClusterIP для "serviceX" в "namespaceB"; "serviceX" в "namespaceA" автоматически назначил другой ClusterIP в подсети 192.168.0.0/16):
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" не был перенаправлен в Pod в "namespaceB" - они истекли. Глядя на настройку iptables, похоже, что для этого нужно было бы дважды выполнить предварительную маршрутизацию NAT.
Единственное, что я обнаружил, что это сработало, но не является удовлетворительным решением, - это поиск фактического IP-адреса модуля, предоставляющего "serviceX", в "namespaceB" и помещение этого адреса в объект "Конечные точки" в "namespaceA". Конечно, это неудовлетворительно, потому что IP-адрес Pod может со временем меняться. Это проблема, которую должны решить IP службы.
Итак, есть ли способ удовлетворить то, что кажется обещанием документации, что я могу указать службу в одном пространстве имен службе, работающей в другом пространстве имен?
Комментатор спросил, почему вы хотели бы сделать это - вот пример использования, который имеет смысл для меня, по крайней мере:
Допустим, у вас есть мультитенантная система, которая также включает в себя общую функцию доступа к данным, которая может совместно использоваться арендаторами. Теперь представьте, что существуют разные варианты этой функции доступа к данным с общими API, но с разными характеристиками производительности. Некоторые арендаторы получают доступ к одному из них, другие арендаторы имеют доступ к другому.
Модули каждого арендатора работают в своих собственных пространствах имен, но каждому из них требуется доступ к одной из этих общих служб доступа к данным, которые обязательно будут находиться в другом пространстве имен (поскольку к нему обращаются несколько арендаторов). Но вы не хотели бы, чтобы арендатор изменил свой код, если его подписка изменится, чтобы получить доступ к высокопроизводительной службе.
Потенциальное решение (самое чистое, которое я могу придумать, если бы оно работало) состоит в том, чтобы включить определение службы в пространство имен каждого клиента для службы доступа к данным, причем каждое из них настроено для соответствующей конечной точки. Это определение службы будет настроено таким образом, чтобы указывать на соответствующую службу доступа к данным, которую каждый арендатор имеет право использовать.
7 ответов
Я наткнулся на ту же проблему и нашел хорошее решение, которое не требует статической конфигурации IP:
Вы можете получить доступ к сервису через его DNS-имя (как вы упомянули): servicename.namespace.svc.cluster.local
Вы можете использовать это DNS-имя для ссылки на него в другом пространстве имен через локальную службу:
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 пространства имен используются для создания виртуальной среды, но все они связаны друг с другом.
Чтобы получить доступ к службам в двух разных пространствах имен, вы можете использовать URL-адрес следующим образом:
HTTP://<your-service-name>.<namespace-with-that-service>.svc.cluster.local
Чтобы перечислить все ваши пространства имен, которые вы можете использовать:
kubectl get namespace
А для обслуживания в этом пространстве имен вы можете просто использовать:
kubectl get services -n <namespace-name>
это вам поможет.
Вы можете добиться этого, развернув что-то на более высоком уровне, чем службы с пространством имен, например, сервис 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.
Доступ к службам в нескольких пространствах имен с помощью автономных служб
На самом деле я нашел этоheadless service
можно получить доступ из нескольких пространств имен без особых усилий. Если вы хотите использовать безголовый сервис, преобразуйте свой сервис следующим образом:
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
NB: я использую сетевой плагин flannel с настройками по умолчанию с k8s версии v1.24.4 и убедитесь, что в вашем контейнере установлен ping.
Ни одно из предыдущих решений не помогло мне в OpenShift 4(.11) с настройками сети по умолчанию.
Я попробовал это на маршруте с включенной https://docs.openshift.com/container-platform/4.11/networking/routes/route-configuration.html#nw-route-admission-policy_route-configuration , но безуспешно. Я могу связаться с podA в NS-A из pod B в NS-B с помощью Curl, но не из службы или маршрута.
В конце концов я нашел решение, используя EndPointSlice: https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/
Там метка, указывающая на службу, — это служба, на которую можно ссылаться в маршруте (она не обязательно должна существовать, кажется, что ее можно использовать по имени метки). Это выглядит примерно так:
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: slice-it
namespace: namespace-a
labels:
kubernetes.io/service-name: test-service
addressType: FQDN
ports:
- name: 'http'
appProtocol: http
protocol: TCP
port: 8080
- name: 'http1'
appProtocol: http
protocol: TCP
port: 8081
endpoints:
- addresses:
- "servicename-in-b.namespace_b.svc"
conditions:
ready: true
Это дает вам возможность создать маршрут в пространстве имен A, который указывает на службу в пространстве имен B, используя имя службы test-service (в пространстве имен A).
Я надеюсь, что это поможет, могут быть лучшие способы, но это сработало для меня, насколько я понимаю, это то, что должен делать тип службы ExternalName, но у меня это вообще не работало ни с обозначением имени, ни с внутренним, ни с внешним.
Обратите внимание, что определены два порта, поскольку службы, которые я использовал, работали на другом порту, похоже, это объединяет их. Доработки и пояснения приветствуются.
Потратив некоторое время на попытки реализовать это в EKS, я нашел решение, которое может быть полезно кому-то еще в будущем.
Поскольку EKS не поддерживает внешние имена , решение состоит в том, чтобы создать входы в каждое пространство имен, где у вас есть службы, и заставить все входы использовать один и тот же балансировщик нагрузки, добавив аннотацию к каждому входу для IngressGroups, например:
alb.ingress.kubernetes.io/group.name: моя группа
Для получения дополнительной информации перейдите по этой ссылке и выполните поиск:Чтобы совместно использовать балансировщик нагрузки приложения для нескольких ресурсов Ingress с помощью IngressGroups.