Здесь есть поклонники Kubernetes? Я уже довольно давно пользуюсь им как на работе, так и в других местах, где занимаюсь частными проектами, но иногда мне нужно место, где можно быстро и без особых затрат разработать и протестировать новые функции или просто, как говорят, "поиграть с программой", скопировать данные в резервное хранилище, обменяться файлами или сделать что-либо подобное.
Я всё подсчитал и понял, что общая стоимость кластера получается ниже, чем стоимость облачных предложений аналогичной вычислительной мощности с таким же количеством нод. Нужно ли ещё что-то объяснять?..
Оборудование
Четыре Raspberry Pi 4B 4GB (каждый по 5548 руб.)
Четыре карты памяти 128 GB U3 (каждая по 2616 руб.)
Один источник питания с несколькими USB-выходами (2777 руб.)
Один корпус для нескольких Pi (2242 руб.)
Не помешает добавить: радиаторы и четыре кабеля USB-B на USB-C.
Почему Raspberry Pi?
TL;DR. Главные причины — цена и вычислительная мощность.
Кластер из четырёх нод учетверяет характеристики каждого из мини-компьютеров (1,5 ГГц, 4 ядра ARM CPU и 4 ГБ RAM), то есть в итоге мы получаем 16 ядер 1,5 ГГц и 16 ГБ RAM.
Подготовка карты памяти
Начинаем с загрузки операционной системы, и это будет самая затратная по времени часть проекта. Большую часть своего времени я работаю с Docker и Kubernetes, и одно из моих любимых занятий — сведение размеров образов Docker к абсолютному минимуму. Чаще всего я пользуюсь Alpine Linux, поэтому свой кластер буду строить именно на этом дистрибутиве.
Заходим в раздел Alpine Linux Downloads и выбираем версию AARCH64 для Raspberry Pi 4 Model B.
Пока загружается дистрибутив, подготовим карту памяти: отформатируем её под файловую систему FAT32. Я — фанат OSX, поэтому, чтобы получить идентификатор диска карты памяти, обычно пользуюсь этой командой:
diskutil list
Чтобы отформатировать всю карту памяти (я назвал её RPI), запустите эту команду:
sudo diskutil eraseDisk FAT32 RPI MBRFormat /dev/diskX
Распакуйте загруженный пакет с Alpine linux и сбросьте его на карту:
sudo tar xf alpine-rpi-3.12.1-aarch64.tar.gz -C /Volumes/RPI
Базовая настройка системы
Поздравляю, вы на шаг ближе к миру Kubernetes, и где? У себя дома! Вставьте карту памяти в Raspberry Pi, монитор или телевизор, соедините с клавиатурой и включите питание. После того как система загрузится и предложит войти, в качестве имени пользователя и пароля используйте root. Настройка начинается с этой команды:
setup-alpine
Я запускаю кластер в домашних условиях, и на моём маршрутизаторе нет нужного количества свободных портов, поэтому я решил воспользоваться сетью Wi-Fi. Вариантов тут не так много, но в любом случае стоит продумать каждый.
После завершения настройки нужно сделать ещё несколько вещей. Alpine по умолчанию запускается из оперативной памяти, но было бы лучше, если бы наши изменения сохранялись на диске, чтобы после возможных перезагрузок каждый раз не вводить их заново.
apk update
apk add cfdisk e2fsprogs # Install disk tools
cfdisk /dev/mmcblk0 # Run cfdisk on your memory card
Вот что нужно сделать:
Изменить размер раздела FAT32 до разумного минимума — в моём случае я задал 1 ГБ.
Для создания нового загрузочного раздела использовать всё оставшееся свободное место.
Не забыть записать только что сделанные изменения.
Полезное руководство: Как работать с cfdisk.
Чтобы завершить весь процесс, нужно запустить ещё несколько команд:
mkfs.ext4 /dev/mmcblk0p2 # Format newly created partition as EXT4
mount /dev/mmcblk0p2 /mnt # Mount it
setup-disk -m sys /mnt # Install system files
mount -o remount,rw /media/mmcblk0p1 # Remount old partition in RW
# Let's do some housekeeping
rm -f /media/mmcblk0p1/boot/*
cd /mnt
rm boot/boot
mv boot/* /media/mmcblk0p1/boot/
rm -Rf boot
mkdir media/mmcblk0p1
ln -s media/mmcblk0p1/boot boot
Обновите записи /etc/fstab
echo "/dev/mmcblk0p1 /media/mmcblk0p1 vfat defaults 0 0" >> etc/fstab
sed -i '/cdrom/d' etc/fstab
sed -i '/floppy/d' etc/fstab
cd /media/mmcblk0p1
И — последние штрихи после перезагрузки системы: имейте в виду, что, если не включить соответствующие cgroups, шаг kubeadm выполнить не удастся.
# Enable edge repository for Alpine
sed -i '/edge/s/^#//' /mnt/etc/apk/repositories
# Force use of new partition as the root one
sed -i 's/^/root=\/dev\/mmcblk0p2 /' /media/mmcblk0p1/cmdline.txt
# Make sure that appropriate cgroups are enabled
echo "cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1" >> /media/mmcblk0p1/cmdline.txt
sed -i ':a;N;$!ba;s/\n/ /g' /media/mmcblk0p1/cmdline.txt
rc-update add wpa_supplicant boot # Make sure your wifi will come back up after restart
И, наконец, очень важная вещь после всех шагов — сделать резервные копии внесённых изменений и перезагрузить систему.
lbu_commit -d
reboot
Настройка других системных параметров
Я уже писал выше, что собираюсь использовать кластер Kubernetes в домашних условиях. Но, упреждая ваш вопрос, отвечу: да, он будет работать и в офисной сети, но в этом случае нужно будет ещё кое-что добавить.
Определите с помощью avahi daemon имя узла в локальной сети
Зачем нужен этот шаг? А затем, что гораздо проще запустить команду ssh pi0.local, чем возиться с соответствующим IP-адресом. Сетевые настройки и настройка параметров кластеризации после этого станут намного проще, особенно если отсутствует возможность использования статических IP-адресов.
apk add dbus avahi
rc-update add dbus boot # avahi won't start without dbus
rc-update add avahi-daemon boot
Разрешить ssh root-доступ
Внесите изменения в файл /etc/ssh/sshd_config — добавьте к нему следующую строку, чтобы предоставить ssh root-доступ.
PermitRootLogin yes
Установите Docker, Kubernetes и оставшиеся пакеты. Они понадобятся нам позже.
apk update
apk add kubernetes docker cni-plugins kubelet kubeadm
rc-update add docker default
rc-update add kubelet default
Силы можно сберечь
На этом шаге всё должно быть готово. Последним шагом я выключил Raspberry Pi, вставил карту памяти обратно в ноутбук и создал её образ, чтобы можно было продублировать его на оставшиеся три карты и, таким образом, сэкономить время.
Не забудьте: чтобы не возникали конфликты, нужно изменять содержимое /etc/hostname для каждого вновь создаваемого компьютера. Я назвал компьютеры pi0, pi1 и pi2 (так легче запомнить) и внёс эти имена в локальный конфигуратор ssh (на именование нет никаких ограничений).
Создание мастер-ноды Kubernetes
service docker start
kubeadm config images pull # Get the necessary images
kubeadm init --pod-network-cidr=10.244.0.0/16
Если будут возникать любые ошибки, связанные с cgroups, останавливающие процесс инициализации, это значит, что вы наверняка пропустили один из шагов. Если всё сделано правильно, должно появиться сообщение, что инициализация панели управления Kubernetes прошла успешно: Your Kubernetes control-plane has initialised successfully!
Сохраните вывод команды, которая начинается с kubeadm join, в безопасном месте. Она понадобится, чтобы добавить к кластеру оставшиеся ноды.
Для сохранения идентификационных данных в домашнем каталоге выполните эти команды:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Как получить доступ к ноде?
Чтобы избежать возможных конфликтов, я скопировал содержимое папки $HOME/.kube/config с ноды на локальную машину, изменив несколько значений по умолчанию. В результате я получил возможность пользоваться с ноутбука такими инструментами, как kubectl и k9s, и могу быть уверен, что всегда доберусь до нужного сервера.
Мастер-нода запущена, что ещё нужно сделать?
Нужно обеспечить сетевую связь между подами — без неё нода будет иметь отметку (taint) и всегда оставаться в состоянии NotReady — “не готова”.
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
По умолчанию на мастер-ноде ничего развёртывать нельзя, и нода будет отображаться c отметкой (taint), но не волнуйтесь — мы можем изменить это командой
kubectl taint nodes --all node-role.kubernetes.io/master-
Теперь что касается дашборда. Покажите мне того человека, которому не нравится, когда в ПО имеется дашборд! В Kubernetes есть собственный и достаточно универсальный дашборд, и с его помощью можно обозревать весь кластер и что-либо у него внутри.
# Add kubernetes-dashboard repository
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
# Deploy a Helm Release named "kubernetes-dashboard" using the kubernetes-dashboard chart
helm install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --set protocolHttp=true,ingress.enabled=true,rbac.create=true,serviceAccount.create=true,service.externalPort=9090,networkPolicy.enabled=true,podLabels.app=dashboard
Как вы, возможно, заметили, я довольно хорошо покопался в настройках Helm-чарта, но для этого были причины.
Ваш дашборд будет работать, но... он ничего не будет показывать, потому что ещё нет разрешений.
kubectl create clusterrolebinding kubernetes-dashboard --clusterrole=cluster-admin --serviceaccount=default:kubernetes-dashboard
Мы почти у цели. У нас есть мастер-нода и дашборд, но доступа к нему в данный момент у нас нет. Конечно, для доступа к дашборду можно было воспользоваться nodePort, но мы пойдём другим путём — получим доступ средствами Kubernetes, а для этого нам понадобится балансировщик нагрузки loadBalancer.
Нода работает в локальной сети, поэтому мы не можем рассчитывать ни на какие "плюшки" от AWS или GoogleCloud, но бояться тут нечего — эту проблему, в принципе, можно решить.
Балансировка нагрузки в домашней сети
Выполните инструкции по установке из MetalLB до конца раздела Installation By Manifest.
ifconfig wlan0 promisc # Set PROMISC mode for WiFi - for ARP
Это команда будет выполняться до тех пор, пока Pi включён. Чтобы не делать лишнюю работу, создавая скрипты запуска, я решил изменить файл /etc/network/if-up.d/dad и установить неразборчивый режим: в нём сетевая плата позволяет принимать все пакеты, независимо от того, кому они адресованы.
#...
ip address show dev $IFACE | grep -q " $1 "
ip link set $IFACE promisc on
#...
Создайте следующий манифест: my-dashboard.yaml
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.50.200-192.168.50.250
---
apiVersion: v1
kind: Service
metadata:
name: k8s-dashboard
annotations:
metallb.universe.tf/address-pool: default
spec:
ports:
- port: 80
targetPort: 9090
selector:
app: dashboard
type: LoadBalancer
Не забудьте изменить раздел адресов в соответствии с настройками локальной сети.
kubectl apply -f my-dashboard.yaml
kubectl get svc k8s-dashboard
Теперь в моём случае к дашборду можно получить доступ по адресу http://192.168.50.200/.
Эта статья, пронизанная любовью к экспериментам, была написана после дня субботних мучений, скрашенного несколькими банками энергетического напитка.
Добавление дополнительных нод
Я придерживаюсь принципов DRY (Don't Repeat Yourself — Не повторяйся) и KISS (Keep It Stupid Simple — Делай проще, тупица), поэтому не буду ничего повторять, а объясню всё простыми словами. Вернитесь к началу статьи и на вновь создаваемых нодах выполните ещё раз все шаги до места “Создание мастер-ноды”, затем запустите следующую команду (не забудьте заменить IP-адрес на IP мастер-ноды или укажите имя узла pi0.local. За эту возможность отдельное спасибо avahi-daemon):
service docker start
kubeadm config images pull
kubeadm join 192.168.50.132:6443 --token dugwjt.0k3n \--discovery-token-ca-cert-hash sha256:55cfadHelloSuperSecretHashbf4970f49dcadf533f86e3dba
Совет: если вы забыли скопировать команду kubeadm во время создания мастер-ноды, не расстраивайтесь, просто запустите на мастер-ноде следующую команду, и команда kubeadm будет распечатана. А если хотите прокачать себя до DevOps инженера — приходите учиться и станьте дефицитным и очень высокооплачиваемым специалистом.
Узнайте, как прокачаться и в других специальностях или освоить их с нуля:
Профессия Data Scientist
Профессия Data Analyst
Курс по Data Engineering
Другие профессии и курсы
ПРОФЕССИИ
Профессия Fullstack-разработчик на Python
Профессия Java-разработчик
Профессия QA-инженер на JAVA
Профессия Frontend-разработчик
Профессия Этичный хакер
Профессия C++ разработчик
Профессия Разработчик игр на Unity
Профессия Веб-разработчик
Профессия iOS-разработчик с нуля
Профессия Android-разработчик с нуля
КУРСЫ
Курс по Machine Learning
Курс "Machine Learning и Deep Learning"
Курс "Математика для Data Science"
Курс "Математика и Machine Learning для Data Science"
Курс "Python для веб-разработки"
Курс "Алгоритмы и структуры данных"
Курс по аналитике данных
Курс по DevOps