22 KiB
| title | date | draft | description | summary | tags | series | series_order | showTableOfContents | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Установить K3s HA кластер | 2025-11-02 | false | Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. Устанавливаем K3s на 3 master и 2 worker ноды, настраиваем kubectl. | Инфраструктура готова: 5 VM работают, ОС настроена, порты открыты. Пора устанавливать K3s. Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. |
|
|
3 | true |
Инфраструктура готова: 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
Шаг 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) - для наблюдения за кластером
Что дальше
Кластер готов, но пока он изолирован от внешнего мира. Чтобы запускать реальные приложения с доступом извне, нужны:
- MetalLB - выдаёт IP-адреса для LoadBalancer сервисов
- Traefik - маршрутизирует HTTP/HTTPS трафик
- cert-manager - автоматически получает SSL сертификаты
Это темы для следующей серии статей.
А пока можно:
- Поэкспериментировать с kubectl
- Задеплоить тестовые приложения
- Изучить как работает scheduling между нодами