17-kubernetes — Kubernetes (k3s + kubectl + helm)

Ставит лёгкий Kubernetes-дистрибутив k3s, ждёт готовности узла, копирует доступ (kubeconfig) деплой-пользователю, ставит Helm и открывает нужные порты в файрволе. Поддерживает одиночный сервер и HA-кластер из нескольких серверов. ☐ опциональный.

Зачем нужен

Kubernetes (сокращённо «k8s») — это система оркестрации контейнеров: она сама запускает ваши приложения-контейнеры на серверах, перезапускает упавшие, держит нужное число копий (реплик), обновляет их без простоя и распределяет нагрузку. Вы описываете «что должно работать», а Kubernetes следит, чтобы так и было.

k3s — это официальный облегчённый дистрибутив Kubernetes (сертифицирован CNCF), собранный в один бинарник. Он идеально подходит для одного продакшен-сервера: внутри уже есть containerd (среда запуска контейнеров), поэтому Docker не требуется. При этом это полноценный Kubernetes — те же kubectl, манифесты и Helm.

Модуль ставит k3s, консольный клиент kubectl (внутри k3s), пакетный менеджер Helm (устанавливает готовые приложения в кластер «одной командой», как apt для Kubernetes), настраивает доступ и файрвол — и даёт команду mitdev k8s deploy для развёртывания ваших контейнеров.

Когда включать и когда не нужен

Зависимости и требования

Три режима кластера

Режим выбирается переменной K8S_CLUSTER_MODE (её задаёт мастер установки):

Режим Что это Когда
single один самостоятельный сервер (по умолчанию) обычный одиночный сервер
init первый сервер HA-кластера, поднимает встроенный etcd когда нужен отказоустойчивый кластер
join дополнительный сервер, присоединяется к существующему кластеру второй, третий и т. д. узлы

etcd («embedded etcd») — это встроенная в k3s распределённая база, где хранится всё состояние кластера; в HA-режиме она реплицируется между серверами, поэтому кластер переживает отказ узла. Для кворума etcd нужно нечётное число серверов (обычно 3).

Режим join требует двух параметров, которые даёт первый узел командой mitdev k8s token:

Ещё одна переменная — K8S_PEERS (список IP остальных серверов кластера): по ним модуль точечно открывает кластерные порты в файрволе.

Что именно делает

Пошагово, в порядке выполнения module_install:

  1. Ставит k3s (канал stable) в выбранном режиме. Если k3s уже установлен и запущен — шаг пропускается. Формируется строка запуска:
    • singleserver;
    • initserver --cluster-init (поднимает встроенный etcd);
    • joinserver --server <K8S_SERVER_URL> (проверяет наличие K8S_SERVER_URL и K8S_TOKEN).
  2. Если обнаружен nginx (state_done nginx или служба активна) — к строке запуска добавляется --disable traefik. traefik — это встроенный в k3s веб-балансировщик (ingress-контроллер), который сам занимает порты 80 и 443; nginx хочет те же порты, поэтому их «драка» устраняется отключением traefik. Публикация приложений тогда идёт через nginx.
  3. Ждёт готовности узла (_k8s_wait_node_ready): до 30 попыток с интервалом 4 секунды (до ~2 минут) опрашивает kubectl get nodes, пока узел не перейдёт в состояние Ready (готов принимать поды).
  4. Копирует kubeconfig деплой-пользователю (если задан DEPLOY_USER): создаёт /home/<user>/.kube (700), кладёт /etc/rancher/k3s/k3s.yaml в ~/.kube/config (600, владелец — деплой-пользователь). kubeconfig — это файл с адресом кластера и ключами доступа; с ним kubectl работает от имени пользователя без sudo.
  5. Ставит Helm официальным скриптом в /usr/local/bin/helm (если ещё не установлен).
  6. Открывает порты в файрволе (если UFW активен):
    • 6443/tcp — API-сервер Kubernetes;
    • 10.42.0.0/16 — сеть подов (pod — минимальная единица запуска, один или несколько контейнеров);
    • 10.43.0.0/16 — сеть сервисов (внутренние виртуальные адреса служб);
    • для каждого пира из K8S_PEERS дополнительно (нужно для HA): 2379:2380/tcp (etcd), 10250/tcp (kubelet — агент на узле), 8472/udp (flannel VXLAN — сетевой туннель между узлами).
  7. Если режим init — печатает подсказку: токен присоединения берётся командой mitdev k8s token.
  8. Верификация и state_mark.

Файлы, службы и порты

Объект Значение Примечание
Служба systemd k3s (или k3s-agent) автозапуск включён
Порт 6443/tcp API-сервер Kubernetes доступ kubectl и присоединение узлов
Порт 2379-2380/tcp etcd только HA, только между пирами
Порт 10250/tcp kubelet только HA, только между пирами
Порт 8472/udp flannel VXLAN только HA, только между пирами
Сеть 10.42.0.0/16 поды внутренняя
Сеть 10.43.0.0/16 сервисы внутренняя
/usr/local/bin/helm Helm пакетный менеджер
/etc/rancher/k3s/k3s.yaml kubeconfig (root) исходный файл доступа
/home/<DEPLOY_USER>/.kube/config kubeconfig деплой-пользователя 600
/var/lib/rancher/k3s/server/node-token токен присоединения (режимы init/join) только root
/usr/local/bin/k3s-uninstall.sh штатный деинсталлятор k3s ставится вместе с k3s
/var/lib/mitdev/k8s/<app>.yaml манифесты приложений mitdev k8s deploy

Учётные данные

Отдельных паролей модуль не сохраняет в /root/.mitdev-credentials. Доступ к кластеру даёт kubeconfig:

Секрет присоединения новых узлов к кластеру — токен в /var/lib/rancher/k3s/server/node-token (читается только root). Его показывает mitdev k8s token.

Настройка

Эти переменные собирает мастер установки (их нет в config/defaults.conf):

Переменная Значения По умолчанию На что влияет Когда задаётся
K8S_CLUSTER_MODE single / init / join single режим узла (одиночный / первый в кластере / присоединяемый) в мастере при выборе одиночного или кластерного сценария
K8S_SERVER_URL https://<ip>:6443 адрес первого узла кластера только в режиме join
K8S_TOKEN строка-токен секрет присоединения к кластеру только в режиме join
K8S_PEERS список IP через пробел пусто адреса других серверов кластера — для них открываются кластерные порты в HA-сценарии

K8S_SERVER_URL и K8S_TOKEN для режима join вы берёте с первого узла командой mitdev k8s token.

Как переопределить настройки

Обычно эти переменные задаются интерактивно в мастере установки при выборе кластерного сценария. Их можно передать и явно перед запуском (флаг -E сохраняет окружение при sudo):

# первый узел HA-кластера
K8S_CLUSTER_MODE=init sudo -E ./install.sh

# присоединяемый узел
K8S_CLUSTER_MODE=join \
  K8S_SERVER_URL=https://10.8.0.1:6443 \
  K8S_TOKEN=<токен-с-первого-узла> \
  K8S_PEERS="10.8.0.1 10.8.0.2" \
  sudo -E ./install.sh

Как пользоваться

Режим single — одиночный сервер

Ничего дополнительно делать не нужно: после установки узел готов, деплойте приложения (см. ниже).

Режим init + join — HA-кластер по шагам

Узел 1 (первый сервер, режим init):

# при установке выбран режим init (или K8S_CLUSTER_MODE=init)
# после установки получите параметры присоединения:
sudo mitdev k8s token
# выведет:  URL: https://<ip-узла-1>:6443   и   Токен: <строка>

Узел 2 и далее (режим join): запустите установку с параметрами, полученными на узле 1:

K8S_CLUSTER_MODE=join \
  K8S_SERVER_URL=https://<ip-узла-1>:6443 \
  K8S_TOKEN=<токен-с-узла-1> \
  K8S_PEERS="<ip-узла-1> <ip-узла-3>" \
  sudo -E ./install.sh

Проверка кластера (на любом узле):

sudo mitdev k8s nodes    # все узлы должны быть Ready
sudo mitdev k8s status

Для кворума etcd держите нечётное число серверов (3 — типичный минимум для HA).

Добавить ещё один узел

Повторите шаг «join» на новом сервере с тем же URL и токеном (токен всегда можно переполучить на первом узле через mitdev k8s token). Не забудьте добавить IP нового узла в K8S_PEERS на нём (и при желании открыть порты для него на других узлах).

Развернуть приложение — mitdev k8s deploy

sudo mitdev k8s deploy <имя> <образ> [порт] [домен] [реплики]

Пример:

sudo mitdev k8s deploy web ghcr.io/org/app:latest 3000 app.example.com 3

Что происходит: рендерится манифест Deployment + Service в /var/lib/mitdev/k8s/web.yaml, применяется kubectl apply, ожидается раскатка (rollout status, до 180 с). Манифест включает podAntiAffinity — правило, которое старается разнести реплики по разным узлам кластера: падение одного сервера уносит максимум одну копию, остальные продолжают обслуживать трафик. Также заданы проверки готовности/живости (readiness/liveness probes) и плавное обновление (rolling update без простоя). Если задан домен: при включённом traefik создаётся Ingress (правило маршрутизации HTTP по домену), при nginx — сайт nginx на выделенный NodePort сервиса (с предложением выпустить SSL через certbot).

Управлять кластером через kubectl

От root — через встроенный клиент:

sudo k3s kubectl get pods -A

От деплой-пользователя — обычный kubectl (kubeconfig уже лежит в ~/.kube/config):

kubectl get pods
kubectl get nodes

Удобные обёртки mitdev:

sudo mitdev k8s status      # версия, узлы, поды, приложения mitdev
sudo mitdev k8s nodes       # узлы (get nodes -o wide)
sudo mitdev k8s pods [ns]   # поды (всех или одного namespace)
sudo mitdev k8s services    # сервисы
sudo mitdev k8s logs <app>  # логи приложения (follow)
sudo mitdev k8s apply <файл.yaml>

Проверка, что всё работает

module_verify считает модуль успешным, если: команда k3s есть, служба k3s активна, узел в состоянии Ready, и установлен helm.

Ручные проверки:

# версия и служба
sudo k3s --version
systemctl status k3s

# узел(ы) должны быть Ready
sudo k3s kubectl get nodes

# системные поды (kube-system) в Running
sudo k3s kubectl get pods -A

# Helm установлен
helm version

# общая диагностика mitdev
sudo mitdev doctor

Типовые задачи

Частые ошибки

Симптом Причина Что сделать
nginx не стартует после установки k3s traefik из k3s занял порты 80/443 k3s должен ставиться с --disable traefik — переустановите модуль, когда nginx уже установлен, либо снимите traefik; смотрите journalctl -u nginx
Узел долго не Ready (больше 2 минут) не скачались системные образы, проблемы с сетью/диском sudo k3s kubectl describe node; journalctl -u k3s -n 100; проверьте интернет и место на диске
mitdev k8s token пишет «токен не найден» узел не сервер кластера (не режим init) токен есть только на серверах, поднятых в режиме init
Узел join не присоединяется закрыт 6443, неверный токен/URL, разные версии k3s проверьте доступность https://<узел-1>:6443, сверьте токен, откройте порты для пиров
Приложение развёрнуто, но недоступно по домену не задан домен, нет ни traefik, ни nginx укажите домен при deploy; при nginx на хосте публикация идёт через него, при traefik — через ingress
Реплики оказались на одном узле podAntiAffinity — «мягкое» правило (preferred) это ожидаемо, если узлов мало; при достаточном числе узлов они разнесутся

Безопасность и продакшен

Модуль уже закрывает: k3s работает через containerd без Docker, kubeconfig деплой-пользователя имеет права 600, порты открываются точечно (API — всем, кластерные — только пирам из K8S_PEERS), traefik отключается при конфликте с nginx.

Остаётся на клиенте:

Откат и удаление

См. также