Имея за плечами опыт работы с Kubernetes в различных облачных провайдерах вроде AWS и Yandex Cloud я столкнулся с необходимостью развертывания кластера вне облака на виртуальных машинах.
В статье расскажу про то, как подготовить high-availability кластер, используя инструмент под названием RKE2 - Rancher Kubernetes Engine.
Отличия self-hosted и облачного K8s
В любом крупном облаке можно запустить Kubernetes как managed сервис. Это снимает с администратора вопрос о том, как конфигурировать master хосты и control plane сервисы, поддерживать их работоспособность, обновлять версии. Все это является зоной ответственности облака, а сам запуск кластера делается путем нескольких шагов в веб-интерфейсе облака или использованием Terraform автоматизаций. Соответственно команда инженеров фокусируется на администрировании worker узлов, самого приложения и вспомогательных компонентов, запущенных в кластере.
Но использовать такой облачный сервис (как и облака в целом) не всегда представляется возможным, и на это могут быть разные причины. В нашем случае, различные внутренние сервисы и тестовые стенды, к которым нет высоких требований по доступности, запускаются на собственном сервере, размещенном в дата-центре. Таким образом сильно сокращаются расходы в сравнении с запуском аналогичных ресурсов в облаке. Но при этом при запуске и администрировании приложения нам хочется оперировать на уже привычном и удобном уровне K8s и Helm, а не возвращаться назад к Docker-Compose и Ansible.
Соответственно, при работе с self-hosted Kubernetes потребуется глубже погружаться в его администрирование. А начать следует с выбора инструмента для развертывания и управления.
Выбор инструментария
Нет инструмента, который был бы единственным правильным вариантом для развертывания Kubernetes. Я рассмотрел наиболее популярные решения, используемые для продакшена.
Kubeadm – инструмент предлагаемый в документации K8s. Он решает только часть задач по настройке кластера. Сюда не входит предварительная настройка самих хостов, установка системных аддонов. Разработчиками K8s подразумевается, что для этого вы напишете свой верхнеуровневый тулинг, который в том числе будет вызывать kubeadm для деплоймента кластера. Как правило, поверх kubeadm делают собственный Ansible Playbook или скрипты.
Kubespray – готовая Ansible автоматизация, которая позволяет выполнить полный цикл настройки кластера. Под капотом вызывается kubeadm, и в том числе решается вопрос конфигурации узлов и установки всех необходимых для работы компонентов. Я слышал от коллег, что тут могут быть сложности при обновлениях версий, особенно если делались кастомизации плейбука.
Rancher (RKE) – продукт для развертывания и управления Kubernetes кластерами, принадлежащий компании SUSE. Позволяет быстро подготовить кластер со всеми необходимыми компонентами. Бесплатная версия обладает полным функционалом, есть платная поддержка.
Openshift – коммерческая платформа от Red Hat, сейчас не актуальна для рынка РФ. Есть бесплатный аналог – OKD, но не похоже, что он пользуется популярностью в индустрии.
Deckhouse – отечественная платформа. Хороший вариант, если вам нужно ПО из Росреестра и с саппортом от производителя. Бесплатная версия тоже имеется, но ее функционал урезан.
Вероятно, самым оптимальным вариантом будет kubeadm и написание своей автоматизации. На это потребуется больше времени, чем использовать готовый дистрибутив или платформу. Но контроль конфигурации даст больше гибкости и понимания как работать с системой, не будет завязывать на сторонний продукт.
Но я решил начать с более удобного варианта – Rancher, а точнее их K8s дистрибутива RKE2. Опыт работы с ним для меня в любом случае полезен: инструмент востребован как на западном, так и отечественном рынке. Плюс мне на поддержку перешел проект на первой версии RKE, который, вероятно, в будущем нужно будет обновлять.
Про Rancher и RKE
При первичном знакомстве с Rancher может возникнуть некоторая путаница в существующем ПО этой экосистемы. Поэтому разберемся с терминологией.
Rancher – платформа с графическим интерфейсом для развертывания и управления K8s кластерами. Позволяет развертывать кластера в облаках на базе managed сервисов, так и в bare-metal окружениях, используя для этого дистрибутив RKE (Rancher Kubernetes Engine). Сам Rancher для тестовых целей может быть запущен как Docker контейнер, но для продакшена рекомендуется устанавливать его в K8s кластер. Получается интересно: чтобы получить платформу, которая будет развертывать K8s кластера, перед этим нужно сделать для нее K8s кластер. Подробнее про Rancher я пишу в отдельной главе этой статьи.
RKE – дистрибутив K8s, который позиционируется разработчиком как решение для упрощения и автоматизации процесса установки, управления Kubernetes. Использует Docker как в качестве container runtime для K8s, так и запускает в нем сервисы control plane.
RKE2 – обновленная и улучшенная версия дистрибутива, который не зависит от Docker в отличие от предшественника. Сервисы control plane запускаются как статические поды под управлением kubelet, а в качестве container runtime – containerd.
RKE это standalone дистрибутивы, т.е. их можно использовать независимо от платформы Rancher. Именно это я и сделал, выбрав вторую версию.
Подготовка к установке RKE2
Я создал 3 виртуальные машины с серверной Ubuntu 22.04 на борту. Выделил каждой 2 vCPU и 4 GB оперативной памяти: это минимально возможные ресурсы судя по документации. Этого хватит для работы control plane и нескольких сервисов, но при увеличении количества приложений потребуется расширить количество ресурсов или добавить новые виртуалки в кластер.
Перед установкой также следует свериться с требованиями в документации RKE2: проверить поддержку требуемой ОС, доступность нужных портов, настроить уникальный hostname для каждого узла и т.д.
Для отказоустойчивости желательно подготовить статический эндпоинт (Fixed Registration Address), по которому будет доступны master ноды. Этот адрес будет использоваться нодами в момент присоединения к кластеру. Таким образом можно избежать проблем, связанных с выходом из строя конкретного узла, ведь под этим адресом будут доступны все master ноды в кластере.
Сам эндпоинт можно сделать различными способами, например, настроив TCP Load Balancer или Round-robin DNS. Я выбрал вариант с DNS.
;; ANSWER SECTION:
k8s-master.example.com. 60 IN A 192.168.100.19
k8s-master.example.com. 60 IN A 192.168.100.21
k8s-master.example.com. 60 IN A 192.168.100.20
Этот же адрес я буду использовать для доступа к Kubernetes API, указав его в kubeconfig на следующем шаге.
Установка RKE2
Для начала установки RKE2 нужно подключиться по SSH на первый хост. Всю конфигурацию необходимо выполнять под root пользователем, выполнив sudo -i:
Скачать и запустить инсталлятор:
curl -sfL https://get.rke2.io | sh -
Будет установлена последняя стабильная версия RKE. На момент написания статьи это 1.28, при этом в Kubernetes уже произошел релиз 1.30. Если нововведения важнее стабильности, можно выполнить установку из latest канала, используя переменную окружения INSTALL_RKE2_CHANNEL=latest.
Так как мы используем fixed registration address, для избежания ошибок сертификата нужно добавить параметр tls-san в конфиг.
Создать файл:
touch /etc/rancher/rke2/config.yaml
Добавить в него:
tls-san:
- k8s-master.example.com
Теперь можно запустить сервис. Далее проверить логи и статус k8s подов:
systemctl enable --now rke2-server.service
journalctl -u rke2-server -f
/var/lib/rancher/rke2/bin/kubectl get pods -A --kubeconfig /etc/rancher/rke2/rke2.yaml
Далее скопировать токен из файла /var/lib/rancher/rke2/server/node-token
. Он потребуется для конфигурации дополнительных узлов.
Переходим к настройке дополнительных мастер нод. В результате в кластере должно быть нечетное количество, рекомендуется три.
Нужно подключиться по SSH к второй и третьей ноде и выполнить шаги аналогичные конфигурации первой ноды, за исключением конфига. Тут дополнительно нужно указать токен и адрес сервера, используя в качестве него ранее настроенный fixed registration address.
Пример конфига
root@k8s-node-02:/etc/rancher/rke2# cat config.yaml
Скрытый текст
tls-san:
- k8s-master.example.com
token: some_token
server: https://k8s-master.example.com:9345
Проверить работоспособность кластера:
/var/lib/rancher/rke2/bin/kubectl get nodes --kubeconfig /etc/rancher/rke2/rke2.yaml
/var/lib/rancher/rke2/bin/kubectl get pods -A --kubeconfig /etc/rancher/rke2/rke2.yaml
В результате сконфигурирован минимальный отказоустойчивый RKE кластер из 3 узлов, в котором запущены etcd, Kubernetes API, CNI, Ingress контроллер, прочие control plane сервисы и компоненты.
По умолчанию на этих же master нодах (в терминологии RKE они именуются как server node) можно запускать и всю пользовательскую нагрузку: приложения и различные сервисы. Такая конфигурация сейчас подходит под мои цели, ведь я не планирую деплоить в данный кластер высоконагруженные приложения с требованиями к отказоустойчивости. Однако по Best Practice для продакшен кластера лучше использовать выделенные worker ноды, которые в RKE называются agent nodes. Конфигурация таких узлов практически идентична серверным и описана в документации. Помимо этого необходимо настроить taint в конфиге master хостов, чтоб исключить запуск на них чего-либо кроме control plane сервисов:
node-taint:
- "CriticalAddonsOnly=true:NoExecute"
Настройка Kubectl
После установки RKE2 на каждой ноде кластера, в директории /var/lib/ranher/rke2/bin/
, находится утилита kubectl, которая используется для управления K8s. А необходимый для утилиты конфиг расположен в /etc/rancher/rke2/rke2.yaml
.
Можно использовать kubectl прямо на самих узлах кластера, но, как правило, для этого используют отдельный bastion (management) хост, либо организуют прямой доступ со своих рабочих мест. При втором варианте главное ограничить доступ к порту K8s API (6443) только для определенных IP адресов на уровне firewall.
Для данной инсталляции я использую bastion хост. Необходимо подключиться к нему по SSH и yстановить kubectl, при скачивании указав версию соответствующую серверной. В моем случае это 1.28:
curl -LO https://dl.k8s.io/release/v1.28.0/bin/linux/amd64/kubectl
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version –client
Далее необходимо создать файл ~/.kube/config, скопировав в него содержимое файла /etc/rancher/rke2/rke2.yaml с любого узла RKE2 кластера, с единственным отличием. Адрес 127.0.0.1 нужно заменить на наш fixed registration address - k8s-master.example.com
После этого проверить работоспособность kubectl:
kubectl get pods -A
Установка Rancher и других компонентов в кластер
Выполним установку в RKE2 кластер различных компонентов, которые могут быть полезны для дальнейшего управления кластером и запуска в нем приложений.
В первую очередь я установил Rancher. Как я писал ранее, он не является обязательным компонентом, RKE кластер полноценно функционирует и без него. Но даже если я не буду использовать весь функционал Rancher, то как минимум графический интерфейс и RBAC (role-based access control), поддерживающий сторонние провайдеры авторизации, могут быть мне полезны.
В документации предлагается установить Rancher через CLI выполнив helm install команды. Я выбрал другой способ. Посмотрев как именно в кластер были автоматизировано установлены такие компоненты как Canal или Nginx Ingress я сделал аналогично и для Rancher.
Одним из дефолтных компонентов RKE2 кластера является Helm Controller. Этот контроллер отслеживает директорию /var/lib/rancher/rke2/server/manifests и автоматически устанавливает манифесты, которые содержатся в ней.
Перед развертыванием Rancher нужно развернуть cert-manager, для возможности выпуска сертификата и работы по HTTPS. Поэтому в первую очередь создаем манифест для его установки.
cd /var/lib/rancher/rke2/server/manifests
touch cert-manager.yaml
Скрытый текст
kind: HelmChart
metadata:
name: cert-manager
namespace: kube-system
spec:
chart: cert-manager
repo: https://charts.jetstack.io
version: v1.15.2
createNamespace: true
set:
installCRDs: "true"
targetNamespace: cert-manager
В metadata.namespace будет развернут сам ресурс HelmChart, тут необходимо оставить значение kube-system. Выбор значения для targetNamespace остается на наш выбор, сюда будут развернуты объекты чарта.
Далее по аналогии нужно добавить манифест для самого Rancher. В парамерах задать пароль администратора, хост по которому будет сконфигурирован Ingress и количество реплик:
Скрытый текст
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: rancher
namespace: kube-system
spec:
chart: rancher
createNamespace: true
repo: https://releases.rancher.com/server-charts/stable
set:
bootstrapPassword: examplePassword123
hostname: rancher.proxmox.example.com
replicas: 1
targetNamespace: cattle-system
version: 2.8.5
HelmController обнаружит появление манифестов и выполнит их деплой. В неймспейсе kube-system будут созданы поды с префиксом helm-install. Например helm-install-rancher-j654n. В случае проблем с развертыванием необходимо смотреть логи этих подов. При успешном выполнении они должны перейти в статус Completed.
Выполнив kubectl get addons -A помимо дефолтных отображаются установленные нами чарты
kubectl get addon -A
Далее через kubectl get pods проверить, что поды cert-manager и Rancher в состоянии Running.
Для автоматизации стоит разместить эти манифесты в git репозитории и выполнять их копирование на хосты с помощью Ansible.
Хотя этот способ установки и удобен, для дальнейшего деплоя приложений я буду устанавливать ArgoCD с паттерном App of Apps, который использую во всех других проектах с Kubernetes. Однако наличие Helm Controller это все равно большой плюс. Он решает проблему курицы и яйца, ведь перед тем как деплоить приложения с помощью ArgoCD, необходимо автоматизированным образом развернуть как сам ArgoCD, так и компоненты типа Ingress контроллера или менеджера сертификатов до него.
Настройка внешнего входящего трафика
Для подключения по http к веб-интерфейсу Rancher или любым другим сервисам в кластере из внешней сети, необходимо решить вопрос с балансировкой. В облачных провайдерах это обычно не вызывает проблем, достаточно одного манифеста, чтоб получить балансировщик 4 или 7 уровня, который будет интегрирован с кластером. Для bare-metal среды потребуется другой подход.
Отличный материал по этой теме представлен в документации ingress-nginx, где описаны несколько вариантов реализации, которые визуализируются схемами.
Наиболее предпочтительным вариантом мне показалось использование внешнего балансировщика - Using a self-provisioned edge.
При такой реализации узлы кластера остаются полностью приватными, а за входящий трафик отвечает Load Balancer за пределами Kubernetes. Этот балансировщик имеет публичный IP адрес и форвардит весь HTTP трафик до K8s хостов. В качестве такого балансера чаще всего выбирают HAproxy, но здесь можно выбирать любой из множества вариантов.
Чтобы этот способ сработал, сервис Ingress контроллера должен быть запущен как NodePort. В Nginx Ingress Controller по умолчанию сервис не создается, поэтому нужно выполнить изменения в конфигурации чарта.
Сам манифест находится в уже знакомой нам директории manifests, файлrke2-ingress-nginx.yaml. Можно просмотреть его содержимое, но редактировать его, как и другие дефолтные манифесты, нельзя. Для кастомизации значений чарта нужно сделать отдельный файл.
touch rke2-ingress-nginx-config.yaml - создать файл, назвав его аналогично основному, но добавив постфикс config
Далее добавить в него:
Скрытый текст
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: rke2-ingress-nginx
namespace: kube-system
spec:
valuesContent: |-
controller:
service:
enabled: true
type: "NodePort"
nodePorts:
http: 32767
https: 32642
Для того, что бы произошло применение манифеста, нужно в metadata выбрать тот же name и namespace, что и у основного манифеста. В valuesContent добавить нужную конфигурацию, которую необходимо переопределить в чарте. В данном случае для controller я выполняю включение service, а в качестве type выбираю NodePort и фиксирую конкретные порты за ним.
Далее на стороне TCP балансировщика нужно настроить прослушивание 80 и 443 порта и форвардинг трафика до IP адресов k8s узлов с портом 32767. На стороне балансировщика необходимо использовать health-чеки, чтобы исключать из пула неработающие узлы.
Работа с Rancher
После настройки Ingress можно перейти в веб-интерфейс Rancher и авторизоваться, используя пользователя admin и пароль из переменной bootstrapPassword, указанной в манифесте.
После авторизации в списке отображается наш кластер, подписанный как local. Теперь есть возможность управлять им и визуализировать происходящее с помощью графического интерфейса. На этом функционал Rancher не заканчивается.
Основная возможность, которую предоставляет Rancher это централизованное управление множеством K8s кластеров. Сюда можно импортировать как существующие, так и использовать сам Rancher для конфигурации новых кластеров. Поддерживается развертывание на bare-metal и VM, так и в облачных провайдерах. В первом случае на узлах будет развернут RKE, а в облаках есть возможность создать K8s на базе managed сервиса. Например EKS в случае с облаком AWS.
Best practice по конфигурации K8s кластеров, предлагаемый самими Rancher: развернуть RKE2 кластер тем способом, который я описал в статье, установить в него Rancher в нескольких репликах, без посторонней нагрузки. Далее с помощью него создавать и управлять downstream кластерами.
Существует также способ при котором Rancher разворачивается в Docker контейнере, но он рекомендуется только для тестовых целей.
Еще из функционала с которыми поставляется Rancher могу отметить следующее:
RBAC с поддержкой сторонних провайдеров авторизации
GitOps контроллер Fleet для организации CI/CD в кластер. Аналог ArgoCD и Flux
Каталог приложений, позволяющий выполнить установку Helm чартов в кластер через GUI
Похоже на данном этапе, единственное для чего мы будем использовать Rancher это графический интерфейс. Локально я предпочитаю Lens IDE, но тут смогу организовывать удобный доступ для команд разработки, не требуя установки ПО и при этом ограничивая полномочия благодаря RBAC.
Rancher как централизованный инструмент для управления всеми существующими кластерами нам не подойдет из-за специфики работы в аутсорс. Мы работаем с разными клиентами, у которых есть свои технические требования по построению инфраструктуры и нельзя всех их завязывать в общий сегмент.
В целом платформа будет удобна для новичков в Kubernetes: тут есть весь функционал из коробки и настраивается через GUI. Для тех кто работает в экосистеме K8s давно, будет удобнее использовать уже сформированный инструментарий и автоматизации, а не осуществлять настройку через GUI и использовать встроенные сервисы вроде Fleet.
Вывод
На этапе конфигурации RKE2 показал себя как отличный инструмент, который позволил быстро и удобно получить отказоустойчивый кластер, готовый к работе. Отдельно отмечу качественную и подробную документацию, позволяющую лучше разобраться в продукте. Помимо конфигурации RKE2, в кластер была установлена платформа Rancher, что позволило расширить функционал, но не являлось обязательным шагом.
Дальнейший опыт с RKE2 будет приобретаться уже при эксплуатации системы: обновления версий, добавления пользовательской нагрузки, траблшутинга возможных проблем.
Буду рад почитать в комментариях про ваш опыт работы с self-hosted Kubernetes и различными инструментами и платформами для его установки.