oakazanin/content/posts/k3s-part3-installation/index.md

648 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "K3s HA для homelab: Ставим K3s HA кластер"
date: 2025-11-02
draft: false
description: "Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. Устанавливаем K3s на 3 master и 2 worker ноды, настраиваем kubectl."
tags: ["kubernetes", "k3s", "homelab", "installation", "ha", "etcd", "devops"]
series: ["K3s HA кластер для homelab"]
series_order: 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 нода не присоединится.
**На локальной машине:**
```bash
# Сгенерировать случайный token
openssl rand -base64 32
```
**Пример вывода:**
```
K10f8c9a7b6e5d4c3b2a1f0e9d8c7b6a5e4d3c2b1a0f9e8d7c6b5a4==
```
**Сохрани этот token** - он понадобится для каждой ноды. Положи в менеджер паролей или временный файл.
```bash
# Для удобства - сохранить в переменную (на время сессии)
export K3S_TOKEN="твой_сгенерированный_token"
echo $K3S_TOKEN
```
---
## Шаг 2: Установить K3s на первую master ноду
Первая нода инициализирует etcd кластер. Она особенная - использует флаг `--cluster-init`.
**SSH на k3s-master-1:**
```bash
ssh k3s@192.168.11.201
```
**Установка:**
```bash
# Задать переменные
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) и запустит все компоненты.
### Проверка
```bash
# 1. Статус сервиса
sudo systemctl status k3s
```
**Ожидаемый результат:**
```
● k3s.service - Lightweight Kubernetes
Loaded: loaded
Active: active (running) since ...
```
```bash
# 2. Статус ноды
sudo k3s kubectl get nodes
```
**Ожидаемый результат:**
```
NAME STATUS ROLES AGE VERSION
k3s-master-1 Ready control-plane,etcd,master 45s v1.31.4+k3s1
```
<!-- СКРИНШОТ: Терминал с выводом `sudo k3s kubectl get nodes` - одна нода в статусе Ready -->
### Checkpoint: Первая master работает
```bash
# Быстрая проверка
sudo systemctl is-active k3s && \
sudo k3s kubectl get nodes | grep -q "Ready" && \
echo "✓ Master-1 готов" || echo "✗ Проблема"
```
**Если статус NotReady или сервис не запустился:**
```bash
# Смотреть логи
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:**
```bash
ssh k3s@192.168.11.202
```
**Установка:**
```bash
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` - адрес существующего кластера
### Проверка
```bash
# На 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:**
```bash
ssh k3s@192.168.11.203
```
**Установка (аналогично master-2):**
```bash
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 ноде:**
```bash
# 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
```
```bash
# 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
```
<!-- СКРИНШОТ: Терминал с выводом etcdctl endpoint health - все 3 endpoint healthy -->
**Теперь у вас настоящий HA.** Можете остановить любую master ноду - кластер продолжит работать.
### Тест отказоустойчивости (опционально)
Хотите убедиться, что HA работает? Проверьте:
```bash
# С локальной машины - остановить 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:**
```bash
ssh k3s@192.168.11.210
```
**Установка:**
```bash
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:**
```bash
ssh k3s@192.168.11.211
```
**Установка:**
```bash
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 ноде:**
```bash
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
```
<!-- СКРИНШОТ: Терминал с выводом kubectl get nodes - все 5 нод Ready -->
**Обрати внимание:**
- Master: роли `control-plane,etcd,master`
- Worker: роли `<none>` - только выполнение workloads
![K3S HA Cluster](mermaid-diagram.svg)
---
## Шаг 6: Настроить kubectl на локальной машине
Сейчас kubectl работает только на master нодах через `sudo k3s kubectl`. Настроим доступ с вашей рабочей машины.
### 6.1. Скопировать kubeconfig
**На локальной машине (не на ноде):**
```bash
# 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:**
```bash
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:**
```bash
brew install kubectl
```
### 6.3. Проверить подключение
```bash
# Версия
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 get nodes и kubectl get pods -A -->
**Если 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
# 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-автодополнение.
---
## Финальная проверка
Полный чеклист работоспособности кластера:
```bash
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
```
---
## Тестовый деплой
Убедимся, что кластер может запускать приложения:
```bash
# 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 ноде - как и должно быть.
```bash
# 4. Проверить доступность изнутри кластера
kubectl run -it --rm debug --image=busybox --restart=Never -- wget -qO- nginx-test
```
**Ожидаемый результат:** HTML-страница nginx.
```bash
# 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.
```bash
# Проверить членов 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 пустые.
```bash
# Проверить taints
kubectl describe node k3s-worker-1 | grep Taints
```
Если есть taints - убрать:
```bash
kubectl taint nodes k3s-worker-1 node.kubernetes.io/not-ready:NoSchedule-
```
---
## Откат: удаление K3s
Если нужно начать заново:
**На master ноде:**
```bash
sudo /usr/local/bin/k3s-uninstall.sh
```
**На worker ноде:**
```bash
sudo /usr/local/bin/k3s-agent-uninstall.sh
```
**Что удаляется:**
- Бинарники K3s
- Systemd сервисы
- Данные из `/var/lib/rancher/k3s/`
- etcd данные (на masters)
- Контейнеры и образы
**⚠️ Внимание:** etcd снапшоты тоже удаляются. Если нужен бэкап:
```bash
# Перед удалением - сохранить снапшоты
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 между нодами