Установка Kubernetes в отказоустойчивой конфигурации

Я в свободное время погружаюсь в Kubernetes. На текущий момент я решил на практике разобраться, как выполняется установка Kubernetes в отказоустойчивой конфигурации. Этому и будет посвящена данная публикация.

Я использовал конфигурацию ниже для знакомства с HA k8s и тестирования различных сценариев. Не уверен, что конфигурация ниже оптимальна для использования в продуктивной среде. Если вы решили использовать её в продуктивной среде, то только на свой страх и риск.

Архитектура решения

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

Конкретно в моем случае архитектура была следующая:

Аппаратная конфигурация всех узлов идентичная — 4 vCPU, 4 GB RAM, 40 GB system drive.

Подготовка серверов

В качестве операционной системы я буду использовать Ubuntu Server 22.04. Материал ниже я взял со страницы документации, но немного адаптировал его под мое окружение.

Отключение swap

Также перед выполнение последующий действий необходимо отключить файл подкачки на всех узлах кластера:

sudo swapoff -a

Чтобы при последующий перезагрузках узлов кластера файл подкачки не активировался вновь необходимо добавить соответствующее задание в файл crontab на всех узлах кластера:

sudo crontab -e

Добавим следующее простое задание:

@reboot /sbin/swapoff -a

Сохраним внесенные изменения и закроем файл crontab. В случае успешного добавления задания должен отобразиться соответствующий вывод на консоль:

crontab: installing new crontabroman@master01:~$

Теперь приступим к выполнению других шагов.

Установка и настройка cri-o

В качестве интерфейса выполнения контейнеров (CRI) я буду использовать cri-o, т.к. начиная с версии Kubernetes 1.24 с docker не все так просто.

Теперь все готово для установки cri-o. Установим необходимые пакеты и подготовим переменные:

sudo apt updatesudo apt install -y apt-transport-https ca-certificates curl gnupg2 software-properties-commonexport OS_VERSION=xUbuntu_20.04export CRIO_VERSION=1.23

Установим gpg ключи:

curl -fsSL https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS_VERSION/Release.key | sudo gpg —dearmor -o /usr/share/keyrings/libcontainers-archive-keyring.gpgcurl -fsSL https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$CRIO_VERSION/$OS_VERSION/Release.key | sudo gpg —dearmor -o /usr/share/keyrings/libcontainers-crio-archive-keyring.gpg

Добавим файлы репозитория:

