Имея за плечами опыт работы с 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. Я рассмотрел наиболее популярные решения, используемые для продакшена.

  1. Kubeadm – инструмент предлагаемый в документации K8s. Он решает только часть задач по настройке кластера. Сюда не входит предварительная настройка самих хостов, установка системных аддонов. Разработчиками K8s подразумевается, что для этого вы напишете свой верхнеуровневый тулинг, который в том числе будет вызывать kubeadm для деплоймента кластера. Как правило, поверх kubeadm делают собственный Ansible Playbook или скрипты.

  2. Kubespray – готовая Ansible автоматизация, которая позволяет выполнить полный цикл настройки кластера. Под капотом вызывается kubeadm, и в том числе решается вопрос конфигурации узлов и установки всех необходимых для работы компонентов. Я слышал от коллег, что тут могут быть сложности при обновлениях версий, особенно если делались кастомизации плейбука.

  3. Rancher (RKE) – продукт для развертывания и управления Kubernetes кластерами, принадлежащий компании SUSE. Позволяет быстро подготовить кластер со всеми необходимыми компонентами. Бесплатная версия обладает полным функционалом, есть платная поддержка.

  4. Openshift – коммерческая платформа от Red Hat, сейчас не актуальна для рынка РФ. Есть бесплатный аналог – OKD, но не похоже, что он пользуется популярностью в индустрии.

  5. 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 и различными инструментами и платформами для его установки.

Комментарии (9)


  1. Dm4k
    31.08.2024 06:39

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


    1. ronrz Автор
      31.08.2024 06:39

      Дайте знать, если напишете статью по этой теме. Если такой информации нет в документации и во всех статьях по развертыванию, то должен выйти максимально полезный материал.


      1. Dm4k
        31.08.2024 06:39

        Я если что не сказал что статья плохая, хорошая статья (не только пересказ раздела установки из документации).

        Но информации о преднастройки, по моему мнению, не хватает (как ее не хватает и по указанной в соответствующем разделе ссылке).


  1. Demosfen
    31.08.2024 06:39
    +1

    Через DNS так себе вариант. А если один узел лежит?

    Чтобы вручную не париться есть неплохая готовая роль под ансиблу: https://github.com/lablabs/ansible-role-rke2

    Там и нормальные варианты HA через keepalived или kubevip.

    В принципе почти без допилинга годится.

    У Rancher, кстати есть ещё особенность - если кластер развернул не он, то функционал по управлению не совсем весь доступен, например управление бакапами.


    1. ronrz Автор
      31.08.2024 06:39
      +1

      Поправил финальный абзац в главе про балансировку внешнего трафика: написал там не то, что хотел. Безусловно, в пуле адресов нужно указывать IP узлов и использовать health чеки, а не ту DNS запись.

      А вариант с DNS в качестве fixed registration address, кажется, не так критичен, учитывая, что узел обращается к нему только разово, когда мы делаем его join в кластер. Соответственно, по round-robin он доберется до рабочего узла и подключится к кластеру. Но соглашусь, что даже тут лучше рассмотреть другой вариант для HA.

      Спасибо за комментарий, роль для Ansible попробую. Круто, что ей же можно сразу задеплоить кастомные манифесты на этапе установки RKE.


  1. vasyakrg
    31.08.2024 06:39
    +1

    Rke2 и ансибл - это конечно хорошо. Но работает так себе, порой через раз. Нормальной идемпотентностью там и не пахнет.

    На rke я могу сколь угодно долго крутить версию куба туда сюда хоть на 5 релизов, на rke2 откатываться крайне сложно (понятно, что кейс редкий, но все же) да и обновлять его весело, одна операция systemctl restart rke2-server чего стоит.

    И не очень понимаю доводы против rke, когда зависимость от докера вдруг стала проблемой ?

    Отсаживаешь cp+etcd компоненты на отдельные три легкие ноды, воркер нагрузку на остальные жирные ноды. И живут себе такие кластера долго и счастливо.


    1. Dm4k
      31.08.2024 06:39

      Ага. Очень грущу от того что они во второй версии отказались от механизма управления кластером и конфигурацией из одной точки. Отличное было решение для gitops подхода.


    1. ronrz Автор
      31.08.2024 06:39

      @vasyakrg

      И не очень понимаю доводы против rke, когда зависимость от докера вдруг стала проблемой ?

      Видимо в тот момент, когда K8s перевел Docker в статус deprecated и он перестал быть индустриальным стандартом как container runtime. По этой же причине, по заявлению SUSE, разработка и поддержка RKE первой версии завершится в 2025 году и всем придется мигрировать на RKE2 для дальнейшей поддержки и обновлений.

      на rke2 откатываться крайне сложно (понятно, что кейс редкий, но все же) да и обновлять его весело, одна операция systemctl restart rke2-server чего стоит.

      А вот это печально. Надо попробовать в ближайшее время. А если в этом плане RKE2 сравнивать с другими инструментами, например с kubeadm?


      1. vasyakrg
        31.08.2024 06:39

        сравнивать rke2 можно разве что с kubespray - и там и там ансиблом хоть как-то можно гитопсить. все что делается руками и из консоли - это какие-то хоум-лабы (hard way k8s если хотите) и не имеет отношения к продуктовой работе, где 30-50-100 кластеров (и разрабам постоянно надо новые\временные катать).

        я сейчас начал присматриваться к k0s (ноль) и talos - подобным решениям.

        на счет deprecated - с одной стороны, да, грустно. с другой - там все еще живые обновления и куб v1.30.3, а бежать вперед паровоза пока не охота.