kubernetes: служба, расположенная в другом пространстве имен

Я пытался найти способ определить службу в одном пространстве имен, которая связана с модулем, работающим в другом пространстве имен. Я знаю, что контейнеры в модуле Pod, работающем в "namespaceA", могут обращаться к "serviceX", определенному в "namespaceB", ссылаясь на него в кластере DNS как "serviceX.namespaceB.svc.cluster.local", но я бы предпочел не иметь код Внутри контейнера нужно знать о местонахождении "serviceX". То есть я хочу, чтобы код просто посмотрел "serviceX" и затем смог получить к нему доступ.

Документация Kubernetes предполагает, что это возможно. В нем говорится, что одна из причин, по которой вы определяете службу без селектора, заключается в том, что "вы хотите указать свою службу для службы в другом пространстве имен или в другом кластере".

Это подсказывает мне, что я должен:

  1. Определите службу "serviceX" в "namespaceA" без селектора (поскольку POD, который я хочу выбрать, находится не в "namespaceA").
  2. Определите службу (которую я также назвал "serviceX") в "namespaceB", а затем
  3. Определите объект 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.

Другие вопросы по тегам