Перейти к основному содержимому

K3s HA для homelab: Ставим K3s HA кластер

·1938 слов·10 минут
Олег Казанин
Автор
Олег Казанин
Строю полезную инфраструктуру на Open Source стеке. Документирую грабли, чтобы вы на них не наступали.
Оглавление
K3s HA кластер для homelab - Эта статья — часть серии.
Часть 3: Ты уже здесь

Инфраструктура готова: 5 VM работают, ОС настроена, порты открыты. Пора устанавливать K3s. Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер.

Звучит слишком просто? Потому что сложная часть уже позади - в предыдущих статьях. Теперь осталось не перепутать флаги и порядок установки.

Результат: 5 нод в статусе Ready, etcd кластер 3/3 healthy, kubectl работает с локальной машины.


Для кого это
#

Подходит:

  • Прошёл статьи 1 и 2 (или имеешь готовые VM с настроенной ОС)
  • Все 5 нод доступны по SSH
  • Понимаешь разницу между master и worker

Не подходит:

  • VM ещё не созданы → статья 2
  • Не понимаешь зачем 3 master ноды → статья 1

Что понадобится
#

КомпонентЗначение
K3s версияv1.31.4+k3s1 (или актуальная stable)
TokenСгенерируем на первом шаге
SSH доступКо всем 5 нодам
Время~15-20 минут

Шаг 1: Сгенерировать token
#

Token - общий секрет для всех нод кластера. Без правильного token нода не присоединится.

На локальной машине:

# Сгенерировать случайный token
openssl rand -base64 32

Пример вывода:

K10f8c9a7b6e5d4c3b2a1f0e9d8c7b6a5e4d3c2b1a0f9e8d7c6b5a4==

Сохрани этот token - он понадобится для каждой ноды. Положи в менеджер паролей или временный файл.

# Для удобства - сохранить в переменную (на время сессии)
export K3S_TOKEN="твой_сгенерированный_token"
echo $K3S_TOKEN

Шаг 2: Установить K3s на первую master ноду
#

Первая нода инициализирует etcd кластер. Она особенная - использует флаг --cluster-init.

SSH на k3s-master-1:

ssh k3s@192.168.11.201

Установка:

# Задать переменные
export K3S_TOKEN="твой_сгенерированный_token"
export INSTALL_K3S_VERSION="v1.31.4+k3s1"

# Установить K3s
curl -sfL https://get.k3s.io | sh -s - server \
  --cluster-init \
  --tls-san=192.168.11.201 \
  --disable=traefik \
  --disable=servicelb \
  --write-kubeconfig-mode=644

Разбор флагов:

ФлагЗачем
serverРежим control plane (не agent)
--cluster-initКлючевой! Инициализирует etcd. Только на первой ноде
--tls-san=192.168.11.201Добавить IP в сертификат API server
--disable=traefikОтключить встроенный Traefik (установим свой через Helm)
--disable=servicelbОтключить встроенный LB (установим MetalLB)
--write-kubeconfig-mode=644Разрешить чтение kubeconfig без sudo

Установка займёт 1-2 минуты. K3s скачает бинарник (~50MB) и запустит все компоненты.

Проверка
#

# 1. Статус сервиса
sudo systemctl status k3s

Ожидаемый результат:

● k3s.service - Lightweight Kubernetes
     Loaded: loaded
     Active: active (running) since ...
# 2. Статус ноды
sudo k3s kubectl get nodes

Ожидаемый результат:

NAME           STATUS   ROLES                       AGE   VERSION
k3s-master-1   Ready    control-plane,etcd,master   45s   v1.31.4+k3s1

Checkpoint: Первая master работает
#

# Быстрая проверка
sudo systemctl is-active k3s && \
sudo k3s kubectl get nodes | grep -q "Ready" && \
echo "✓ Master-1 готов" || echo "✗ Проблема"

Если статус NotReady или сервис не запустился:

# Смотреть логи
sudo journalctl -u k3s -f --no-pager | tail -50
Ошибка в логахПричинаРешение
cgroup v1 is not supportedНужен cgroup v2Вернись к статье 2, шаг 6.5
port 6443 already in useЧто-то занимает портsudo ss -tlnp | grep 6443
etcd failed to startМало места на дискеdf -h, увеличь диск

Шаг 3: Добавить вторую master ноду
#

Теперь присоединяем вторую master. Она подключается к существующему кластеру - без --cluster-init.

SSH на k3s-master-2:

ssh k3s@192.168.11.202

Установка:

export K3S_TOKEN="твой_сгенерированный_token"
export INSTALL_K3S_VERSION="v1.31.4+k3s1"

