За последние пару лет мне довольно часто приходилось сталкиваться с Kubernetes, однако обычно это всегда происходило с позиции разработчика. Посмотреть список подов, их статусы, логи и т.п. В общем, ничего сложного.
Недавно я решил заняться изучением Kubernetes чуть глубже, чтобы лучше понимать, как он работает и что умет. Но тут возникла очевидная проблема: нужна какая-то песочница, в которой можно было бы экспериментировать, не боясь при этом что-то сломать, а ещё лучше иметь возможность в любой момент откатиться назад.
Обычно изучение Kubernetes стоит начинать с minikube - консольной утилиты, которая умеет быстро создавать кластер из одного узла с использованием VirtualBox. Это самый лучший вариант для быстрого старта.
Но есть и другой вариант. Можно взять VirtualBox, создать несколько виртуальных машин, например, с Ubuntu Server и настроить небольшой кластер Kubernetes прямо на своём домашнем компьютере.
Я решил пойти по второму пути, но по ходу настройки начал сталкиваться с различными проблемами, решение которых было для меня не так очевидно, как хотелось бы. Вообще в интернете можно найти достаточно много статей на английском языке, где описан процесс настройки, но, к сожалению, ни одна из них не была для меня достаточно полной. Пришлось собирать информацию по кусочкам из разных источников.
Здесь я решил собрать всё вместе и описать процесс настройки небольшого кластера Kubernetes с использованием VirtualBox настолько подробно, насколько мне самому хотелось бы его видеть. Надеюсь, что эта информация сможет помочь другим разработчикам в их экспериментах.
Осторожно, трафик! В статье очень много скриншотов.
Подготовка
Итак, в этой статье мы, используя VirtualBox, шаг за шагом настроим кластер Kubernetes, состоящий из трёх виртуальных машин c Ubuntu Server 20.04. В качестве хоста я использовал компьютер с Windows 10 Home. В процессе настройки нам понадобятся:
VirtualBox 6.1.16 и VirtualBox 6.1.16 Extension Pack. Скачать их можно здесь.
Установочный образ Ubuntu Server 20.04. Скачать его можно здесь.
Утилита kubectl для управления кластером. Инструкции по установке можно найти здесь.
Дисклаймер
Вообще, обычно я пишу на .NET и не занимаюсь подобной настройкой, поэтому не исключаю, что все описанные ниже шаги хоть и работают, но могут быть сделаны не совсем грамотно. Если я где-то ошибся или вообще написал полную ерунду - смело пишите об этом в комментариях =)
Отключаем Hyper-V
Первым делом, если на вашем компьютере установлены Docker или Windows Subsystem for Linux (WLS), то скорее всего включен Hyper-V. VirtualBox не будет с ним работать, а при попытке запустить виртуальную машину будет выдавать ошибку вроде этой:
Поэтому первым делом Hyper-V нужно отключить. Для этого открываем командную строку от имени администратора и вводим команду:
bcdedit /set hypervisorlaunchtype off
Чтобы включить его обратно вводим команду:
bcdedit /set hypervisorlaunchtype auto
Перезагружаем компьютер, чтобы изменения вступили в силу.
Устанавливаем VirtualBox
Далее нам необходимо установить VirtualBox и VirtualBox Extension Pack. Последний понадобится нам чуть позже, когда мы будем настраивать общие папки между хостом и виртуальными машинами. После установки в меню File > Preferences > Extensions можно перейти в настройки и убедиться, что Extension Pack установлен:
Настраиваем сетевой адаптер
В VirtualBox каждая виртуальная машина может иметь до четырёх сетевых адаптеров, а каждый такой адаптер имеет определённый режим работы, такой как NAT, NAT Network, Bridged, Host-only и т.п. В основном эти режимы отвечают за то, как виртуальные машины будут взаимодействовать с хостом и между собой, а также будет ли у них доступ в интернет и смогут ли другие устройства в сети хоста взаимодействовать с этими виртуальными машинами. Если хотите лучше разобраться как работают эти режимы, рекомендую почитать здесь и тут (по второй ссылке есть картинки, которые неплохо всё это иллюстрируют).
Нам потребуются два адаптера с режимами NAT и Host-only.
NAT адаптер позволяет виртуальным машинам получать доступ в интернет через хост, но не позволяет им взаимодействовать друг с другом (хотя это позволяет NAT Network). Но самое главное, что в этом режиме наши виртуальные машины будут получать IP адрес динамически, поэтому мы не сможем использовать такие адреса для настройки кластера.
Host-only адаптер создаёт одну общую сеть между хостом и всеми виртуальными машинами, а также позволяет назначить каждой из них статический IP адрес, что отлично подходит для настройки кластера. Однако такой режим требует создания на хосте виртуального сетевого адаптера. VirtualBox создаёт такой виртуальный адаптер при установке, и вы можете найти его в меню File > Host Network Manager...:
Прежде всего необходимо убедиться для для адаптера выключен режим DHCP Server, чтобы IP адреса не назначались виртуальным машинам автоматически, как это происходит с NAT.
Поле IPv4 Address задаёт IP адрес хоста. Виртуальным машинам мы сможем назначать адрес из той же подсети, которая задаётся полем IPv4 Network Mask. Например, если в данном случае IP хоста 192.168.92.1, а маска подсети 255.255.255.0, то мы сможем назначить виртуальным машинам адреса 192.168.92.2, 192.168.92.10, 192.168.92.42 и т.п.
Стоит отметить, что VirtualBox генерирует адрес хоста случайно и у вас он может отличаться. Я не очень люблю запоминать случайные цифры, поэтому рекомендую изменить адрес хоста на что-то более запоминающееся, я использовал 192.168.10.1:
(На всякий случай напишу, что адрес 192.168.1.1 лучше не использовать, т.к. он обычно используется всякими Wi-Fi роутерами и это может привести к конфликтам и неработающему интернету)
После изменения адреса сетевого адаптера необходимо обязательно перезагрузить компьютер, т.к. без этого виртуальные машины не будут запускаться.
Создаём шаблонную виртуальную машину
Все виртуальные машины, которые мы будем создавать будут немного отличаться настройками, однако большая часть настроек у них будет одинаковая. Чтобы не делать все эти настройки по нескольку раз мы создадим одну шаблонную виртуальную машину, а затем просто клонируем её нужное количество раз и донастроим.
Создаём новую виртуальную машину Kube Template:
Увеличиваем количество памяти хотя бы до 2Гб:
Далее все настройки идут по умолчанию. Создаём для виртуальной машины новый виртуальный жёсткий диск в формате VDI (VirtualBox Disk Image), динамическим выделением места и размером 10Гб:
И получаем нашу новую виртуальную машину:
Но это ещё не всё. Эту виртуальную машину необходимо донастроить. Для этого идём в Machine > Settings... > System > Processor и увеличиваем количество процессоров как минимум до 2, иначе Kubernetes откажется на ней запускаться:
Далее переходим на вкладку Network и проверяем, что первым сетевым адаптером является адаптер с режимом NAT:
Переключаемся на второй сетевой адаптер (он скорее всего выключен), включаем его и переключаем в режим Host-only и выбираем виртуальный сетевой адаптер, который мы ранее настроили (он там будет один):
Сохраняем, и на этом настройка нашей шаблонной виртуальной машины завершена. Можно переходить к установке Ubuntu Server.
Устанавливаем Ubuntu Server
Запускаем нашу шаблонную виртуальную машину. При первом старте VirtualBox предложит выбрать установочный диск. Выбираем iso файл для Ubuntu Server 20.04 и начинаем установку:
Когда установка запустится, начальные параметры, такие как язык и раскладку клавиатуры выбираем по умолчанию. Также отказываемся от обновления:
Но дойдя до настроек сетевых подключений останавливаемся:
Здесь мы видим, что у нашей виртуальной машины два сетевых адаптера, первый из которых уже получил свой IP адрес динамически, а вот второй - нет. Выбираем второй сетевой адаптер и вручную указываем его настройки:
Здесь мы указываем подсеть и статический IP адрес нашей виртуальной машины в этой подсети. Напомню, что IP адрес хоста у нас 192.168.10.1. Остальные параметры можно опустить.
После этого оба сетевых адаптера должны иметь свой собственный IP адрес:
Далее оставляем все настройки по умолчанию, пока не дойдём до настроек профиля. Я назвал машину kube-template, а в качестве username/password использовал test/test:
На следующем шаге нам будет предложено установить OpenSSH server. Я рекомендую воспользоваться этой возможностью, чтобы в будущем иметь возможность подключаться к виртуальным машинам по SSH с помощью PuTTY и Multi PuTTY Manager. Я не стал описывать это в статье, чтобы ещё больше не увеличивать объём:
(Следующий шаг с установкой дополнительных функций пропускаем.)
И наконец запускается сама установка. Теперь мы смело можем сходить пообедать или принять душ, т.к. процесс может занять некоторое время:
После того, как установка завершится, выбираем перезагрузку виртуальной машины (если при этом увидите ошибки об отсутствии cdrom, просто нажмите любую клавишу):
Проверяем настройки сети
После перезагрузки желательно проверить, что сеть между хостом и виртуальной машиной нормально работает, а виртуальная машина имеет доступ в интернет:
Запускаем командную строку на хосте и пингуем виртуальную машину по статическому IP адресу:
Переключаемся в интерфейс виртуальной машины и делаем тоже самое:
И там же проверяем, что виртуальная машина имеет доступ в интернет:
curl https://ya.ru
Если всё работает, то можно переходить к настройке виртуальной машины. В этот момент можно сделать snapshot, чтобы в случае чего не выполнять установку заново.
Устанавливаем mc
Я не очень люблю работать с командной строкой, особенно когда нужно найти и отредактировать какой-нибудь файл. Поэтому первым делам устанавливаем mc:
sudo apt-get update
sudo apt-get install -y mc
sudo mc
Если не знакомы с mc, то тут есть список хоткеев, которые нам понадобятся.
F3 - Посмотреть содержимое файла
F4 - Редактировать содержимое файла. При первом редактировании обычно предлагает выбрать редактор, выбирайте mcedit (3).
F2 - Cохранить изменения в файле.
F5 - Скопировать файл из левой панели в правую.
F8 - Удалить файл.
CTRL+O - Скрыть панели.
Отключаем swap
Kubernetes откажется запускаться, если на виртуальной машине включен swap, поэтому его надо отключить.
Проверить, что swap включен или выключен можно командой:
cat /proc/swaps
Если файлы есть, то swap включен, поэтому переходим в папку /etc
, ищем там файл fstab
и открываем его на редактирование. У меня это выглядит следующим образом:
Закомментируем (или просто удалим) последнюю строку (в которой упоминается файл /swap.img
), сохраним файл и перезагрузим виртуальную машину командой:
shutdown -r now
После перезагрузки также удалим файл /swap.img
:
Настраиваем общие папки между хостом и виртуальной машиной
Далее нам необходимо установить Docker и Kubernetes, но тут возникает проблема. Для установки нам нужно ввести достаточно длинные команды, которые можно найти на официальных сайтах. Только вот взять и скопировать и вставить их мы не сможем, т.к. буфер обмена между хостом и виртуальной машиной не работает.
Вместо этого, чтобы не вводить все команды вручную, мы создадим между хостом и виртуальной машиной общую папку, скопируем команды для установки в файл из этой папки, а потом выполним этот файл как скрипт в виртуальной машине.
Я честно признаюсь, что это решение я взял отсюда.
Для начала выключаем виртуальную машину (иначе общая папка будет временной и не сохранится при перезагрузке) командой:
shutdown now
Открываем настройки виртуальной машины и переходим на вкладку Shared Folders. Там указываем расположение общей папки на хосте, а также её название в виртуальной машине:
Сохраняем изменения и снова включаем виртуальную машину.
После загрузки сначала подключаем специальный CD диск содержащий VirtualBox Guest Additions, который будет доступен благодаря тому, что мы установили VirtualBox Extension Pack. Найти его можно в меню виртуальной машины Devices > Insert Guest Additions CD image:
После нам необходимо выполнить серию команд. Сначала монтируем CD:
sudo mkdir /media/cdrom
sudo mount -t iso9660 /dev/cdrom /media/cdrom
Затем устанавливаем зависимости, которые понадобятся нам для скрипта установки:
sudo apt-get update
sudo apt-get install -y build-essential linux-headers-`uname -r`
И наконец запускаем скрипт установки:
sudo /media/cdrom/VBoxLinuxAdditions.run
После установки перезагружаем виртуальную машину:
shutdown -r now
После перезагрузки сначала создаём нашу общую папку в домашнем каталоге пользователя:
mkdir ~/shared
Поскольку у меня пользователь называется test, то полный путь к этой папке будет /home/test/shared
.
Далее снова запускам mc и идём редактировать уже знакомый нам файл /etc/fstab
. Необходимо добавить туда следующую строчку (Вместо <username> используйте имя пользователя, в моём случае это test):
shared /home/<username>/shared vboxsf defaults 0 0
У меня это выглядит следующим образом:
Также нам необходимо отредактировать файл /etc/modules
, добавив туда строчку:
vboxsf
У меня это выглядит следующим образом:
И снова перезагружаемся:
shutdown -r now
Осталось убедиться, что всё работает. Для этого сначала заходим в общую папку на хосте и создаём там тестовый файл:
Затем переключаемся на виртуальную машину, заходим в папку ~/shared
и убеждаемся, что файл появился:
Устанавливаем Docker и Kubernetes
Для начала установим Docker. Полные инструкции по установке можно найти здесь, но фактически нам нужно просто выполнить следующий скрипт (пока не выполняем его):
#!/usr/bin/env bash
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
С Kubernetes история аналогичная. Инструкции по установке можно найти тут, но нам нам достаточно будет следующего скрипта:
#!/usr/bin/env bash
sudo apt-get update
sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
Можно конечно просто последовательно ввести все эти команды, но мы воспользуемся общей папкой, которую настроили ранее. Создадим в ней на хосте два файла: install-docker.sh
и install-kubernetes.sh
, а затем скопируем туда содержимое скриптов.
При сохранении обязательно убедитесь, что переносы строк имеют формат LF, а не CRLF.
Теперь переходим в общую папку на виртуальной машине, находим наши скрипты и запускаем их, сначала устанавливая Docker, а затем Kubernetes:
Изменяем cgroup driver для Docker и Kubernetes на systemd
В Linux есть такой механизм, называемый Control Groups (или просто cgroups), который используется для ограничения ресурсов, выделяемых процессам. При запуске Docker и Kubernetes мы можем указать для каждого из них драйвер cgroups, который будет использоваться: cgroupfs или systemd. При этом важно, чтобы и Docker и Kubernetes использовали один и тот же драйвер.
В различных источниках рекомендуется перейти именно на драйвер systemd, как на более современный. Если этого не сделать, Kubernetes при старте может выводить предупреждения.
Для начала сделаем это для Docker. Для этого перейдём в папку /etc/docker
и создадим в ней файл daemon.json
со следующим содержимым:
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
Должно получиться так:
Для изменения драйвера в Kubernetes отредактируем файл /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
. Найдём последнюю строку:
ExecStart=/usr/bin/kubelet <тут много всяких параметров>
И добавим в конец этой строки ещё один параметр --cgroup-driver=systemd
. Должно получиться следующее:
Сохранив оба файла, выключаем виртуальную машину:
shutdown now
Клонируем виртуальные машины
Мы закончили настройку нашей шаблонной виртуальной машины. Пришло время сделать из неё узлы нашего кластера. Для этого открываем контекстное меню и выбираем Clone...:
Назовём наши новые виртуальные машины: Kube Master, Kube Node1 и Kube Node2.
При клонировании обязательно измените поле MAC AddressPolicy на Generate new MAC address for all network adapters.
В итоге получим следующее:
Изменяем hostname и IP адреса виртуальных машин
Теперь нам необходимо сделать так, чтобы каждая виртуальная машина имела свой уникальный hostname и IP адрес.
Сначала запустим виртуальную машину Kube Master и сделаем изменения на ней.
Для начала изменим hostname. Это делается простой командой:
sudo hostnamectl set-hostname kube-master
Настройки IP адреса в Ubuntu Server 20.04 хранятся в файле /etc/netplan/00-installer-config.yaml
. Откроем этот файл и поменяем IP адрес на 192.198.10.10:
После сохранения изменений их необходимо применить командой:
sudo netplan apply
Проверить, что изменения hostname и IP адреса успешно применились можно командой:
hostname && hostname -I
Но это ещё не всё. Когда Kubernetes (а если быть точным, то kubelet) будет запускаться на узле, он в качестве IP узла будет использовать IP адрес сетевого адаптера по умолчанию, а это NAT с динамическими адресами, который нам не подходит.
Чтобы использовать статический IP адрес, который мы указали, отредактируем файл /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
. Ранее, мы уже добавили в него параметр --cgroup-driver=systemd
. Теперь добавим ещё один: --node-ip=192.168.10.10
:
Сохраняем изменения, выключаем виртуальную машину и повторяем эти изменения для двух остальных машин.
Для машины Kube Node1 укажем имя хоста kube-node1 и IP адрес 192.168.10.11.
Для машины Kube Node2 укажем имя хоста kube-node2 и IP адрес 192.168.10.12.
Настраиваем кластер
Теперь у нас всё готово и можем наконец приступить к настройке кластера.
Для начала нам нужно инициализировать master узел следующей командой:
sudo kubeadm init --apiserver-advertise-address=192.168.10.10 --pod-network-cidr=10.10.0.0/16 > ~/shared/kubeadm-join.sh
Здесь нам обязательно необходимо указать два параметра.
Параметр --apiserver-advertise-address
отвечает за то, по какому IP адресу будет доступен apiserver. Без него будет использоваться IP адрес сетевого адаптера по умолчанию, которым является NAT, и мы не сможем присоединить узлы к master узлу. Нам нужно использовать значение 192.168.10.10.
Параметр --pod-network-cidr
указывает подсеть, в которой подам будут выделяться виртуальные IP адреса внутри кластера. Главное, чтобы эта подсеть не пересекалась с другими подсетями, используемыми виртуальными машинами. Я использовал значение 10.10.0.0/16.
В результате выполнения этой команды произойдёт настройка master узла, а в output будет выведена команда kubeadm join ...
для присоединения к кластеру. Чтобы сохранить эту команду и использовать её на двух других узлах, мы записываем весь вывод в файл в общей папке.
Инициализация может занять несколько минут, но после того как она завершится, мы можем перейти в общую папку и найти там там файл kubeadm-join.sh
:
В самом конце файла мы найдём нужную нам команду. Удалим весь остальной текст и сохраним файл:
Теперь последовательно зайдём на виртуальные машины Kube Node1 и Kube Node2, перейдём в общую папку и выполним скрипт kubeadm-join.sh
:
Подключаемся к кластеру с хоста
Мы только что создали кластер Kubernetes из трёх узлов. Теперь нам необходимо как-то подключиться к нему с хоста. Для этого нам потребуется файл admin.conf
из папки /etc/kubernetes
, который можно найти на master узле.
Сначала копируем admin.conf
в общую папку и заодно переименовываем его в config
:
sudo cp /etc/kubernetes/admin.conf ~/shared/config
После этого уже на хосте копируем файл config
в папку %UserProfile%/.kube
.
Запускаем на хосте командную строку и проверяем доступность кластера командой:
kubectl get nodes -o wide
Настраиваем сетевой плагин
На последнем скриншоте мы видим, что у нас в кластере три узла, но все они в статусе NotReady.
Также, если мы попробуем получить список подов, то увидим, что некоторые поды не запускаются, а остаются в статусе Pending:
kubectl get pods -A
Причина в том, что Kubernetes имеет абстрактную модель работы с сетью, а конкретная её реализация определяется сетевыми плагинами, такими как Flannel или Calico. Подробнее почитать про сравнение разных плагинов можно тут.
Мы установим Flannel (т.к. по описанию он самый простой).
Зайдя на страницу проекта на GitHub видим, что для настройки плагина достаточно выполнить команду:
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Не делаем этого, т.к. это не будет работать. Вместо этого скачиваем файл kube-flannel.yml себе на рабочий стол.
Открываем файл и находим там следующий кусок:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
Заменяем значение 10.244.0.0 на 10.10.0.0. Это то значение, которое мы передавали в команду kubectl init
.
Сохраняем изменения и вот теперь уже устанавливаем Flannel командой:
kubectl apply -f .\Desktop\kube-flannel.yml
Теперь, если мы посмотрим список подов, то увидим, что там появились поды Flannel. Все поды через некоторое время перейдут в состояние Running и кластер будет готов к использованию.
Узлы также перейдут в состояние Ready:
Теперь наш кластер готов к использованию.
Заключение
На этом у меня всё. Статья получилась несколько больше, чем я ожидал, поэтому я не стал описывать шаги с проверкой кластера и созданием тестовых объектов в нём.
Надеюсь, что данный материал будет полезен разработчикам, которые захотят настроить себе тестовый кластер для изучения Kubernetes.
AlexGluck
Коллеги, перестаньте себе ломать пальцы молотком. Не надо на линукс виртуальных машинах работать через окошко гипервизора. Не надо на windows 10 использовать putty. Есть windows terminal preview и встроенный клиент ssh. Даже просто обычные консоли cmd и ps могут запускать встроенный ssh client, это избавит вас от монтирования общих папок на ВМ и вы просто будете копировать из буфеа обмена команды.
Ordos Автор
О, отлично, не знал про такую возможность. Спасибо!
alex1t
Тоже пока чтал статью хотел об этом сказать. Давно пользуюсь Windows Terminal — всё устраивает. Главное ssh по ключу настроить и можно сделать готовый профиль для входа в систему.
Кстати, автор, а вы я надеюсь потом виртуалки в headless режиме запускали?
Ordos Автор
Да, headless, но я по старинке пользовался мультипутти.