Статьи

Балансировка нагрузки сервисов Kubernetes с NGINX Plus

Kubernetes — это система с открытым исходным кодом, разработанная Google для запуска и управления контейнерными приложениями на основе микросервисов в кластере. Людям, которые используют Kubernetes, часто нужно, чтобы службы, которые они создают в Kubernetes, были доступны за пределами их кластера Kubernetes.

Хотя Kubernetes предоставляет встроенные решения для предоставления услуг, описанные ниже, эти решения ограничивают вас балансировкой нагрузки уровня 4 или циклическим распределением нагрузки HTTP.

В этом посте показано, как использовать NGINX Plus в качестве усовершенствованного решения для балансировки нагрузки уровня 7 для предоставления услуг Kubernetes Интернету, независимо от того, используете ли вы Kubernetes в облаке или в собственной инфраструктуре.

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

Предоставление услуг Kubernetes с помощью встроенных решений

Kubernetes предлагает несколько вариантов предоставления услуг . Два из них — NodePort и LoadBalancer — соответствуют определенному типу сервиса. Третий вариант, Ingress API, стал доступен как бета-версия в выпуске Kubernetes 1.1.

NodePort

Указание типа сервиса в качестве NodePort делает сервис доступным на том же порту на каждом узле Kubernetes. Чтобы предоставить службу Интернету, вы должны открыть один или несколько узлов на этом порту. Для обеспечения высокой доступности вы можете предоставить доступ к нескольким узлам и использовать балансировку нагрузки на основе DNS для распределения трафика между ними, либо вы можете разместить узлы за балансировщиком нагрузки по вашему выбору.

Когда входящий трафик попадает на узел в порту, он распределяется по нагрузке между модулями службы. Балансировка нагрузки, выполняемая прокси-сервером Kubernetes, работающим на каждом узле, ограничена балансировкой нагрузки TCP / UDP.

LoadBalancer

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

Решение LoadBalancer поддерживается только некоторыми облачными провайдерами и Google Container Engine и недоступно, если вы используете Kubernetes в собственной инфраструктуре. Кроме того, Kubernetes позволяет настроить циклическую балансировку нагрузки TCP, даже если облачный балансировщик нагрузки имеет расширенные функции, такие как сохранение сеанса или сопоставление запросов.

Входной API

Создание ресурса Ingress позволяет вам предоставлять сервисы Интернету по индивидуальным URL-адресам (например, сервис A по URL / foo и сервис B по URL / bar ) и нескольким именам виртуальных хостов (например, foo.example.com для одна группа услуг и bar.example.com для другой группы). Контроллер Ingress использует ресурс Ingress и устанавливает внешний балансировщик нагрузки.

Контроллер Ingress не является частью стандартного развертывания Kubernetes: вам нужно выбрать контроллер, который лучше всего соответствует вашим потребностям, или внедрить его самостоятельно и добавить его в свой кластер Kubernetes. Ожидается, что многие реализации контроллера появятся в ближайшее время, но на данный момент единственной доступной реализацией является контроллер для Google Compute Engine HTTP Balancer , который работает, только если вы используете Kubernetes в Google Compute Engine или Google Container Engine . Входной API поддерживает только циклическую балансировку нагрузки HTTP, даже если фактический балансировщик нагрузки поддерживает расширенные функции.

На момент написания статьи и Ingress API, и контроллер для Google Compute Engine HTTP Load Balancer находятся в стадии бета-тестирования.

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

Предоставление услуг Kubernetes с NGINX Plus

Чтобы интегрировать NGINX Plus с Kubernetes, нам нужно убедиться, что конфигурация NGINX Plus остается синхронизированной с Kubernetes, отражая изменения в службах Kubernetes, такие как добавление или удаление модулей. С помощью программного обеспечения NGINX с открытым исходным кодом вы вручную изменяете файл конфигурации NGINX и выполняете перезагрузку конфигурации. С NGINX Plus есть два способа динамического обновления конфигурации :

  • С API. Этот метод использует API реконфигурации NGINX Plus « на лету» для добавления и удаления записей для модулей Kubernetes в конфигурации NGINX Plus, а также API Kubernetes для получения IP-адресов модулей. Этот метод требует от нас написания некоторого кода, и мы не будем подробно его обсуждать. Подробности смотрите на вебинаре Келси Хайтауэра « Вывод Kubernetes на грань с NGINX Plus» , на котором он исследует API и создает приложение, которое их использует.
  • По именам DNS повторно разрешения  — Этот метод требует только правильной конфигурации одноразового Nginx Plus, как описан в следующем разделе.

Использование реконфигурации на основе DNS