curl -sfL https://get.k3s.io | sh -s - server \
  --server=https://192.168.11.201:6443 \
  --tls-san=192.168.11.202 \
  --disable=traefik \
  --disable=servicelb \
  --write-kubeconfig-mode=644

Ключевое отличие:

  • ❌ Нет --cluster-init - кластер уже инициализирован
  • ✅ Есть --server=https://192.168.11.201:6443 - адрес существующего кластера

Проверка
#

# На master-2
sudo k3s kubectl get nodes

Ожидаемый результат:

NAME           STATUS   ROLES                       AGE   VERSION
k3s-master-1   Ready    control-plane,etcd,master   3m    v1.31.4+k3s1
k3s-master-2   Ready    control-plane,etcd,master   30s   v1.31.4+k3s1

Две ноды - но кворума ещё нет. etcd требует большинство, а 2 из 3 - это ещё не “большинство от трёх”.


Шаг 4: Добавить третью master ноду
#

SSH на k3s-master-3:

ssh k3s@192.168.11.203

Установка (аналогично master-2):

export K3S_TOKEN="твой_сгенерированный_token"
export INSTALL_K3S_VERSION="v1.31.4+k3s1"

curl -sfL https://get.k3s.io | sh -s - server \
  --server=https://192.168.11.201:6443 \
  --tls-san=192.168.11.203 \
  --disable=traefik \
  --disable=servicelb \
  --write-kubeconfig-mode=644

Checkpoint: Control Plane HA готов
#

На любой master ноде:

# 1. Все 3 master ноды Ready
sudo k3s kubectl get nodes

Ожидаемый результат:

NAME           STATUS   ROLES                       AGE   VERSION
k3s-master-1   Ready    control-plane,etcd,master   5m    v1.31.4+k3s1
k3s-master-2   Ready    control-plane,etcd,master   3m    v1.31.4+k3s1
k3s-master-3   Ready    control-plane,etcd,master   1m    v1.31.4+k3s1
# 2. etcd кластер здоров
sudo k3s kubectl exec -n kube-system \
  $(sudo k3s kubectl get pods -n kube-system -l component=etcd -o name | head -1) \
  -- etcdctl endpoint health --cluster

Ожидаемый результат:

https://192.168.11.201:2379 is healthy: successfully committed proposal: took = 2.1ms
https://192.168.11.202:2379 is healthy: successfully committed proposal: took = 1.8ms
https://192.168.11.203:2379 is healthy: successfully committed proposal: took = 2.3ms

Теперь у вас настоящий HA. Можете остановить любую master ноду - кластер продолжит работать.

Тест отказоустойчивости (опционально)
#

Хотите убедиться, что HA работает? Проверьте:

# С локальной машины - остановить master-2
ssh k3s@192.168.11.202 "sudo systemctl stop k3s"

# Подождать 30-40 секунд, затем на master-1:
ssh k3s@192.168.11.201 "sudo k3s kubectl get nodes"
# master-2 станет NotReady, но кластер работает

# Проверить etcd - 2/3 кворум есть
ssh k3s@192.168.11.201 "sudo k3s kubectl exec -n kube-system \
  \$(sudo k3s kubectl get pods -n kube-system -l component=etcd -o name | head -1) \
  -- etcdctl endpoint health --cluster"
# 2 из 3 healthy - кворум есть

# Вернуть master-2
ssh k3s@192.168.11.202 "sudo systemctl start k3s"

Шаг 5: Добавить worker ноды
#

Worker ноды устанавливаются как agent - они не участвуют в etcd и не запускают control plane.

5.1. Установить K3s agent на worker-1
#

SSH на k3s-worker-1:

ssh k3s@192.168.11.210

Установка:

export K3S_TOKEN="твой_сгенерированный_token"
export INSTALL_K3S_VERSION="v1.31.4+k3s1"

curl -sfL https://get.k3s.io | sh -s - agent \
  --server=https://192.168.11.201:6443

Отличия от master:

  • agent вместо server - режим worker
  • Нет флагов --disable и --tls-san - они не нужны для worker
  • Только --server - куда подключаться

5.2. Установить K3s agent на worker-2
#

SSH на k3s-worker-2:

ssh k3s@192.168.11.211

Установка:

export K3S_TOKEN="твой_сгенерированный_token"
export INSTALL_K3S_VERSION="v1.31.4+k3s1"

curl -sfL https://get.k3s.io | sh -s - agent \
  --server=https://192.168.11.201:6443

Checkpoint: Все ноды в кластере
#

На любой master ноде:

sudo k3s kubectl get nodes -o wide

Ожидаемый результат:

NAME           STATUS   ROLES                       AGE   VERSION        INTERNAL-IP
k3s-master-1   Ready    control-plane,etcd,master   10m   v1.31.4+k3s1   192.168.11.201
k3s-master-2   Ready    control-plane,etcd,master   8m    v1.31.4+k3s1   192.168.11.202
k3s-master-3   Ready    control-plane,etcd,master   6m    v1.31.4+k3s1   192.168.11.203
k3s-worker-1   Ready    <none>                      2m    v1.31.4+k3s1   192.168.11.210
k3s-worker-2   Ready    <none>                      1m    v1.31.4+k3s1   192.168.11.211

Обрати внимание:

  • Master: роли control-plane,etcd,master
  • Worker: роли <none> - только выполнение workloads
K3S HA Cluster

Шаг 6: Настроить kubectl на локальной машине
#

Сейчас kubectl работает только на master нодах через sudo k3s kubectl. Настроим доступ с вашей рабочей машины.

6.1. Скопировать kubeconfig
#

На локальной машине (не на ноде):

# 1. Создать директорию
mkdir -p ~/.kube

# 2. Скопировать конфиг с master-1
scp k3s@192.168.11.201:/etc/rancher/k3s/k3s.yaml ~/.kube/config

# 3. Заменить localhost на реальный IP
sed -i 's/127.0.0.1/192.168.11.201/g' ~/.kube/config

# Для macOS:
# sed -i '' 's/127.0.0.1/192.168.11.201/g' ~/.kube/config

# 4. Права доступа
chmod 600 ~/.kube/config

6.2. Установить kubectl (если нет)
#

Linux:

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
rm kubectl

macOS:

brew install kubectl

6.3. Проверить подключение
#

# Версия
kubectl version

# Ноды
kubectl get nodes

# Все поды
kubectl get pods -A

Ожидаемый результат kubectl get pods -A:

NAMESPACE     NAME                                      READY   STATUS    RESTARTS   AGE
kube-system   coredns-xxx                               1/1     Running   0          10m
kube-system   local-path-provisioner-xxx                1/1     Running   0          10m
kube-system   metrics-server-xxx                        1/1     Running   0          10m

Если kubectl не подключается:

СимптомПричинаРешение
connection refusedk3s не запущенssh k3s@192.168.11.201 "sudo systemctl status k3s"
connection timeoutFirewall блокируетПроверь UFW на master: sudo ufw status | grep 6443
certificate signed by unknown authorityНеправильный kubeconfigСкопируй заново с master ноды

6.4. Настроить автодополнение (опционально)
#

# Bash
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc
source ~/.bashrc

# Zsh
echo 'source <(kubectl completion zsh)' >> ~/.zshrc
source ~/.zshrc

Теперь работает k get nodes и Tab-автодополнение.


Финальная проверка
#

Полный чеклист работоспособности кластера:

echo "=== Проверка K3s HA кластера ==="

echo -n "1. Все ноды Ready: "
[ $(kubectl get nodes --no-headers | grep -c "Ready") -eq 5 ] && echo "✓ (5/5)" || echo "✗"

echo -n "2. Master ноды: "
kubectl get nodes --no-headers | grep -c "control-plane" | xargs -I {} echo "✓ ({}/3)"

echo -n "3. Worker ноды: "
kubectl get nodes --no-headers | grep -c "<none>" | xargs -I {} echo "✓ ({}/2)"

echo -n "4. etcd healthy: "
kubectl exec -n kube-system \
  $(kubectl get pods -n kube-system -l component=etcd -o name | head -1) \
  -- etcdctl endpoint health --cluster 2>/dev/null | grep -c "is healthy" | xargs -I {} echo "✓ ({}/3)"

echo -n "5. CoreDNS Running: "
kubectl get pods -n kube-system -l k8s-app=kube-dns --no-headers | grep -q "Running" && echo "✓" || echo "✗"

echo -n "6. Metrics Server Running: "
kubectl get pods -n kube-system -l k8s-app=metrics-server --no-headers | grep -q "Running" && echo "✓" || echo "✗"

echo ""
echo "=== Информация о кластере ==="
kubectl cluster-info

Ожидаемый результат:

=== Проверка K3s HA кластера ===
1. Все ноды Ready: ✓ (5/5)
2. Master ноды: ✓ (3/3)
3. Worker ноды: ✓ (2/2)
4. etcd healthy: ✓ (3/3)
5. CoreDNS Running: ✓
6. Metrics Server Running: ✓

