Как стать автором
Обновить
831.7
OTUS
Цифровые навыки от ведущих экспертов

Анонсирование HAProxy Kubernetes Ingress Controller 1.5

Время на прочтение12 мин
Количество просмотров3K
Автор оригинала: Moemen Mhedhbi

Перевод статьи подготовлен в рамках курса «Инфраструктурная платформа на основе Kubernetes». Если интересно узнать о курсе подробнее, приходите на день открытых дверей онлайн.


Мы выпустили версию 1.5 контроллера HAProxy Kubernetes Ingress Controller. Эта версия разблокирует доступ к необработанному языку конфигурации HAProxy для опытных пользователей, чтобы получить более полный контроль. Вы также можете включить взаимную TLS-аутентификацию между ингресс-контроллером и сервисами, принудительную аутентификацию Basic и возвращать потребителям пользовательские страницы ошибок. В этой версии улучшены внутренние компоненты контроллера, что привело к более эффективной конфигурации HAProxy, а за кулисами HAProxy был обновлен до версии 2.3.

Он также предоставляет возможность запускать ингресс-контроллер вне кластера Kubernetes, но при этом следить за изменениями в подах кластера. Некоторые команды предпочитают такой подход при переходе своих приложений на контейнерную платформу. Вы можете запустить контроллер на отдельном сервере и предоставить ему доступ к сети пода.

Этот релиз был бы невозможен без напряженной работы всех участников сообщества разработчиков открытого кода, как на GitHub, так и в Slack.

Внешний ингресс-контроллер 

Согласно документации Kubernetes:

Приложения, запущенные в кластере Kubernetes, могут обнаруживаться и взаимодействовать друг с другом и с внешним миром через абстракцию службы (Service abstraction).

Сам ингресс-контроллер работает как служба Kubernetes. Служба полагается на kube-proxy для маршрутизации запросов к подам, в которых работает приложение, на каком бы узле это ни было. Однако некоторые пользователи хотят обойти службу Kubernetes, чтобы иметь меньше прокси-серверов или пакетных фильтров для прохождения запроса. Поэтому одним из возможных решений является запуск ингресс-контроллера и инстанса HAProxy вне кластера, как в традиционном обратном прокси.

Хотя это решение не обеспечивает масштабируемости, что является одной из основных причин запуска приложений внутри Kubernetes, оно предлагает более плавный способ миграции существующей инфраструктуры в Kubernetes. Вы можете начать миграцию приложений в Kubernetes, но сохранить при этом инстансы HAProxy за его пределами, вместе с выбранным вами решением высокой доступности (например, Keepalived).

Запуск ингресс-контроллера вне кластера Kubernetes также может быть весьма полезен для отладки, пробного запуска, CI/CD пайплайнов и т.д., для тех, кто предпочитает обходиться без контейнеров. Все, что вам нужно:

  • бинарный контроллер HAProxy Kubernetes Ingress Controller;

  • бинарный файл HAProxy;

  • файл Kubeconfig для доступа к вашему кластеру Kubernetes;

  • сетевая конфигурация, которая позволяет экземплярам HAProxy направлять трафик в сеть подов.

Последнее требование может быть достаточно простым - добавить подобный маршрут на ваш сервер, где <node-ip> - это IP-адрес одного из узлов кластера, доступных с того места, где запущен HAProxy:

$ ip route add <pod-network> via <node-ip>

Чтобы запустить ингресс-контроллер вне Kubernetes, вы можете выполнить его запуск с помощью следующей команды:

$ ./kubernetes-ingress -e \                                                              
    --configmap=default/haproxy-kubernetes-ingress \                                                                            
    --program=/usr/bin/haproxy \
    --disable-ipv6 \
    --ipv4-bind-address=10.0.3.100
    --http-bind-port=8080 \
    --https-bind-port=8443

Все аргументы контроллера описаны здесь.

Служба взаимной аутентификации TLS

