Как прочитать клиентские IP-адреса из HTTP-запросов за сервисами Kubernetes?

Мое веб-приложение работает как модуль Kubernetes за обратным прокси-сервером nginx для SSL. И прокси-сервер, и мое приложение используют службы Kubernetes для балансировки нагрузки (как описано здесь).

Проблема в том, что все мои журналы HTTP-запросов показывают только внутренние IP-адреса кластера, а не адреса реальных HTTP-клиентов. Есть ли способ заставить службы Kubernetes передавать эту информацию на мои серверы приложений?

6 ответов

Решение

Вы можете получить kube-proxy из цикла полностью двумя способами:

  1. Используйте Ingress для настройки вашего nginx для балансировки на основе исходного IP-адреса и отправки трафика прямо на конечную точку ( https://github.com/kubernetes/contrib/tree/master/ingress/controllers)

  2. Разверните haproxy serviceloadbalancer ( https://github.com/kubernetes/contrib/blob/master/service-loadbalancer/service_loadbalancer.go#L51) и установите аннотацию баланса в serivce, чтобы он использовал "источник".

Начиная с версии 1.5, если вы работаете в GCE (с расширением GKE) или AWS, вам просто нужно добавить аннотацию к вашей службе, чтобы обеспечить сохранение исходного кода HTTP.

...
kind: Service
metadata:
  annotations:
    service.beta.kubernetes.io/external-traffic: OnlyLocal
...

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

В 1.7 этот конфиг стал GA, так что вы можете установить "externalTrafficPolicy": "Local" на вашем сервисе спец.

Нажмите сюда, чтобы узнать больше

externalTrafficPolicy: Локальный

Это параметр, который можно указать в списке служб Kubernetes типа балансировки нагрузки или типа NodePort. (Входные контроллеры обычно включают yaml для предоставления LB-сервисов.)

externalTrafficPolicy: Локальный

Делает 3 вещи:
1. Отключает SNAT, так что вместо входного модуля контроллера, который видит исходный IP-адрес в качестве IP-адреса узла Kubernetes, он должен видеть реальный исходный IP-адрес.
2. Избавляется от дополнительного сетевого прыжка, добавив 2 правила:
-если трафик попадает на порт узла без входных модулей, он отбрасывается.
- если трафик попадает в порт узла с входными модулями, он перенаправляется в модуль на том же узле.
3. Обновляет HealthCheck Cloud Load Balancer с конечной точкой /healthz, которая должна сделать так, чтобы LB не переадресовывал узлам, если бы он был отброшен, и только перенаправлял на узлы с входными модулями.
(Перефразируя для пояснения: по умолчанию, так называемый "externalTrafficPolicy: Cluster", трафик распределяется по нагрузке между узлами NodePorts каждого рабочего узла. "ExternalTrafficPolicy: Local" позволяет отправлять трафик только подмножеству узлов, на которых запущены модули контроллеров входа. Таким образом, если у вас есть кластер из 100 узлов, вместо облачного балансировщика нагрузки, отправляющего трафик на 97 узлов, он будет отправлять его только на ~3-5 узлов, на которых запущены модули Ingress Controller)


Важная заметка!:
"externalTrafficPolicy: Local" не поддерживается в AWS.
(Предположительно, он отлично работает на GCP и Azure, и, как говорится, я также помню, что читал, что в минорной версии Kubernetes 1.14 + была регрессия, которая нарушала его, были некоторые версии Cilium CNI, где он также ломался, поэтому имейте в виду, что по умолчанию externalTrafficPolicy: Cluster стабильно стабильный и, как правило, предпочтительнее, если вам не нужны функциональные возможности. Также имейте в виду, что если у вас есть WAF в качестве службы в любом случае, то вы можете использовать это, чтобы увидеть откуда приходит клиентский трафик.)

(Это вызывает проблемы с копами и EKS, другие дистрибутивы, работающие на AWS, могут быть фактически не затронуты, подробнее об этом ниже.)

"externalTrafficPolicy: Local", не поддерживаемый в AWS, является проблемой, известной сопровождающим Kubernetes, но недостаточно документированной. Кроме того, раздражает то, что если вы попробуете это, вам повезет с этим / это будет работать, и это заставит достаточно людей думать, что это работает.

externalTrafficPolicy: локальный сбой в AWS выполняется двумя способами, и для обоих разрывов есть обходные пути, заставляющие его работать:
1-й перерыв + обходной путь: первоначальное создание конечной точки /healthz некорректно + логика цикла согласования нарушена.
После первоначального применения он будет работать для некоторых узлов, а не для других, и затем никогда не будет обновляться.
https://github.com/kubernetes/kubernetes/issues/80579
^ описывает проблему более подробно.
https://github.com/kubernetes/kubernetes/issues/61486
^ описывает обходной путь, чтобы заставить его работать, используя крючок Kops
(Когда вы решаете логику цикла согласования конечной точки /healthz, вы разблокируете преимущества № 2 и № 3, меньше переходов + LB, только отправляя трафик на подмножество рабочих узлов. Но исходный IP-адрес преимущества № 1 по-прежнему не будет правильным.)

2-й перерыв + 2 варианта обхода:
Желаемый конечный результат - входной модуль, который видит истинный IP-адрес клиента.
Но что действительно произошло, так это то, что входной модуль сместился с просмотра исходного IP-адреса узла k8s на просмотр исходного IP-адреса Классического ELB.

Вариант обхода 1)
Переключитесь на сетевой LB (L4 LB), который больше работает как Azure LB. Это связано с тем, что вы не можете использовать ACM (Менеджер сертификатов AWS) для прекращения использования TLS при подготовке и ротации сертификатов TLS AWS LB / handle.

вариант 2)
Продолжая использовать AWS classic ELB (и вы продолжаете использовать ACM), вам просто нужно добавить конфигурацию к обоим классическим ELB (в форме аннотации службы LB) + добавить конфигурацию на входной контроллер. Так что оба используют прокси-протокол или x forward заголовки, я вспоминаю еще одну статью о переполнении стека, которая освещает это, поэтому я не буду повторять это здесь.