Мы предполагаем, что у вас уже есть работающий кластер Kubernetes и хост с kubectlутилитой, доступной для управления кластером; инструкции см. в руководстве по началу работы с Kubernetes для вашего типа кластера. Вам также необходимо создать образ NGINX Plus Docker, и инструкции доступны в этом блоге .

Вот схема того, что мы будем делать:

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

Примечания: Мы протестировали решение, описанное в этом блоге, с Kubernetes 1.0.6, работающим на Google Compute Engine, и локальной установкой Vagrant , которую мы используем ниже.

В командах значения, которые могут отличаться для вашей настройки Kubernetes, выделены курсивом.

Настройка модуля NGINX Plus

Мы помещаем NGINX Plus в модуль Kubernetes на узле, который мы выставляем в Интернете. Наш модуль создан контроллером репликации, который мы также настраиваем. Наш специфический для Kubernetes файл конфигурации NGINX Plus находится в папке, совместно используемой модулем NGINX Plus и узлом, что упрощает обслуживание.

Выбор узла, в котором находится стручок NGINX Plus

Чтобы обозначить узел, где работает модуль NGINX Plus, мы добавляем метку для этого узла. Мы получаем список всех узлов, выполнив:

$ kubectl get nodes
NAME         LABELS                              STATUS
10.245.1.3   Kubernetes.io/hostname=10.245.1.3   Ready
10.245.1.4   Kubernetes.io/hostname=10.245.1.4   Ready
10.245.1.5   Kubernetes.io/hostname=10.245.1.5   Ready

Мы выбираем первый узел и добавляем к нему метку, выполняя:

$ kubectl label node 10.245.1.3 role=nginxplus

Настройка контроллера репликации для модуля NGINX Plus

Мы не создаем модуль NGINX Plus напрямую, а через контроллер репликации. Мы настраиваем контроллер репликации для модуля NGINX Plus в файле объявления Kubernetes с именем nginxplus-rc.yaml .

  • Мы устанавливаем число replicasв единицу, что означает, что Kubernetes гарантирует, что один модуль NGINX Plus всегда работает: если модуль выходит из строя, он заменяется новым модулем.
  • В nodeSelectorполе мы указываем, что модуль NGINX Plus создается на узле с меткой role: nginxplus.
  • Наш контейнер NGINX Plus предоставляет два порта, 80 и 8080, и мы настроили сопоставление между ними и портами 80 и 8080 на узле.
  • Наш контейнер NGINX Plus также совместно использует папку /etc/nginx/conf.d, которая находится на узле. Как будет объяснено ниже, общий доступ к папке позволяет нам перенастроить NGINX Plus без перестройки образа контейнера.
apiVersion: v1
kind: ReplicationController
metadata:
  name: nginxplus-rc
spec:
  replicas: 1
  selector:
    app: nginxplus
  template:
    metadata:
      labels:
        app: nginxplus
    spec:
      nodeSelector:
        role: nginxplus
      containers:
      - name: nginxplus
        image: nginxplus
        ports:
          - name: http
            containerPort: 80
            hostPort: 80
          - name: http-alt
            containerPort: 8080
            hostPort: 8080
        volumeMounts:
          - mountPath: "/etc/nginx/conf.d"
            name: etc-nginx-confd
      volumes:
        - hostPath:
            path: "/etc/nginx/conf.d"
          name: etc-nginx-confd

Обеспечение доступности образа докера NGINX Plus на узле

Как мы уже говорили выше, мы уже создали образ NGINX Plus Docker. Теперь мы делаем его доступным на узле. Для простоты мы не используем частный репозиторий Docker, а просто загружаем изображение на узел вручную.

На хосте, на котором мы создали образ Docker, мы запускаем следующую команду, чтобы сохранить образ в файл:

$ docker save -o nginxplus.tar nginxplus

Мы передаем nginxplus.tar на узел и запускаем следующую команду на узле, чтобы загрузить изображение из файла:

$ docker load -i nginxplus.tar

Настройка NGINX Plus

В папке / etc / nginx контейнера NGINX Plus мы сохраняем основной файл конфигурации nginx.conf по умолчанию, который поставляется с пакетами NGINX Plus. includeДиректива в файле по умолчанию считывает в других файлах конфигурации из /etc/nginx/conf.d папки. Как указано в для контроллера репликации NGINX Plus ( nginxplus-rc.yaml ), мы совместно используем папку /etc/nginx/conf.d на узле NGINX Plus с контейнером. Совместное использование означает, что мы можем вносить изменения в файлы конфигурации, хранящиеся в папке (на узле), без необходимости перестраивать образ NGINX Plus Docker, что мы должны были бы сделать, если бы создали папку непосредственно в контейнере. Мы помещаем наш специфичный для Kubernetes файл конфигурации (backend.conf ) в общей папке.

