Подтвердить что ты не робот

Как вывести безрецептурный сервис для StatefulSet извне в Кубернете

Используя kubernetes-kafka в качестве отправной точки с минибуком.

Для обнаружения службы внутри кластера используется служба StatefulSet и служба безгласных.

Цель состоит в том, чтобы разоблачить отдельных Kafka Brokers извне, которые внутренне адресованы как:

kafka-0.broker.kafka.svc.cluster.local:9092
kafka-1.broker.kafka.svc.cluster.local:9092 
kafka-2.broker.kafka.svc.cluster.local:9092

Ограничение состоит в том, что эта внешняя служба может адресовать брокеров конкретно.

Каков правильный (или один возможный) способ обойти это? Возможно ли открыть внешнюю службу за kafka-x.broker.kafka.svc.cluster.local:9092?

4b9b3361

Ответ 1

Решения до сих пор были недостаточно удовлетворительными для меня, поэтому я собираюсь опубликовать свой ответ. Мои цели:

  1. Домены должны по-прежнему динамически управляться через StatefulSet как можно больше.
  2. Создайте внешнюю услугу на Pod (т.е. Kafka Broker) для клиентов Producer/Consumer и избегайте балансировки нагрузки.
  3. Создайте внутреннюю службу безглавых, чтобы каждый Брокер мог общаться друг с другом.

Начиная с Yolean/kubernetes-kafka, единственное, чего не хватает, это разоблачение услуги извне и две проблемы при этом.

  1. Создание уникальных ярлыков для каждого брокерского модуля, чтобы мы могли создать внешнюю службу для каждого из брокерских модулей.
  2. Сообщание брокерам обмениваться информацией друг с другом с использованием внутренней службы при настройке Kafka, чтобы сообщить производителю/потребителям обмениваться информацией через внешнюю службу.

На этикетках и внешних услугах:

Чтобы генерировать метки в папке, эта проблема была действительно полезна. Используя его в качестве руководства, мы добавляем следующую строку в свойство 10broker-config.yml init.sh с:

kubectl label pods ${HOSTNAME} kafka-set-component=${HOSTNAME}

Мы сохраняем существующее безгласное обслуживание, но мы также генерируем внешнюю услугу на модуль с использованием метки (я добавил их в 20dns.yml):

apiVersion: v1
kind: Service
metadata:
  name: broker-0
   namespace: kafka
spec:
  type: NodePort
  ports:
  - port: 9093
    nodePort: 30093
selector:
  kafka-set-component: kafka-0

Настройка Kafka с помощью внутренних/внешних слушателей

Я нашел эту проблему невероятно полезной, пытаясь понять, как настроить Kafka.

Это снова требует обновления свойств init.sh и server.properties в файле 10broker-config.yml со следующим:

Добавьте на server.properties чтобы обновить протоколы безопасности (в настоящее время используется PLAINTEXT):

listener.security.protocol.map=INTERNAL_PLAINTEXT:PLAINTEXT,EXTERNAL_PLAINTEXT:PLAINTEXT
inter.broker.listener.name=INTERNAL_PLAINTEXT

Динамически определять внешний IP и внешний порт для каждого Pod в init.sh:

EXTERNAL_LISTENER_IP=<your external addressable cluster ip>
EXTERNAL_LISTENER_PORT=$((30093 + ${HOSTNAME##*-}))

Затем настройте listeners и advertised.listeners IP - адрес для EXTERNAL_LISTENER и INTERNAL_LISTENER (также в init.sh собственности):

sed -i "s/#listeners=PLAINTEXT:\/\/:9092/listeners=INTERNAL_PLAINTEXT:\/\/0.0.0.0:9092,EXTERNAL_PLAINTEXT:\/\/0.0.0.0:9093/" /etc/kafka/server.properties
sed -i "s/#advertised.listeners=PLAINTEXT:\/\/your.host.name:9092/advertised.listeners=INTERNAL_PLAINTEXT:\/\/$HOSTNAME.broker.kafka.svc.cluster.local:9092,EXTERNAL_PLAINTEXT:\/\/$EXTERNAL_LISTENER_IP:$EXTERNAL_LISTENER_PORT/" /etc/kafka/server.properties

Очевидно, что это не полное решение для производства (например, для обеспечения безопасности для внешних брокеров), и я все еще уточняю свое понимание того, как также позволить внутреннему производителю/потребителям также общаться с брокерами.

Однако до сих пор это лучший подход для моего понимания Кубернеса и Кафки.

Ответ 2

Мы решили это в 1.7, изменив Type=NodePort службу на Type=NodePort и установив Type=NodePort externalTrafficPolicy=Local. Это обходит внутреннюю балансировку нагрузки Сервиса и трафик, предназначенный для определенного узла на этом узле, порт будет работать, только если на этом узле находится модуль Kafka.

apiVersion: v1
kind: Service
metadata:
  name: broker
spec:
  externalTrafficPolicy: Local
  ports:
  - nodePort: 30000
    port: 30000
    protocol: TCP
    targetPort: 9092
  selector:
    app: broker
  type: NodePort

Например, у нас есть два узла nodeA и nodeB, nodeB работает под управлением kafka. nodeA: 30000 не будет подключаться, но nodeB: 30000 будет подключаться к kafka pod, работающему на узле B.

https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typenodeport

Обратите внимание, что это также было доступно в версиях 1.5 и 1.6 в качестве аннотации к бетам, здесь можно найти дополнительную информацию о доступности функций: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/# сохраняющих-на-клиент-источник-IP

Заметим также, что, хотя это связывает модуль kafka с конкретным внешним сетевым идентификатором, он не гарантирует, что ваш объем хранилища будет привязан к этому сетевому идентификатору. Если вы используете VolumeClaimTemplates в StatefulSet, то ваши тома привязаны к модулю, тогда как kafka ожидает, что том будет привязан к идентификатору сети.

Например, если перезапуск kafka-0 и kafka-0 появляется на nodeC вместо nodeA, kafka-0 pvc (при использовании VolumeClaimTemplates) имеет данные, что это для nodeA, а брокер, работающий на kafka-0, начинает отклонять запросы, думающие что это nodeA not nodeC.

Чтобы исправить это, мы с нетерпением ждем локальных постоянных томов, но прямо сейчас у нас есть один ПВХ для нашего состояния kafka StatefulSet, и данные хранятся в $NODENAME на этом ПВХ для привязки данных объема к определенному узлу.

https://github.com/kubernetes/features/issues/121 https://kubernetes.io/docs/concepts/storage/volumes/#local

Ответ 3

Я хотел бы сказать, что я прочитал этот Вопрос и Ответ 3 раза прежде, чем попытался обернуть мою голову тем, что было Безголовыми Службами/в чем смысл их. (и я никогда полностью не понимал Headless Services или что это за вопросы и ответы.)
И в 4-м чтении (пересмотрев его после дальнейшего обучения себя) оно наконец щелкнуло/я наконец понял.

Таким образом, цель этого ответа состоит в том, чтобы переформулировать вопрос/проблему/надира и ответить так, как если бы он объяснял это школьнику. Так что другие, кто наткнется на это, получат значимость Надира в первом чтении.

Полезные базовые знания:

  • Существует служба типа: ExternalName.
    Служба ExternalName просто указывает на адрес DNS.
    Существует два вида сервиса ExternalName:

    1. Без кластерного IP:
      Хорошим вариантом использования было бы использование кластера тестирования и производственного кластера для совместного использования как можно большего количества кода. (и для простого удобства в некоторых случаях) Контроллеры как в тестировании, так и в производстве будут указывать на одно и то же имя DNS-адреса внутреннего кластера службы, которое будет предсказуемым кодом многократного использования. Разница будет в том, что среда тестирования будет иметь службу, которая указывает на службу SQL, которая существует внутри кластера. Рабочий кластер будет использовать Службу ExternalName, которая будет перенаправлять/указывать на DNS-адрес решения SQL, управляемого облачными провайдерами.
    2. С IP-адресом кластера:
      Это версия службы ExternalName, которая является ключом к решению.

  • Набор Stateful состоит из 3 частей:

    1. Порядковый номер (число)
    2. Постоянное хранение
    3. Постоянное и предсказуемое DNS-имя внутреннего кластера (оно вытекает из требования, что оно должно поставляться со службой Headless)

  • О Kube-Proxy нужно помнить 3 важных момента:

    1. Это гарантирует, что у всего есть уникальный IP.
    2. Он отвечает за реализацию IP виртуального статического кластера (IP-адрес виртуального статического кластера считается виртуальным, поскольку он существует только в каждом узле iptables в реализации iptables Kube-Proxy или в хэш-таблице ядра в версии ip-vs следующего поколения Kube-Proxy), а также отвечает за эффект логической балансировки нагрузки, возникающий при работе с обычными сервисами Kubernetes, имеющими IP-адрес кластера.
    3. KubeProxy отвечает за сопоставление трафика, поступающего через NodePorts, с соответствующей службой Kubernetes со статическим IP-адресом кластера. <- Это очень важно для требования о том, что сервисы с отслеживанием состояния должны быть доступны извне, предполагается, что NodePorts всегда участвует, когда дело касается внешних служб.

  • Есть четыре важных момента, которые нужно помнить о службе безголовых:

    1. Это создает предсказуемый адрес DNS.
    2. Он не действует как внутренний кластер Load Balancer. Вы говорите напрямую с модулем, идентифицированным предсказуемым адресом DNS. (что очень желательно для рабочих нагрузок с состоянием)
    3. У него нет IP статического кластера.
    4. Как побочный эффект качеств 2 и 3, он выходит за пределы области Kube-Proxy (который отвечает за направление трафика, поступающего на порты узлов в службы.) Я перефразирую это несколько раз, чтобы проблема усваивалась: NodePorts может обычно не перенаправляют трафик в Headless Services. Внешний трафик, поступающий в кластер, обычно не может быть перенаправлен в Headless Services. Это не интуитивно понятно, как внешне выставить безголовую службу.


Теперь, когда мы лучше понимаем проблему, давайте вернемся к вопросу: как внешняя защита может предоставлять служба без головы (которая указывает на отдельного члена набора с состоянием)?

Решение Часть 1:
Любой модуль в кластере может общаться с членами Statefulset.
Поскольку с сохранением состояния генерирует безголовую службу с предсказуемым внутренним кластерным DNS-адресом в форме:
statefulsetname- # associatedheadlessservice.namespace.svc.cluster.local:. Порт
Кафка-0.broker.kafka.svc.cluster.local: 9092
Кафка-1.broker.kafka.svc.cluster.local: 9092
Кафка-2.broker.kafka.svc.cluster.local: 9092
broker.kafka.svc.cluster.local: 9092, также может использоваться для ссылки на какой-либо доступный.

Решение Часть 2:
Вы разрешаете внешнему трафику общаться с членами набора с сохранением состояния, вводя вторую службу, которая может принимать внешний трафик, а затем перенаправляя трафик из этой службы в безголовую службу, которая может принимать только интернет-трафик.
Для каждого модуля в Stateful Set создается служба типа ExternalName с виртуальным статическим IP-адресом ClusterIP, управляемым Kube-Proxy. Каждая из этих Служб ExternalName указывает на/перенаправляет трафик на предсказуемый статический DNS-адрес внутреннего кластера, определенный в Решении 1, и поскольку эта служба ExternalName имеет виртуальный статический кластер IP, управляемый через Kube-Proxy, может существовать сопоставление с NodePorts.

Ответ 4

Измените службу из безголового ClusterIP в узел NodePort, который будет перенаправлять запрос на любой из узлов на установленном порту (30092 в моем примере) на порт 9042 на Kafkas. Вы попали бы в один из стручков, случайно, но я думаю, что все в порядке.

20dns.yml становится (что-то вроде этого):

# A no longer headless service to create DNS records
---
apiVersion: v1
kind: Service
metadata:
  name: broker
  namespace: kafka
spec:
  type: NodePort
  ports:
  - port: 9092
  - nodePort: 30092
  # [podname].broker.kafka.svc.cluster.local
  selector:
    app: kafka

Отказ от ответственности: вам могут потребоваться две службы. Один безголовый для внутренних имен DNS и один NodePort для внешнего доступа. Я не пробовал это сам.

Ответ 5

Из документации kubernetes kafka:

Внешний доступ с хостом

Альтернативой является использование хоста для внешнего доступа. При использовании этого только один кафка-брокер может работать на каждом хосте, что в любом случае является хорошей идеей.

Чтобы переключиться на hostport, адрес рекламы kafka необходимо переключить на имя ExternalIP или ExternalDNS узла, на котором запущен брокер. в kafka/10broker-config.yml переключиться на

OUTSIDE_HOST=$(kubectl get node "$NODE_NAME" -o jsonpath='{.status.addresses[?(@.type=="ExternalIP")].address}')
OUTSIDE_PORT=${OutsidePort}

и в kafka/50kafka.yml добавьте hostport:

    - name: outside
      containerPort: 9094
      hostPort: 9094