Какую роль выполняет proxy у каждой pod приложения
Kubernetes иногда называют “операционной системой для дата-центров” – и в этом есть логика. K8s позволяет представить группу серверов (условный ЦОД) как единое вычислительное пространство. Оператор просто бросает задания в K8s, а тот сам выбирает, где тот или иной контейнер лучше разместить. Чаще всего делает это он хорошо. Но иногда появляется необходимость как-то управлять этим процессом. Об этом я и расскажу.
Зачем управлять распределением POD-ов?
Зачем вообще нужно привязывать поды к определенным узлам? Это может быть связано с производительностью, безопасностью или надежность. Например – pod может требовать доступ к специфическому железу (видеокарты и ML-ускорители для задач машинного обучения, аппаратные криптоускорители). Это может быть продиктовано безопасностью: критические части проекта будут размещаться на машинах, где физически не может быть ничего, кроме них. Это снижает шансы на то, что удачный взлом, скажем, сервиса регистраций раскроет данные о платежах. Некоторые стандарты безопасности (включая PCI DSS) имеют даже требования к физической безопасности серверов – датчики вскрытия, пломбы на корпусках, запрет на доступ. Отдельная удобная особенность – tier-инг. Нагрузку в кластере можно разделить на “важную” и “не очень”. Под важную выделять мощные современные машины с резервированием PSU, горячей замены дисков и памяти, под “не очень” – соскрести какой-нибудь хлам. В облаках это делается даже проще за счет spot instances. Такие инстансы дешевле (порой радикально), но их работу никто не гарантирует – инстанс может отключится в любой момент (вместо него появится новый). Это вызовет пересоздание POD-ов, но для чего-то маловажного это, может – и не страшно совсем.
Способы управления
NodeSelector
Это самый простой способ управления аллокацией. Он предельно прямолинеен – запутаться в нем невозможно. Выполняется в 2 этапа. Сначала надо поставить метки на node командой label:
Проверить, какие метки уже есть можно через kubectl describe nodes
Теперь можно указать pod-у требование на привязку к конкретной метке. Для аллокации пода будут использоваться только помеченые узлы, то есть при включении nodeSelector для пода ноды без меток будут игнорироваться:
Для deployment nodeSelector передается в шаблон pod-а, как обычно. Не смотря на удобство и прямолинейность подхода – nodeSelector имеет три минуса:
- nodeSelector применяется в момент аллокации пода и бесполезен, если под уже аллоцирован. Если вам нужно “освободить” ноду – придется поставить на нее метку и затем выкинуть оттуда поды командой drain
- nodeSelector не особенно гибкий и работает по принципу “один к одному”. К примеру, можно сделать метки для машин small , medium и large и указать поду, что он должен развернуться на машине класса small . Но нельзя – на машине класса medium или large – возможен только один вариант.
- nodeSelector не запрещает аллокаций. То есть на машине с меткой могут размещаться поды без nodeAffinity. Для решения этой проблемы придуман иной подход.
Taints and Tolerations
Taints – это NodeAffinity наоборот. Если nodeAffinity говорит scheduler-у, где он должен размещать pod-ы, то taint говорит, где pod-ы размещать нельзя. Любой taint запрещает размещение на машине любых подов (есть одно исключение, про него дальше). Однако можно создать под, который будет игнорировать ( tolerate ) этот запрет – и данный pod запустится на данной машине. Даже если у вас есть совершенно пустой нормальный кластер kubernetes – у вас уже есть taint. По умолчанию kubernetes запрещает размещать обычные поды на master nodes – это taint node-role.kubernetes.io/master
taint создается с помощью команды kubectl taint . Общий вид:
taintKey и taintValue – это просто метки, они могут быть произвольными. У taintEffect есть 3 возможных значения:
- NoSchedule – новые поды не будут аллоцироваться, однако существующие продолжат свою работу
- PreferNoSchedule – новые поды не будут аллоцироваться, если в кластере есть свободное место
- NoExecute – все запущенные поды без tolerations должны быть убраны
Теперь о том, как прописываются tolerations. Язык tolerations слегка сложнее прямолинейного подхода nodeAffinity:
в данном примере мы создадим под, который будет игнорировать taint, созданный вот такой командой:
Есть более сложный вариант – можно учитывать не только факт наличия метки, но и ее значение. Создадим пару taint-ов:
В данном примере pod vault будет создан только на ноде secure-1, потому что только на ней secGroup равен secure .
Taint-ов можно создать сколь угодно много и условия проверки можно сочетать, как в примере ниже:
В данном примере мы выделяем пул выделенных машин taint-ом “dedicatedNode” и отдельно помечаем группу максимальной безопасности значением secure для группы secGroup.
Удалить taint можно, добавив в конец знак минуса:
nodeAffinity
Не смотря на простоту и эффективность механизма nodeSelector – механизм это прямолинейный и не особенно гибкий. Авторы kubernetes предлагают более мощный, гибкий (а так же – сложный и неудобный) механизм – nodeAffinity. Язык описания nodeAffinity предлагает несколько мощных возомжностей:
- логические операторы для выбора условия размещения – IN (размещать на одной из нод с разными метками) или AND (размещать на нодах, имеющих обе метки сразу)
- можно выбраить политики размещения pod-ов относительно друг друга: например – запретить экземплярам кэша оказываться на одной физической машине или требовать размещение приложения вместе с экземпляром кеша на одном физическом узле
Минус nodeAffinity в том, что язык очень многословный и читается тяжело. Общая спецификация выглядит так:
affinityClass влияет на строгость выбора узла:
- requiredDuringSchedulingIgnoredDuringExecution: обязательно размещать pod-ы по требованию nodeAffinity. Если разместить не получится – pod застрянет в статусе Pending
- preferredDuringSchedulingIgnoredDuringExecution: по возможности размещать pod-ы по требованиям affinity. Если поды не влезли – scheduler разместит их “как получится”
affinityKey – это метка (ключ), по которой мы будем искать ноды для размещения pod-ов. affinityValues – это значения метки, которые нам подойдут affinityOperator – это тот логический оператор, по которому будет производится выбор метки. Варианты:
- In – подойдет любое из перечисленных значений
- NotIn – противоположно In
- Exists – метка просто есть (values игнорируется)
- DoesNotExists – противоположно Exists
- Gt – Greater than – значение метки больше указанного в политике числа. Сработает только для чисел
- Lt – Less than – противоположно Gt
affinityClass preferredDuringSchedulingIgnoredDuringExecution слегка отличается – вместо nodeSelectorTerms используется поле preference (синтаксис такой же), плюс есть обязательное поле weight – оно отвечает за приоритет при выборе node.
affinity не учитывается для уже аллоцированных nodes, так что если нужно освободить node-у от всех подов которые там уже есть – поможет команда kubectl node drain
Лирическое отступление – PodAffinitty и PodAntiAffinitty
Механизм, который помогает размещать pod-ы относительно нод – может так же помочь и разместить pod-ы относительно друг друга – за это отвечают классы PodAffinity и PodAntiAffinity. Все три класса можно сочетать друг с другом, синтаксис внутри одинаковый, по этому просто покажу пример:
В этом примере мы запрещаем экземплярам redis размещаться на одном узле. Каждый pod в этом deployment получит метку app:store , политика podAntiAffinity запрещает размещать второй под с меткой app=store на ноде с таким же hostname. Важный параметр тут – topologyKey. Именно по нему scheduler понимает, какие node-ы считаются одной зоной размещения,а какие – нет. Усложним пример, добавив web worker:
В этом примере мы размещаем nginx на разных node (как мы сделали с redis), но при этом требуем, чтобы nginx размещался вместе с redis. Это может быть удобно для кешей. Проверим, что получилось:
Static pod allocations
Это очень редкий случай, но не упомянуть его было бы нечестно. Pod-ы можно аллоцировать полностью статически, вручную привязав к конкретной node. В этом случае scheduler никак на них не влияет. На них не действуют taints, nodeSelector и podAffinity. Даже node drain ничего не сможет с такими подами сделать. Зачем это может потребоваться? Ну, во-первых для запуска таких pod-ов не нужен работающий scheduler или apiserver. Это делает размещение таких подов сверхнадежным – они будут работать всегда. Именно так kubeadm устанавливает свои компоненты – это не полноценные демоны, а контейнеры, которые вручную привязаны к master node.
Во-вторых такой подход может потребоваться в случае, если какой-то контейнер надо привязать к конкретной, строго определенной node вручную и ни при каких условиях не давать ему оттуда уезжать. Скажем, у вас какое-то особое шифрование и оно зависит от HSM, который физически подключен к определенной, особо защищенной машине. Вообще – это порочная практика и такой сценарий лучше решается через nodeSelector + taint, но мало ли?
Выполнить статическую аллоакцию очень просто – нужно положить манифесты pod-ов в папку со статическими подами. Этот путь можно задать двумя путями:
- через аргумент командной строки kubelet: --pod-manifest-path
- через параметр конфига staticPodPath
Если у вас kubernetes установлен через kubeadm – этот параметр там уже есть, kubelet будет искать статические манифесты по адресу /etc/kubernetes/manifests . Kubelet перечитывает папку с манифестами каждые 10 секунд. Если удалить манифест – kubernetes удалит pod.
Просто создадим манифест статического пода
И проверим, что получилось:
Порядок применения
Первым всегда применяется static pod. Он игнорирует все (taints, affinities, node selectors).
Вторым по списку применяется taint. Если у pod нет toleration – он не будет размещен, по этому taint – это очень эффективный способ “разогнать” pod-ы с определенного узла (или группы узлов).
В случае, если есть nodeAffinity и nodeSelector – должны сработать оба условия сразу (то есть – и метка селектора и условия affinity).
Заключение
Kubernetes – мощный, богатый на возможности инструмент. Он кажется слегка неудобным, но ровно до момента понимания логики его работы. Scheduler у kubernetes практически ключевой компонент, и он достаточно гибок, пусть и не самым лучшим образом описан. Надеюсь – эта статья кому-то поможет. Высокого вам аптайма!
Чтобы получить данные обо всех подах проекта с помощью СLI:
Перейдите в проект:
Используйте команду oc get pods .
Опционально, добавьте флаг -o wide для получения данных об IP-адресе пода.
Добавление Pod в проект¶
Под создается автоматически при запуске приложения. Администратор может добавлять новые поды в проект:
Перейдите в меню Workloads → Pods .
Нажмите на кнопку Create Pod .
Внесите необходимые изменение в кодовом поле. Пример исходного кода Pod смотрите здесь.
Нажмите на кнопку Create .
Изменение Pod¶
Чтобы внести изменения в под:
Перейдите в меню Workloads → Pods .
Выберите Pod, который необходимо изменить, нажмите на кнопку и выберите Edit Pod .
Удаление Pod¶
Чтобы удалить под:
Перейдите в меню Workloads → Pods .
Выберите службу, которую необходимо удалить, нажмите на кнопку и выберите Delete Pod .
Ограничение пропускной способности пода¶
Чтобы ограничить пропускную способность пода:
Добавьте в файл JSON определение объекта и параметр скорости движения данных с использованием kubernetes.io ingress-bandwidth и kubernetes.io/egress-bandwidth .
Например, чтобы ограничить пропускную способность входного и выходного канала пода до 10 Мбит/с:
Создайте под, используя определение объекта:
Подробнее об управлении пропускной способностью пода смотрите здесь.
Использование Pod disruption budget¶
Pod disruption budget — это объект API Kubernetes, который определяет минимальное количество или процент реплик пода, которые должны функционировать одновременно.
Чтобы настроить Pod disruption budget:
Создайте файл YAML с определением объекта следующего типа:
apiVersion — Pod disruption budget является частью группы policy/v1beta1 API.
minAvailable — минимальное количество подов, которые должны быть доступны одновременно. Это может быть целое число или процент, например 25%.
Анализ нескольких способов доступа к приложениям в кластерах Kubernetes извне
В общем, кластерная сеть Kubernetes является частной сетью, и доступ к развернутым приложениям можно получить только внутри кластерной сети. Итак, как приложения в кластере Kubernetes могут быть доступны внешней сети и предоставлять услуги для внешних пользователей?
В этой статье будет рассказано о нескольких часто используемых реализациях доступа к Pod и Serivce в Kubernetes Cluster из внешней сети.
Отношения между Pod и Service
Давайте сначала разберемся с концепцией Pod и Service в Kubernetes и их взаимосвязью.
Pod (группа контейнеров), Pod на английском языке означает pod. Как видно из значения названия, Pod представляет собой набор контейнеров с зависимостями. Pod - это самый простой объект ресурса в кластере Kubernetes. Каждый Pod состоит из одного или нескольких бизнес-контейнеров и корневого контейнера (контейнера Pause).
Kubernetes назначает уникальный IP-адрес (например, IP-адрес модуля) каждому модулю, и несколько контейнеров в нем используют этот IP-адрес. В дополнение к IP-адресу контейнеры в модуле также используют одно и то же сетевое пространство имен, порт, объем хранилища и т. Д., Что означает, что эти контейнеры могут взаимодействовать через Localhost. Контейнеры, содержащиеся в модуле, будут работать на одном узле, и несколько идентичных модулей могут быть запущены одновременно для аварийного переключения или балансировки нагрузки.
Жизненный цикл пода короткий. Kubernetes будет создавать и уничтожать поды в соответствии с конфигурацией приложения и масштабировать их на основе индикаторов мониторинга. Kubernetes может выбрать любой неактивный узел в кластере при создании пода, поэтому его сетевой адрес не фиксирован. Из-за этой особенности Pod, как правило, не рекомендуется напрямую обращаться к приложению через адрес Pod.
Чтобы решить проблему неудобного прямого доступа к Pod, Kubernetes использует Service для инкапсуляции Pod. Служба - это абстракция группы модулей, которые предоставляют службы на бэкэнде. Служба привязана к фиксированному виртуальному IP-адресу. Виртуальный IP-адрес виден только в кластере Kubernetes, но на самом деле виртуальный IP-адрес не соответствует виртуальному или физическому устройству, а просто правилу в IPtables, а затем запрос на обслуживание направляется в серверный Pod через IPtables. Таким образом можно гарантировать, что потребители услуг могут стабильно получать доступ к сервисам, предоставляемым модулем, не беспокоясь об изменениях, таких как создание, удаление, миграция и использование группы модулей для балансировки нагрузки.
Ключ к реализации функции Сервиса - это Kube-Proxy в Kubernetes. Kube-Proxy запускается на каждом узле, отслеживает изменения объектов службы на сервере API, а затем реализует сетевую пересылку, управляя IPtables. Kube-Proxy в настоящее время поддерживает три режима: UserSpace, IPtables и IPVS. Давайте поговорим о сходствах и различиях между этими режимами:
UserSpace позволяет Kube-Proxy прослушивать порт в пространстве пользователя, все службы перенаправляются на этот порт, а Kube-Proxy перенаправляет его на внутренний уровень приложения.
IPtables полностью реализует метод IPtables, который напрямую использует IPtables в качестве записи пользовательского режима, а реальным поставщиком услуг является Netilter ядра. Kube-Proxy служит только в качестве контроллера, который в настоящее время также является методом по умолчанию.
Метод IPtables Kube-Proxy также поддерживает функции Round Robin и Session Affinity.
Примечание. Несмотря на то, что к службе, предоставляемой внутренним модулем, можно получить доступ через IP-адрес кластера и порт службы, IP-адрес кластера не может быть опрошен. Причина в том, что IP-адрес кластера является только правилом в IPtables и не соответствует ни одному сетевому устройству. IP-адрес кластера в режиме IPVS можно проверить.
Kubernetes добавил поддержку IPVS с версии 1.8, и IPVS будет более эффективным, чем IPtables. Для использования режима IPVS требуется установка ipvsadm 、 ipset Инструментарий и загрузка ip_vs Модуль ядра.
Примечание: IPVS является частью проекта LVS и представляет собой четырехуровневый балансировщик нагрузки, работающий в ядре Linux и обладающий исключительной производительностью. С настроенным ядром он может легко обрабатывать более 100 000 запросов пересылки в секунду. В настоящее время в средних и крупных интернет-проектах широко используется IPVS для захвата трафика при входе на сайт.
Поняв базовые концепции Pod и Service, давайте поговорим о нескольких распространенных реализациях доступа к Pod и Serivce в Kubernetes Cluster из внешней сети. В настоящее время в основном это:
Воздействие через капсулу
hostNetwork: true
Это способ напрямую определить сеть Pod.
Если используется в Pod hostNetwork:true Если настроено, приложение, работающее в этом модуле, может напрямую видеть сетевой интерфейс хоста модуля. К приложению можно получить доступ на всех сетевых интерфейсах хоста. Ниже приведен пример определения модуля, использующего хост-сеть:
Порт доступа 80 хоста, на котором расположен Pod:
Вы увидите страницу приветствия Nginx по умолчанию, указывающую, что вы можете получить к ней доступ в обычном режиме.
Примечание.Каждый раз, когда Pod запускается, он может быть запланирован для другого узла, так что IP-адрес хоста узла, на котором осуществляется доступ к Pod извне, также не фиксирован, и при планировании Pod необходимо учитывать, подключен ли он к порту на хосте. Конфликт, поэтому, как правило, используйте его, только если вы не знаете, что вам нужно конкретное приложение для занятия определенного порта на определенном хосте. hostNetwork: true Путь.
Одно из применений этого сетевого режима Pod заключается в том, что сетевой плагин можно упаковать в Pod, а затем развернуть на каждом хосте, чтобы Pod мог управлять всеми сетями на хосте.
hostPort
Это также способ напрямую определить сеть Pod.
hostPort Это прямая маршрутизация порта контейнера с портом на запланированном узле, чтобы пользователь мог добавить его через IP-адрес хоста. <hostPort> Приходите в гости к Pod, например: <hostIP>:<hostPort> 。
Порт доступа 8088 хоста, на котором расположен Pod:
Вы увидите страницу приветствия Nginx по умолчанию, указывающую, что вы можете получить к ней доступ в обычном режиме.
Таким образом и hostNetwork: true Он имеет тот же недостаток, потому что, когда Pod перепланирован, хост, на который запланирован Pod, может измениться, поэтому <hostIP> Это изменилось. Пользователи должны поддерживать соответствие между модулем и его хостом.
Этот сетевой метод можно использовать для Ingress Controller , Весь внешний трафик должен проходить через порты 80 и 443 узла узла Kubenretes.
Port Forward
Это пропуск kubectl port-forward Инструкции по реализации методов пересылки данных. kubectl port-forward Команды могут установить переадресацию портов для модуля Pod. При указании портов прослушивания на этом компьютере запросы на доступ к этим портам будут перенаправляться на соответствующие порты в контейнере модуля.
Для начала посмотрим, как работает Kubernetes Port Forward:
После использования Kubectl для создания переадресации портов Kubectl будет активно отслеживать указанный локальный порт.
Когда соединение порта установлено с локальным портом и данные отправляются на порт, поток данных будет проходить через следующие шаги:
Данные отправляются на локальный порт, контролируемый Kubctl.
Kubectl отправляет данные на ApiServer по протоколу SPDY.
ApiServer устанавливает соединение с Kubelet целевого узла и отправляет данные на порт целевого Pod'а по протоколу SPDY.
После получения данных Kubelet целевого узла связывается с Socat через PIPE (STDIN, STDOUT).
Socat отправляет данные STDIN в указанный порт контейнера внутри модуля и записывает возвращенные данные в STDOUT.
Данные STDOUT принимаются Kubelet и отправляются обратно по обратному пути.
Убедитесь, что порт отслеживается сервером Nginx:
Перенаправьте порт 8900 на узле на порт 80 Nginx Pod:
Примечание. Socat необходимо установить на всех узлах Kubernetes. Более подробное введение в Socat см. В: "Вводное руководство по Socat」 。
Убедитесь, что пересылка прошла успешно:
Поскольку этот тип порта пересылки привязан локально, этот метод подходит только для отладки служб.
Воздействие через службу
Тип Службы (ServiceType) определяет, как Служба предоставляет внешние службы. В зависимости от типа разные сервисы могут быть видны только в кластере Kubernetes или доступны за пределами кластера. В настоящее время существует три типа службы: ClusterIP, NodePort и LoadBalancer.
Значение трех общих портов в сервисе:
Порт службы, представленный на IP-адресе кластера, - это порт, к которому привязан виртуальный IP-адрес. Порт - это вход, предоставляемый клиентам в кластере для доступа к Сервису.
nodePort - это вход, предоставляемый Kubernetes для внешних клиентов для доступа к Сервису.
TargetPort - это порт контейнера в модуле. Данные, введенные из порта и nodePort, в конечном итоге поступят в порт контейнера в модуле серверной части через Kube-Proxy. Если targetPort не объявлен явно, он будет перенаправлен на порт, где Служба принимает запрос по умолчанию (то же значение, что и порт порта).
Как правило, порт и nodePort являются служебными портами. Первый предоставляется клиентам в кластере для доступа к службам, а второй предоставляется клиентам за пределами кластера для доступа к службам. Данные, поступающие из этих двух портов, должны пройти через обратный прокси-сервер Kube-Proxy, чтобы попасть в порт контейнера в бэкэнд-поде, чтобы достичь контейнера на поде.
ClusterIP
ClusterIP - это тип службы по умолчанию. Этому типу службы автоматически назначается виртуальный IP-адрес, доступ к которому возможен только внутри кластера, а именно: ClusterIP. ClusterIP предоставляет вам службу, к которой могут обращаться другие приложения в кластере, но нельзя получить доступ извне. YAML службы ClusterIP выглядит так:
Хотя мы не можем напрямую получить доступ к службе ClusterIP извне кластера, вы можете использовать Kubernetes Proxy API для доступа к ней.
Создайте прокси-сервис для Kubernetes API на главном узле:
kubectl proxy По умолчанию отслеживается 127.0.0.1.Если вам нужно предоставить внешний доступ, вы можете использовать некоторые базовые механизмы безопасности.
Если вам нужна дополнительная помощь с командами, вы можете использовать kubectl help proxy 。
Теперь вы можете использовать Kubernetes Proxy API для доступа к нему. Например: требуется доступ к услуге, вы можете использовать /api/v1/namespaces/<NAMESPACE>/services/<SERVICE-NAME>/proxy/ 。
Если вам нужен прямой доступ к модулю, вы можете использовать /api/v1/namespaces/<NAMESPACE>/pods/<POD-NAME>/proxy/ 。
Поскольку для доступа к Kubernetes Proxy API требуется, чтобы авторизованный пользователь запускал kubectl, вам не следует использовать этот метод, чтобы предоставить свою службу в общедоступную сеть или использовать ее в производственной среде. Этот метод в основном используется для отладки служб.
NodePort
NodePort - это широко используемый метод предоставления услуг в Kubenretes. На основе функций, предоставляемых ClusterIP, привяжите порт для службы к каждому узлу кластера Kubernetes, а именно к NodePort. Вне кластера может быть основан любой NodeIP:NodePort Для доступа к Сервису в форме. Сервис доступен на порту NodePort каждого узла.
Между службой NodePort и службой ClusterIP по умолчанию в определении YAML есть два различия: во-первых, это тип NodePort. Во-вторых, есть параметр под названием nodePort, чтобы указать, какой порт открыть на узле. Если вы не укажете этот порт, он выберет случайный порт.
nodePort Диапазон значений по умолчанию - 30000-32767, это значение можно использовать в файле конфигурации сервера API. --service-node-port-range Настроить.
Тип службы NodePort будет единообразно предоставлять порт на всех узлах Kubenretes (узлы, на которых работает Kube-Proxy) для предоставления услуг извне, так что IP-адрес любого узла Kubernetes может использоваться вне кластера плюс указанный порт (определенный здесь: : 31000) для доступа к службе. Kube-Proxy будет автоматически перенаправлять трафик на каждый Pod Сервиса в циклическом режиме.
Служба типа NodePort не влияет на исходный метод доступа виртуального IP, внутренние узлы все еще могут проходить VIP:Port Способ доступа. Метод предоставления сервиса NodePort также имеет некоторые недостатки:
Каждый порт на узле может иметь только одну службу.
Если IP-адрес узла изменится, потребуется соответствующий механизм для решения проблемы.
По указанным выше причинам NodePort больше подходит для демонстрационных программ или временных приложений. Не рекомендуется использовать этот метод для предоставления услуг в производственной среде.
LoadBalancer
LoadBalancer основан на внешнем балансировщике нагрузки, предоставляемом NodePort и поставщиками облачных услуг, через который внешние запросы перенаправляются каждому NodeIP:NodePort Для достижения услуг внешнего воздействия.
LoadBalancer Может быть определено только в Сервисе. LoadBalancer - это балансировщик нагрузки, предоставляемый некоторыми конкретными общедоступными облаками и требующий поддержки определенных поставщиков облачных услуг. Например: AWS, Azure, OpenStack и GCE (Google Container Engine).
Внутри кластера вы можете использовать порт ClusterIP plus для доступа к службе, например 10.97.121.42:8086.
Внешний может получить доступ к услуге двумя способами:
Используйте IP-адрес любого узла плюс порт 30051 для доступа к службе.
Использовать EXTERNAL-IP Приходите посетить, это VIP, IP-адрес балансировщика нагрузки, предоставленный облачным провайдером, например: 10.13.242.236:8086.
Самым большим недостатком LoadBalancer является то, что каждая открытая служба должна использовать IP-адрес балансировщика нагрузки, предоставляемый общедоступным облаком, что может стоить относительно больших затрат.
Судя по выводам вышеупомянутых нескольких типов Сервисов, текущая функция балансировки нагрузки, предоставляемая Сервисом, имеет следующие ограничения в использовании:
Обеспечивается только 4-уровневая балансировка нагрузки, а функция 7-уровневой балансировки нагрузки не поддерживается.Например, запрос пересылки не может быть настроен в соответствии с необходимыми правилами сопоставления.
Чтобы использовать тип службы NodePort, внешний балансировщик нагрузки должен быть развернут вне кластера.
Чтобы использовать тип службы LoadBalancer, Kubernetes должен работать в определенной облачной службе.
Воздействие через Ingress
В отличие от службы, Ingress на самом деле не является службой. Вместо этого он находится перед несколькими службами и действует как интеллектуальный маршрутизатор или точка входа в кластер.
Ingress Это тип ресурса, представленный в версии Kubernetes 1.1. Ingress поддерживает доступ к Сервису за пределами кластера Kubernetes, и в то же время вы можете настроить политику доступа Сервиса. Ingress может настроить Сервис как URL-адрес, к которому можно получить доступ из внешней сети, а также поддерживает предоставление функции виртуального хоста, доступ к которой осуществляется по имени домена. Например, через балансировщик нагрузки разные доменные имена второго уровня могут быть доступны для разных служб.
Фактически Ingress Это просто общий термин, который вызван Ingress с участием Ingress Controller Он состоит из двух частей. Ingress Он используется для абстрагирования исходных правил, которые необходимо настроить вручную, в объект Ingress, который создается и управляется с использованием файлов формата YAML. Ingress Controller Он используется для динамического восприятия изменений правил Ingress в кластере посредством взаимодействия с Kubernetes API.
Использовать Ingress Сначала необходимо развернуть Ingress Controller , Ingress Controller Он предоставляется в виде плагина. Ingress Controller Обычно это Docker-контейнер, развернутый на Kubernetes, Ingress Controller Образ Docker содержит балансировщик нагрузки, такой как Nginx или HAProxy, и Ingress Controller 。 Ingress Controller Получит от Kubernetes то, что ему нужно Ingress Настройте, затем динамически создайте файл конфигурации Nginx или HAProxy и перезапустите процесс балансировки нагрузки, чтобы изменения вступили в силу. другими словами, Ingress Controller Это балансировщик нагрузки, управляемый Kubernetes.
Примечание. Независимо от того, какое программное обеспечение для балансировки нагрузки (такое как Nginx, HAProxy, Traefik и т. Д.) Используется для реализации Ingress Controller, официальное лицо в совокупности именует его Ingress Controller.
В целом Ingress - это очень гибкий метод предоставления услуг, который все чаще поддерживается поставщиками, включая Nginx, HAProxy, Traefik и различные Service Mesh, в то время как другие методы предоставления услуг более подходят для отладки служб и специальных приложений. развернуть.
Прим. перев.: Эта статья продолжает цикл материалов о базовом устройстве сетей в Kubernetes, что описывается в доступной форме и с наглядными иллюстрациями (впрочем, конкретно в этой части иллюстраций уже практически не оказалось). Переводя две предшествующие части этого цикла, мы объединили их в одну публикацию, в которой рассказывалось о сетевой модели K8s (взаимодействие внутри узлов и между узлами) и оверлейных сетях. Её предварительное чтение желательно (рекомендуется самим автором). Продолжение же посвящено сервисам Kubernetes и обработке исходящего и входящего трафика.
NB: Текст автора для удобства дополнен ссылками (преимущественно — на официальную документацию K8s).
Динамика кластера
Из-за постоянно изменяющейся, динамичной, природы Kubernetes и распределённых систем в целом pod'ы (и, как следствие, их IP-адреса) тоже постоянно меняются. Причины тому разнятся от приходящих обновлений для достижения нужного состояния и событий, приводящих к масштабированию, до непредвиденных падений pod'а или узла. Поэтому IP-адреса pod'а не могут напрямую использоваться для взаимодействия.
В дело вступает сервис (Service) в Kubernetes — виртуальный IP с группой IP-адресов pod'а, используемых в качестве конечных точек (endpoints) и идентифицируемых через селекторы лейблов (label selectors). Такой сервис работает как виртуальный балансировщик нагрузки, IP-адрес которого остаётся постоянным, а в то же самое время IP-адреса представленного им pod'а могут постоянно меняться.
Label selector в объекте Service в Kubernetes
За всей реализацией этого виртуального IP стоят правила iptables (у последних версий Kubernetes также появилась возможность использования IPVS, но это тема для отдельного разговора), которые управляются компонентом Kubernetes под названием kube-proxy. Однако такое его название в сегодняшних реалиях вводит в заблуждение. Kube-proxy действительно использовался в качестве прокси во времена до релиза Kubernetes v1.0, но это приводило к большому потреблению ресурсов и тормозам из-за постоянных операций копирования между пространством ядра и user space. Теперь же это всего лишь контроллер — как и многие другие контроллеры в Kubernetes. Он следит за сервером API на предмет изменений в endpoints и обновляет правила iptables соответствующим образом.
Согласно этим правилам iptables, если пакет предназначен IP-адресу сервиса, для него делается DNAT (Destination Network Address Translation): это означает, что его IP-адрес изменится с IP сервиса на один из endpoints, т.е. один из IP-адресов pod'а, который случайным образом выбирается iptables. Таким образом достигается гарантия, что нагрузка равномерно распределена между pod'ами.
DNAT в iptables
В случае такого DNAT'а необходимая информация хранится в conntrack — таблице учёта подключений в Linux (она хранит пятипарные трансляции, сделанные iptables: protocol , srcIP , srcPort , dstIP , dstPort ). Всё устроено таким образом, что при возвращении ответа может произойти обратная DNAT'у операция (un-DNAT), т.е. замена IP источника c Pod IP на Service IP. Благодаря этому клиенту совершенно не нужно знать, как «за кулисами» происходит работа с пакетами.
Пятипарные (5-tuple) записи в таблице conntrack
Итак, используя сервисы Kubernetes, мы можем работать с одними и теми же портами без каких-либо конфликтов (потому что возможно переназначение портов на endpoints). Благодаря этому обнаружение сервисов делается очень просто. Достаточно лишь воспользоваться внутренним DNS и за-hard-code-ить хосты сервисов. Можно даже воспользоваться предварительно настроенными в Kubernetes переменными с хостом и портом сервиса.
Подсказка: Выбрав второй путь, вы сохраните множество ненужных вызовов DNS!
Исходящий трафик
Описанные выше сервисы Kubernetes работают в пределах кластера. На практике же приложениям обычно нужен доступ к каким-то внешним API/сайтам.
В общем случае у узлов могут быть и частные, и публичные IP-адреса. Для доступа к интернету предусмотрено подобие NAT'а один-к-одному для этих частных и публичных IP-адресов — в особенности это касается облачных окружений.
Для нормального взаимодействия из узла с внешним IP-адресом IP источника меняется с частного IP узла на публичный IP для исходящих пакетов, а для входящих пакетов — в обратную сторону. Однако в тех случаях, когда подключение к внешнему IP инициировано pod'ом, IP-адресом источника является Pod IP, о котором не знает механизм NAT'а облачного провайдера. Поэтому он попросту отбросит пакеты с IP-адресами источника, которые отличаются от IP-адресов узла.
И тут, как вы догадались, iptables понадобится нам ещё больше! На этот раз правила, которые тоже добавляются kube-proxy, выполняют SNAT (Source Network Address Translation), он же IP MASQUERADE (маскарадинг). Ядру говорят вместо IP-адреса источника использовать IP интерфейса, с которого приходит пакет. В conntrack тоже появляется запись для дальнейшего выполнения обратной операции (un-SNAT) над ответом.
До сих пор всё было хорошо. Pod'ы могут общаться между собой и с интернетом. Однако нам всё ещё не хватает главного — обслуживания пользовательского трафика. В настоящий момент есть два пути реализации:
1. NodePort/Cloud Load Balancer (уровень L4 : IP и порт)
Тип сервиса LoadBalancer в облачных окружениях создаёт перед всеми узлами облачный балансировщик нагрузки (например, ELB), работающий далее с тем же NodePort .
Сетевые политики
Сетевые политики можно представить себе как списки управления доступом (security groups/ACL) для pod'ов. Правила NetworkPolicy позволяют разрешать/запрещать трафик между pod'ами. Их точная реализация зависит от сетевого уровня/CNI, но большая их часть попросту использует iptables.
Вот и всё. В предыдущих частях мы изучили основы сети в Kubernetes и как работают оверлеи. Теперь мы знаем, как абстракция Service помогает в динамичном кластере и делает обнаружение сервисов по-настоящему простым. Мы также рассмотрели, как проходит исходящий/входящий трафик и что сетевые политики могут быть полезными для обеспечения безопасности в кластере.
Читайте также: