Открыть список
Как стать автором
Обновить
443.08
Рейтинг
SkillFactory
Школа Computer Science. Скидка 10% по коду HABR

Как я сломал и починил кластер Kubernetes, работающий на Raspberry Pi

SkillFactoryНастройка LinuxDevOpsDIY или Сделай самKubernetes
Перевод
Автор оригинала: Lukasz Raczylo

В преддверии старта нашего курса по DevOps, делимся с вами продолжением статьи Как я собрал домашний кластер Kubernetes на базе Raspberry Pi.

Я доигрался с обновлениями и это привело к катастрофе: все ноды внезапно перестали видеть сетевые интерфейсы, и, сколько я ни бился, оживить кластер у меня не получилось.

Мой домашний кластерёнок вырос в зрелый кластер из шести нод (всё благодаря супруге, которая знала, что мне подарить на день рождения, — естественно, Raspberry Pi, и не один!), и я встал перед выбором — либо ещё раз выполнить собственные инструкции из статьи об установке кластера Kubernetes на Raspberry Pi, либо, применив системную инженерию (DevOps и SRE), полностью автоматизировать процессы переделки кластера и создания системы управления кластером.


Соображения о времени и трудозатратах

На тот момент у меня было два варианта, я обдумывал их на ночь глядя. Оба получались затратными по времени.

Вариант 1

Встать с утра пораньше и повторить все операции вручную в соответствии с инструкциями из моей собственной статьи и, возможно, кое-что в процессе улучшить.

Но этот метод, как я уже неоднократно убеждался, чреват ошибками и ляпами, возникающими в основном из-за неверно вводимых данных или пропуска одного или двух шагов, при этом тратится уйма времени на выяснение того, что же, чёрт побери, могло такое случиться, что эта штука опять не работает.

Затем всё чистится под ноль и начинается снова. Я проделывал это уже не единожды. Но, в конце концов, никому не будет вреда, если попытаться ещё раз.

Вариант 2

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

Конечно, для этого потребуется куда больше времени. Но светят и долгосрочные преимущества: мне больше не надо будет заботиться о самом кластере, я буду знать, что это решение стабильно и можно перенести на него всю свою домашнюю инфраструктуру.

Хроника автоматизации. Начало

Прежде всего я проработал основы и определил ряд принципов, которым следовал в течение всего процесса. Среда у меня получилась довольно необычная, однако с хорошим логическим разделением кода между её частями, так что вам будет несколько проще менять любые части кода или комментировать целые куски файлов, чтобы в файлах отключить те или иные функции.

Принцип 1. Настройка кластера Kubernetes на Raspberry Pi выполняется в три этапа: это настройка карты памяти, настройка нод на системном уровне и развёртывание ресурсов Kubernetes.

Принцип 2. На моём старом Intel NUC работает NFS-сервер, подключённый к хранилищу DROBO. Было бы заманчиво использовать его в качестве постоянного общего хранилища для всех нод.

Принцип 3. Кластер Raspberry Pi работает в моей домашней сети VLAN, поэтому вопросы безопасности меня волнуют не особо. Все службы и ноды должны быть легко доступны без всяких хитростей с именами и паролями.

Итак, держа всё это в голове, я приступил к программированию моего маленького Франкенштейна. Для повторения результатов (то есть для того, чтобы система заработала) вам понадобятся:

  • Mac (для форматирования карты). Если выберу время для установки Linux VM, попытаюсь обновить скрипт обнаружения платформы.

  • Ansible (я использовал версию 2.10.6).

  • Terraform (я использовал версию 0.13.4, но 0.14.8 тоже подойдёт).

  • Make, полезный инструмент, чтобы не колдовать над параметрами.

Кластер rPi. Первый шаг

В качестве жёсткого диска Raspberry Pi использует карту памяти. Возможно, это не вполне оптимальное и не самое быстрое решение для чтения и записи, но его должно быть достаточно для игр и любительских проектов.

Что происходит на первом шаге?

  • Форматируется карта памяти.

  • Карта памяти делится на два раздела: 1 ГБ плюс то, что останется.

  • Образ Alpine Linux копируется на карту памяти.

  • Создаётся системный оверлей.

Системный оверлей настраивает eth0 на "неразборчивый" (promisc) режим, это нужно для работы MetalLB, и разрешает подключение SSH к нодам Raspberry Pi без пароля.

Важно: проверьте источник 001-prepare-card.sh и убедитесь, что /dev/disk5 — это именно вставленная карта памяти, иначе можно потерять данные.

Результат: подготовка шести карт памяти займёт около одной минуты.

Кластер rPi. Второй шаг

Начинается самое интересное. Итак, вы вставили в Raspberry Pi карты памяти, подсоединили все кабели (сетевые и питания) и загрузили систему. Теперь нужно получить IP-адреса устройств. Это можно сделать, либо подключив экран к каждому из них и запустив команду ifconfig eth0, либо зайдя в маршрутизатор и проверив информацию на нём. Введите в файл pi-hosts.txt соответствующие значения.

