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

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

Олег Казанин
Автор
Олег Казанин
Строю полезную инфраструктуру на 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 refused k3s не запущен ssh k3s@192.168.11.201 "sudo systemctl status k3s"
connection timeout Firewall блокирует Проверь 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 status sudo 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: Ты уже здесь

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