DIY: Создай своё облако с Kubernetes (часть 1)
Автор: Andrei Kvapil (Ænix)
В Ænix мы испытываем глубокую симпатию к Kubernetes и мечтаем, что все современные технологии вскоре начнут использовать его замечательные паттерны.
Задумывались ли вы когда-нибудь о создании собственного облака? Уверен, что да. Но возможно ли сделать это, используя только современные технологии и подходы, не покидая уютную экосистему Kubernetes? Наш опыт разработки Cozystack потребовал от нас глубоко в это погрузиться.
Вы можете возразить, что Kubernetes не предназначен для этой цели, и почему бы просто не использовать OpenStack для серверов bare metal и не запускать Kubernetes внутри него, как задумано. Но, поступая так, вы бы просто переложили ответственность со своих плеч на плечи администраторов OpenStack. Это добавило бы в вашу экосистему как минимум ещё одну огромную и сложную систему.
Зачем усложнять? — ведь на данный момент в Kubernetes уже есть всё необходимое для запуска арендаторских кластеров Kubernetes.
Я хочу поделиться с вами нашим опытом разработки облачной платформы на базе Kubernetes, отметив проекты с открытым исходным кодом, которые мы используем сами и которые, по нашему мнению, заслуживают вашего внимания.
В этой серии статей я расскажу вам нашу историю о том, как мы готовим управляемый Kubernetes из bare metal, используя только технологии с открытым исходным кодом. Начиная с базового уровня подготовки дата-центра, запуска виртуальных машин, изоляции сетей, настройки отказоустойчивого хранилища и заканчивая провижинингом полнофункциональных кластеров Kubernetes с динамическим выделением томов, балансировщиками нагрузки и автомасштабированием.
Этой статьёй я начинаю серию, состоящую из нескольких частей:
- Часть 1: Подготовка фундамента для вашего облака. Проблемы, с которыми сталкиваешься при подготовке и эксплуатации Kubernetes на bare metal, и готовый рецепт для провижининга инфраструктуры.
- Часть 2: Сеть, хранилище и виртуализация. Как превратить Kubernetes в инструмент для запуска виртуальных машин и что для этого нужно.
- Часть 3: Cluster API и как начать провижинить кластеры Kubernetes нажатием кнопки. Как работает автомасштабирование, динамическое выделение томов и балансировщики нагрузки.
Я постараюсь описать различные технологии максимально независимо, но в то же время поделюсь нашим опытом и тем, почему мы пришли к тому или иному решению.
Для начала давайте разберёмся с главным преимуществом Kubernetes и с тем, как он изменил подход к использованию облачных ресурсов.
Важно понимать, что использование Kubernetes в облаке и на bare metal различается.
Kubernetes в облаке
Когда вы эксплуатируете Kubernetes в облаке, вам не нужно беспокоиться о постоянных томах, облачных балансировщиках нагрузки или процессе провижининга узлов. Всё это берёт на себя ваш облачный провайдер, который принимает ваши запросы в виде объектов Kubernetes. Другими словами, серверная сторона полностью скрыта от вас, и вам, по сути, не хочется знать, как именно её реализует облачный провайдер, поскольку это не входит в вашу зону ответственности.
A diagram showing cloud Kubernetes, with load balancing and storage done outside the cluster
Kubernetes предлагает удобные абстракции, которые работают одинаково везде, позволяя вам развернуть ваше приложение на любом Kubernetes в любом облаке.
В облаке у вас очень часто есть несколько отдельных сущностей: control plane Kubernetes, виртуальные машины, постоянные тома и балансировщики нагрузки как самостоятельные сущности. Используя эти сущности, вы можете создавать в высшей степени динамичные среды.
Благодаря Kubernetes виртуальные машины теперь рассматриваются лишь как вспомогательная сущность для утилизации облачных ресурсов. Вы больше не храните данные внутри виртуальных машин. Вы можете в любой момент удалить все свои виртуальные машины и пересоздать их, не сломав своё приложение. Control plane Kubernetes продолжит хранить информацию о том, что должно работать в вашем кластере. Балансировщик нагрузки будет продолжать направлять трафик к вашей рабочей нагрузке, просто меняя endpoint, чтобы отправлять трафик на новый узел. А ваши данные будут надёжно храниться во внешних постоянных томах, предоставляемых облаком.
Этот подход является основополагающим при использовании Kubernetes в облаках. Причина этого вполне очевидна: чем проще система, тем она стабильнее, и именно за эту простоту вы и покупаете Kubernetes в облаке.
Kubernetes на bare metal
Использовать Kubernetes в облаках действительно просто и удобно, чего нельзя сказать об установках на bare metal. В мире bare metal Kubernetes, напротив, становится невыносимо сложным. Во-первых, потому что вся сеть, бэкенд-хранилище, облачные балансировщики и т. д. обычно запускаются не снаружи, а внутри вашего кластера. В результате такую систему гораздо сложнее обновлять и обслуживать.
A diagram showing bare metal Kubernetes, with load balancing and storage done inside the cluster
Судите сами: в облаке, чтобы обновить узел, вы обычно удаляете виртуальную машину
(или даже используете kubectl delete node) и позволяете вашим инструментам управления узлами
создать новый на основе неизменяемого образа. Новый узел присоединится к кластеру и «просто
заработает» как узел, следуя очень простому и широко используемому паттерну в мире Kubernetes.
Многие кластеры заказывают новые виртуальные машины каждые несколько минут просто потому, что
могут использовать более дешёвые spot-инстансы. Однако, когда у вас есть физический сервер, вы
не можете просто удалить и пересоздать его — во-первых, потому что на нём часто работают какие-то
кластерные сервисы, хранятся данные, и процесс его обновления значительно сложнее.
Существуют разные подходы к решению этой проблемы — от обновлений на месте (in-place), как это делают kubeadm, kubespray и k3s, до полной автоматизации провижининга физических узлов через Cluster API и Metal3.
Мне нравится гибридный подход, предлагаемый Talos Linux, где вся ваша система описывается в одном конфигурационном файле. Большинство параметров этого файла можно применить без перезагрузки или пересоздания узла, включая версию компонентов control-plane Kubernetes. При этом он сохраняет максимальную декларативную природу Kubernetes. Такой подход минимизирует лишнее воздействие на кластерные сервисы при обновлении узлов bare metal. В большинстве случаев при минорных обновлениях вам не придётся мигрировать ваши виртуальные машины и пересобирать файловую систему кластера.
Подготовка основы для вашего будущего облака
Итак, предположим, вы решили построить собственное облако. Чтобы с чего-то начать, вам нужен базовый слой. Вам нужно думать не только о том, как вы будете устанавливать Kubernetes на свои серверы, но и о том, как вы будете его обновлять и обслуживать. Учтите тот факт, что вам придётся думать о таких вещах, как обновление ядра, установка необходимых модулей, а также пакетов и патчей безопасности. Теперь вам приходится думать о гораздо большем, о чём не нужно беспокоиться при использовании готового Kubernetes в облаке.
Конечно, вы можете использовать стандартные дистрибутивы вроде Ubuntu или Debian, а можете рассмотреть специализированные, такие как Flatcar Container Linux, Fedora Core и Talos Linux. У каждого есть свои преимущества и недостатки.
А что насчёт нас? В Ænix мы используем довольно много специфичных модулей ядра, таких как ZFS, DRBD и OpenvSwitch, поэтому мы решили пойти по пути заблаговременного формирования системного образа со всеми необходимыми модулями. В этом случае Talos Linux оказался для нас наиболее удобным. Например, такого конфига достаточно, чтобы собрать системный образ со всеми необходимыми модулями ядра:
arch: amd64
platform: metal
secureboot: false
version: v1.6.4
input:
kernel:
path: /usr/install/amd64/vmlinuz
initramfs:
path: /usr/install/amd64/initramfs.xz
baseInstaller:
imageRef: ghcr.io/siderolabs/installer:v1.6.4
systemExtensions:
- imageRef: ghcr.io/siderolabs/amd-ucode:20240115
- imageRef: ghcr.io/siderolabs/amdgpu-firmware:20240115
- imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20240115
- imageRef: ghcr.io/siderolabs/i915-ucode:20240115
- imageRef: ghcr.io/siderolabs/intel-ice-firmware:20240115
- imageRef: ghcr.io/siderolabs/intel-ucode:20231114
- imageRef: ghcr.io/siderolabs/qlogic-firmware:20240115
- imageRef: ghcr.io/siderolabs/drbd:9.2.6-v1.6.4
- imageRef: ghcr.io/siderolabs/zfs:2.1.14-v1.6.4
output:
kind: installer
outFormat: raw
Затем мы используем инструмент командной строки docker, чтобы собрать образ ОС:
cat config.yaml | docker run --rm -i -v /dev:/dev --privileged "ghcr.io/siderolabs/imager:v1.6.4" -
И в результате мы получаем образ Docker-контейнера со всем необходимым, который можем использовать для установки Talos Linux на наши серверы. Вы можете сделать то же самое; этот образ будет содержать все необходимые прошивки и модули ядра.
Но возникает вопрос: как доставить свежесформированный образ на ваши узлы?
Я довольно давно обдумывал идею загрузки по PXE. Например, проект Kubefarm, о котором я писал статью два года назад, был полностью построен на этом подходе. Но, к сожалению, он помогает вам развернуть самый первый родительский кластер, который будет держать остальные. Так что теперь у вас есть готовое решение, которое поможет вам сделать это точно так же, используя подход PXE.
По сути, всё, что вам нужно, — это запустить временные серверы DHCP и PXE внутри контейнеров. Затем ваши узлы загрузятся с вашего образа, и вы сможете использовать простой скрипт в стиле Debian, чтобы помочь себе выполнить bootstrap ваших узлов.
Исходный код этого скрипта talos-bootstrap
доступен на GitHub.
Этот скрипт позволяет развернуть Kubernetes на bare metal за пять минут и получить kubeconfig для доступа к нему. Однако впереди ещё много нерешённых вопросов.
Доставка системных компонентов
На этом этапе у вас уже есть кластер Kubernetes, способный запускать различные рабочие нагрузки. Однако он ещё не полностью функционален. Другими словами, вам нужно настроить сеть и хранилище, а также установить необходимые расширения кластера, такие как KubeVirt для запуска виртуальных машин, а также стек мониторинга и другие общесистемные компоненты.
Традиционно это решается установкой Helm-чартов в ваш кластер. Вы можете сделать это, запуская
команды helm install локально, но такой подход становится неудобным, когда вы хотите отслеживать
обновления, а также если у вас несколько кластеров и вы хотите поддерживать их единообразными.
На самом деле есть множество способов сделать это декларативно. Для решения этой задачи я рекомендую
использовать лучшие практики GitOps. Я имею в виду такие инструменты, как ArgoCD и FluxCD.
В то время как ArgoCD более удобен для задач разработки благодаря своему графическому интерфейсу и центральному control plane, FluxCD, с другой стороны, лучше подходит для создания дистрибутивов Kubernetes. С FluxCD вы можете указать, какие чарты и с какими параметрами должны запускаться, и описать зависимости. А дальше FluxCD позаботится обо всём за вас.
Предлагается выполнить однократную установку FluxCD в вашем недавно созданном кластере и предоставить ему конфигурацию. Это установит всё необходимое, приведя кластер к ожидаемому состоянию.
Выполнив единственную установку FluxCD в вашем новоиспечённом кластере и соответствующим образом настроив его, вы позволяете ему автоматически развёртывать всё самое необходимое. Это позволит вашему кластеру самостоятельно обновиться до желаемого состояния. Например, после установки нашей платформы вы увидите следующие преднастроенные Helm-чарты с системными компонентами:
NAMESPACE NAME AGE READY STATUS
cozy-cert-manager cert-manager 4m1s True Release reconciliation succeeded
cozy-cert-manager cert-manager-issuers 4m1s True Release reconciliation succeeded
cozy-cilium cilium 4m1s True Release reconciliation succeeded
cozy-cluster-api capi-operator 4m1s True Release reconciliation succeeded
cozy-cluster-api capi-providers 4m1s True Release reconciliation succeeded
cozy-dashboard dashboard 4m1s True Release reconciliation succeeded
cozy-fluxcd cozy-fluxcd 4m1s True Release reconciliation succeeded
cozy-grafana-operator grafana-operator 4m1s True Release reconciliation succeeded
cozy-kamaji kamaji 4m1s True Release reconciliation succeeded
cozy-kubeovn kubeovn 4m1s True Release reconciliation succeeded
cozy-kubevirt-cdi kubevirt-cdi 4m1s True Release reconciliation succeeded
cozy-kubevirt-cdi kubevirt-cdi-operator 4m1s True Release reconciliation succeeded
cozy-kubevirt kubevirt 4m1s True Release reconciliation succeeded
cozy-kubevirt kubevirt-operator 4m1s True Release reconciliation succeeded
cozy-linstor linstor 4m1s True Release reconciliation succeeded
cozy-linstor piraeus-operator 4m1s True Release reconciliation succeeded
cozy-mariadb-operator mariadb-operator 4m1s True Release reconciliation succeeded
cozy-metallb metallb 4m1s True Release reconciliation succeeded
cozy-monitoring monitoring 4m1s True Release reconciliation succeeded
cozy-postgres-operator postgres-operator 4m1s True Release reconciliation succeeded
cozy-rabbitmq-operator rabbitmq-operator 4m1s True Release reconciliation succeeded
cozy-redis-operator redis-operator 4m1s True Release reconciliation succeeded
cozy-telepresence telepresence 4m1s True Release reconciliation succeeded
cozy-victoria-metrics-operator victoria-metrics-operator 4m1s True Release reconciliation succeeded
Заключение
В результате вы получаете легко воспроизводимое окружение, которое можете предоставить кому угодно, зная, что оно работает именно так, как задумано. Собственно, именно это и делает проект Cozystack, который вы можете совершенно бесплатно попробовать сами.
В следующих статьях я расскажу, как подготовить Kubernetes для запуска виртуальных машин и как запускать кластеры Kubernetes нажатием кнопки. Оставайтесь с нами, будет интересно!
Впервые опубликовано на https://kubernetes.io 5 апреля 2024 года.