[masters]
pi0 ansible_host=192.168.50.132 # Pi0

[workers]
pi1 ansible_host=192.168.50.135 # Pi1
pi3 ansible_host=192.168.50.60  # Pi3
pi4 ansible_host=192.168.50.36  # Pi4
pi2 ansible_host=192.168.50.85  # Pi2
pi5 ansible_host=192.168.50.230 # Pi5

Важно: для работы некоторых программ может потребоваться имя узла pi0.

Добавьте в файл ~/.ssh/config следующую строку, она даст root-доступ для всех нод с именами pi*.

Host pi?
  User root
  Hostname %h.local

Теперь наши микровычислители (вот видите, насколько я стар!) готовы, и нам нужно подготовить их к запуску Ansible. Это можно легко сделать с помощью скрипта 001-prepare-ansible.sh, который подключиться по ssh к каждому определённому в файле pi-hosts серверу, на каждом сервере настроит chrony для NTP и установит интерпретатор Python.

Важно: возможно, потребуется открыть файл rpi.yaml и изменить раздел vars в соответствии с вашими предпочтениями. Я поступил именно так.

После этого шага запускаем на Ansible команду ansible-playbook rpi.yaml -f 10, которая выполнит следующие действия:

ОБЩИЕ:

  • Установит необходимые пакеты.

  • Разобьёт на разделы и отформатирует карту памяти RPI.

  • Настроит параметры "большого" раздела как системного диска.

  • Добавит записи в файл fstab.

  • Подтвердит изменения.

  • Перезапустит Pi, чтобы тот загрузился с "постоянного" раздела.

KUBEMASTER:

  • Настроит мастер-ноду с помощью kubeadm.

  • Сохранит токены локально (в файле static/token_file).

  • Определит на Pi пользователя с правами root с доступом к kubectl.

  • Сохранит настройки Kubernetes локально (в файле static/kubectl.conf).

KUBEWORKER:

  • Скопирует токены на рабочие ноды.

  • После этого через файл токенов рабочие ноды будут присоединены к мастер-ноде.

  • Скопирует kubectl.conf на root-пользователей рабочих нод.

БАЗОВЫЕ:

  • Снимет отметку с мастер-ноды, что позволит ей принимать рабочие нагрузки.

  • Установит на ноды py3-pip, PyYaml и Helm.

Если вы дошли до этого места, мои поздравления! Вы только что создали базовый кластер Kubernetes, который делать пока ничего не умеет, но готов научиться, если уделить ему немного внимания. Всё оказалось довольно элементарно — нужно просто запустить несколько скриптов и дождаться их завершения. Я считаю, что это определённо лучшее решение, чем возиться руками.

Важно: скрипты можно запускать сколько угодно раз. Переформатировать карты памяти после каждого раза не нужно.

Результат: подготовка шести нод с базовой установкой Kubernetes занимает пару минут, в зависимости от скорости подключения к Интернету.

Кластер rPi. Третий шаг

После успешного выполнения двух предыдущих шагов кластер Pi готов к первым развёртываниям. Настройка осуществляется в несколько шагов, и, конечно, эти шаги также можно автоматизировать, об этом позаботится Terraform.

Вначале посмотрим на конфигурацию.

# Variables used for barebone kubernetes setup
network_subnet    = "192.168.50"

net_hosts = {
  adguard = "240"
  adguard_catchall = "249"
  traefik = "234"
  torrent_rpc = "245"
}

nfs_storage = {
  general = "/media/nfs"
  torrent = "/mnt/drobo-storage/docker-volumes/torrent"
  adguard = "/mnt/drobo-storage/docker-volumes/adguard"
}

# ENV variable: TRAEFIK_API_KEY sets traefik_api_key
# ENV variable: GH_USER, GH_PAT for authentication with private containers

Кластер запускается в сети по адресу 192.168.50.0/24, но по умолчанию MetalLB будет использовать "конец" пула сетевых адресов с адресами 200-250. Поскольку у меня есть домашний торрент-сервер и DNS от Adguard, мне нужно задать для них конкретные адреса. Также мне нужен обслуживающий дашборды и прочие инструменты балансировщик нагрузки Traefik.

Важные замечания:

Значения nfs_*_path должны быть совместимы с настройками, заданными на втором шаге.

Убедитесь, что в конфигурационный файл Kubernetes ~/.kube/config is добавлены данные из файла static/kubernetes.conf. В качестве контекстного имени я использую home-k8s.

Что делает Terraform?

Устанавливает flannel, а также патч конфигурационных параметров для host-gw; устанавливает metalLB и задает сетевые параметры var.network_subnet 200–250.

Устанавливает прокси Traefik и открывает к нему доступ в домашней сети через балансировщик нагрузки metalLB. Доступ к самому дашборду Traefik осуществляется по traefik.local.