=== Информация о кластере ===
Kubernetes control plane is running at https://192.168.11.201:6443
CoreDNS is running at https://192.168.11.201:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://192.168.11.201:6443/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy

Тестовый деплой
#

Убедимся, что кластер может запускать приложения:

# 1. Создать тестовый под
kubectl run nginx-test --image=nginx:alpine --port=80

# 2. Подождать запуска
kubectl wait --for=condition=Ready pod/nginx-test --timeout=60s

# 3. Проверить на какой ноде запустился
kubectl get pod nginx-test -o wide

Ожидаемый результат:

NAME         READY   STATUS    RESTARTS   AGE   IP           NODE
nginx-test   1/1     Running   0          30s   10.42.1.5    k3s-worker-1

Под запустился на worker ноде - как и должно быть.

# 4. Проверить доступность изнутри кластера
kubectl run -it --rm debug --image=busybox --restart=Never -- wget -qO- nginx-test

Ожидаемый результат: HTML-страница nginx.

# 5. Удалить тестовые ресурсы
kubectl delete pod nginx-test

Troubleshooting
#

Нода не присоединяется к кластеру
#

Симптом: После установки нода не появляется в kubectl get nodes.

ПричинаДиагностикаРешение
Неправильный tokenЛоги: sudo journalctl -u k3s-agent -fПроверь token, переустанови
Firewall блокируетcurl -k https://192.168.11.201:6443Открой порт 6443 на masters
DNS не резолвитping k3s-master-1Проверь /etc/hosts или DNS

etcd не формирует кворум
#

Симптом: etcdctl endpoint health показывает unhealthy.

# Проверить членов etcd
sudo k3s kubectl exec -n kube-system \
  $(sudo k3s kubectl get pods -n kube-system -l component=etcd -o name | head -1) \
  -- etcdctl member list --write-out=table
ПричинаДиагностикаРешение
Порты 2379-2380 закрытыsudo ufw statussudo ufw allow 2379:2380/tcp
Нода недоступна по сетиping 192.168.11.20XПроверь сеть, UFW
etcd ещё стартуетuptime на нодеПодожди 2-3 минуты

Поды не запускаются на workers
#

Симптом: Все поды на master нодах, workers пустые.

# Проверить taints
kubectl describe node k3s-worker-1 | grep Taints

Если есть taints - убрать:

kubectl taint nodes k3s-worker-1 node.kubernetes.io/not-ready:NoSchedule-

Откат: удаление K3s
#

Если нужно начать заново:

На master ноде:

sudo /usr/local/bin/k3s-uninstall.sh

На worker ноде:

sudo /usr/local/bin/k3s-agent-uninstall.sh

Что удаляется:

  • Бинарники K3s
  • Systemd сервисы
  • Данные из /var/lib/rancher/k3s/
  • etcd данные (на masters)
  • Контейнеры и образы

⚠️ Внимание: etcd снапшоты тоже удаляются. Если нужен бэкап:

# Перед удалением - сохранить снапшоты
sudo cp -r /var/lib/rancher/k3s/server/db/snapshots/ ~/k3s-backup/

Итог
#

Что сделано:

  • ✅ Сгенерирован token для кластера
  • ✅ Установлен K3s на 3 master ноды с embedded etcd
  • ✅ Добавлены 2 worker ноды
  • ✅ Настроен kubectl на локальной машине
  • ✅ Проверена работоспособность кластера

Что имеем:

  • HA Control Plane - выдерживает падение 1 master ноды
  • 2 worker ноды для приложений
  • kubectl доступ с локальной машины
  • Встроенные компоненты: CoreDNS, metrics-server, local-path storage

Что ещё не настроено (следующие статьи):

  • LoadBalancer (MetalLB) - для доступа к сервисам извне
  • Ingress Controller (Traefik) - для HTTP/HTTPS routing
  • SSL сертификаты (cert-manager) - для автоматического HTTPS
  • Мониторинг (Prometheus/Grafana) - для наблюдения за кластером

Что дальше
#

Кластер готов, но пока он изолирован от внешнего мира. Чтобы запускать реальные приложения с доступом извне, нужны:

  1. MetalLB - выдаёт IP-адреса для LoadBalancer сервисов
  2. Traefik - маршрутизирует HTTP/HTTPS трафик
  3. cert-manager - автоматически получает SSL сертификаты

Это темы для следующей серии статей.

А пока можно:

  • Поэкспериментировать с kubectl
  • Задеплоить тестовые приложения
  • Изучить как работает scheduling между нодами
K3s HA кластер для homelab - Эта статья — часть серии.
Часть 3: Ты уже здесь

Статьи по теме