Сначала давайте создадим папку /etc/nginx/conf.d на узле.

$ mkdir /etc/nginx/conf.d

Затем мы создаем файл backend.conf и включаем эти директивы:

  • resolver — Определяет IP-адрес преобразователя DNS Kubernetes, используя IP-адрес по умолчанию, 10.0.0.10. validПараметр говорит NGINX Plus для повторной решимости любого DNS имени каждых пяти секунд. IP-адрес вашей службы DNS Kubernetes может отличаться. Запустите эту команду, чтобы узнать это:
    $ kubectl get svc kube-dns --namespace=kube-system
  • upstream — Создает вышестоящую группу под названием backend для службы Kubernetes, которую мы представляем. Мы идентифицируем серверы в вышестоящей группе по имени хоста и включаем resolveдирективу, чтобы NGINX повторно разрешил имя хоста во время выполнения.
  • server (дважды) — Определите два виртуальных сервера:

    • Первый сервер прослушивает порт 80 и распределяет нагрузку по входящим запросам для / nginx-service среди модулей нашего сервиса. Мы также настроили активные проверки здоровья .
    • Второй сервер прослушивает порт 8080. Здесь мы настроили мониторинг активности NGINX Plus. Позже мы будем использовать его для проверки правильности перенастройки NGINX Plus.
resolver 10.0.0.10 valid=5s;

upstream backend {
    zone upstream-backend 64k;
    server nginx-service.default.svc.cluster.local resolve;
}

server {
    listen 80;

    status_zone backend-servers;

    location /nginx-service/ {
        proxy_pass http://backend/;
        health_check;
    }
}

server {
    listen 8080;

    root /usr/share/nginx/html;

    location = /status.html { }

    location /status {
        status;
    }
}

Создание контроллера репликации

Теперь мы готовы создать контроллер репликации, выполнив эту команду на нашем узле:

$ kubectl create -f nginxplus-rc.yaml

Чтобы убедиться, что модуль NGINX Plus был создан, мы запускаем:

$ kubectl get pods
NAME                 READY     STATUS    RESTARTS   AGE
nginxplus-rc-0ts5t   1/1       Running   0          17s

Мы запускаем Kubernetes на локальной установке Vagrant, поэтому мы знаем, что внешний IP-адрес нашего узла — 10.245.1.3, и мы будем использовать этот адрес до конца этого примера. Если вы используете Kubernetes на облачном провайдере, вы можете получить внешний IP-адрес вашего узла, выполнив:

$ kubectl get nodes node-name -o json | grep -i externalIP -A 1
                "type": "ExternalIP",
                "address": XXX.XXX.XXX.XXX

Если вы работаете в облаке, не забудьте настроить правило брандмауэра, чтобы разрешить узлу NGINX Plus принимать входящий трафик. Пожалуйста, обратитесь к документации вашего облачного провайдера.