Дашборд Traefik запускается на кластере Pi.
Дашборд Traefik запускается на кластере Pi.

Устанавливает DNS-службу Adguard с запросами к хранилищам данных (persistent volumes claims) с использованием NFS; открывает доступ к дашборду (adguard.local) через Traefik и к самой службе через выделенный в домашней сети IP-адрес.

Adguard Home запускается на кластере Pi.
Adguard Home запускается на кластере Pi.

Устанавливает и развёртывает на всех нодах стек мониторинга Prometheus и Grafana. Вносит изменения в DaemonSet Prometheus, устраняя необходимость в монтировании томов. Также через Traefik определяет Grafana как grafana.local. Имя и пароль пользователя Grafana по умолчанию — admin:admin. В Grafana уже есть предустановленный плагин devopsprodigy-kubegraf-app. Я считаю его лучшим для мониторинга кластеров.

На кластере Pi запускается дашборд Grafana.
На кластере Pi запускается дашборд Grafana.

Устанавливает дашборд Kubernetes и через Traefik определяет его как k8s.local.

Дашборд Kubernetes запускается на кластере Pi.
Дашборд Kubernetes запускается на кластере Pi.

Устанавливает и развёртывает торрент-сервер (rTorrent) с веб-интерфейсом Flood. Дашборд отображается как torrent.local. Для хранения данных (в том числе конфигурационных) дашборд использует множество точек монтирования. Причина, по которой значение репликации должно быть установлено в 1, объясняется просто. У rTorrent есть проблемы с файлами блокировки, и, поскольку эта программа использует разделяемый каталог, она просто не запустится, если будет обнаружен файл блокировки. У меня rTorrent настроен на прослушивание на порте 23340.

Поскольку Raspberry Pi запускается с карты памяти, эта карта из-за постоянных операций чтения-записи со временем может выйти из строя. Поэтому я решил регулярно делать резервные копии etcd на NFS. Программа резервного копирования запускается раз в сутки с параметрами, устанавливаемыми Terraform. Каждая резервная копия “весит” около 32 мегабайт.

Запуск Terraform

Чтобы несколько упростить задачу, я создал Makefile, который может оказаться полезным при настройке. Возможно, понадобится установить следующие переменные окружения:

TRAEFIK_API_KEY // Traefik API key
GH_USER // Github user
GH_PAT // Github Personal Access Token

Важно: учётные данные Github сейчас не задействованы, но в ближайшее время я планирую добавить функцию аутентификации для извлечения частных образов из GHCR.

ADDITIONAL_ARGS=-var 'traefik_api_key=$(TRAEFIK_API_KEY)' -var "github_user=$(GH_USER)" -var "github_pat=$(GH_TOKEN)"

apply:
	cd infrastructure; terraform apply $(ADDITIONAL_ARGS) -auto-approve -var-file ../variables.tfvars

plan:
	cd infrastructure; terraform plan $(ADDITIONAL_ARGS) -var-file ../variables.tfvars

destroy:
	cd infrastructure; terraform destroy $(ADDITIONAL_ARGS) -var-file ../variables.tfvars

destroy-target:
	cd infrastructure; terraform destroy $(ADDITIONAL_ARGS) -var-file ../variables.tfvars -target $(TARGET)

refresh:
	cd infrastructure; terraform refresh $(ADDITIONAL_ARGS) -var-file ../variables.tfvars

init:
	cd infrastructure; rm -fr .terraform; terraform init

import:
	cd infrastructure; terraform import $(ADDITIONAL_ARGS) -var-file ../variables.tfvars $(ARGS)

lint:
	terraform fmt -recursive infrastructure/

Заключительные замечания

Полный код можно взять на GitHub. Им можно свободно пользоваться и менять как угодно (как обычно, ваши комментарии и замечания горячо приветствуются). Я также опубликовал переделанные образы Docker (rTorrent и Flood) с multiarch-архитектурой, поддерживающей процессоры ARM64. Я довольно часто вычищаю весь кластер и делаю сборку с нуля, используя упомянутый репозиторий, а по мере появления новых функций я буду вносить в него соответствующие изменения.

Автоматизация экономит время и нервы и — надо признать! — хорошо выполняет свою работу. Именно поэтому DevOps-инженеров так ценят на рынке (зарплаты с пятью нулями в месяц — уже обычное дело). Если хотите так же — приходите учиться, помимо современных учебных программ, у нас есть менторы из ведущих компаний Рунета, которые поделятся с вами своим практическим опытом.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Теги:skillfactorykuberneteslinuxсделай самdevopssshansibleавтоматизацияmakefileterraform
Хабы: SkillFactory Настройка Linux DevOps DIY или Сделай сам Kubernetes
Всего голосов 9: ↑7 и ↓2 +5
Просмотры7.1K

Похожие публикации

Лучшие публикации за сутки