В этой версии добавлена поддержка взаимной аутентификации TLS (mTLS) между ингресс-контроллером и бэкенд-серверами, на которые он направляет трафик. Взаимная аутентификация TLS — это не протокол, а скорее практика безопасности, которая гарантирует, что обе стороны в сеансе TLS, клиент и сервер, доверяют друг другу. Это означает, что когда клиент и сервер общаются по TLS, клиент проверяет личность сервера, убеждаясь, что его сертификат TLS был подписан общеизвестным Центром Сертификации (Certificate Authority) (CA); одновременно клиент отправляет серверу свой собственный сертификат, чтобы сервер мог выяснить личность клиента. Таким образом, каждая из сторон может проверить сертификат другого, прежде чем согласиться на дальнейшее взаимодействие. В этом случае ингресс-контроллер является клиентом для бэкенд-серверов.

Для этого мы добавили аннотации server-ca и server-crt, которые можно применять в Ingress, Service или ConfigMap в зависимости от того, применяется ли сконфигурированный сертификат ко всем или только к некоторым сервисам. Аннотация server-ca содержит в себе секрет Kubernetes, который включает в себя сертификат CA, используемый для проверки TLS-сертификата бэкенд-сервера. Аннотация server-crt содержит секрет Kubernetes, включающий клиентский сертификат, который ингресс-контроллер будет предъявлять серверу.

Ниже приведен пример, демонстрирующий установку этих аннотаций в описании службы (Service):

apiVersion: v1
kind: Service
metadata:
  labels:
    run: web
  name: web
  annotations:
    haproxy.org/server-ca: "default/server-tls-secret"
    haproxy.org/server-crt: "default/client-tls-secret"
# ... other service settings...

Если вы не хотите проверять сертификат сервера, можно использовать старую аннотацию server-ssl, чтобы установить TLS-соединение с сервером без проверки сертификата.

Базовая аутентификация

Расположение ингресс-контроллера перед вашими службами делает его идеальным местом для реализации мер безопасности, таких как аутентификация. Это потому, что он выполняет роль шлюза API, через который проходит весь трафик. Можно перенести всю сложную работу на этот уровень, и он будет обеспечивать выполнение всех ваших сервисов.

В версии 1.5 мы добавили поддержку базовой HTTP-аутентификации. Эта форма аутентификации отображает приглашение к входу в систему, когда клиент впервые пытается получить доступ к службе. Чтобы включить ее, установите аннотацию auth-type на basic-auth в определении ConfigMap или Ingress. Добавьте аннотацию auth-secret, которая описывает секрет Kubernetes, содержащий имена пользователей и их пароли в виде username: encrypted и base-64 encoded password.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: web-ingress
  namespace: default
  annotations:
    haproxy.org/ssl-redirect: "true"
    haproxy.org/ssl-redirect-code: "301"
    haproxy.org/ssl-certificate: "default/tls-secret"
    haproxy.org/auth-type: basic-auth
    haproxy.org/auth-secret: "default/logins"
# ... other ingress settings...

Хотя пароли шифруются при хранении, учетные данные базовой аутентификации передаются в открытом виде при использовании, поэтому следует всегда включать TLS для шифрования связи при ее применении.

Сниппеты конфигурации

Одним из преимуществ использования ингресс-контроллера является то, что он скрывает детали того, как сконфигурирован основной балансировщик нагрузки. Вместо того чтобы вручную редактировать конфигурационный файл HAProxy, haproxy.cfg, вы можете просто добавить аннотации в Kubernetes Ingress, Service или ConfigMap файлы и применить их с помощью kubectl. Синтаксис аннотаций лаконичен. Это сводит функциональность балансировщика нагрузки к его сути. Например, чтобы включить SSL с перенаправлением с HTTP на HTTPS, вы должны добавить эти аннотации в ваше определение Ingress:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: web-ingress
  namespace: default
  annotations:
    haproxy.org/ssl-redirect: "true"
    haproxy.org/ssl-redirect-code: "301"
    haproxy.org/ssl-certificate: "default/tls-secret"
# ... other ingress settings...

Эта простота делает настройку расширенных функций балансировки нагрузки удобной для пользователей. Фактически, вам даже не нужно изучать язык конфигурации HAProxy, чтобы использовать HAProxy Kubernetes Ingress Controller. Аннотации обеспечивают достаточно хорошее покрытие для большинства случаев использования. Иногда аннотации выполняют дополнительную работу за кадром, например, аннотация ssl-certificate будет не только применять сертификат, но и отслеживать его статус и обновлять привязку, если он изменится.

