Запускаем HAProxy Kubernetes Ingress Controller вне кластера для уменьшения сетевой задержки и числа хопов

Содержание:
В чем проблем запуска внутри кластера
Обычно вы можете запустить HAProxy Kubernetes Ingress Controller как под внутри Kubernetes-кластера. Как под, он имеет доступ к другим подам, потому что они используют внутреннюю сеть Kubernetes-кластера. Это дает возможность управлять маршрутизацией и балансировать трафик к приложениям, запущенным в кластере. Но возникает проблема, как передать внешний трафик во внутренний Ingress Controller.
Как и другие поды, Ingress Controller существует внутри изолированной среды Kubernetes, поэтому клиенты не получат к нему доступ, пока его не пробросят с помощью Service. Когда вы создаете сервис типа NodePort или LoadBalancer, Kubernetes открывает доступ к поду из внешнего мира.
В облаках более распространен вариант LoadBalancer, потому что он говорит облачному провайдеру развернуть один из его облачных балансировщиков нагрузки (например, AWS Network Load Balancer) и поместить его перед Ingress Controller. В случае self hosted Kubernetes обычно используют NodePort и затем вручную устанавливают балансировщик нагрузки. Так что практически в каждом случае балансировщик нагрузки размещается перед вашим Ingress Controller, что означает наличие двойного проксирования, через которые должен пройти трафик для достижения приложений.
В обычной схеме внешний балансировщик нагрузки (Load Balancer) посылает трафик на один из воркеров, а затем Kubernetes передает его на узел, на котором запущен под с Ingress Controller:

Начиная с версии 1.5 HAProxy Ingress Controller, у вас есть возможность запустить его снаружи вашего Kubernetes-кластера, что избавляет от необходимости дополнительного балансировщика перед Ingress Controller. Это дает доступ к физической сети и позволяет достучаться до кластера внешним клиентам. Проблема в том, что теперь Ingress Controller не запущен как под и находится вне кластера. Поэтому ему нужен доступ к внутренней сети кластера каким-то другим способом.
Для решения этой проблемы мы установим Calico в качестве сетевого плагина в Kubernetes и настроим маршрутизацию с помощью протокола BGP. В продакшне BGP будет работать на третьем уровне сети, но для демонстрации этого мы используем в качестве роутера демона BIRD, установленного на той же VM, что Ingress Controller.

