В этой заметке развернем кластер куба используя инструмент - kubespray. Если вы уже читали про методы установки кластера, то косвенно знакомы с kubespray. Вкратце это сборник ansible-плэйбуков и ролей, дающий нам возможности:
- Основное это установка кластера k8s, установка как актуальной версии, так у более старой версии кластера,
- Установка контейнерного runtime (Docker, Cri-o, Containerd),
- Установка сетевых решений (Calico, Flannel),
- Установка и настройка кластера etcd,
- Возможность установки дополнительных компонентов - это DNS, Ingress-контроллеры, Cert-менеджеры, Storage-драйверы и прочее.
- Добавление/Удаление нод кластера.
- Обновеление кластера.
Стоит сделать отступление относительно установки дополнительных компонентов, все же рекомендуют накатывать компоненты независимо от kubespray. Так как обновление или обычная замена версии компонента, может привести к проблемкам.
Подготовка серверов
Если же говорить про продакшен сетап кластера, то рекомендуется использовать некое количество нод:
- 3/5 - нод кластера под Control Plane,
- 3/5 - экземпляров etcd,
- 2 - ноды под Ingress-контроллер,
- 2 - ноды под рабочие нагрузки, worker-ноды.
Для запуска тестового кластера я выделил сервера. Три ноды непосредственно под сам кластер и одна маленькая тачка под менеджмент и установку кластеров:
Server name | Server IP | Short comment |
---|---|---|
k8s-node1 | 192.168.122.51 | K8s node master, etcd |
k8s-node2 | 192.168.122.52 | K8s worker node |
k8s-node3 | 1192.168.122.53 | K8s worker node |
k8s-bootstrap | 192.168.122.5 | K8s, bootstrap server |
Итак на всех серверах нужно провести ряд подготовительных работ. Во первых это отключить firewalld, сам кубернетес за счет iptables будет разруливать правилами. Далее отключаем swap-раздел, так как kubelet не умеет работать со свапом.
# Отключаем firewalld
[All Cluster Servers]# systemctl disable --now firewalld
# Отключаем swap, также не забываем закоментировать в /etc/fstab
[All Cluster Servers]# swapoff -a
Что касаемо используемого дистрибутива, я этом сетапе я использую - Almalinux 8.6 (К сожалению, только такой заготовленный шаблон был =). Ну одно из важных требований к установки - это версия ядра, в рекомендациях допускается к использованию ядро выше версии 4.20. У меня к сожалению текущая версия ядра 4.18:
Linux k8s-node3.domain.local 4.18.0-372.32.1.el8_6.x86_64 #1 SMP Tue Oct 25 05:53:57 EDT 2022 x86_64 x86_64 x86_64 GNU/Linux
Поэтому будем обновлять ядро.
Более-менее актуальное ядро будем ставить из репозитория elrepo, импортируем gpg-ключи и устанавливаем репозиторий.
[All Cluster Servers]# rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
[All Cluster Servers]# yum install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm
Ну и запускаем установку нового ядра из этой репы. В данной инсталяции я использую long term релиз ядра.
[All Cluster Servers]# yum --enablerepo=elrepo-kernel install kernel-lt
Перезапускаем сервак, и смотрим текущую версию ядра:
[All Cluster Servers]# uname -r
5.4.242-1.el8.elrepo.x86_64
Далее разрешаем форвард межсетевого трафика между интерфейсами:
[All Cluster Servers]# echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
Запускать kubespray будем из под пользователя - root, поэтому я разрешил логин из под рута. Далее сгенерил пару ключей и публичную часть ключа раскидал на сервера:
# Генерим новый ключ
[root@k8s-bootstrap ~]# ssh-keygen -t rsa -b 2048 -C "K8s, bootstrap srv"
# Раскидываем ключи на сервера
[root@k8s-bootstrap ~]# ssh-copy-id [email protected]
[root@k8s-bootstrap ~]# ssh-copy-id [email protected]
[root@k8s-bootstrap ~]# ssh-copy-id [email protected]
Установка кластера
В первую очередь нам нужно заполучить репозиторий, по этому идем на гибхаб страничку kubespray, и клонируем репозиторий:
[root@k8s-bootstrap ~]# yum install git
[root@k8s-bootstrap ~]# git clone https://github.com/kubernetes-sigs/kubespray
Обратите внимание, что все делаем на bootstrap-сервере. Запуск кубеспрея, логично, тоже будет запускать с этого сервака.
Теперь проваливаемся внутрь репозитория, и начинаем ставить зависимости python.
# Ставим сам питон
[root@k8s-bootstrap kubespray]# yum install python39
# Запускаем установку зависимостей и ожидаем окончание процесса..
[root@k8s-bootstrap kubespray]# pip3 install -r requirements.txt
Настройка inventory
Внутри репозитория будет содержаться каталог - inventory, в котором мы и будем описывать наши сервера и настройки кластера.
Авторы kubespray во внутрь инвертаря заложили семпл каталог для примера. Копируем семпл, имя для нового каталога указываем аналогично имени кластера:
[root@k8s-bootstrap kubespray]# cp -rp inventory/sample/ inventory/k8s-dev-cluster
Смотрим, что у нас есть:
[root@k8s-bootstrap kubespray]# tree inventory/k8s-dev-cluster/ -L 1
inventory/k8s-dev-cluster/
├── group_vars
├── inventory.ini
└── patches
Внутри нашего инвенторя есть основные компоненты содержимого на сегодня:
inventory.ini
- файл с инвентарем, содержащий настройки подключения к серверам и список самих серверов.group_vars
- в этом каталоге содержать переменные для установок различных характеристик нашего кластера.
На данном этапе нам требуется заполнить файл с инвентарем. Сделать это мы можем двумя способами.:
- Первый способ за счет ручного редактирования файла inventory.ini.
- Второй способ через питоновский скрипт, который входит в состав этой репы. Этот скрипт якобы упрощает нам заполнение инвенторя от части это так, но мне все равно по факту приходилось редактировать полученный файл с хостами.
Мы же пойдем первым способом, и просто ручками отредактируем inventory.ini. Открываем инвентарь, и прописываем наши сервера. Далее указываем роли для них.
[root@k8s-bootstrap kubespray]# vim inventory/k8s-dev-cluster/inventory.ini
---
[all]
k8s-node1 ansible_host=192.168.122.51 ip=192.168.122.51
k8s-node2 ansible_host=192.168.122.52 ip=192.168.122.52
k8s-node3 ansible_host=192.168.122.53 ip=192.168.122.53
[kube_control_plane]
k8s-node1
[etcd]
k8s-node1
[kube_node]
k8s-node2
k8s-node3
[k8s_cluster:children]
kube_control_plane
kube_node
По итогу инвентарь должен выглядить таким образом. В контексте all
, идет перечесление всех нод кластера. Далее уже в отдельных контекстах группируем ноды. Здесь kubespray засетапит control plane и etcd на первую ноду. Остальные сервера настроит как обычные ноды куба.
Настройка group_vars
Теперь перейдем к настройке переменных в каталоге group_vars
:
[root@k8s-bootstrap kubespray]# tree inventory/k8s-dev-cluster/group_vars/ -L 1
inventory/k8s-dev-cluster/group_vars/
├── all
├── etcd.yml
└── k8s_cluster
В ansible переменные для групп хостов могут быть обозначаны в обычном yml-файле, или же в отдельном каталоге с названием группы. Внутри группы содержаться множество файлов с переменными.
Так например, каталог - all внутри его определяются переменные которые применяются всех хостов инвенторя. Переменые вынесены по отдельным атомарным файлам, и влияют на конфигурацию того или иного сервиса. Если же взглянуть во внутрь docker.yml
, файл содержит переменные влиящие на настройку докера.
inventory/k8s-dev-cluster/group_vars/all
├── all.yml
├── aws.yml
├── azure.yml
├── containerd.yml
├── coreos.yml
├── cri-o.yml
├── docker.yml
├── etcd.yml
└── vsphere.yml
Далее файл etcd.yml
, содержит набор переменных для настройки etcd кластера.
И самое интересное это группа k8s_cluster
, здесь лежат все переменные для настройки серверов, на которых будет запущен k8s кластер. В этой группе и будем вносить все последующие изменения.
[root@k8s-bootstrap kubespray]# vim inventory/k8s-dev-cluster/group_vars/k8s_cluster/k8s-cluster.yml
---
cluster_name: k8s-dev-cluster
kube_version: v1.26.3
kube_network_plugin: flannel
kube_proxy_mode: iptables
kube_service_addresses: 10.233.0.0/18
kube_pods_subnet: 10.233.64.0/18
dns_mode: coredns
kubeconfig_localhost: true
Большинство переменных уже определено по умолчанию, но все же нужные мне переменные я стараюсь перепроведить и подправить походу.
cluster_name
- тут в значении указывается имя кластера,kube_version
- версия кластера, по умолчанию стоит самая последняя версияkube_network_plugin
- используемый сетевой плагин (cni). В этом сетапе я буду использовать flannel. Этот плагин намного проще, и идеально вписывается в нашу конценцию где все ноды кластера работают из одной сети.kube_proxy_mode
- режим проксирования, по умолчанию стоит ipvs. Я всеже предпочитаю iptables.kube_service_addresses
- здесь прописываем адресный пул, ip-адреса из которой будут выдаваться нашим сервисом с режимом работы ClusterIP.kube_pods_subnet
- и здесь указывается пул, из которого будут выдаваться адреса для подов.dns_mode
- здесь указываем dns сервер, который будет обслуживать наш кластерkubeconfig_localhost
- а включение этой опции, сгенерит kube-конфиг для подключения к кластеру.
Теперь нужно внести изменения в файл с описанием переменных, которые настраивают Flannel. Как я ранее говорил, все настройки сгруппированные по отдельным файлам. Открываем файл с переменнымы для flannel, и раскомментируем:
[tony@i3Arch kubespray]$ vim inventory/k8s-dev-cluster/group_vars/k8s_cluster/k8s-net-flannel.yml
---
flannel_interface_regexp: '192\\.168\\.122\\.\\d{1,3}'
flannel_backend_type: "host-gw"
flannel_backend_type
- здесь мы переопределяем режим работы плагина, на режим host gateway.flannel_interface_regexp
- эта регулярка описывающая, в какой сети у меня будут подняты сервера
В процессе установки кластера столкнулся с проблемой настройки flannel. По этой проблеме есть issue на гитхабе kubespray - github. Что бы исправить это нужно в файле - roles/download/defaults/main.yml, подправить путь к образу flannel:
Найти: flannel_image_repo: "{{ docker_image_repo }}/flannelcni/flannel"
Заменить на: flannel_image_repo: "{{ docker_image_repo }}/flannel/flannel"
И еще одна опциональная настройка. Если ваши сервера также как и у меня спрятаны за семью прокси, то дополнительно в group_vars нужно указать параметры подключения к прокси:
[root@k8s-bootstrap kubespray]# vim inventory/k8s-dev-cluster/group_vars/all/all.yml
---
https_proxy: "srv-proxy.local:3357"
Запуск kubespray и первичный осмотр
После всех приготовлений можем запустить установку кластера.
Я запускаю ssh агента, и добавляю ключ для него.
[root@k8s-bootstrap kubespray]# eval `ssh-agent`
[root@k8s-bootstrap kubespray]# ssh-add ../.ssh/id_rsa
Ну и запускаю сценарий на ansible:
[root@k8s-bootstrap kubespray]# ansible-playbook -i inventory/k8s-dev-cluster/inventory.ini -u root cluster.yml
Установка будет займет от 10-20 минут времени. Поэтому идем пить кофе, в надежде что нечего не сфейлится. По окончанию установки нас будет ждать вот такой recap ансибла:
PLAY RECAP **********************************************************************************
k8s-node1 : ok=741 changed=135 unreachable=0 failed=0 skipped=1248 rescued=0 ignored=8
k8s-node2 : ok=512 changed=79 unreachable=0 failed=0 skipped=763 rescued=0 ignored=1
k8s-node3 : ok=512 changed=79 unreachable=0 failed=0 skipped=762 rescued=0 ignored=1
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
В каталоге inventory/k8s-dev-cluster/artifacts/
лежит конфиг для подключения к кластеру, который нам любезно сгенерил kubespray.
Используя этот конфиг мы можем обратится на api сервер нашего кластера прямо с ансибл сервера, через kubectl утилиту:
[root@k8s-bootstrap ~]# KUBECONFIG=kubespray/inventory/k8s-dev-cluster/artifacts/admin.conf kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-node1 Ready control-plane 1h v1.26.3 192.168.122.51 <none> AlmaLinux 8.6 (Sky Tiger) 5.4.242-1.el8.elrepo.x86_64 containerd://1.7.0
k8s-node2 Ready <none> 1h v1.26.3 192.168.122.52 <none> AlmaLinux 8.6 (Sky Tiger) 5.4.242-1.el8.elrepo.x86_64 containerd://1.7.0
k8s-node3 Ready <none> 1h v1.26.3 192.168.122.53 <none> AlmaLinux 8.6 (Sky Tiger) 5.4.242-1.el8.elrepo.x86_64 containerd://1.7.0
Как можно заметить из вывода, у наших worker нодах неопределена роль текущий статус - <none>
. Честно говоря, я не нашел причину такого поведения, но поправить этот момент можно путем релэйблинга ноды:
[root@k8s-bootstrap ~]# export KUBECONFIG=kubespray/inventory/k8s-dev-cluster/artifacts/admin.conf
[root@k8s-bootstrap ~]# kubectl label nodes k8s-node2 kubernetes.io/role=worker
[root@k8s-bootstrap ~]# kubectl label nodes k8s-node3 kubernetes.io/role=worker
Теперь давайте просмотрим на компоненты и текущее состояние нашего кластера. Выведим список всех namespaces:
[root@k8s-bootstrap ~]# kubectl get ns
NAME STATUS AGE
default Active 14h
kube-node-lease Active 14h
kube-public Active 14h
kube-system Active 14h
default
- это namespace, который используется по умолчанию. Если при запуске приложения мы не указываем нужный namespace, то поды будут размещены в этом пространстве.kube-node-lease
- namespace, который содержит lease-объекты для каждой ноды.kube-public
- данный namespace создается автоматически, и доступен для всех пользователей (даже неаутентифицированных). Этот неймспэйс может быть использован, когда создаваемые объекты должны быть доступны для всего кластера.kube-system
- это системный неймспейс, в котором обитают все компоненты control plane.
Посмотрим, какие поды запущены в kube-system
:
[root@k8s-bootstrap ~]# kubectl get po -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-645b46f4b6-9b56x 1/1 Running 0 3h43m
coredns-645b46f4b6-lf469 1/1 Running 0 3h43m
dns-autoscaler-659b8c48cb-zhp8t 1/1 Running 0 3h43m
kube-apiserver-k8s-node1.local 1/1 Running 2 3h44m
kube-controller-manager-k8s-node1.local 1/1 Running 2 3h44m
kube-flannel-7l2t8 1/1 Running 0 3h43m
kube-flannel-9qbvz 1/1 Running 0 3h43m
kube-flannel-z7zmz 1/1 Running 0 3h43m
kube-proxy-2z88x 1/1 Running 0 3h44m
kube-proxy-pztwt 1/1 Running 0 3h44m
kube-proxy-v6g5l 1/1 Running 0 3h44m
kube-scheduler-k8s-node1.local 1/1 Running 1 3h44m
nginx-proxy-k8s-node2.local 1/1 Running 0 3h43m
nginx-proxy-k8s-node3.local 1/1 Running 0 3h43m
nodelocaldns-bwsgc 1/1 Running 0 3h43m
nodelocaldns-sqlfj 1/1 Running 0 3h43m
nodelocaldns-zcf29 1/1 Running 0 3h43m
Вывод этой команды вернет нам список запущенных статик подов, то есть подов которые были запущены самим kubelet из манифеста.
Здесь же можем увидеть запущенные по одному экземпляру - apiserver, controller-manager, scheduler. Coredns - запущенный dns отвечающий за service discovery, dns-autoscaler компонент который мониторит количество узлов в кластере и если мы добаляем новую ноду в кластер, то он увеличивает поды coredns. nginx-proxy - специальный прокси, который запускается на каждом узле и обеспечивает постоянную связь kubelet с api-сервером. Ощутимая польза от использования этой штуки, когда в кластере используется 3 api-сервер, и с помошью этого компоненты kubelet всегда сможет кинуть запрос на api.
Отлично, мы подняли кластер за 30 минут для дальнейших экспериментов.