Однако опытные пользователи HAProxy предпочитают иметь в своем распоряжении полный язык конфигурации. В этой версии ингресс-контроллера вы можете вставлять необработанные директивы HAProxy в базовую конфигурацию, что открывает возможности, которые не были раскрыты с помощью аннотаций. Используйте global-config-snippet в ConfigMap ингресс-контроллера, чтобы добавить директивы в глобальные настройки HAProxy, где вы будете применять правила, которые влияют на все маршруты и сервисы.

В следующем примере мы добавляем четыре глобальные директивы: ssl-default-bind-options, ssl-default-bind-ciphers, tune.ssl.default-dh-param и tune.bufsize, которые являются лишь некоторыми из доступных настроек:

apiVersion: v1
kind: ConfigMap
metadata:
  name: haproxy-kubernetes-ingress
  namespace: default
data:
  global-config-snippet: |  
    ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
    tune.ssl.default-dh-param 2048
    tune.bufsize 32768

Вы также можете применить директивы к бэкенду в конфигурации HAProxy. Бэкенд определяет пул серверов, на которые направляются запросы. Ингресс-контроллер абстрагируется от этой концепции, но службы Kubernetes отображаются на бэкенды, и эти два понятия примерно аналогичны. Добавьте аннотацию backend-config-snippet к определению сервиса, как показано ниже:

apiVersion: v1
kind: Service
metadata:
  labels:
    run: web
  name: web
  annotations:
    haproxy.org/backend-config-snippet: |
      stick-table type binary size 1000 store http_req_rate(5s)
      http-request track-sc0 url32+src
      http-request deny if {  url32+src,table_http_req_rate() gt 50 }
# ... other service settings...

Здесь мы добавляем таблицу stick-table, которая хранит IP-адрес клиента и URL, который он запросил. Из нее мы отслеживаем количество запросов каждого клиента по каждому URL с помощью http-request track-sc0 и устанавливаем ограничение скорости с помощью http-request deny. Все эти директивы доступны только при наличии доступа к использованию языка с широкими возможностями для конфигурирования HAProxy. Ингресс-контроллер уже предоставляет аннотацию rate-limit-requests для включения ограничения скорости на основе IP-адреса, но с помощью сниппетов конфигурации вы можете получить больше контроля, например, установить различные ограничения скорости для разных URL.

Вы также можете поместить эту аннотацию в определение Ingress, в этом случае она применяется ко всем службам, на которые ссылается ingress. Или добавить ее в ConfigMap ингресс-контроллера, в этом случае она будет применяться ко всем службам. Вы выбираете область применения, которая лучше всего подходит для вашего случая использования.

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

Пользовательские страницы ошибок

При некоторых обстоятельствах HAProxy генерирует определенные HTTP ошибки. Например, он отвечает 403 Forbidden, когда запрос отклоняется правилом http-request deny. Или он отвечает 503 Service Unavailable, когда нет доступного сервера для обработки запроса. Новый аргумент контроллера --configmap-errorfile позволяет вернуть пользовательское сообщение об ошибке для заданного кода состояния ответа HTTP.

Сначала добавьте ConfigMap, который определяет HTML, который вы хотите вернуть для заданного статуса:

apiVersion: v1
  kind: ConfigMap
  metadata:
    name: customerrors
    namespace: default
  data:
    503: |-
      HTTP/1.0 503 Service Unavailable
      Cache-Control: no-cache
      Connection: close
      Content-Type: text/html
  
      <html><body><h1>Oops, that's embarassing!</h1>
      <p>There are no servers available to handle your request.</p>
      </body></html>

Затем передайте аргумент --configmap-errorfile с именем ConfigMap при создании ингресс-контроллера:

args:
  - --configmap-errorfile=default/customerrors

Другие аннотации

В дополнение к основным функциям, рассмотренным выше, было добавлено несколько других аннотаций:

Аннотация

Описание

src-ip-header

Установите IP-адрес источника из заголовка HTTP-запроса, а не из L3-соединения. Это особенно полезно, если Ингресс-Контроллер находится за облачным балансировщиком нагрузки или любым другим компонентом, который изменяет исходный IP источника. Эта аннотация повлияет на все механизмы контроля доступа, основанные на IP-адресе источника (белые списки, черные списки, ограничение скорости).