Диаграмма поясняет схему, где Ingress Controller находится снаружи Kubernetes-кластера и использует BIRD для взаимодействия с сетью кластера:
Для самостоятельного повторения примеров загрузите демо-проект из GitHub. В нем используются VirtualBox и Vagrant для создания тестового окружения с 4 виртуальными машинами. Одна VM запускает Ingress Controller и BIRD, а 3 остальные формируют Kubernetes-кластер. Вызовите vagrant up из директории проекта для создания виртуальных машин.
Мы пошагово разберем, как запустить внешний HAProxy Kubernetes Ingress Controller и как установить Kubernetes-кластер с Calico.
Kubernetes-кластер
В демо-проекте используются Vagrant и VirtualBox для создания виртуальных машин. 3 из них составляют Kubernetes-кластер:
один мастер-узел, который управляет кластером
два рабочих узла для запуска подов
Vagrant автоматизирует большую часть установки через запуск Bash-скрипта, который запускает необходимые команды для установки Kubernetes и Calico. Если вы задаетесь вопросом, зачем нам понадобилось устанавливать Calico, напомню, что Kubneretes — модульный фреймворк. Некоторые его компоненты можно заменить другими при условии, что они будут реализовывать нужный интерфейс. Сетевые плагины должны реализовывать CNI. Среди популярных вариантов Calico, Flannel, Cilium и Weave Net. BGP-пиринг в Calico делает его хорошим вариантом конкретно в нашем случае.
Чтобы посмотреть, как настраивается мастер-узел, посмотрите Bash-скрипт проекта setup_kubernetes_control_plane.sh. Поверхностно этот процесс делится на такие этапы:
Установка Docker
Установка утилит
kubeadmиkubectlВызов
kubeadm initдля для начальной установки Kubernetes-кластераКопирование файл kubeconfig в домашнюю директорию пользователя, чтобы при подключении по SSH к этой виртуальной машине этот пользователь мог запускать
kubectl-командыУстановка Calico в качестве сетевого плагина с включенным BGP
Установка утилиты
calicoctlСоздание ConfigMap-объекта в Kubernetes haproxy-kubernetes-ingress, который требуется для Ingress Controller
Два рабочих узла инициализируются с помощью Bash-скрипта setup_kubernetes_worker.sh. Он выполняет следующие шаги:
Установка Docker
Установка
kubeadmиkubectlВызов
kubeadmjoin для присоединения к кластеру в качестве воркера
Особенно интересна та часть, в которой мы установим Calico на мастер. Скрипт сначала устанавливает оператор Calico, а затем создает Kubernetes-объект Installation, который включает BGP и назначает диапазон IP-адресов для сети:
apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: # Configures Calico networking. calicoNetwork: bgp: Enabled # Note: The ipPools section cannot be modified post-install. ipPools: - blockSize: 26 cidr: 172.16.0.0/16 encapsulation: IPIP natOutgoing: Enabled nodeSelector: all()
После этого скрипт устанавливает calicoctl и использует его для создания BGPConfiguration и объекта BGPPeer в Kubernetes.
apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: logSeverityScreen: Info nodeToNodeMeshEnabled: true asNumber: 65000 --- apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: my-global-peer spec: peerIP: 192.168.50.21 asNumber: 65000
Объект BGPConfiguration устанавливает уровень логирования для BGP-коннекторов, включает режим full mesh network и назначает номер Autonomous System (AS) для Calico. Объект BGPPeer получает IP-адрес виртуальной машины, где находится Ingress Controller, где будет запущен BIRD, который установит номер AS для BIRD-маршрутизатора. Я выбрал номер AS 65000. Так Calico расшар��т сетевые маршруты BIRD, чтобы их мог использовать Ingress Controller.
Ingress Controller и BIRD
В демо-проекте мы устанавливаем HAProxy Kubernetes Ingress Controller и BIRD на одну и ту же виртуальную машину. Эта VM существует вне Kubernetes-кластера, где BIRD получает IP-маршруты от Calico, и Ingress Controller использует их для передачи клиентского трафика на поды.
Vagrant вызывает Bash-скрипт setup_ingress_controller.sh для выполнения шагов:
Устанавливает HAProxy, при этом дизейблит сервис
Вызывает команду
setcapдля разрешения HAProxy слушать привилегированные порты 80 и 443Загружает HAProxy Kubernetes Ingress Controller и копирует его в /usr/local/bin
Настраивает Systemd для запуска Ingress Controller
Копирует в корень домашней директории пользователя файл kubeconfig, который необходим кластеру для отслеживания Ingress-объектов и смены сервисов
Устанавливает BIRD
Ingress Controller должен быть уже запущен, так как он настроен как сервис Systemd. Он выполняет команду:
/usr/local/bin/haproxy-ingress-controller \ --external \ --configmap=default/haproxy-kubernetes-ingress \ --program=/usr/sbin/haproxy \ --disable-ipv6 \ --ipv4-bind-address=0.0.0.0 \ --http-bind-port=80
Аргумент --external позволяет Ingress Controller запуститься вне Kubernetes. Также он может общаться с кластером и конфигурировать HAProxy, потому что у него есть файл kubeconfig в /root/.kube/config. Однако запросы будут падать, пока мы не сделаем сеть 172.16.0.0/16 доступной.
Calico может взаимодействовать с BIRD, так что она может посылать информацию о внутренней сети. BIRD заполняет таблицу маршрутизации на сервере, где он запущен, что делает IP-адреса подов доступными для Ingress Controller. Перед тем как показать, как настроить BIRD для получения маршрутов от Calico, будет полезно увидеть, как назначаются маршруты со стороны Kubernetes.
Calico и BGP-обмен
Чтобы дать вам представление о том, как это работает, давайте познакомимся с демонстрационной средой. Виртуальные машины получили следующие IP-адреса в сети 192.168.50.0/24:
узел Ingress Controller = 192.168.50.21
мастер-узел = 192.168.50.22
worker 1 = 192.168.50.23
worker 2 = 192.168.50.24
Сначала подключимся по SSH к мастер-узлу Kubernetes, используя команду vagrant ssh:
$ vagrant ssh controlplane
Вызовем kubectl get nodes, чтобы увидеть, что все узлы подняты и готовы:
$ kubectl get nodes NAME STATUS ROLES AGE VERSION controlplane Ready control-plane,master 4h7m v1.21.1 worker1 Ready <none> 4h2m v1.21.1 worker2 Ready <none> 3h58m v1.21.1
Вызовем calicoctl node status для проверки, какая из VM делится маршрутом через BGP:
$ sudo calicoctl node status Calico process is running. IPv4 BGP status +---------------+-------------------+-------+----------+--------------------------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +---------------+-------------------+-------+----------+--------------------------------+ | 192.168.50.21 | global | start | 00:10:03 | Active Socket: Connection | | | | | | refused | | 192.168.50.23 | node-to-node mesh | up | 00:16:11 | Established | | 192.168.50.24 | node-to-node mesh | up | 00:20:08 | Established | +---------------+-------------------+-------+----------+--------------------------------+
Последние две строки с адресами 192.168.50.23 и 192.168.50.24 показывают, что рабочие узлы установили BGP-подключение и совместно используют маршруты. Однако на первой строке с адресом 192.168.50.21 мы видим, что подключение виртуальной машины Ingress Controller не выполнено, потому что мы пока не настроили BIRD.
Calico назначила каждому из воркеров Kubernetes-кластера IP адреса из подмножества более крупной сети 172.16.0.0/16, которая работает поверх сети 192.168.50.0/24. Вы можете вызвать kubectl describe blockaffinities, чтобы увидеть назначенные сетевые диапазоны.
$ kubectl describe blockaffinities | grep -E "Name:|Cidr:" Name: controlplane-172-16-49-64-26 Cidr: 172.16.49.64/26 Name: worker1-172-16-171-64-26 Cidr: 172.16.171.64/26 Name: worker2-172-16-189-64-26 Cidr: 172.16.189.64/26
Здесь мы видим, что worker 1 получил диапазон 172.16.171.64/26, а worker 2 — 172.16.171.64/26. Нам нужно отправить эту информацию в BIRD, чтобы:
запрос клиента ушел в worker 1, если включает IP в диапазоне 172.16.171.64/26
запрос клиента ушел в worker 2, если включает IP в диапазоне 172.16.189.64/26
Мастер Kubernetes запускает поды на один из этих узлов.
Чтобы настроить конфигурацию BIRD, подключитесь по SSH к той виртуальной машине, на которой находится Ingress Controller.
$ vagrant ssh ingress
Откройте файл /etc/bird/bird.conf и добавьте protocol bgp для каждого воркера. Обратите внимание, что секция import filter говорит BIRD, что ему следует принимать только те IP-диапазоны, которые назначены этому узлу, а все остальные блокировать:
router id 192.168.50.21; log syslog all; # мастер-узел protocol bgp { local 192.168.50.21 as 65000; neighbor 192.168.50.22 as 65000; direct; import filter { if ( net ~ [ 172.16.0.0/16{26,26} ] ) then accept; }; export none; } # worker1 protocol bgp { local 192.168.50.21 as 65000; neighbor 192.168.50.23 as 65000; direct; import filter { if ( net ~ [ 172.16.0.0/16{26,26} ] ) then accept; }; export none; } # worker2 protocol bgp { local 192.168.50.21 as 65000; neighbor 192.168.50.24 as 65000; direct; import filter { if ( net ~ [ 172.16.0.0/16{26,26} ] ) then accept; }; export none; } protocol kernel { scan time 60; #import none; export all; # insert routes into the kernel routing table } protocol device { scan time 60; }
Перезапустите BIRD, чтобы принять изменения:
$ sudo systemctl restart bird
Давайте убедимся, что все работает. Вызовите birdc show protocols чтобы увидеть со стороны BIRD, что BGP-обмен установлен:
$ sudo birdc show protocols BIRD 1.6.8 ready. name proto table state since info bgp1 BGP master up 23:18:17 Established bgp2 BGP master up 23:18:17 Established bgp3 BGP master up 23:18:59 Established kernel1 Kernel master up 23:18:15 device1 Device master up 23:18:15
Вы также можете вызвать birdc show route protocol для проверки соответствия сетей IP-адресам виртуальных машин и сети подов:
$ sudo birdc show route protocol bgp2 BIRD 1.6.8 ready. 172.16.171.64/26 via 192.168.50.23 on enp0s8 [bgp2 23:18:18] * (100) [i] $ sudo birdc show route protocol bgp3 BIRD 1.6.8 ready. 172.16.189.64/26 via 192.168.50.24 on enp0s8 [bgp3 23:19:00] * (100) [i]
Также вы можете проверить таблицу маршрутизации сервера, чтобы убедиться, что туда добавились новые маршруты.
$ route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default _gateway 0.0.0.0 UG 100 0 0 enp0s3 10.0.2.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s3 _gateway 0.0.0.0 255.255.255.255 UH 100 0 0 enp0s3 172.16.49.64 192.168.50.22 255.255.255.192 UG 0 0 0 enp0s8 172.16.171.64 192.168.50.23 255.255.255.192 UG 0 0 0 enp0s8 172.16.189.64 192.168.50.24 255.255.255.192 UG 0 0 0 enp0s8 192.168.50.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s8
Если вы вернетесь к виртуальной машине мастера и запустите calicoctl node status, вы увидите, что Calico уже зафиксировала установку BGP-обмена с виртуальной машиной Ingress Controller, 192.168.50.21:
$ sudo calicoctl node status Calico process is running. IPv4 BGP status +---------------+-------------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +---------------+-------------------+-------+----------+-------------+ | 192.168.50.21 | global | up | 00:32:13 | Established | | 192.168.50.23 | node-to-node mesh | up | 00:16:12 | Established | | 192.168.50.24 | node-to-node mesh | up | 00:20:09 | Established | +---------------+-------------------+-------+----------+-------------+
Добавляем Ingress
Благодаря обмену BGP-маршрутами между Kubernetes-кластером и сервером Ingress Controller мы готовы приступить к работе. Давайте добавим объект Ingress, чтобы убедиться, что все работает. Следующий YAML раскладывает 5 экземпляров приложения и создает Ingress-объект:
apiVersion: apps/v1 kind: Deployment metadata: labels: run: app name: app spec: replicas: 5 selector: matchLabels: run: app template: metadata: labels: run: app spec: containers: - name: app image: jmalloc/echo-server ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: labels: run: app name: app spec: selector: run: app ports: - name: port-1 port: 80 protocol: TCP targetPort: 8080 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: test-ingress namespace: default spec: rules: - host: test.local http: paths: - path: / pathType: Prefix backend: service: name: app port: number: 80
Ingress-объект настраивает Ingress Controller так, чтобы отправлять любой запрос от test.local к приложению, которое вы только что развернули. Вам понадобится обновить файл /etc/hosts на вашем хосте для сопоставления test.local с IP-адресом виртуальной машины Ingress Controller, 192.168.50.21.
Разложите объекты через kubectl:
$ kubectl apply -f app.yaml
Откройте test.local в вашем браузере, и вас встретит приложение, которое просто напечатает подробности вашего HTTP-запроса. Поздравляю! Вы запустили HAProxy Kubernetes Ingress Controller снаружи Kubernetes-кластера! Вам больше не нужно использовать двойное проксирование.
Чтобы увидеть созданный файл harpoxy.cfg, откройте /tmp/haproxy-ingress/etc/haproxy.cfg. Он сгенерировал backend с именем default-app-port-1, который содержит строку server для каждого из запущенных в приложении подов. Конечно, IP-адреса в каждой строке server теперь доступны. Вы можете масштабировать приложение в большую или меньшую сторону, и Ingress Controller автоматически настроит соответствующую конфигурацию.
backend default-app-port-1 mode http balance roundrobin option forwardfor server SRV_1 172.16.171.67:8080 check weight 128 server SRV_2 172.16.171.68:8080 check weight 128 server SRV_3 172.16.189.68:8080 check weight 128 server SRV_4 172.16.189.69:8080 check weight 128 server SRV_5 172.16.189.70:8080 check weight 128
Заключение
В этой статье мы разобрали, как запустить HAProxy Kubernetes Ingress Controller снаружи Kubernetes-кластера. Это избавляет от необходимости запуска дополнительного балансировщика нагрузки. Такой подход позволит снизить время ожидания к��иента, поскольку требует меньшего количества сетевых переходов. Для обеспечения высокой доступности вы дополнительно можете настроить Keepalive.
Другие статьи про DevOps для начинающих:
Другие статьи про DevOps для продолжающих:
За последние годы де-факто стандартом оркестрации и запуска приложений стал Kubernetes. Поэтому умение управлять Kubernetes-кластерами является особенно важным в работе любого современного DevOps-инженера.
Порог входа может казаться достаточно высоким из-за большого числа компонентов и связей между ними внутри системы. На курсе «Деплой приложений в Kubernetes» мы рассмотрим самые важные концепции, необходимые для управления кластерами любой сложности и научим применять эти знания на практике.
? Почитать про курс подробнее можно здесь: https://vk.cc/cn0ty6
