DIY: Створіть власну хмару з Kubernetes (Частина 1)

У Ænix ми дуже любимо Kubernetes і мріємо, що всі сучасні технології незабаром почнуть використовувати його чудові патерни.

Чи думали ви коли-небудь про створення власної хмари? Я впевнений, що так. Але чи можливо це зробити, використовуючи лише сучасні технології та підходи, не виходячи за межі затишної екосистеми Kubernetes? Наш досвід у розробці Cozystack змусив нас глибоко заглибитись у це питання.

Ви можете стверджувати, що Kubernetes не призначений для цього, і чому б просто не використовувати OpenStack на "звичайних" серверах та запускати Kubernetes всередині нього, як це задумано. Але в цьому випадку ви просто передаєте відповідальність зі своїх рук у руки адміністраторів OpenStack. Це додасть ще одну велику та складну систему до вашої екосистеми.

Навіщо ускладнювати речі? — адже Kubernetes вже має все необхідне для запуску кластерів орендаря Kubernetes на цьому етапі.

Я хочу поділитися з вами нашим досвідом у розробці хмарної платформи на основі Kubernetes, виділивши відкриті проєкти, які ми використовуємо самі та вважаємо, що вони заслуговують вашої уваги.

У цій серії статей я розповім вам нашу історію про те, як ми готуємо керовані кластери Kubernetes на "голому" залізі, використовуючи лише відкриті технології. Починаючи з базового рівня підготовки дата-центру, запуску віртуальних машин, ізоляції мереж, налаштування відмовостійкого сховища до забезпечення повнофункціональних кластерів Kubernetes з динамічним розподілом томів, балансувальниками навантаження та автомасштабуванням.

Цією статтею я починаю серію, яка складатиметься з кількох частин:

  • Частина 1: Підготовка основи для вашої хмари. Виклики, з якими стикаються під час підготовки та експлуатації Kubernetes на "голому" залізі, та готовий рецепт для забезпечення інфраструктури.
  • Частина 2: Мережі, сховища та віртуалізація. Як перетворити Kubernetes на інструмент для запуску віртуальних машин і що для цього потрібно.
  • Частина 3: Cluster API і як почати розгортання кластерів Kubernetes натисканням однієї кнопки. Як працює автомасштабування, динамічний розподіл томів і балансувальники навантаження.

Я спробую описати різні технології якнайбільше незалежно, але водночас поділюся нашим досвідом і поясню, чому ми прийшли до того чи іншого рішення.

Почнемо з розуміння основної переваги Kubernetes та того, як він змінив підхід до використання хмарних ресурсів.

Важливо розуміти, що використання Kubernetes у хмарі та на "голому" залізі відрізняється.

Kubernetes у хмарі

Коли ви оперуєте Kubernetes у хмарі, ви не хвилюєтеся про постійні томи, хмарні балансувальники навантаження або процес виділення вузлів. Усе це робить ваш хмарний провайдер, який приймає ваші запити у вигляді обʼєктів Kubernetes. Іншими словами, серверна сторона повністю прихована від вас, і вам не дуже цікаво знати, як саме хмарний провайдер реалізує це, оскільки це не ваша зона відповідальності.

Діаграма, що показує Kubernetes у хмарі, де балансування навантаження та зберігання виконуються поза кластером

Діаграма, що показує Kubernetes у хмарі, де балансування навантаження та зберігання виконуються поза кластером

Kubernetes пропонує зручні абстракції, які працюють однаково всюди, дозволяючи розгортати ваші застосунки на будь-якому Kubernetes у будь-якій хмарі.

У хмарі дуже часто є кілька окремих сутностей: панель управління Kubernetes, віртуальні машини, постійні томи та балансувальники навантаження як окремі сутності. Використовуючи ці сутності, ви можете створювати дуже динамічні середовища.

Завдяки Kubernetes, віртуальні машини тепер розглядаються лише як інструмент для використання хмарних ресурсів. Ви більше не зберігаєте дані всередині віртуальних машин. Ви можете видалити всі ваші віртуальні машини в будь-який момент і відтворити їх без порушення роботи вашого застосунку. панель управління Kubernetes продовжуватиме зберігати інформацію про те, що має працювати у вашому кластері. Балансувальник навантаження продовжить направляти трафік до вашого робочого навантаження, просто змінюючи точку доступу для передачі трафіку на новий вузол. А ваші дані будуть безпечно зберігатися у зовнішніх постійних томах, наданих хмарою.

