Привет, я Кирилл Шаталаев, инженер инфраструктуры и автоматизации в X5 Tech.

Я в курсе, что статей на эту тему достаточно, в том числе и на Habr. И когда у меня возникла задача поднять кластер, я их все перечитал. Где-то очень подробно рассказывается, как ставить виртуалки с убунтой на Windows под virtualbox, и очень скудно про сам кубер. Где-то досконально описано, как это всё круто можно провернуть с terraform в Яндекс.Облаке. Где-то про сам kubespray скупо пару слов, зато куча скриншотов прометея с кибаной.

В итоге до большинства ключевых моментов пришлось доходить самостоятельно, гуглежом и изучением исходников ролей kubespray. Поехали!

Небольшое введение

В настоящее время существует большое количество способов поднять локальный kubernetes быстро и грязно. Официальная документация рекомендует использовать для этого minikube. Canonical продвигает свой MicroK8s. Есть ещё k0s, k3s, kind — в общем, инструменты на любой вкус.

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

Тут выход один — поднять нормальный кластер на виртуалках.

Плюсов такого решения — масса. Можно играться с кластером как угодно, делать с нодами drain и cordon, ковыряться с LoadBalancer, можно имитировать сетевые проблемы и смотреть, что будет происходить с kubernetes при этом. Можно даже поднять рядом в виртуалке, допустим, Gitlab CE, подцепить к нему ваш кластер и обкатывать CI/CD.

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

Краткое описание решения

Виртуальные машины:

Мастер-ноды - 2 ядра, 4 Гб памяти, 20 Гб HDD

master0 — 192.198.122.10

master1 — 192.168.122.11

master2 — 192.168.122.12

Воркер-ноды - 4 ядра, 16 Гб памяти, 20 Гб HDD

worker0 — 192.198.122.20

worker1 — 192.168.122.21

worker2 — 192.168.122.22

ОС - Ubuntu 20.04.3

Инструмент разворачивания кластера — kubespray

Инструменты управления кластером (локальные) — kubectl, k9s

Шаг 1. Гипервизор

Я кратко опишу развёртывание виртуалок на моей локальной машине при помощи qemu и virt-manager. Если вы предпочитаете другой гипервизор, у вас уже есть виртуалки где-то в облаке или у вас есть, ну, я не знаю, отдельный сервер proxmox для экспериментов — пропустите шаги 1 и 2 и делайте по-своему.

Хост-система - Ubuntu 20.04, CPU AMD Ryzen 7 3700X, оперативной памяти - 128 Гб. Конфигурация средняя, за исключением памяти (да, я маньяк) — но память нынче дёшевая, в отличие от видеокарт. Плюс ко всему, с учётом аппетита современных приложений, многие уже 32Гб считают минимально достаточным количеством оперативки. Хотя лучше в наших реалиях всё же иметь 64Гб.

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

sudo apt install qemu qemu-kvm libvirt-clients libvirt-daemon-system virtinst bridge-utils virt-manager libguestfs-tools

Добавляем себя в группу kvm, чтобы управлять виртуалками без использования прав суперпользователя:

sudo usermod -aG kvm ksh

Теперь надо перелогиниться (в консоли, чтобы обновить список групп, можно использовать newgrp, но графическая сессия не всегда корректно подхватывает новые группы) и можно запускать менеджер виртуальных машин.

Ключевой момент тут один — виртуальная сеть.

При установке libvirt уже создана виртуальная сеть default, которая при помощи NAT транслирует трафик виртуальных машин во внешнюю сеть. Виртуалки гостей при этом цепляются к bridge-интерфейсу хоста. Хост также выполняет роль DHCP и DNS-серверов. Для этих целей выделена подсеть 192.168.122.0/24, адрес 192.168.122.1 назначается на bridge-интерфейс хоста, этот адрес является шлюзом по умолчанию и основным DNS-сервером для виртуалок.

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

Вы также можете настроить новые виртуальные сети, можете сделать их маршрутизируемыми или даже связать сеть с физическим интерфейсом хоста (в этом случае виртуалки будут подключены ко внешней сети напрямую). В рамках данной задачи это нам не нужно.

Шаг 2. Создание виртуальных машин

Мы сделаем только первую виртуалку master0, а затем просто склонируем её.

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

Количество ядер и память
Количество ядер и память
Жёсткий диск
Жёсткий диск
Подключаем к сети default
Подключаем к сети default

Важные моменты при установке системы

Сразу задаём статический IP-адрес
Сразу задаём статический IP-адрес
LVM и сложная настройка разделов на диске не нужна
LVM и сложная настройка разделов на диске не нужна
Не забудьте поставить ssh-server
Не забудьте поставить ssh-server

Установка завершена, дальше дожидаемся перезагрузки и добавляем на машину свой ssh-ключ и пробуем зайти на неё по ssh:

ssh-copy-id user@192.168.122.10
ssh user@192.168.122.10

На свежесозданной виртуалке добавляем наш ключ к учетной записи root:

sudo cp /home/user/.ssh/authorized_keys /root/.ssh/

Выходим и пробуем зайти под root по ssh. Должно пустить.

ssh root@192.168.122.10

После этих тривиальных настроек мы выключаем нашу виртуалку и приступаем к клонированию.

Клонирование  — это нажать две кнопки в интерфейсе
Клонирование — это нажать две кнопки в интерфейсе

Теперь важный момент номер два — клонирование создаёт полную копию машины, со всеми ключами, UUID и прочим. Чтобы заново инициализировать каждую из виртуалок, выполняем команду virt-sysprep для склонированной виртуалки:

virt-sysprep --operations defaults,-ssh-userdir,customize -d k8s-master-1

пустит, т.к. предыдущая команда снесла все ключи, а как заставить sysprep сгенерировать их сразу, я не нашёл).

Выполняем команды:

sudo ssh-keygen -A
sudo systemctl restart sshd

Проверяем, что сервер ssh успешно запущен:

systemctl status sshd

Последним этапом мы меняем IP в настройках сетевого интерфейса и устанавливаем правильный hostname

Настройки сети содержатся в файле /etc/netplan/00-installer-config.yaml

network:
  ethernets:
    enp1s0:
      addresses:
      - 192.168.122.10/24
      gateway4: 192.168.122.1
      nameservers:
        addresses:
        - 192.168.122.1
        search:
        - k8s-lab.internal
  version: 2

Здесь нам нужно поменять единственную строчку - ip-адрес с 192.168.122.10/24 на 192.168.122.11/24

sudo sed -i 's/192.168.122.10\/24/192.168.122.11\/24/' /etc/netplan/00-installer-config.yaml

На всякий случай проверяем содержимое файла, после чего выполняем команду:

sudo netplan apply

В завершение — hostname:

sudo hostnamectl set-hostname master1
sudo sed -i 's/master0/master1/' /etc/hosts

Перезагружаем виртуалку и пробуем залогиниться под root:

ssh root@192.168.122.11

Дальше повторяем эту процедуру для оставшихся нод — master2, worker0, worker1, worker2. Не забудьте, что для worker-нод надо будет установить нужное количество ядер и памяти. Делается это элементарно — через интерфейс управления виртуальными машинами.

Да, такие вещи неплохо автоматизировать, но мне не пришло в голову сходу, как это сделать. Конечно, можно было использовать для этих целей vagrant, но не хотелось, так как там куча своих заморочек и в итоге потратил бы гораздо больше времени. Если у вас есть идеи — буду рад услышать в комментариях.

Шаг 3. Собственно кубер

Кубер мы будем разворачивать при помощи kubespray. Это набор ansible-ролей, которые позволяют с минимальными усилиями получить готовый продакшн кластер. Тут всё ещё проще.

Ещё раз обращаю внимание, что дополнительно ничего на машинах делать не надо — ни править hosts, ни отключать swap. Достаточно стандартной установки дистрибутива. Обо всём позаботится kubespray.

Клонируем себе репозиторий:

git clone https://github.com/kubernetes-sigs/kubespray.git && cd kubespray

На момент написания статьи стабильный выпуск у нас 2.17.1. В репозитории релизы отмечены соответствующими тегами:

git checkout v2.17.1

Репозиторий содержит образец конфигурации в директории inventory/sample. Мы создадим копию и будем её портить.

cp -rfp inventory/sample inventory/lab

Итак, первый файл, который нам нужно исправить — это inventory.ini. В принципе, он снабжён комментариями и понятен.

Приводим его к следующему виду. Тут всё ясно — определяем, кто у нас будет мастером, а кто воркером:

[all]
master0 ansible_host=192.168.122.10 ansible_user=root etcd_member_name=etcd0
master1 ansible_host=192.168.122.11 ansible_user=root etcd_member_name=etcd1
master2 ansible_host=192.168.122.12 ansible_user=root etcd_member_name=etcd2
worker0 ansible_host=192.168.122.20 ansible_user=root
worker1 ansible_host=192.168.122.21 ansible_user=root
worker2 ansible_host=192.168.122.22 ansible_user=root

[kube_control_plane]
master0
master1
master2

[etcd]
master0
master1
master2

[kube_node]
worker0
worker1
worker2

[calico_rr]

[k8s_cluster:children]
kube_control_plane
kube_node
calico_rr

Теперь, собственно, настройки кластера. Я укажу те переменные, которые нужно поменять.

inventory/lab/group_vars/k8s_cluster/k8s-cluster.yml

Здесь меняем только одну переменную для того, чтобы работал балансировщик:

kube_proxy_strict_arp: true

inventory/lab/group_vars/k8s_cluster/addons.yml

Включаем метрики:

metrics_server_enabled: true
metrics_server_kubelet_insecure_tls: true
metrics_server_metric_resolution: 15s
metrics_server_kubelet_preferred_address_types: "InternalIP"

Включаем nginx ingress controller:

ingress_nginx_enabled: true
ingress_nginx_host_network: false
ingress_publish_status_address: ""
ingress_nginx_nodeselector:
  kubernetes.io/os: "linux"
ingress_nginx_namespace: "ingress-nginx"
ingress_nginx_insecure_port: 80
ingress_nginx_secure_port: 443
ingress_nginx_configmap:
  map-hash-bucket-size: "128"
  ssl-protocols: "TLSv1.2 TLSv1.3"

Включаем MetalLB. Для балансировщика выделяем диапазон IP из нашей виртуальной сети default:

metallb_enabled: true
metallb_speaker_enabled: true
metallb_ip_range:
   - "192.168.122.50-192.168.122.99"

Provisioning для PV не включаем. Можно это сделать потом, по желанию, но надо поменять конфигурацию ваших нод и сделать хотя бы одну с достаточным дисковым пространством.

Можно поднимать кластер.

Команды такие:

mkdir venv
python3 -m venv ./venv
source ./venv/bin/activate
pip install -r requirements.txt
ansible-playbook -i ./inventory/lab/inventory.ini --become --become-user=root cluster.yml

Роль должна отработать без всяких ошибок. По времени это занимает минут 15.

Проверяем, что всё поднялось. Заходим на любую из мастер-нод и выполняем:

root@master0:~# kubectl get nodes
NAME      STATUS   ROLES                  AGE     VERSION
master0   Ready    control-plane,master   15m     v1.21.6
master1   Ready    control-plane,master   15m     v1.21.6
master2   Ready    control-plane,master   15m     v1.21.6
worker0   Ready    <none>                 15m     v1.21.6
worker1   Ready    <none>                 15m     v1.21.6
worker2   Ready    <none>                 15m     v1.21.6

Отлично, кластер работает. Утаскиваем себе на локальную машину kubeconfig:

scp root@192.168.122.10:/root/.kube/config /tmp/kubeconfig

На локальной машине в файле меняем строчку:

server: https://127.0.0.1:6443

На:

server: https://192.168.122.10:6443

Дальше либо копируем его себе в ~/.kube/ целиком, либо правим существующий там файл, если есть уже настроенные кластеры. И пробуем приконнектиться, например, k9s

Работающий кластер
Работающий кластер

Шаг 4 — последние штрихи

Теперь несколько неочевидный момент.

kubespray выкатывает ingress-nginx в виде daemonset-а. Как известно, в kubernetes есть разные способы публикации сервисов. Можно было не заморачиваться и включить в файле inventory/lab/group_vars/k8s_cluster/addons.yml

ingress_nginx_host_network: true

но это некрасиво. Красиво — это MetalLB в режиме Layer2. Почти как на настоящем продакшне.

Как работает MetalLB? Если коротко, он берёт адрес из указанного ему диапазона, назначает его одной из нод и сообщает об этом окружающим посредством ARP (может и по BGP, но это не для игрушечных кластеров). Все запросы, приходящие на заданный адрес, он направляет соответствующему сервису kubernetes. Если нода внезапно подохнет, то MetalLB переназначает IP другой ноде.

Создаём манифест под названием ingress-nginx-service.yaml со следующим содержимым:

apiVersion: v1
kind: Service
metadata:
  annotations:
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.112.50
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: http
      appProtocol: http
    - name: https
      port: 443
      protocol: TCP
      targetPort: https
      appProtocol: https
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

И применяем его:

kubectl apply -f ingress-nginx-service.yaml

Проверяем:

kubectl get svc ingress-nginx-controller --namespace ingress-nginx
NAME                       TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                      AGE
ingress-nginx-controller   LoadBalancer   10.233.55.8   192.168.112.50   80:31783/TCP,443:31029/TCP   5m

curl -I http://192.168.112.50
HTTP/1.1 404 Not Found
Date: Thu, 23 Dec 2021 13:21:35 GMT
Content-Type: text/html
Content-Length: 146
Connection: keep-alive

Nginx отвечает по указанному адресу.

А если нужно что-то поменять?

В большинстве случаев меняете соответствующие переменные и запускаете роль снова. Повторный прогон kubespray кластер не сломает.

Заключение

Надеюсь, статья вам поможет, и вы потратите на задачу «поднять кластер на поиграться» не так много времени, сколько пришлось потратить мне.

Комментарии (13)


  1. Ru6aKa
    14.01.2022 17:47
    +1

    https://github.com/MusicDin/terraform-kvm-kubespray

    Все тоже самое и даже больше.


  1. Mnemonik
    14.01.2022 17:56
    +6

    Меня одного смутило "Bare-metal" в названии и рассказ о том как запустить всё на виртуалках?..

    Ещё конечно бы хотелось услышать обоснование аж трёх мастеров для тестового кластера, тогда как и продакшн справится с одним. Ведь вы же знаете что мастер не критичен для работы кластера, это control-plane. Им кластер управляется, то есть если мастера выключить, то всё мгновенно не исчезент в варпе, просто пропадёт возможность что-то менять и вообще доступ к API. но поды всё так же будут запущены и будут работать и отвечать? Знаете же да? Что в кластере с одним мастером если его перегрузить пока он перегружается особо ничего не произойдёт? Ну так зачем в тестовом кластере ТРИ мастера?


    1. JuriM
      15.01.2022 15:47

      Bare-metal имеется ввиду не готовое решение в облаке, а уставка кубернетес на ноды (в домашних условиях виртуалку проще создать, чем собирать железные сервера).

      Три мастера - в статье говорилось что это эмуляция кубернетеса в продакшн. И кофигурация трех мастеров это не одно и тоже, что кофигурация одного мастера


  1. GrgPlus93
    14.01.2022 18:06
    +4

    >>> Устанавливаем на bm kubernetes

    >>> Долистал до половины - все еще мануал по созданию виртуалок

    >>> Ура, устанавливаем куб - ну кароч кубспрей там в паре мест конфиги поправили и кароч оно работает, если что потом можете конфиги поправить

    Ну тупо топ инструкция я считаю, дайте еще


  1. indestructable
    14.01.2022 19:19

    Спасибо за k9s, шикарно!


  1. BubaVV
    14.01.2022 21:12

    Создание ВМок и инвентори для ансибла можно автоматизировать через https://github.com/virt-lightning/virt-lightning


  1. AlexKMK
    14.01.2022 23:17

    А почему не терраформ и опенстек?

    Скрипткидди гораздо проще бездумно скопировать .tf чем из статьи выдергивать команды и запускать в консоли ????


  1. Roman2dot0
    14.01.2022 23:31

    1. Ставим на железо убунту.

    2. Ставим microk8s

    Готово, у вас k8s bare metal. Хотите ВМок? Без проблем, kubevirt)


  1. JuriM
    15.01.2022 01:19

    Если виртуалка ставилась с какогото кастомного модифицированного имиджа, как у меня, то кьюбспрей может и не отработать. Нужна именно ванильная ось. С дебианом были проблемы, нужно переключить iptables в легаси режим.


  1. Xop
    15.01.2022 02:51

    Насколько я понимаю в качестве CNI у вас Calico? Тогда зачем нужен MetalLB, если bird в составе calico-node отлично анонсирует по iBGP весь диапазон ClusterIP, а если очень надо - то и дальше? Я дома ради общего развития упоролся и развернул куберовский кластер с мастером на сервачке на антресоли и несколькими воркерами, в том числе на еще одном совсем маленькой машинке, которая заодно играет роль роутера - и сервисы, которые деплоятся с обычным ClusterIP отлично видны с любого устройства во внутренней сети - даже если там кубера нет. При том никакой оверлейной сети нет, всё работает за счет маршрутизации.


  1. vainkop
    15.01.2022 19:17
    +1

    Официальная документация рекомендует использовать для этого minikube. Canonical продвигает свой MicroK8s. Есть ещё k0s, k3s, kind — в общем, инструменты на любой вкус.

    Все эти игрушечные кластеры подходят максимум для целей разработки или проверки концепций.

    Игрушечные? Конечно же нет. Когда слышу, что K3s подходит только для локальной машины и не серьёзных задач, то сразу понимаю, что серьезного опыта работы у вас с ним нет.

    Тут выход один — поднять нормальный кластер на виртуалках.

    Виртуалки - это прекрасная возможность потратить кучу системных ресурсов на ОС каждой из виртуалок и в целом много лишних действий, но зачем это делать, если есть, например, K3D (не путать с K3s) где можно прекрасно запустить сколько хотите master'ов и worker'ов в docker контейнерах? Говорю про Linux, где для docker не нужна никакая виртуализация.

    Можно даже поднять рядом в виртуалке, допустим, Gitlab CE, подцепить к нему ваш кластер и обкатывать CI/CD.

    Для этого достаточно поставить Gitlab runner в ваш кластер Helm'ом и это 2 строчки кода и никаких виртуалок. Впрочем, если хотите поставить целый self hosted Gitlab, то это тот же helm install в тот же кубер.

    В целом статьи на Хабре всё больше разочаровывают тем, что они про каких-то сферических коней в вакууме и совершенно не понятно в какие же такие ограничения дистрибутива того или иного кубера кто-то вообще упирается со своим hello world?


  1. vazir
    16.01.2022 19:11

    Ну, у вас несколько не bare metal, крутите то в тяжелой VM. А раз крутите убунту то вообще лучше сделать кубер типа k3s в LXC/LXD - без VM оверхеда и в 10 раз проще.

    https://github.com/corneliusweig/kubernetes-lxd/blob/master/README-k3s.md

    убунтовкий microk8s крайне не рекомендую... падуч без внятного объяснения причин, ставится через snap - который может его начать обновлять без спроса и прибьет в процессе кластер к чертям, о чем много ругани в гитхабе, и на что забила убунта


  1. dra90n7
    17.01.2022 12:18

    Как вариант вместо kvm и libvirt можно использовать lxc. Ресурсов меньше требуется.

    https://habr.com/ru/post/420913/