Прямо сейчас нет.

Сервисы используют kube_proxy для распределения трафика по своим бэкэндам. Kube-proxy использует iptables для маршрутизации IP-адреса службы на локальный порт, где он прослушивает, а затем открывает новое соединение с одним из бэкэндов. Внутренний IP-адрес, который вы видите - это IP: порт kube-proxy, работающий на одном из ваших узлов.

Только iptables kube-proxy находится в разработке. Это сохранит исходный IP-адрес.

Для Кубернетес 1.7+ комплект service.spec.externalTrafficPolicy в Local разрешит это. Больше информации здесь: Kubernetes Docs

Для не-HTTP запросов (HTTPS, gRPC и т. Д.) Это планируется поддерживать в Kubernetes 1.4. Смотрите: https://github.com/kubernetes/features/issues/27

Начиная с Kubernetes 1.1, существует основанный на iptables kube-proxy, который решает эту проблему в некоторых случаях. По умолчанию он отключен; см. этот пост для получения инструкций о том, как включить его. В итоге сделайте:

for node in $(kubectl get nodes -o name); do kubectl annotate $node net.beta.kubernetes.io/proxy-mode=iptables; done

В случае трафика Pod-to-Pod, с iptables kube-proxy вы теперь увидите истинный IP-адрес источника в модуле назначения.

Однако если ваша служба пересылает трафик из-за пределов кластера (например, NodePort, служба LoadBalancer), то нам все равно придется заменить (SNAT) исходный IP-адрес. Это связано с тем, что мы делаем DNAT для входящего трафика, чтобы направить его в Pod службы (возможно, на другой узел), поэтому узел DNATing должен вставить себя в обратный путь, чтобы иметь возможность отменять DNAT ответа.

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