Kubernetes: использование Ingress
В этой статье будет показано, как организовать удобную балансировку трафика на ваши приложения при помощи встроенной в Kubernetes функциональности – Ingress.
В одной из прошлых статей “Kubeadm: создание Kubernetes кластера за 10 минут” было показано, как быстро развернуть собственный кластер Kubernetes, а также развернуть внутри него демо-приложение Sock-Shop. Далее в статье “Kubernetes: постоянные диски на GlusterFS и heketi” мы обсудили, как организовать пространство для постоянных дисков контейнеров внутри вашего кластера на базе GlusterFS.
Решаемая проблема
В этой статье будет показано, как организовать удобную балансировку трафика на ваши приложения при помощи встроенной в Kubernetes функциональности — Ingress. Эта функциональность является особенно полезной в случаях, когда вы используете Kubernetes в системе виртуализации или облаке, для которого в Kubernetes нет поддержки функциональности LoadBalancer . Выполняя инструкции из первой статьи вы наверняка заметили, что публикация приложения Sock-Shop на всех вычислительных узлах кластера при помощи функциональности NodePort совершенно не удобна с точки зрения поддержки приложения, т.к. при таком подходе придется использовать внешний балансировщик трафика и держать на нем в актуальном состоянии список всех вычислительных хостов вашего кластера, а также список актуальных опубликованных приложений. А это задача не тривиальная.
Мне очень хотелось предоставить возможность конечным пользователям кластера самостоятельно управлять процессом публикации своих приложений и самостоятельно настраивать балансировщик под себя, свои требования, процессы и приложения. Для этого в Kubernetes существуют два типа сущностей: Ingress и IngressController .
Ingress— это набор правил внутри вашего кластера, предназначенных для того, чтобы входящие подключения могли достичь сервисов (Services) ваших приложений.
Основные задачи, которые решает Ingress:
- Организация для ваших приложений внешне доступных URL
- Обеспечение балансировки трафика
- Терминация SSL
- Виртуальный хостинг на основе имен и т.д
В Kubernetes Ingress работает в паре с IngressController-ом следующим образом: в Ingress описываются правила балансировки трафика до сервисов ваших приложений, применяемые внутри балансировщика, который представляет собой ни что иное, как отдельный Pod с упакованным в него Nginx, Traefik и т.д.
Описание решения
В качестве практического решения данной задачки мне очень понравился элегантный подход, описанный в статье " Kubernetes, Ingress Controllers and Traefik " создателями балансировщика Traefik . Архитектурная схема кластера, реализующего этот подход, приведена на рисунке ниже.
По сути, весь кластер логически разбивается на несколько групп серверов:
- мастеры — серверы, ответственные за управление кластером
- вычислительные узлы — серверы, ответственные за обеспечение работы конечных приложений
- роутеры — это те же самые вычислительные узлы вашего кластера, предназначенные только для DaemonSet -ов IngressController -ов, балансирующих трафик на конечные приложения
Именно этот подход мы и воплотим в жизнь, добавив к нашему уже существующему кластеру один или несколько дополнительных серверов, которые будут предназначены исключительно для запуска IngressController-ов. Также мы создадим отдельный IngressController для опубликованного ранее приложения Sock-Shop, и сделаем это приложение доступным по 80 порту сервера-балансировщика (edge-router.comp.avmaksimov.ru). В качестве балансировщика трафика будет использоваться Nginx , однако вы легко можете заменить его на Traefik или любой другой IngressController. Архитектура созданного ранее кластера изменится и будет выглядеть следующим образом:
Настройка сервера для хостинга балансировщиков
Добавьте к вашему кластеру Kubernetes отдельный вычислительный сервер. Настройка сервера выполняется так же, как это было сделано в статье " Kubeadm: создание Kubernetes кластера за 10 минут ".
Чтобы запретить кластеру Kubernetes запускать на сервере-балансировщике какие-либо Pod-ы кроме IngressController-ов, необходимо перевести добавленный сервер в режим обслуживания :
kubectl drain edge-router.comp.avmaksimov.ru —ignore-daemonsets
Флаг —ignore-daemonsets используется для того, чтобы мы могли использовать виртуализацию сети и запускать IngressController-ы, определяя их как DaemonSet-ы.
Также этот сервер необходимо как-то пометить, например, присвоив ему метку ( label ) role=edge-router:
kubectl label nodes edge-router.comp.avmaksimov.ru role=edge-router
После этих нехитрых манипуляций, все IngressController-ы балансировщиков, запускаемых как DaemonSet-ы с селектором узла (nodeSelector) role: edge-router будут попадать именно на этот сервер.
Пример балансировки трафика для приложения sock-shop
Предполагается, что вы запустили внутри своего кластера демо-приложение Sock-Shop. Если нет, сделайте это, пожалуйста, в противном случае вы не сможете продолжить далее. Как только приложение Sock-Shop запустится, его сервис front-end начнет принимать и обслуживать для него внешний трафик на 80 порту. Именно этот порт экспортирован в порт 30001 на каждом вычислительном узле вашего кластера.
Установка IngressController-А
Для каждого отдельного сервиса приложения, перед которым вы хотите поставить балансировщик, необходимо разворачивать отдельный IngressController. Для приложения Sock-Shop это делается следующим образом.
TLSсертификат
Обязательным требованием для работы IngressController-а является TLS сертификат. Создать его можно командой:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj «/CN=socks.comp.avmaksimov.ru/O=nginx»
Далее необходимо опубликовать этот сертификат в качестве секрета ( Secrets ):
kubectl create secret tls tls-secret —key tls.key —cert tls.crt
DefaultBackend
Еще одним обязательным требованием для работы IngressController-а является наличие бекенда по-умолчанию, который предназначен для отдачи 404 ошибки на неизвестные запросы. Деплой этого бекенда выполняется следующим образом:
kubectl create -f default-backend.yaml
Содержание default-backend.yaml:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: default-http-backend
labels:
k8s-app: default-http-backend
namespace: kube-system
spec:
replicas: 1
template:
metadata:
labels:
k8s-app: default-http-backend
spec:
terminationGracePeriodSeconds: 60
containers:
— name: default-http-backend
# Any image is permissable as long as:
# 1. It serves a 404 page at /
# 2. It serves 200 on a /healthz endpoint
image: gcr.io/google_containers/defaultbackend:1.0
livenessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
ports:
— containerPort: 8080
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
name: default-http-backend
namespace: kube-system
labels:
k8s-app: default-http-backend
spec:
ports:
— port: 80
targetPort: 8080
selector:
k8s-app: default-http-backend
Запуск балансировщика
После того как определены бекенд по-умолчанию и TLS-сертификат для Nginx IngressController-а, можно выполнить его запуск:
kubectl create -f nginx-ingress-controller.yaml
Содержание nginx-ingress-controller.yaml:
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: nginx-ingress-controller
labels:
k8s-app: nginx-ingress-controller
namespace: kube-system
spec:
template:
metadata:
labels:
k8s-app: nginx-ingress-controller
spec:
# hostNetwork makes it possible to use ipv6 and to preserve the source IP correctly regardless of docker configuration
# however, it is not a hard dependency of the nginx-ingress-controller itself and it may cause issues if port 10254 already is taken on the host
# that said, since hostPort is broken on CNI ( https://github.com/kubernetes/kubernetes/issues/31307 ) we have to use hostNetwork where CNI is used
# like with kubeadm
hostNetwork: true
terminationGracePeriodSeconds: 60
containers:
— image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
name: nginx-ingress-controller
readinessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
livenessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
timeoutSeconds: 1
ports:
— containerPort: 80
hostPort: 80
— containerPort: 443
hostPort: 443
env:
— name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
— name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
— /nginx-ingress-controller
— —default-backend-service=$(POD_NAMESPACE)/default-http-backend
nodeSelector:
role: edge-router
Определение правил для балансировки
После того, как запущен IngressController, можно создать сущность Ingress, в которой определить как именно наше демо приложение будет видно из интернета. Для этого выполните следующую команду:
kubectl create -f socks-ingress.yaml
Содержание socks-ingress.yaml:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: front-end
namespace: sock-shop
spec:
rules:
— host: socks.comp.avmaksimov.ru
http:
paths:
— path: /
backend:
serviceName: front-end
servicePort: 80
В данном примере мы сообщаем IngressController-у, что сервис front-end, находящийся в пространстве имен sock-shop и слушающий трафик на 80 порту, будет доступен извне по URL socks.comp.avmaksimov.ru (указывает на внешний IP-адрес сервера edge-router.comp.avmaksimov.ru ).
После всех данных операций демо-приложение Sock-Shop стало доступно на внешнем IP-адресе сервера-балансировщика edge-router.comp.avmaksimov.ru и открывается по URL socks.comp.avmaksimov.ru .
Заключение
Как видите, при помощи сущностей типа Ingress и IngressController можно легко и просто публиковать приложения, развернутые внутри вашего Kubernetes кластера, во внешние сети. Другие особенности работы с балансировщиками в Kubernetes будут освещены по вашим запросам.