Мы можем проверить, что наш модуль NGINX Plus запущен и работает, посмотрев на панель мониторинга NGINX Plus в режиме реального времени, которая доступна через порт 8080 по внешнему IP-адресу узла (например, http://10.245.1.3:8080/status .html в нашем случае). Однако если мы посмотрим на этот момент, мы не увидим никаких серверов для нашего сервиса, потому что мы еще не создали его.

Панель мониторинга активности NGINX Plus перед созданием сервисов Kubernetes

Создание простого сервиса Kubernetes

Теперь пришло время создать сервис Kubernetes. Наш сервис состоит из двух серверов NGINX (с открытым исходным кодом), которые обслуживают статические веб-страницы.

Создание контроллера репликации для службы

Сначала мы создаем контроллер репликации, чтобы Kubernetes следил за тем, чтобы указанное количество реплик (модулей) веб-сервера NGINX всегда работало в кластере. Вот файл декларации ( nginx-rc.yaml ):

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx-rc
spec:
  replicas: 2
  selector:
    app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

Наш контроллер состоит из двух веб-серверов NGINX. Мы объявляем контроллер, состоящий из модулей с одним контейнером NGINX, открывающим порт 80. Образ NGINX будет извлечен из Docker Hub.

Чтобы создать контроллер репликации, мы запускаем следующие команды на узле:

$ kubectl create -f nginx-rc.yaml

Чтобы проверить, что наши модули были созданы, мы можем запустить следующую команду. Мы используем селектор меток, app=nginxчтобы получить только модули, созданные контроллером репликации на предыдущем шаге:

$ kubectl get pods -l app=nginx
NAME             READY     STATUS    RESTARTS   AGE
nginx-rc-544f1   1/1       Running   0          2m
nginx-rc-uk6pm   1/1       Running   0          2m

Создание сервиса

Затем мы создаем сервис для модулей, созданных нашим контроллером репликации. Мы объявляем сервис со следующим файлом ( nginx-service.yaml ):

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ClusterIP: None
  ports:
  - port: 80 
    targetPort: 80
    protocol: TCP
  selector:
    app: nginx

Здесь мы объявляем специальный безголовый сервис , устанавливая ClusterIPполе в None. При использовании этого типа службы IP-адрес кластера не выделяется, а служба недоступна через прокси-сервер kube. DNS-запрос к Kubernetes DNS возвращает несколько A записей (IP-адреса наших модулей).

Устанавливая selectorполе в app: nginx, мы объявляем, какие модули принадлежат сервису, а именно, модули, созданные нашим контроллером репликации NGINX (определено в nginx-rc.yaml ).

На узле мы запускаем следующую команду, которая создает сервис:

$ kubectl create -f nginx-service.yaml

Теперь, если мы обновим страницу панели инструментов и перейдем на вкладку Upstreams в верхнем правом углу, мы увидим два добавленных сервера.

Информационная панель мониторинга активности NGINX Plus после создания сервисов Kubernetes

Мы также можем проверить, что NGINX Plus является трафиком с балансировкой нагрузки между модулями сервиса. Если это так, мы видим страницу приветствия NGINX по умолчанию при доступе к http://10.245.1.3/nginx-service/ в браузере.

Страница приветствия подтверждает, что NGINX Plus балансирует нагрузку наших сервисов Kubernetes.

Если мы обновим эту страницу несколько раз и посмотрим на панель состояния, мы увидим, как запросы распределяются по двум вышестоящим серверам.

Масштабирование службы Kubernetes

Теперь давайте добавим еще два модуля в наш сервис и убедимся, что конфигурация NGINX Plus снова обновляется автоматически. Мы запускаем эту команду, чтобы изменить количество модулей на четыре, масштабируя контроллер репликации:

$ kubectl scale rc nginx-rc --replicas=4
scaled

Чтобы проверить, что NGINX Plus был перенастроен, мы могли бы снова взглянуть на приборную панель, но на этот раз мы вместо этого используем API статуса NGINX Plus. На нашем узле мы выполняем следующую команду, где 10.245.1.3 является внешним IP-адресом нашего узла NGINX Plus. Чтобы отформатировать вывод JSON, мы направляем вывод в jq.

$ curl -s 10.245.1.3:8080/status/upstreams/backend | jq .
{
  "peers": [
    {
      "id": 1,
      "server": "10.0.0.1:80",
      "backup": false,
      "weight": 1,
      "state": "unhealthy",
      "active": 0,
      "requests": 1,
      "responses": {
        "1xx": 0,
        "2xx": 0,
        "3xx": 0,
        "4xx": 0,
        "5xx": 0,
        "total": 0
      },
      "sent": 0,
      "received": 0,
      "fails": 0,
      "unavail": 0,
      "health_checks": {
        "checks": 1,
        "fails": 1,
        "unhealthy": 1,
        "last_passed": false
      },
      "downtime": 33965,
      "downstart": 1445378182275,
      "selected": 1445378131000
    },
    {
      "id": 2,
      "server": "10.246.1.6:80",
      ...
    },
    {
      "id": 3,
      "server": "10.246.3.2:80",
       ...
    {
      "id": 4,
      "server": "10.0.0.2:80",
      ...
    }
  ],
  "keepalive": 0
}

peersМассив на выходе JSON имеет ровно четыре элемента, по одному для каждого веб — сервера Nginx.

Теперь давайте уменьшим количество стручков с четырех до одного и снова проверим статус NGINX Plus:

$ kubectl scale rc nginx-rc --replicas=1
scaled

$ curl -s 10.245.1.3:8080/status/upstreams/backend | jq .

Теперь peersмассив в выводе JSON содержит только один элемент.

Теперь, когда NGINX Plus запущен и работает, мы можем начать использовать его расширенные функции, такие как сохранение сеанса , завершение SSL , маршрутизация запросов , расширенный мониторинг и многое другое .

Резюме

Варианты реконфигурации «на лету», доступные в NGINX Plus, позволяют легко интегрировать его с Kubernetes: программно через API или полностью с помощью DNS. Использование NGINX Plus для предоставления услуг Kubernetes Интернету предоставляет множество функций, которые отсутствуют в современных встроенных решениях балансировки нагрузки Kubernetes.