Такий підхід є базовим при використанні Kubernetes у хмарах. Причина цього досить очевидна: чим простіша система, тим вона стабільніша, і саме за цю простоту ви купуєте Kubernetes у хмарі.

Kubernetes на "голому" залізі

Використання Kubernetes у хмарах дійсно просте та зручне, чого не можна сказати про установки на "голому" залізі. У світі "голого" заліза Kubernetes, навпаки, стає нестерпно складним. По-перше, тому що вся мережа, сховище даних, балансувальники навантаження тощо зазвичай працюють не зовні, а всередині вашого кластера. У результаті така система набагато складніша для оновлення та підтримки.

Діаграма, що показує Kubernetes на "голому" залізі, де балансування навантаження та зберігання виконуються всередині кластера

Діаграма, що показує Kubernetes на "голому" залізі, де балансування навантаження та зберігання виконуються всередині кластера

Судіть самі: у хмарі, щоб оновити вузол, ви зазвичай видаляєте віртуальну машину (або навіть використовуєте команду kubectl delete node) і дозволяєте вашому інструменту керування вузлами створити новий на основі незмінного образу. Новий вузол приєднається до кластера і "просто працюватиме" як вузол; дотримуючись дуже простого та поширеного шаблону у світі Kubernetes. Багато кластерів замовляють нові віртуальні машини кожні кілька хвилин, просто тому, що вони можуть використовувати дешевші spot-екземпляри. Однак коли у вас є фізичний сервер, ви не можете просто видалити та відтворити його, по-перше, тому що він часто запускає деякі кластерні служби, зберігає дані, і процес його оновлення значно складніший.

Існують різні підходи до розвʼязання цієї проблеми, починаючи від оновлень на місці, як це роблять kubeadm, kubespray та k3s, до повної автоматизації забезпечення фізичних вузлів за допомогою Cluster API та Metal3.

Мені подобається гібридний підхід, який пропонує Talos Linux, де вся ваша система описується в одному конфігураційному файлі. Більшість параметрів цього файлу можна застосувати без перезавантаження або перестворення вузла, включаючи версію компонентів панелі управління Kubernetes. При цьому зберігається максимальна декларативність Kubernetes. Такий підхід мінімізує непотрібний вплив на кластерні сервіси при оновленні «голих» вузлів. У більшості випадків вам не потрібно буде мігрувати віртуальні машини та перебудовувати файлову систему кластера при незначних оновленнях.

Підготовка бази для майбутньої хмари

Отже, ви вирішили створити власну хмару. Щоб почати, потрібно створити базовий шар. Вам слід подумати не лише про те, як ви встановите 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, щоб допомогти вам ініціалізувати вузли.

asciicast

Вихідний код для цього сценарію talos-bootstrap доступний на GitHub.

Цей сценарій дозволяє розгорнути Kubernetes на "голому" залізі за п’ять хвилин і отримати kubeconfig для доступу до нього. Однак попереду ще багато невирішених питань.

Доставка системних компонентів

На цьому етапі у вас вже є кластер Kubernetes, здатний запускати різні робочі навантаження. Однак він ще не повністю функціональний. Іншими словами, вам потрібно налаштувати мережу та зберігання, а також встановити необхідні розширення кластера, такі як KubeVirt для запуску віртуальних машин, а також стек моніторингу та інші системні компоненти.

Традиційно це вирішується шляхом встановлення Helm чартів у ваш кластер. Ви можете зробити це, виконуючи команди helm install локально, але цей підхід стає незручним, коли ви хочете відстежувати оновлення, і якщо у вас кілька кластерів і ви хочете, щоб вони залишалися однорідними. Насправді існує багато способів зробити це декларативно. Для подолання цієї задачі я рекомендую використовувати найкращі практики GitOps. Я маю на увазі інструменти, такі як ArgoCD та FluxCD.

Хоча ArgoCD зручніший для розробки завдяки графічному інтерфейсу та централізованій панелі управління, 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 натисканням кнопки.

Залишайтеся з нами, буде цікаво!