echo «deb [signed-by=/usr/share/keyrings/libcontainers-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS_VERSION/ /» | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.listecho «deb [signed-by=/usr/share/keyrings/libcontainers-crio-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$CRIO_VERSION/$OS_VERSION/ /» | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$CRIO_VERSION.list

Запустим установку пакетов для cri-o:

sudo apt updatesudo apt install -y cri-o cri-o-runc

Создадим конфигурационные файлы для cri-o:

cat <<EOF | sudo tee /etc/modules-load.d/crio.confoverlaybr_netfilterEOF# Set up required sysctl params, these persist across reboots.cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.confnet.bridge.bridge-nf-call-iptables = 1net.ipv4.ip_forward = 1net.bridge.bridge-nf-call-ip6tables = 1EOF

Загрузим модули для корректной работы сети нашего кластера:

sudo modprobe overlaysudo modprobe br_netfilter

Перечитаем параметры конфигурации ядра со всех конфигурационных файлов:

sudo sysctl —system

Запустим службы:

sudo systemctl daemon-reloadsudo systemctl enable crio —now

Установка kubeadm, kubelet и kubectl

Установим gpg ключи и добавим репозитории:

echo «deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main» | sudo tee /etc/apt/sources.list.d/kubernetes.list

Установим kubelet, kubeadm и kubectl:

curl -fsSL https://dl.k8s.io/apt/doc/apt-key.gpg | sudo gpg —dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpgsudo apt update -ysudo apt install -y kubelet kubeadm kubectlsudo apt-mark hold kubelet kubeadm kubectl

Предварительная настройка узлов

1. Сначала настроим сервис kubelet, чтобы он имел более высший приоритет, чем тот, который был настроен автоматически при установке kubelet выше. Это действие необходимо выполнить для каждого узла, где будет размешаться etcd:

cat << EOF > /etc/systemd/system/kubelet.service.d/kubelet.conf# Replace «systemd» with the cgroup driver of your container runtime. The default value in the kubelet is «cgroupfs».# Replace the value of «containerRuntimeEndpoint» for a different container runtime if needed.#apiVersion: kubelet.config.k8s.io/v1beta1kind: KubeletConfigurationauthentication: anonymous: enabled: false webhook: enabled: falseauthorization: mode: AlwaysAllowcgroupDriver: systemdaddress: 127.0.0.1containerRuntimeEndpoint: unix:///var/run/crio/crio.sockstaticPodPath: /etc/kubernetes/manifestsEOFcat << EOF > /etc/systemd/system/kubelet.service.d/20-etcd-service-manager.conf[Service]ExecStart=ExecStart=/usr/bin/kubelet —config=/etc/systemd/system/kubelet.service.d/kubelet.conf

Restart=alwaysEOF

systemctl daemon-reload

systemctl restart kubelet

Проверим статус сервиса:

systemctl status kubelet

2. Теперь создадим конфигурационный файл для kubeadm. Файл необходимо будет сгенерировать для каждого узла с etcd. Скрипт по генерации конфигурации приведен ниже (только измените IP-адреса и имена узлов). Я сгенерирую все файлы на первом узле (etc01):

# Update HOST0, HOST1 and HOST2 with the IPs of your hostsexport HOST0=10.10.10.46

export HOST1=10.10.10.43

export HOST2=10.10.10.39

# Update NAME0, NAME1 and NAME2 with the hostnames of your hostsexport NAME0=»etc01″export NAME1=»etc02″export NAME2=»etc03″

# Create temp directories to store files that will end up on other hosts

mkdir -p /tmp/${HOST0}/ /tmp/${HOST1}/ /tmp/${HOST2}/HOSTS=(${HOST0} ${HOST1} ${HOST2})NAMES=(${NAME0} ${NAME1} ${NAME2})

for i in «${!HOSTS[@]}»; doHOST=${HOSTS[$i]}NAME=${NAMES[$i]}cat << EOF > /tmp/${HOST}/kubeadmcfg.yaml—api

Version: «kubeadm.k8s.io/v1beta3»

kind: Init

ConfigurationnodeRegistration: name: ${NAME}localAPIEndpoint: advertiseAddress: ${HOST}—apiVersion: «kubeadm.k8s.io/v1beta3″kind: ClusterConfigurationetcd: local: serverCertSANs: — «${HOST}» peerCertSANs: — «${HOST}» extraArgs: initial-cluster: ${NAMES[0]}=https://${HOSTS[0]}:2380,${NAMES[1]}=https://${HOSTS[1]}:2380,${NAMES[2]}=https://${HOSTS[2]}:2380 initial-cluster-state: new name: ${NAME} listen-peer-urls: https://${HOST}:2380 listen-client-urls: https://${HOST}:2379 advertise-client-urls: https://${HOST}:2379 initial-advertise-peer-urls: https://${HOST}:2380EOFdone

Результат работы скрипта:

ls -l /tmp/10*root@etc01:/home/roman# ls -l /tmp/10*/tmp/10.10.10.39:total 4-rw-r—r— 1 root root 772 Aug 12 11:39 kubeadmcfg.yaml/tmp/10.10.10.43:total 4-rw-r—r— 1 root root 772 Aug 12 11:39 kubeadmcfg.yaml/tmp/10.10.10.46:total 4-rw-r—r— 1 root root 772 Aug 12 11:39 kubeadmcfg.yamlroot@etc01:/home/roman#

3. Сгенерируем необходимые сертификаты:

kubeadm init phase certs etcd-ca

Результат работы команды:

ls -l /etc/kubernetes/pki/etcd/

root@etc01:/home/roman# ls -l /etc/kubernetes/pki/etcd/

total 8

-rw-r—r— 1 root root 1094 Aug 12 11:41 ca.crt

-rw——- 1 root root 1675 Aug 12 11:41 ca.key

4. Сгенерируем сертификаты для остальных узлов:

kubeadm init phase certs etcd-server —config=/tmp/${HOST2}/kubeadmcfg.yaml

kubeadm init phase certs etcd-peer —config=/tmp/${HOST2}/kubeadmcfg.yaml

kubeadm init phase certs etcd-healthcheck-client —config=/tmp/${HOST2}/kubeadmcfg.yaml

kubeadm init phase certs apiserver-etcd-client —config=/tmp/${HOST2}/kubeadmcfg.yaml

cp -R /etc/kubernetes/pki /tmp/${HOST2}/

# cleanup non-reusable certificates

find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete

kubeadm init phase certs etcd-server —config=/tmp/${HOST1}/kubeadmcfg.yaml

kubeadm init phase certs etcd-peer —config=/tmp/${HOST1}/kubeadmcfg.yaml

kubeadm init phase certs etcd-healthcheck-client —config=/tmp/${HOST1}/kubeadmcfg.yaml

kubeadm init phase certs apiserver-etcd-client —config=/tmp/${HOST1}/kubeadmcfg.yaml

cp -R /etc/kubernetes/pki /tmp/${HOST1}/find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete

kubeadm init phase certs etcd-server —config=/tmp/${HOST0}/kubeadmcfg.yaml

kubeadm init phase certs etcd-peer —config=/tmp/${HOST0}/kubeadmcfg.yaml

kubeadm init phase certs etcd-healthcheck-client —config=/tmp/${HOST0}/kubeadmcfg.yaml

kubeadm init phase certs apiserver-etcd-client —config=/tmp/${HOST0}/kubeadmcfg.yaml

# No need to move the certs because they are for HOST0

# clean up certs that should not be copied off this host

find /tmp/${HOST2} -name ca.key -type f -delete

find /tmp/${HOST1} -name ca.key -type f -delete

Листинг работы команд выше:

# No need to move the certs because they are for HOST0

# clean up certs that should not be copied off this hostfind /tmp/${HOST2} -name ca.key -type f -delete

find /tmp/${HOST1} -name ca.key -type f -delete

[certs] Generating «etcd/server» certificate and key

[certs] etcd/server serving cert is signed for DNS names [etc03 localhost] and IPs [10.10.10.39 127.0.0.1 ::1]

[certs] Generating «etcd/peer» certificate and key

[certs] etcd/peer serving cert is signed for DNS names [etc03 localhost] and IPs [10.10.10.39 127.0.0.1 ::1]

[certs] Generating «etcd/healthcheck-client» certificate and key[certs] Generating «apiserver-etcd-client» certificate and key

[certs] Generating «etcd/server» certificate and key

[certs] etcd/server serving cert is signed for DNS names [etc02 localhost] and IPs [10.10.10.43 127.0.0.1 ::1]

[certs] Generating «etcd/peer» certificate and key

[certs] etcd/peer serving cert is signed for DNS names [etc02 localhost] and IPs [10.10.10.43 127.0.0.1 ::1]

[certs] Generating «etcd/healthcheck-client» certificate and key[certs] Generating «apiserver-etcd-client» certificate and key

[certs] Generating «etcd/server» certificate and key

[certs] etcd/server serving cert is signed for DNS names [etc01 localhost] and IPs [10.10.10.46 127.0.0.1 ::1]

[certs] Generating «etcd/peer» certificate and key

[certs] etcd/peer serving cert is signed for DNS names [etc01 localhost] and IPs [10.10.10.46 127.0.0.1 ::1]

[certs] Generating «etcd/healthcheck-client» certificate and key

[certs] Generating «apiserver-etcd-client» certificate and key

5. Скопируем сгенерированные файлы в соответствующие директории на всех хостах etcd (только скорректируйте имя пользователя для подключения):

USER=roman

HOST=${HOST1}

scp -r /tmp/${HOST}/* ${USER}@${HOST}:ssh ${USER}@${HOST}USER@HOST

$ sudo -Esroot@HOST

$ chown -R root:root pkiroot@HOST

$ mv pki /etc/kubernetes/

Листинг команды выше:

root@etc01:/home/roman# USER=roman

root@etc01:/home/roman# HOST=${HOST1}

root@etc01:/home/roman# scp -r /tmp/${HOST}/* ${USER}@${HOST}:

roman@10.10.10.43’s password:

root@etc01:/home/roman# ssh ${USER}@${HOST}

roman@10.10.10.43’s password:

roman@etc02:~$ sudo -Es

[sudo] password for roman:

root@etc02:~# chown -R root:root pki

root@etc02:~# mv pki /etc/kubernetes/

Выполняем аналогичное копирование и для последнего узла etcd:

USER=romanHOST=${HOST2}scp -r /tmp/${HOST}/* ${USER}@${HOST}:ssh ${USER}@${HOST}USER@HOST

$ sudo -Es

root@HOST

$ chown -R root:root pkiroot@HOST

$ mv pki /etc/kubernetes/

6. Убедитесь, что в директории /tmp у вас подготовлен конфигурационный файл kubeadmcfg.yaml для каждого из узлов:

ls -l /tmp/10.10.10.*/kubeadmcfg.yaml

root@etc01:/home/roman# ls -l /tmp/10.10.10.*/kubeadmcfg.yaml

-rw-r—r— 1 root root 772 Aug 12 11:39 /tmp/10.10.10.39/kubeadmcfg.yaml

-rw-r—r— 1 root root 772 Aug 12 11:39 /tmp/10.10.10.43/kubeadmcfg.yaml

-rw-r—r— 1 root root 772 Aug 12 11:39 /tmp/10.10.10.46/kubeadmcfg.yaml

root@etc01:/home/roman#

Также убедитесь, что на каждый узел были скопированы необходимые файлы в директорию /etc/kubernetes/pki:

tree /etc/kubernetes/pki

root@etc01:/home/roman#

tree /etc/kubernetes/pki

/etc/kubernetes/pki

├── apiserver-etcd-client.crt

├── apiserver-etcd-client.key

└── etcd ├── ca.crt

├── ca.key

├── healthcheck-client.crt

├── healthcheck-client.key

├── peer.crt ├── peer.key

├── server.crt

└── server.key

1 directory, 10 files

root@etc01:/home/roman#

7. Теперь необходимо создать манифесты. Выполним вот эту команду на первом узле:

kubeadm init phase etcd local —config=/tmp/${HOST0}/kubeadmcfg.yamlroot@etc01:/home/roman# kubeadm init phase etcd local —config=/tmp/${HOST0}/kubeadmcfg.yaml[etcd] Creating static Pod manifest for local etcd in «/etc/kubernetes/manifests»

А вот такой командой создаем манифесты на двух других узлах etcd:

kubeadm init phase etcd local —config=$HOME/kubeadmcfg.yamlroman@etc02:~$ sudo kubeadm init phase etcd local —config=$HOME/kubeadmcfg.yaml[sudo] password for roman: [etcd] Creating static Pod manifest for local etcd in «/etc/kubernetes/manifests»

8. Теперь можно установить etcdctl и проверить состояние здоровье кластера etcd:

ETCDCTL_API=3 etcdctl —cert /etc/kubernetes/pki/etcd/peer.crt —key /etc/kubernetes/pki/etcd/peer.key —cacert /etc/kubernetes/pki/etcd/ca.crt —endpoints https://${HOST0}:2379 endpoint healthroot@etc01:/home/roman/etcd-v3.5.9-linux-amd64# ETCDCTL_API=3 etcdctl —cert /etc/kubernetes/pki/etcd/peer.crt —key /etc/kubernetes/pki/etcd/peer.key —cacert /etc/kubernetes/pki/etcd/ca.crt —endpoints https://${HOST0}:2379 endpoint healthhttps://10.10.10.46:2379 is healthy: successfully committed proposal: took = 11.053467ms

Команда выше должна выполниться успешно для каждого узла — ${HOST0}, ${HOST1} и ${HOST2}.

Подготовка узлов плоскости управления и рабочей нагрузки

Нетеперь для узлов, на которых будет размещена нагрузка плоскости управления и рабочей нагрузки необходимо выполнить предварительную подготовку серверов. Установить среду выполнения контейнеров, kubeadm и kubectl. Подготовка выполняется аналогично подготовке узлов для etcd. В качестве операционной системы я буду использовать Ubuntu Server 22.04.

Отключение swap

Также перед выполнение последующий действий необходимо отключить файл подкачки на всех узлах кластера:

sudo swapoff -a

Чтобы при последующий перезагрузках узлов кластера файл подкачки не активировался вновь необходимо добавить соответствующее задание в файл crontab на всех узлах кластера:

sudo crontab -e

Добавим следующее простое задание:

@reboot /sbin/swapoff -a

Сохраним внесенные изменения и закроем файл crontab. В случае успешного добавления задания должен отобразиться соответствующий вывод на консоль:

crontab: installing new crontabroman@master01:~$

Теперь приступим к выполнению других шагов.

Установка и настройка cri-o

В качестве интерфейса выполнения контейнеров (CRI) я буду использовать cri-o, т.к. начиная с версии Kubernetes 1.24 с docker не все так просто.

Теперь все готово для установки cri-o. Установим необходимые пакеты и подготовим переменные:

sudo apt updatesudo apt install -y apt-transport-https ca-certificates curl gnupg2 software-properties-commonexport OS_VERSION=xUbuntu_20.04export CRIO_VERSION=1.23

Установим gpg ключи:

curl -fsSL https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS_VERSION/Release.key | sudo gpg —dearmor -o /usr/share/keyrings/libcontainers-archive-keyring.gpgcurl -fsSL https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$CRIO_VERSION/$OS_VERSION/Release.key | sudo gpg —dearmor -o /usr/share/keyrings/libcontainers-crio-archive-keyring.gpg

Добавим файлы репозитория:

echo «deb [signed-by=/usr/share/keyrings/libcontainers-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS_VERSION/ /» | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.listecho «deb [signed-by=/usr/share/keyrings/libcontainers-crio-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$CRIO_VERSION/$OS_VERSION/ /» | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$CRIO_VERSION.list

Запустим установку пакетов для cri-o:

sudo apt updatesudo apt install -y cri-o cri-o-runc

Создадим конфигурационные файлы для cri-o:

cat <<EOF | sudo tee /etc/modules-load.d/crio.confoverlaybr_netfilterEOF# Set up required sysctl params, these persist across reboots.cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.confnet.bridge.bridge-nf-call-iptables = 1net.ipv4.ip_forward = 1net.bridge.bridge-nf-call-ip6tables = 1EOF

Загрузим модули для корректной работы сети нашего кластера:

sudo modprobe overlaysudo modprobe br_netfilter

Перечитаем параметры конфигурации ядра со всех конфигурационных файлов:

sudo sysctl —system

Запустим службы:

sudo systemctl daemon-reloadsudo systemctl enable crio —now

Установка kubeadm, kubelet и kubectl

Установим gpg ключи и добавим репозитории:

echo «deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main» | sudo tee /etc/apt/sources.list.d/kubernetes.list

Установим kubelet, kubeadm и kubectl:

curl -fsSL https://dl.k8s.io/apt/doc/apt-key.gpg | sudo gpg —dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpgsudo apt update -ysudo apt install -y kubelet kubeadm kubectlsudo apt-mark hold kubelet kubeadm kubectl

Подготовка отказоустойчивого балансировщика

Для того, чтобы балансировщик не был единичной точкой отказа его также нужно подготовить в отказоустойчивой конфигурации. Один из вариантов — это использование haproxy и keepalived. Соответственно, нужно подготовить два балансировщика с haproxy и keepalived. В качестве операционной системы я буду использовать Ubuntu Server 22.04.

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

sudo apt updatesudo apt install -y keepalived haproxy

Создадим файл с конфигурацией на первом балансировщике:

/etc/keepalived/keepalived.conf

Содержимое конфигурационного файла:

! /etc/keepalived/keepalived.conf! Configuration File for keepalivedglobal_defs { router_id LVS_DEVEL}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 ens33 virtual_router_id 51 priority 101 authentication { auth_type PASS auth_pass 42 } virtual_ipaddress { 10.10.10.63 } track_script { check_apiserver }}

Сохраним внесенные изменения.

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

Создадим конфигурационный файл на втором балансировщике:

/etc/keepalived/keepalived.conf

Но его конфигурация будет немного отличаться:

! /etc/keepalived/keepalived.conf! Configuration File for keepalivedglobal_defs { router_id LVS_DEVEL}vrrp_script check_apiserver { script «/etc/keepalived/check_apiserver.sh» interval 3 weight -2 fall 10 rise 2}vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 51 priority 100 authentication { auth_type PASS auth_pass 42 } virtual_ipaddress { 10.10.10.63 } track_script { check_apiserver }}

Второй балансировщик будет резервный, т.е. его статус будет BACKUP. Также изменен приоритет.

Теперь необходимо настроить скрипт проверки состояния сервера api, который мы указали в конфигурации keepalived (script «/etc/keepalived/check_apiserver.sh»):

sudo nano /etc/keepalived/check_apiserver.sh

Содержимое скрипта:

#!/bin/sherrorExit() { echo «*** $*» 1>&2 exit 1}curl —silent —max-time 2 —insecure https://localhost:6443/ -o /dev/null || errorExit «Error GET https://localhost:6443/»if ip addr | grep -q 10.10.10.63; then curl —silent —max-time 2 —insecure https://10.10.10.63:6443/ -o /dev/null || errorExit «Error GET https://10.10.10.63:6443/»fi

Аналогичный скрипт необходимо создать и на втором балансировщике.

Теперь отредактируем конфигурационный файл haproxy:

sudo nano /etc/haproxy/haproxy.cfg

Итоговое содержимое моего конфигурационного файла следующее:

globallog /dev/loglocal0log /dev/loglocal1 noticechroot /var/lib/haproxystats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listenersstats timeout 30suser haproxygroup haproxydaemon# Default SSL material locationsca-base /etc/ssl/certscrt-base /etc/ssl/private# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-ticketsdefaultslogglobalmodehttpoptionhttplogoptiondontlognull timeout connect 5000 timeout client 50000 timeout server 50000option 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 10serrorfile 400 /etc/haproxy/errors/400.httperrorfile 403 /etc/haproxy/errors/403.httperrorfile 408 /etc/haproxy/errors/408.httperrorfile 500 /etc/haproxy/errors/500.httperrorfile 502 /etc/haproxy/errors/502.httperrorfile 503 /etc/haproxy/errors/503.httperrorfile 504 /etc/haproxy/errors/504.http#———————————————————————# apiserver frontend which proxys to the control plane nodes#———————————————————————frontend apiserver bind *:6443 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 kpn01 10.10.10.47:8443 checkserver kpn02 10.10.10.48:8443 checkserver kpn03 10.10.10.50:8443 check

Перезапустим службы и настроем автозапуск:

sudo systemctl enable haproxy —nowsudo systemctl enable keepalived —nowsudo systemctl restart haproxysudo systemctl restart keepalived

Последний шаг настройки балансировщиков — проверка доступности виртуального IP-адреса по опубликованному порту. Однако тут есть другой аспект — сам сервер API еще не настроен. Попробуем выполнить вот такую команду с любого сервера плоскости управления:

nc -v 10.10.10.63 6443

Вы должны увидеть следующий вывод:

root@cpn01:/home/roman# nc -v 10.10.10.63 6443Connection to 10.10.10.63 6443 port [tcp/*] succeeded!

Да, api-server еще не настроен, но haproxy уже должен принимать входящие подключения. Можно так же попробовать выполнить запрос с браузера:

Ответы вида «ERR_CONNECTION_REFUSED» и «ERR_EMPTY_RESPONSE» говорят, что мы с вами на верном пути — haproxy принял наше подключение, но ему некуда его транслировать далее.

Однако, если вы получили ответ вида «ERR_CONNECTION_TIMED_OUT», то что-то пошло не так и балансировщики или VIP (Virtual IP) недоступны. Перед тем, как идти далее необходимо разобраться в первопричинах и устранить ошибку.

Настройка узлов плоскости управления и рабочей нагрузки

Мы с вами подготовили кластер etcd и haproxy в отказоустойчивой конфигурации. Теперь перейдем к настройке серверов плоскости управления и серверов для рабочей нагрузки.

1. Скопируем сертификаты с любого сервера etcd на первый сервер плоскости управления:

export CONTROL_PLANE=»roman@10.10.10.47″scp /etc/kubernetes/pki/etcd/ca.crt «${CONTROL_PLANE}»:scp /etc/kubernetes/pki/apiserver-etcd-client.crt «${CONTROL_PLANE}»:sudo scp /etc/kubernetes/pki/apiserver-etcd-client.key «${CONTROL_PLANE}»:

2. Подключимся на первый узел плоскости управления и переместим сертификаты в нужное расположение:

sudo mkdir -p /etc/kubernetes/pki/etcdsudo mv ca.crt /etc/kubernetes/pki/etcd/ca.crtsudo mv apiserver-etcd-client.crt /etc/kubernetes/pki/apiserver-etcd-client.crtsudo mv apiserver-etcd-client.key /etc/kubernetes/pki/apiserver-etcd-client.key

3. На первом узле плоскости управления создадим файл манифеста конфигурации для kubeadm:

nano kubeadm-config.yaml

Содержимое файла:

—apiVersion: kubeadm.k8s.io/v1beta3kind: ClusterConfigurationnetworking: podSubnet: «192.168.0.0/16» # —pod-network-cidrkubernetesVersion: stablecontrolPlaneEndpoint: «10.10.10.63:6443» # change this (see below)etcd: external: endpoints: — https://10.10.10.46:2379 # change ETCD_0_IP appropriately — https://10.10.10.43:2379 # change ETCD_1_IP appropriately — https://10.10.10.39:2379 # change ETCD_2_IP appropriately caFile: /etc/kubernetes/pki/etcd/ca.crt certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key

Подробное описание всех частей манифеста приведено в документации.

4. Теперь подготовим первый узел плоскости управления:

sudo kubeadm init —config kubeadm-config.yaml —upload-certs

Пример вывода на консоль в случае успешного завершения процедуры инициализации первого узла плоскости управления:

Your Kubernetes control-plane has initialized successfully!To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/configAlternatively, if you are the root user, you can run: export KUBECONFIG=/etc/kubernetes/admin.confYou should now deploy a pod network to the cluster.Run «kubectl apply -f [podnetwork].yaml» with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/You can now join any number of the control-plane node running the following command on each as root: kubeadm join 10.10.10.63:6443 —token 9cmrgy.vr51qagnitsnsa8p —discovery-token-ca-cert-hash sha256:c422ac3a716f26df82716573c55c5d90a55735c30c1bee361bf5486c776873ec —control-plane —certificate-key 13d8f82492aa9c6b9a4fb25eca2484b0a34eec7d95b97a089e62fddebc9826d6Please note that the certificate-key gives access to cluster sensitive data, keep it secret!As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use»kubeadm init phase upload-certs —upload-certs» to reload certs afterward.Then you can join any number of worker nodes by running the following on each as root:kubeadm join 10.10.10.63:6443 —token 9cmrgy.vr51qagnitsnsa8p —discovery-token-ca-cert-hash sha256:c422ac3a716f26df82716573c55c5d90a55735c30c1bee361bf5486c776873ec

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

5. Выполним настройку окружения для подключения к кластеру:

mkdir -p $HOME/.kubesudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/configsudo chown $(id -u):$(id -g) $HOME/.kube/config

6. Проверим состояние служебных подов:

kubectl get pod -n kube-system

roman@cpn01:~$ kubectl get pod -n kube-system

NAME READY STATUS RESTARTS AGE

coredns-5d78c9869d-fvl64 1/1 Running 0 2m39s

coredns-5d78c9869d-gvfxc 1/1 Running 0 2m39s

kube-apiserver-cpn01 1/1 Running 0 2m50s

kube-controller-manager-cpn01 1/1 Running 0 2m50s

kube-proxy-p8snc 1/1 Running 0 2m40s

kube-scheduler-cpn01 1/1 Running 0 2m50s

roman@cpn01:~$

7. Установим CNI. Я буду использовать плагин Calico.

kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.1/manifests/calico.yaml

Проверяем поды:

kubectl get pod -n kube-system

roman@cpn01:~$ kubectl get pod -n kube-system

NAME READY STATUS RESTARTS AGE

calico-kube-controllers-674fff74c8-v5lxz 1/1 Running 0 68s

calico-node-z8cb5 1/1 Running 0 68s

coredns-5d78c9869d-fvl64 1/1 Running 0 5m7s

coredns-5d78c9869d-gvfxc 1/1 Running 0 5m7s

kube-apiserver-cpn01 1/1 Running 0 5m18s

kube-controller-manager-cpn01 1/1 Running 0 5m18s

kube-proxy-p8snc 1/1 Running 0 5m8skube-scheduler-cpn01 1/1 Running 0 5m18s

roman@cpn01:~$

8. Скопируем сертификаты с первого узла плоскости управления на осnальные узлы плоскости управления:

USER=roman

# customizable

CONTROL_PLANE_IPS=»10.10.10.48 10.10.10.50″

for host in ${CONTROL_PLANE_IPS}; do sudo scp /etc/kubernetes/pki/ca.crt «${USER}»@$host: sudo scp /etc/kubernetes/pki/ca.key «${USER}»@$host: sudo scp /etc/kubernetes/pki/sa.key «${USER}»@$host: sudo scp /etc/kubernetes/pki/sa.pub «${USER}»@$host: sudo scp /etc/kubernetes/pki/front-proxy-ca.crt «${USER}»@$host: sudo scp /etc/kubernetes/pki/front-proxy-ca.key «${USER}»@$host: sudo scp /etc/kubernetes/pki/etcd/ca.crt «${USER}»@$host:etcd-ca.crt

done

9. Затем на каждом дополнительном узле управления переместите скопированные сертификаты в нужное расположение:

USER=roman

# customizable

sudo mkdir -p /etc/kubernetes/pki/etcdsudo

mv /home/${USER}/ca.crt /etc/kubernetes/pki/

sudo mv /home/${USER}/ca.key /etc/kubernetes/pki/

sudo mv /home/${USER}/sa.pub /etc/kubernetes/pki/

sudo mv /home/${USER}/sa.key /etc/kubernetes/pki/

sudo mv /home/${USER}/front-proxy-ca.crt /etc/kubernetes/pki/

sudo mv /home/${USER}/front-proxy-ca.key /etc/kubernetes/pki/

sudo mv /home/${USER}/etcd-ca.crt /etc/kubernetes/pki/etcd/ca.crt

10. Теперь попробуем присоединить второй узел плоскости управления:

sudo kubeadm join 10.10.10.63:6443 —token 9cmrgy.vr51qagnitsnsa8p —discovery-token-ca-cert-hash sha256:c422ac3a716f26df82716573c55c5d90a55735c30c1bee361bf5486c776873ec —control-plane —certificate-key 13d8f82492aa9c6b9a4fb25eca2484b0a34eec7d95b97a089e62fddebc9826d6

11. Аналогично командой из предыдущего пункта присоединяем к кластера третий узел плоскости управления.

12. После успешного присоединения всех узлов плоскости управления наш кластер должен насчитывать три узла:

kubectl get node

roman@cpn02:~$ kubectl get node

NAME STATUS ROLES AGE VERSION

cpn01 Ready control-plane 20m v1.27.4

cpn02 Ready control-plane 2m38s v1.27.4

cpn03 Ready control-plane 87s v1.27.4

roman@cpn02:~$

13. Осталось добавить в кластер узлы рабочей нагрузки. Добавление узлов нагрузки выполняется вот такой командой:

sudo kubeadm join 10.10.10.63:6443 —token 9cmrgy.vr51qagnitsnsa8p —discovery-token-ca-cert-hash sha256:c422ac3a716f26df82716573c55c5d90a55735c30c1bee361bf5486c776873ec

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

14.Проверим состав нашего кластера после добавления узлов под рабочую нагрузку:

kubectl get noderoman@cpn02:~$ kubectl get node

NAME STATUS ROLES AGE VERSION

cpn01 Ready control-plane 25m v1.27.4

cpn02 Ready control-plane 7m31s v1.27.4

cpn03 Ready control-plane 6m20s v1.27.4

wkn01 Ready <none> 42s v1.27.4wkn02 Ready <none> 12s v1.27.4

wkn03 Ready <none> 2s v1.27.4

roman@cpn02:~$

15. Еще я бы добавил сервер метрики:

kubectl apply -f https://raw.githubusercontent.com/techiescamp/kubeadm-scripts/main/manifests/metrics-server.yaml

16. И еще добавлю Ingress контроллер:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/baremetal/deploy.yaml

17. Итоговый перечень pod, который у меня получился. Для Ingress контроллера:

kubectl get pods -n ingress-nginx

roman@cpn01:~$ kubectl get pods -n ingress-nginx

NAME READY STATUS RESTARTS AGE

ingress-nginx-admission-create-6ph8k 0/1 Completed 0 57s

ingress-nginx-admission-patch-jcst6 0/1 Completed 0 57s

ingress-nginx-controller-846649f84c-vsx5w 0/1 Running 0 57s

Для плоскости управления:

kubectl get pods -n kube-system

roman@cpn01:~$ kubectl get pods -n kube-system

NAME READY STATUS RESTARTS AGEcalico-kube-controllers-674fff74c8-v5lxz 1/1 Running 0 9hcalico-node-4s27g 1/1 Running 0 9hcalico-node-8dp2h 1/1 Running 0 9hcalico-node-jb7lb 1/1 Running 0 9hcalico-node-lg2lq 1/1 Running 0 9hcalico-node-pl79v 1/1 Running 0 9hcalico-node-z8cb5 1/1 Running 0 9hcoredns-5d78c9869d-fvl64 1/1 Running 0 9hcoredns-5d78c9869d-gvfxc 1/1 Running 0 9hkube-apiserver-cpn01 1/1 Running 0 9hkube-apiserver-cpn02 1/1 Running 0 9hkube-apiserver-cpn03 1/1 Running 0 9hkube-controller-manager-cpn01 1/1 Running 0 9hkube-controller-manager-cpn02 1/1 Running 0 9hkube-controller-manager-cpn03 1/1 Running 0 9hkube-proxy-f55q8 1/1 Running 0 9hkube-proxy-hhdsn 1/1 Running 0 9hkube-proxy-lp584 1/1 Running 0 9hkube-proxy-p8snc 1/1 Running 0 9hkube-proxy-snlf7 1/1 Running 0 9hkube-proxy-z5p2c 1/1 Running 0 9hkube-scheduler-cpn01 1/1 Running 0 9hkube-scheduler-cpn02 1/1 Running 0 9hkube-scheduler-cpn03 1/1 Running 0 9hmetrics-server-754586b847-ktjzz 1/1 Running 0 3m9s

roman@cpn01:~$

Итоговое тестирование отказоустойчивости

Переходим к самой интересной части — тестирование. Предварительно нам нужно будет создать рабочую нагрузку.

Создание рабочей нагрузки для тестирования

Для развертывания тестовой нагрузки я буду использовать следующий манифест из одной моей прошлой публикации:

nano svc.yml

Содержимое файла:

apiVersion: apps/v1kind: Deploymentmetadata: name: web-appv1spec: selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: — name: web-appv1 image: gcr.io/google-samples/hello-app:1.0—apiVersion: v1kind: Servicemetadata: name: web-service-appv1spec: selector: app: web ports: — protocol: TCP port: 50001 targetPort: 8080—apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: web-ingress-appv1 annotations: nginx.ingress.kubernetes.io/ssl-redirect: «false» nginx.ingress.kubernetes.io/force-ssl-redirect: «false»spec: ingressClassName: nginx rules: — host: web.itproblog.ru http: paths: — path: / pathType: Prefix backend: service: name: web-service-appv1 port: number: 50001

Создадим необходимые ресурсы:

kubectl apply -f svc.yaml

Проверим созданные ресурсы:

kubectl get pod

kubectl get svc

kubectl get ingress

roman@cpn01:~$ kubectl get pod

kubectl get svckubectl get ingress

NAME READY STATUS RESTARTS AGE

web-appv1-76765768c6-v6f59 1/1 Running 0 49s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10

hweb-service-appv1 ClusterIP 10.108.77.85 <none> 50001/TCP 49s

NAME CLASS HOSTS ADDRESS PORTS AGE

web-ingress-appv1 nginx web.itproblog.ru 10.10.10.54 80 49s

roman@cpn01:~$

Также в файле hosts мне нужно будет добавить записи на имя web.itproblog.ru на IP-адреса серверов рабочей нагрузки. Или зарегистрировать DNS имя. Я отредактирую файл hosts на своей рабочей станции, чтобы в последующем выполнять тестирование непосредственно с неё:

sudo nano /etc/hosts

10.10.10.51 web.itproblog.ru

10.10.10.54 web.itproblog.ru

10.10.10.56 web.itproblog.ru

Теперь обратимся к нашему ingress контроллеру, чтобы понять, какой порт он прослушивает:

kubectl get svc -n ingress-nginx

roman@cpn01:~$ kubectl get svc -n ingress-nginx

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

ingress-nginx-controller NodePort 10.107.17.12 <none> 80:32567/TCP,443:31751/TCP 10m

ingress-nginx-controller-admission ClusterIP 10.104.23.189 <none> 443/TCP 10m

roman@cpn01:~$

Как видно из листинга выше — для HTTP запросов прослушивается порт 32567.

Вот теперь попробуем обратиться к нашему тестовому приложению:

curl http://web.itproblog.ru:32567

roman@mintwks:~$ curl http://web.itproblog.ru:32567

Hello, world!Version: 1.0.0

Hostname: web-appv1-76765768c6-v6f59

roman@mintwks:~$

Аналогичную проверку вы можете запустить непосредственно в браузере:

http://web.itproblog.ru:32567

Приложение доступно. И это хорошо — значит у нас по крайней мере получилось корректно развернуть Kubernetes. Теперь нужно протестировать его на отказоустойчивость.

Тестирование отказа узла рабочей нагрузки

Начнем с самого верхнего уровня — узлы рабочей нагрузки. Давайте проверим, на каком из узлов развернута pod с нашим приложением:

kubectl get pod -o=wide

roman@cpn01:~$ kubectl get pod -o=wide

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES

web-appv1-76765768c6-v6f59 1/1 Running 0 14m 192.168.83.66

wkn03 <none> <none>

roman@cpn01:~$

Как видно из скриншота выше — pod развернут на узле wkn03. Я отключу сетевой адаптер на узле wkn03 и проверим, что произойдет.

curl http://web.itproblog.ru:32567

roman@mintwks:~$ curl http://web.itproblog.ru:32567

<html><head><title>503 Service Temporarily Unavailable</title></head><body><center><h1>503 Service Temporarily Unavailable</h1></center><hr><center>nginx</center></body></html>

Ответ очевиден — что то пошло не так.

Посмотрим статусы узлов и подов:

kubectl get nodekubectl get pod -o=wide

roman@cpn01:~$ kubectl get node

NAME STATUS ROLES AGE VERSION

cpn01 Ready control-plane 10h v1.27.4

cpn02 Ready control-plane 10h v1.27.4

cpn03 Ready control-plane 10h v1.27.4

wkn01 Ready <none> 10h v1.27.4

wkn02 Ready <none> 10h v1.27.4

wkn03 NotReady <none> 10h v1.27.4

roman@cpn01:~$ kubectl get pod -o=wide

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES

web-appv1-76765768c6-lxnm7 1/1 Running 0 27s 192.168.243.2 wkn01 <none> <none>

web-appv1-76765768c6-v6f59 1/1 Terminating 0 41m 192.168.83.66 wkn03 <none> <none>

roman@cpn01:~$

Как видно на скриншоте выше, узел wkn03 недоступен. По истечении определенного времени (по умолчанию 5 минут) Kubernetes создаст новую поду на узле wkn01 и удалил старую. Чтобы избежать задержек можно в определении развертывания указать нужно количество подов. Например, 2 или 3.

Проверим приложение теперь:

roman@mintwks:~$ curl http://web.itproblog.ru:32567

Hello, world!Version: 1.0.0

Hostname: web-appv1-76765768c6-lxnm7

roman@mintwks:~$

Работает. Отказ узла рабочей нагрузки с активной подой кластер пережил. Настало время для дальнейших проверок.

Тестирование отказа узла плоскости управления

Я оставлю узел рабочей нагрузки wkn03 вне сети. Пусть пока он будет недоступен. Смоделируем еще одну ситуацию — отказ одного из узлов плоскости управления. Теперь я отключу сетевой интерфейс на сервере cpn03.

Спустя буквально минуту проверим статус узлов кластера Kubernetes:

kubectl get noderoman@cpn01:~$ kubectl get node

NAME STATUS ROLES AGE VERSION

cpn01 Ready control-plane 10h v1.27.4

cpn02 Ready control-plane 10h v1.27.4

cpn03 NotReady control-plane 10h v1.27.4

wkn01 Ready <none> 10h v1.27.4wkn02 Ready <none> 10h v1.27.4

wkn03 NotReady <none> 10h v1.27.4

roman@cpn01:~$

Теперь помимо одного из узлов для рабочей нагрузки у нас в кластере «отказал» еще и один из узлов плоскости управления.

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

roman@mintwks:~$ curl http://web.itproblog.ru:32567

Hello, world!Version: 1.0.0

Hostname: web-appv1-76765768c6-lxnm7

roman@mintwks:~$

Хорошо, приложение доступно. Попробуем создать дополнительную рабочую нагрузку в нашем кластере:

kubectl run busybox —image=busyboxroman@cpn01:~$ kubectl run busybox —image=busyboxpod/busybox createdroman@cpn01:~$

Создание дополнительного ресурса прошло успешно. Проверим статусы всех подов:

kubectl get pod

roman@cpn01:~$ kubectl get pod

NAME READY STATUS RESTARTS AGEbusybox 0/1 Completed 3 (37s ago) 65s

web-appv1-76765768c6-lxnm7 1/1 Running 0 21m

web-appv1-76765768c6-v6f59 1/1 Terminating 0 62m

roman@cpn01:~$

Поскольку никаких дополнительных параметров при создании под мы не передавали, то она сразу завершилась, что ожидаемо.

Мы проверили основные моменты — кластер Kubernetes продолжает обслуживать уже имеющуюся нагрузку. Более того, кластер успешно принимает запросы на создание дополнительных ресурсов и успешно их обрабатывает.

Тестирование отказа узла etcd

Два узла кластера мы уже «потеряли» — один узел рабочей нагрузки и один узел плоскости управления.

Всего у нас три узла в кластере с etcd:

sudo ETCDCTL_API=3 etcdctl —cacert=/etc/kubernetes/pki/etcd/ca.crt —cert=/etc/kubernetes/pki/etcd/server.crt —key=/etc/kubernetes/pki/etcd/server.key —endpoints=10.10.10.46:2379 member list4f5b17401266f2b5, started, etc01, https://10.10.10.46:2380, https://10.10.10.46:2379, falsea95398094ef8f5e0, started, etc02, https://10.10.10.43:2380, https://10.10.10.43:2379, falseb7fde4d1b69c7cb0, started, etc03, https://10.10.10.39:2380, https://10.10.10.39:2379, false

Продолжим «терять» узлы. Теперь я отключу сетевой адаптер на втором узле с etcd.

Проверим здоровье узлов etcd:

sudo ETCDCTL_API=3 etcdctl —cacert=/etc/kubernetes/pki/etcd/ca.crt —cert=/etc/kubernetes/pki/etcd/server.crt —key=/etc/kubernetes/pki/etcd/server.key —endpoints=10.10.10.46:2379,10.10.10.43:2379,10.10.10.39:2379 endpoint health

10.10.10.46:2379 is healthy: successfully committed proposal: took = 9.228848ms

10.10.10.39:2379 is healthy: successfully committed proposal: took = 12.114634ms

10.10.10.43:2379 is unhealthy: failed to commit proposal: context deadline exceeded

Error: unhealthy cluster

Проверим доступность приложения:

roman@mintwks:~$ curl http://web.itproblog.ru:32567Hello, world!Version: 1.0.0Hostname: web-appv1-76765768c6-lxnm7roman@mintwks:~$

Приложение доступно. Теперь попробуем создать дополнительный ресурс в кластере:

kubectl run ngx —image=nginxroman@cpn01:~$ kubectl run ngx —image=nginxpod/ngx createdroman@cpn01:~$

Ресурс создан. Проверим его статус:

kubectl get pod -o=wide

roman@cpn01:~$ kubectl get pod -o=wide

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES

busybox 0/1 CrashLoopBackOff 7 (3m23s ago) 14m 192.168.212.66 wkn02 <none> <none>

ngx 1/1 Running 0 55s 192.168.243.3 wkn01 <none> <none>

web-appv1-76765768c6-lxnm7 1/1 Running 0 34m 192.168.243.2 wkn01 <none> <none>

web-appv1-76765768c6-v6f59 1/1 Terminating 0 75m 192.168.83.66 wkn03 <none> <none>

roman@cpn01:~$

Как видно из скриншота выше, вся рабочая нагрузка распределяется между оставшимися узлами рабочей нагрузки — wkn01 и wkn02.

Резюмирую, как и в случае с тестирование отказа узла плоскости управления, все текущие ресурсы доступны и кластер принимает о обрабатывает запросы на создание новых ресурсов.

Тестирование отказа одного из балансировщиков

Последняя проверка. В нашем развертывании остался еще один компонент, который мы не проверяли — балансировщик. У нас их два, работают они в режиме active-pasive. Активный узел — lb01, пассивный lb02. Я отключу сетевой адаптер на узле lb01.

Если теперь посмотреть на конфигурацию сетевых адаптеров сервера lb02, то можно заметить, что VIP был добавлен в перечень IP-адресов адаптера:

ip addrroman@lb02:~$ ip addr

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever

2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 00:0c:29:b7:69:ef brd ff:ff:ff:ff:ff:ff altname enp2s1 inet 10.10.10.62/24 metric 100 brd 10.10.10.255 scope global dynamic ens33 valid_lft 399sec preferred_lft 399sec inet 10.10.10.63/32 scope global ens33 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:feb7:69ef/64 scope link valid_lft forever preferred_lft foreverroman@lb02:~$

Проверим доступность приложения:

roman@mintwks:~$ curl http://web.itproblog.ru:32567

Hello, world!

Version: 1.0.0

Hostname: web-appv1-76765768c6-lxnm7

roman@mintwks:~$

Приложение доступно. Теперь попробуем создать еще один дополнительный ресурс в кластере:

kubectl run ngx2 —image=nginx

roman@cpn02:~$ kubectl run ngx2 —image=nginxpod/ngx2 created

roman@cpn02:~$

Ресурс создан. Проверим его статус:

kubectl get pod

roman@cpn02:~$ kubectl get pod

NAME READY STATUS RESTARTS AGE

busybox 0/1 CrashLoopBackOff 10 (98s ago) 28m

ngx 1/1 Running 0 14m

ngx2 1/1 Running 0 26s

web-appv1-76765768c6-lxnm7 1/1 Running 0 48m

web-appv1-76765768c6-v6f59 1/1 Terminating 0 89m

roman@cpn02:~$

Наш последний тест завершился успешно. Текущие и новые ресурсы обрабатываются кластером корректно.

Вместо заключения

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

Друзья помогите этому контенту стать доступнее в социальных сетях.

Не проходи мимо жмакни по кнопке возможно кому то еще он будет полезен!