send-proxy-protocol

Используйте протокол PROXY при подключении к бэкэнд-серверам.

request-redirect

Перенаправьте HTTP-запрос на указанный хост и порт, обновив заголовок HTTP Location. Код перенаправления можно задать с помощью аннотации request-redirect-code.

rate-limit-status-code

Установите код состояния, который будет возвращаться при срабатывании ограничения скорости.

ssl-redirect-port

Задайте порт для возврата при перенаправлении на HTTPS.

cors-enable

Включает правила CORS для соответствующего Ingress-трафика.

cors-allow-origin

Устанавливает заголовок ответа Access-Control-Allow-Origin, чтобы сообщить браузерам, какому источнику разрешен доступ к запрашиваемому ресурсу.

cors-allow-methods

Устанавливает заголовок ответа Access-Control-Allow-Methods, чтобы сообщить браузерам о методах HTTP, разрешенных при доступе к запрашиваемому ресурсу.

cors-allow-credentials

Устанавливает заголовок ответа Access-Control-Allow-Credentials, чтобы сообщить браузерам, можно ли использовать учетные данные для доступа к запрашиваемому ресурсу.

cors-allow-headers

Устанавливает заголовок ответа Access-Control-Allow-Headers, чтобы сообщить браузерам, какие HTTP-заголовки можно использовать при доступе к ресурсу запроса.

cors-max-age

Устанавливает заголовок ответа Access-Control-Allow-Age, чтобы указать браузерам, как долго может кэшироваться результат preflight-запроса.

Внутренние усовершенствования

Важной частью работы над этим выпуском было усовершенствование внутренних компонентов ингресс-контроллера. Одним из аспектов этой работы было обновление кода, чтобы он больше использовал файлы HAProxy Map, поскольку это значительно упрощает конфигурацию, которая отображается.

Взгляните на конфигурацию ниже, которая была сгенерирована до внесения улучшений. Она слишком многословна. У нас есть правило use_backend для каждого ингресс-правила и длинная комбинация ACL, заканчивающая строки http-request deny и http-request capture:

frontend http
  mode http
  bind 0.0.0.0:80 name bind_1
  bind :::80 v4v6 name bind_2
  http-request set-var(txn.host) req.hdr(Host),field(1,:),lower
  http-request set-var(txn.path) path
  http-request set-var(txn.base) base
  http-request deny deny_status 403 if { var(txn.host),concat(,txn.path) -m beg -f /etc/haproxy/maps/16510262515213450.lst } { src -f /etc/haproxy/maps/7895261178644353572.lst } or { var(txn.host) -f /etc/haproxy/maps/16510262515213450.lst } { src -f /etc/haproxy/maps/7895261178644353572.lst } or { var(txn.path) -m beg -f /etc/haproxy/maps/16510262515213450.lst } { src -f /etc/haproxy/maps/7895261178644353572.lst }
  http-request capture "hdr(Referer)" len 128 if { var(txn.host),concat(,txn.path) -m beg -f /etc/haproxy/maps/18288779858306557702.lst } or { var(txn.host) -f /etc/haproxy/maps/18288779858306557702.lst } or { var(txn.path) -m beg -f /etc/haproxy/maps/18288779858306557702.lst }
  http-request capture "hdr(User-Agent)" len 128 if { var(txn.host),concat(,txn.path) -m beg -f /etc/haproxy/maps/15330672981640189476.lst } or { var(txn.host) -f /etc/haproxy/maps/15330672981640189476.lst } or { var(txn.path) -m beg -f /etc/haproxy/maps/15330672981640189476.lst }
  use_backend echo-echo-3-http-echo-8080 if { var(txn.host) echo.k8s.local } { var(txn.path) -m beg /echo-3 }
  use_backend echo-echo-2-http-echo-8080 if { var(txn.host) echo.k8s.local } { var(txn.path) -m beg /echo-2 }
  use_backend echo-echo-3-http-echo-8080 if { var(txn.host) echo-3.k8s.local }
  use_backend echo-echo-2-http-echo-8080 if { var(txn.host) echo-2.k8s.local }
  use_backend echo-echo-1-http-echo-8443 if { var(txn.host) echo-1.k8s.local }
  use_backend echo-echo-3-http-echo-8080 if { var(txn.path) -m beg /echo-3 }
  use_backend echo-echo-2-http-echo-8080 if { var(txn.path) -m beg /echo-2 }
  default_backend default-haproxy-1-4-kubernetes-ingress-default-backend-8080

