ALT Container OS подветка K8S. Создание HA кластера
Настройка узлов
Создание ethernet-моста (bridge)
Для выделения IP-адресов узлов кластера в рамках IP-адресов локальной сети необходимо в интерфейсе создания сетевых интерфейсов в HOST-системе создать мост (например, br0) и связать с ним основной ethernet-интерфейс локальной сети:
В данном примере IP-адрес HOST-системы — 10.150.0.4/24, подсеть — 10.150.0.0/24.
В дальнейшем при создании виртуальных машин в пункте конфигурирования сетевого интерфейса укажите:
- Создать на базе: Устройство моста
- Название устройства: br0
- Состояние связи: активно
При развертывания виртуальных машин им будут присваиваться статические адреса из подсети 10.150.0.0/24.
Конфигурирование параметров ядра
Проверьте в sysctl настройку переменных ядра:
# sysctl -a | grep net.bridge.bridge-nf-call
net.bridge.bridge-nf-call-arptables = 0
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
Перечисленные переменные должны иметь нулевое значение, иначе связь между виртуальными машинами может фильтроваться правилами iptables, arptables.
Если вывод команды пустой, подключите модуль br_netfilter:
# modprobe br_netfilter
и обеспечьте загрузку этого модуля после перезагрузки.
Если часть переменных имеет ненулевые значения, сформируйте файл /etc/sysctl.d/99-sysctl.conf:
# # Configure kernel parameters at boot. # See sysctl.d(5) for more details. # net.bridge.bridge-nf-call-ip6tables = 0 net.bridge.bridge-nf-call-iptables = 0 net.bridge.bridge-nf-call-arptables = 0
и запустите команду:
# sysctl -f /etc/sysctl.d/99-sysctl.conf
Конфигурирование балансировшика
Для работы высокодоступного (Highly Available) kubernetes-кластера необходимо на всех трех узлах поднять балансировщик, распределяющий входящие запросы между мастер-узлами кластера. Для поддержки отказоустойчивости самого балансировщика на HOST-системах запускаются балансировщики. Одному их балансировщику присваивается виртуальный адрес 10.150.0.160. При выходе из строя или потери доступности основного балансировщика виртуальный адрес присваивается другому доступному балансировщику.
Для создания балансировщика установите пакеты haproxy и keepalived:
# apt-get install haproxy keepalived
Конфигурирование haproxy
Создайте файл конфигурации haproxy /etc/haproxy/haproxy.cfg:
# /etc/haproxy/haproxy.cfg #--------------------------------------------------------------------- # Global settings #--------------------------------------------------------------------- global log /dev/log local0 log /dev/log local1 notice daemon #--------------------------------------------------------------------- # common defaults that all the 'listen' and 'backend' sections will # use if not designated in their block #--------------------------------------------------------------------- defaults mode http log global option httplog option dontlognull option http-server-close option forwardfor except 127.0.0.0/8 option redispatch retries 1 timeout http-request 10s timeout queue 20s timeout connect 5s timeout client 20s timeout server 20s timeout http-keep-alive 10s timeout check 10s #--------------------------------------------------------------------- # apiserver frontend which proxys to the control plane nodes #--------------------------------------------------------------------- frontend apiserver bind *:8443 mode tcp option tcplog default_backend apiserver #--------------------------------------------------------------------- # round robin balancing for apiserver #--------------------------------------------------------------------- backend apiserver option httpchk GET /healthz http-check expect status 200 mode tcp option ssl-hello-chk balance roundrobin server master01 10.150.0.161:6443 check server master02 10.150.0.162:6443 check server master03 10.150.0.163:6443 check
В секции balance roundrobin укажите список имен, IP-адресов и портов 6443 API-интерфейсов мастер-узлов.
haproxy будет принимать входящие соединения на порту 8443 и передавать их на один из перечисленных master-серверов на порт 6443.
Конфигурирование keepalived
Создайте файл конфигурации 'keepalived' /etc/keepalived/keepalived.conf:
! /etc/keepalived/keepalived.conf ! Configuration File for keepalived global_defs { router_id LVS_K8S } vrrp_script check_apiserver { script "/etc/keepalived/check_apiserver.sh" interval 3 weight -2 fall 10 rise 2 } vrrp_instance VI_1 { state MASTER interface br0 virtual_router_id 51 priority 101 authentication { auth_type PASS auth_pass 42 } virtual_ipaddress { 10.150.0.160 } track_script { check_apiserver } }
На одном из узлов установите параметр state в значение MASTER и параметр priority в значение 101. На остальных параметр state в значение BACKUP и параметр priority в значение 100.
Скрипт /etc/keepalived/check_apiserver.sh проверяет доступность балансировщика haproxy:
#!/bin/sh errorExit() { echo "*** $*" 1>&2 exit 1 } APISERVER_DEST_PORT=8443 APISERVER_VIP=10.150.0.160 curl --silent --max-time 2 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/" if ip addr | grep -q ${APISERVER_VIP}; then curl --silent --max-time 2 --insecure https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/" fi
Параметр APISERVER_DEST_PORT задает порт балансировщиков haproxy, параметр APISERVER_VIP виртуальный адрес, через который будут взаимодействовать master (control plane) узлы кластера k8s.
Скрипт проверяет работоспособность haproxy на локальной машине. А если в настоящее время виртуальный адрес принадлежит текущему узлу, то и работоспособность haproxy через виртуальный адрес.
Добавьте флаг на выполнение скрипта:
# chmod a+x /etc/keepalived/check_apiserver.sh
При работающем балансировщике и хотя бы одному доступному порту 6443 на master-узлах скрипт должен завершаться с кодом 0.
Запуск сервисов
Для запуска сервисов наберите команды:
# systemctl enable haproxy --now
# systemctl enable keepalived --now
Так как master-узлы k8s еще не подняты haproxy выведет на консоль сообщение:
haproxy[nnnn]: backend apiserver has no server available!
Конфигурирование и запуск кластера
Получение образов ветки kubetnetes
Скачать образ можно тут
Описание базовых butane YML-файлов
На основе базовых butane YML-файлов программой butane создаются ignition-файлы, используемые для создания конечных 'master_NN.ign', 'worker_NN.jgn' ignition-файлов для разворачивания master и worker узлов кластера.
Файл users.yml описания пользователя altcos
yml описания пользователя altcos имеет следующую структуру:
variant: fcos version: 1.3.0 passwd: users: - name: altcos groups: - wheel - docker password_hash: ... ssh_authorized_keys: - ssh-rsa ... - ssh-rsa ... storage: files: - path: /etc/sudoers.d/altcos contents: inline: | altcos ALL=NOPASSWD: ALL
В поле password_hash помещается хеш-пароля, сгенерированный командой
mkpasswd --method=yescrypt
В поле ssh_authorized_keys — массив открытых ключей пользователей, для которых необходим беспарольный доступ к пользователю altcos виртуальной машины.
В файл /etc/sudoers.d/altcos записывается строка, обеспечивающая беспарольный доступ пользователя altcos к sudo.
Файл hosts.yml описания имен и IP-адресов узлов
Файл hosts.yml содержит строки привязки имен и IP-адресов виртуальных машин в файле /etc/hosts:
variant: fcos version: 1.3.0 storage: files: - path: /etc/hosts append: - inline: | 10.150.0.161 master01 10.150.0.162 master02 10.150.0.163 master03 10.150.0.171 worker01 10.150.0.172 worker02 10.150.0.173 worker03
Файл btrfs.yml инициализации btrfs тома
variant: fcos version: 1.3.0 storage: disks: - device: /dev/sdb # создадим на диске /dev/sdb партицию /dev/sdb1 wipe_table: true partitions: - number: 1 label: docker filesystems: - device: /dev/sdb1 # создадим в партиции /dev/sdb1 файловую систему BTRFS format: btrfs wipe_filesystem: true label: docker with_mount_unit: false directories: - path: /var/mnt/docker # создадим каталог монтирования тома overwrite: true files: - path: /etc/fstab # добавим строку монтирования btrfs-тома на каталог /var/mnt/docker append: - inline: | LABEL=docker /var/mnt/docker btrfs defaults 0 2 /var/mnt/docker/docker/ /var/lib/docker none bind 0 0 /var/mnt/docker/containers/ /var/lib/containers/ none bind 0 0 # заменим в конфигурации dockerd-демона: # тип storage-driver с overlay2 на btrfs - path: /etc/docker/daemon.json overwrite: true contents: inline: | { "init-path": "/usr/bin/tini", "userland-proxy-path": "/usr/bin/docker-proxy", "default-runtime": "docker-runc", "live-restore": false, "log-driver": "journald", "runtimes": { "docker-runc": { "path": "/usr/bin/runc" } }, "default-ulimits": { "nofile": { "Name": "nofile", "Hard": 64000, "Soft": 64000 } }, "data-root": "/var/lib/docker/", "storage-driver": "btrfs" } # заменим в конфигурации CRI-O тип driver с overlay на btrfs - path: /etc/crio/crio.conf.d/00-btrfs.conf overwrite: true contents: inline: | [crio] root = "/var/lib/containers/storage" runroot = "/var/run/containers/storage" storage_driver = "btrfs" storage_option = [] [crio.runtime] conmon = "/usr/bin/conmon" [crio.network] plugin_dirs = [ "/usr/libexec/cni", "/opt/cni/bin/" ] # заменим в конфигурации podman тип driver с overlay2 на btrfs - path: /etc/containers/storage.conf overwrite: true contents: inline: | [storage] driver = "btrfs" runroot = "/var/run/containers/storage" graphroot = "/var/lib/containers/storage" [storage.options] additionalimagestores = [ ] [storage.options.overlay] mountopt = "nodev,metacopy=on" # исключим определение flannel-подсети в CRIO - path: /etc/cni/net.d/100-crio-bridge.conf overwrite: true contents: inline: | {"type": "bridge"}
В текущей версии пакета cri-o-1.22.1-alt1.x86_64 файл конфигурации /etc/cni/net.d/100-crio-bridge.conf задает
адрес подсети для интерфейса cni0. Для корректной работы сетевого плугина flannel необходимо исключить определение
подсети, так как flannel самостоятельно на основе полученной конфигурации сети конфигурирует IP-адрес интерфейса cni0.
Plugin flannel конфигурирования cni-интерфайса в образе rancher/mirrored-flannelcni-flannel-cni-plugin располагается в каталоге /opt/cni/bin/. В обычной Linux-системе при запуске POD'а это plugin копируется в каталог /usr/libexec/cni откуда он загружается plugin'ом CRI (Container Runtime Interface). В ALTCOS каталог /usr смонтирован в режиме только не чтение и копирование не производится. Для решения этой проблемы в подключаемых узлах добавляется файл конфигурации /etc/crio/crio.conf.d/00-btrfs.conf. Он настраивает `cri-o` на использование файловой системы btrfs и в параметре plugin_dirs для подключения plugins кроме стандартного каталога /usr/libexec/cni использует каталог /opt/cni/bin/.
Файл initk8s.yml описания сервиса инициализации кластера k8s с сетью flannel
Файл описывает сервис initk8s инициализации кластера и может быть включен только в YML-файл 1-го master-узла кластера master01.
variant: fcos version: 1.3.0 systemd: units: - name: initk8s.service enabled: false contents: | [Unit] Description=Start up kubernetes in master mode After=crio.service kube-proxy.service kubelet.service systemd-networkd.service systemd-resolved.service [Service] Type=oneshot RemainAfterExit=yes Environment="KUBECONFIG=/etc/kubernetes/admin.conf" ExecStartPre=loadDockerArchiveImages.sh ExecStart=kubeadm init --cri-socket=/var/run/crio/crio.sock \ --pod-network-cidr=10.244.0.0/16 \ --control-plane-endpoint 10.150.0.160:8443 \ --upload-certs ExecStartPost=kubectl apply -f /usr/share/k8s/flannel/kube-flannel.yml #ExecStartPost=kubectl taint nodes master01 node-role.kubernetes.io/master- [Install] WantedBy=multi-user.target
Если вы разворачиваете кластер в локальной сети без доступа или с низкоскоростным доступом в Интернет, то необходимо оставить вызов скрипта loadDockerArchiveImages.sh разворачивания архивов docker-образов, хранящихся в ostree-образе.
Если Вы планируете загрузить docker-образы из Интернет, то строку ExecStartPre можно удалить.
В отличие от варианта разворачивания кластера с одним master-узлом в команду kubeadm init добавлены параметры:
- control-plane-endpoint в котором указан URL основного haproxy-сервера, работающего на виртуальном адресе 10.150.0.160 и предоставляющий доступ на порту 8443.
- upload-certs, обеспечивающий загрузку сгенерированных сертификатов и раздачу их другим master-узлам.
Сетевой драйвер flannel обеспечивает выделение адресов для POD'ов и роутинг в рамках подсети 10.244.0.0/16. Данная подсеть при инициализации кластера указывается параметром pod-network-cidr.
При подключении узла к кластеру сетевой драйвер flannel запрашивает у планировщика подсетку ``10.244.x.0/24
и согласно полученной подсетке он
- создает интерфейс flannel.x и присваевает ему адрес ``10.244.x.0/32
- при создании первого POD'а конфирурирует интерфейс cni0 с адресом 10.244.x.1/32.
Все создаваемые на этом узле POD'ы получают IP-адреса 10.244.x.2-255/32.
Файл k8senv.yml создание profile инициализации kubernetes-среды
variant: fcos version: 1.3.0 storage: files: - path: /etc/profile.d/kube.sh mode: 0755 contents: inline: | # Set kube environment if [ `id -u ` = 0 ] then export KUBECONFIG=/etc/kubernetes/admin.conf else if [ -f /etc/kubernetes/admin.conf ] then if [ ! -d ~/.kube ] then mkdir -p ~/.kube sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config sudo chown $(id -u):$(id -g) ~/.kube/config kubectl completion bash >> ~/.bashrc fi fi fi
Файл обеспечивает создание профайла /etc/profile.d/kube.sh. Для привелигированного пользователя (sudo -i bash) в среду добавляется переменная KUBECONFIG. Для непривилигиванного пользователя обеспечивается формирование каталога .kube.
Файл mastersUsers.yml описания открытых ключей мастер-узлов кластера
Данный файл обеспечивает для пользователя altcos беспарольный доступ с master-узлов на 'worker'-узлы. Структура файла следующая:
variant: fcos version: 1.3.0 passwd: users: - name: altcos ssh_authorized_keys: - ssh-rsa ... altcos@master01 - ssh-rsa ... altcos@master02 - ssh-rsa ... altcos@master03
В него помещаются открытые ключи пользователей altcos master-узлов кластера.
Конфигурирование и запуск мастер-узлов
Создание butane-YML файлов описания конфигурации master-узлов
YML-файл master_01.yml для первого узла master01:
variant: fcos version: 1.3.0 ignition: config: merge: - local: users.ign - local: hosts.ign - local: btrfs.ign - local: k8senv.ign - local: initk8s.ign storage: files: - path: /etc/hostname overwrite: true contents: inline: master01 - path: /etc/systemd/network/20-wired.network overwrite: true contents: inline: | [Match] Name=eth0 [Network] DHCP=no Address=10.150.0.161/24 Gateway=10.150.0.1 DNS=10.150.0.1
В элементе ignition.config.merge описываются ignition-файлы сгенерированные командой butane из вышеперечисленных YML-файлов.
В файле /etc/hostname указывается имя узла master01. В файле /etc/systemd/network/20-wired.network описываются адрес Address узла, шлюз Gateway и DNS для узла кластера.
Остальные YML-файлы конфигурации master-узлов master_02.yml, master_03.yml описываются схожим образом за исключением:
- файл описания сервиса local: initk8s.ign не подключается, так как инициализация кластера производится на узле master01;
- в файле /etc/hostname указываются имена узлов master02 и master03 соответственно;
- в файле '/etc/systemd/network/20-wired.network в поле Address указываются IP-адреса master-узлов 10.150.0.162 и 10.150.0.163 соответственно.
Пример YML-файл master_02.yml для первого узла master02:
variant: fcos version: 1.3.0 ignition: config: merge: - local: users.ign - local: hosts.ign - local: btrfs.ign - local: k8senv.ign storage: files: - path: /etc/hostname overwrite: true contents: inline: master02 - path: /etc/systemd/network/20-wired.network overwrite: true contents: inline: | [Match] Name=eth0 [Network] DHCP=no Address=10.150.0.162/24 Gateway=10.150.0.1 DNS=10.150.0.1
Скрипт createNode.sh запуска master и worker узлов
Исходный код скрипта:
#!/bin/sh case $1 in master) Mem=4096;break;; worker) Mem=6144;break;; *) echo "Тип нод может быть только master или worker"; exit 1; esac N=`printf "%02d" $2 2>/dev/null` if [ $N = '00' ] then echo "Номер ноды должен быть числовым и больше нуля"; exit 1; fi if [ ! -f $3 ] then echo "Образ $3 не существует";exit 1; fi IMAGE=$3 prefix=${type}_${N} diskSize=30G sdaDisk=${prefix}_sda.qcow2 sdbDisk=${prefix}_sdb.qcow2 ignFile=$PWD/${prefix}.ign ymlfiles="users k8senv btrfs hosts initk8s mastersUsers $prefix" for ymlfile in $ymlfiles do if ! butane -d . -p $ymlfile.yml > $ymlfile.ign then exit 1; fi done cp $IMAGE $sdaDisk qemu-img create -f qcow2 $sdbDisk $diskSize virt-install --name $prefix \ --vcpus 2 \ --ram $Mem \ --os-variant altlinux1.0 \ --import \ --disk $sdaDisk \ --disk $sdbDisk \ --vnc \ --qemu-commandline="-fw_cfg name=opt/com.coreos/config,file=$ignFile"
Скрипт принимает три параметра:
- Тип узла - master или worker.
- Номер узла (1, 2, 3, ...).
- Тропа до файла qcow2-образа.
Скрипт на основе типа виртуальной машины и ее номера формирует prefix ${type}_${N} (например master_01) который используется для определения:
- имен основных yml и ignition файлов;
- имен основного (/dev/sda) и дополнительного btrfs (/dev/sdb) qcow2-файла дисков;
- имен виртуальных машин.
Далее производятся следующие действия:
- Указанный 3-м параметром файл-образ копируется в файл загрузочного qcow2-образа.
- Команда qemu-img create ... создает образ btrfs-диска указанного размера.
- Команда virt-install --name $prefix ... создает виртуальную машину, передавая ignition файл конфигурации ${prefix}.ign.
Запуск виртуальных машин master-узлов
В HOST-системах NODE3 10.140.0.3, NODE4 10.140.0.4, NODE5 10.140.0.5 запустите виртуальные машины командами:
- NODE3:
createNode.sh master 1 images/k8s.qcow2
- NODE4:
createNode.sh master 2 images/k8s.qcow2
- NODE5:
createNode.sh master 3 images/k8s.qcow2
Генерация ssh-ключей и их использование
Дождитесь запуска виртуальных машин (несколько минут) и зайдите под пользователем altcos на них:
- NODE3:
ssh altcos@10.150.0.161
- NODE4:
ssh altcos@10.150.0.162
- NODE5:
ssh altcos@10.150.0.163
Сгенерируйте ssh-ключи командой:
ssh-keygen
Передайте открытые ключи на остальные master-узлы:
- master01 (10.150.0.161):
ssh-copy-id master02 ssh-copy-id master03
- master02 (10.150.0.162):
ssh-copy-id master01 ssh-copy-id master03
- master03 (10.150.0.163):
ssh-copy-id master01 ssh-copy-id master02
Добавьте открытый ключи из файла ~altcos/.ssh/id_rsa.pub в файл mastersUsers.yml описания открытых ключей мастер-узлов кластера.
Иниализация кластера на первом узле master01
На узле master01 (10.150.0.161) запустите сервис initk8s:
sudo systemctl start initk8s
Инициализация кластера производится несколько минут. После окончания проверьте наличие одного узла в кластере:
kubectl get nodes NAME STATUS ROLES AGE VERSION master01 Ready control-plane,master 1m v1.20.8
Подключение остальных master-узлов
На узле master01 (10.150.0.161) запустите сервис initk8s:
journalctl -u initk8s
Найдите в логах строки подключения master-узлов:
kubeadm join 10.150.0.160:8443 --token ... \ --discovery-token-ca-cert-hash sha256:... \ --control-plane --certificate-key ...
добавьте к ним параметр --cri-socket=/var/run/crio/crio.sock и запишите полученную команду в переменную
joinmaster='kubeadm join ...'
Загрузите необходимые docker-образы на узлах master02, master03:
ssh master02 sudo loadDockerArchiveImages.sh ssh master03 sudo loadDockerArchiveImages.sh
Подключите дополнительные master-узлы командами:
ssh master02 sudo $joinmaster ssh master03 sudo $joinmaster
Подключение master'ов происходит за пару минут.
После подключения проверьте список узлов кластера:
kubectl get nodes NAME STATUS ROLES AGE VERSION master01 Ready control-plane,master 3h9m v1.20.8 master02 Ready control-plane,master 179m v1.20.8 master03 Ready control-plane,master 178m v1.20.8
Конфигурирование и запуск рабочих (worker) узлов
Создание butane-YML файлов описания конфигурации worker-узлов
YML-файл для первого узла worker01:
variant: fcos version: 1.3.0 ignition: config: merge: - local: users.ign - local: btrfs.ign - local: hosts.ign - local: mastersUsers.ign storage: files: - path: /etc/hostname overwrite: true contents: inline: worker01 - path: /etc/systemd/network/20-wired.network overwrite: true contents: inline: | [Match] Name=eth0 [Network] DHCP=no Address=10.150.0.171/24 Gateway=10.150.0.1 DNS=10.150.0.1
Отличие он конфигурации master-файлов:
- отсутствует подключение ignition-файлов k8senv.ign, initk8s.ign;
- подключается файл mastersUsers.ign описания открытых ключей мастер-узлов кластера.
Аналогичным образом конфигурируются файлы для worker02, worker03.
Запуск виртуальных машин worker-узлов
В HOST-системах NODE3 10.140.0.3, NODE4 10.140.0.4, NODE5 10.140.0.5 запустите виртуальные worker-машины командами:
- NODE3:
createNode.sh worker 1 images/k8s.qcow2
- NODE4:
createNode.sh worker 2 images/k8s.qcow2
- NODE5:
createNode.sh worker 3 images/k8s.qcow2
Подключение worker-узлов
Зайдите на один из master-узлов. например на master01. Запишите в переменную joinworker строку подключения:
joinworker="`kubeadm token create --print-join-command` --cri-socket=/var/run/crio/crio.sock"
Подключите дополнительные worker-узлы командами:
ssh worker01 sudo $joinworker ssh worker02 sudo $joinworker ssh worker03 sudo $joinworker
Подключение worker'ов происходит менее минуты.
После подключения проверьте на одном из master-узлов список узлов кластера:
kubectl get nodes NAME STATUS ROLES AGE VERSION master01 Ready control-plane,master 3h9m v1.20.8 master02 Ready control-plane,master 179m v1.20.8 master03 Ready control-plane,master 178m v1.20.8 worker01 Ready <none> 180m v1.20.8 worker02 Ready <none> 185m v1.20.8 worker03 Ready <none> 189m v1.20.8