В версии 1.5 те же входные данные создают кардинально другую конфигурацию:

frontend http 
  mode http
  bind 0.0.0.0:80 name bind_1
  bind :::80 name bind_2 v4v6
  http-request set-var(txn.base) base
  http-request set-var(txn.path) path
  http-request set-var(txn.host) req.hdr(Host),field(1,:),lower,map(/etc/haproxy/maps/host.map)
  http-request set-var(txn.host) req.hdr(Host),field(1,:),regsub(^[^.]*,,),lower,map(/etc/haproxy/maps/host.map,'') if !{ var(txn.host) -m found }
  http-request set-var(txn.match) var(txn.host),concat(,txn.path,),map(/etc/haproxy/maps/path-exact.map)
  http-request set-var(txn.match) var(txn.host),concat(,txn.path,),map_beg(/etc/haproxy/maps/path-prefix.map) if !{ var(txn.match) -m found }
  http-request deny deny_status 403 if { var(txn.match) -m dom 819381936 } { src -f /etc/haproxy/maps/blacklist-2602162148.map }
  http-request capture "hdr(Referer)" len 128 if { var(txn.match) -m dom 4205828474 }                                     
  http-request capture "hdr(User-Agent)" len 128 if { var(txn.match) -m dom 2786470064 }                        
  use_backend %[var(txn.match),field(1,.)]                                                                           
  default_backend default-haproxy-kubernetes-ingress-default-backend-8080

Строки use_backend и http-request теперь основываются на поиске данных из файлов HAProxy Map, что обеспечивает лучшую читабельность и более эффективную конфигурацию. Поскольку мы можем обновлять файлы HAProxy Map во время выполнения без повторной загрузки, в итоге мы получаем меньше перезагрузок.

Вот как в приведенном выше примере работает маршрутизация к нужному сервису на основе заголовка хоста:

  • Сначала выполняется поиск в файле Host map, чтобы найти точное соответствие запрашиваемого заголовка Host.

  • Если оно не найдено, выполняется второй поиск, чтобы определить, есть ли совпадение для домена запрашиваемого заголовка Host.

  • Затем выполняется поиск по конкатенации "host/path", чтобы найти точное совпадение.

  • Если оно не найдено, выполняется второй поиск, чтобы найти "префиксное" совпадение.

  • На этом этапе либо нет совпадений - в этом случае будет обслуживаться бэкенд по умолчанию, либо результат поиска будет иметь следующий формат:

BackendName.ruleID1.ruleID2.ruleID3

который будет использоваться для выбора соответствующего бэкенда и правил HAProxy.

Например:

echo.k8s.local/echo-2 echo-echo-2-http-echo-8080.4205828474.278647006

Во время этого цикла выпуска код контроллера был рефакторизован и реорганизован, чтобы разделить его на различные модули (хранилище, аннотации, правила/карты HAProxy и т.д.) для более удобного обслуживания и дальнейшего внесения доработок в процесс.

И последнее, но не менее важное: были добавлены юнит- и сквозные тесты.

Контрибьюторы

Мы хотели бы поблагодарить авторов кода, которые помогли сделать эту версию возможной:

Заключение

Релиз HAProxy Kubernetes Ingress Controller 1.5 предоставляет несколько интересных возможностей, которые позволяют вам контролировать базовую конфигурацию, включать механизмы аутентификации, определять пользовательские страницы ошибок и даже запускать их вне кластера Kubernetes. В следующем выпуске мы планируем использовать еще больше возможностей HAProxy через Kubernetes Custom Resource Definitions. Следите за новостями!

Хотите быть в курсе похожих тем? Подпишитесь на наш блог! Вы также можете следить за нами в Twitter и присоединиться к беседе в Slack.


Узнать подробнее о курсе «Инфраструктурная платформа на основе Kubernetes»

Теги:
Хабы:
+4
Комментарии0

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS