No description
Find a file
2026-07-20 17:37:34 +03:00
.gigacode_vsc/plans refactor(uyuni): comment out unused storageClass and duplicate mode setting 2026-07-20 17:28:38 +03:00
inventory/prod refactor(infra): restructure inventory and update dependencies 2026-07-16 21:28:44 +03:00
playbooks docs: add playbook documentation for NVIDIA device plugin and vLLM setup 2026-07-20 17:37:34 +03:00
research initial commit from gigacoms 2026-07-15 11:14:21 +03:00
roles feat(nvidia_device_plugin): add initial NVIDIA device plugin role skeleton 2026-07-20 17:32:30 +03:00
.ansible-lint initial commit from gigacoms 2026-07-15 11:14:21 +03:00
.gitignore initial commit from gigacoms 2026-07-15 11:14:21 +03:00
.gitlab-ci.yml initial commit from gigacoms 2026-07-15 11:14:21 +03:00
ansible.cfg initial commit from gigacoms 2026-07-15 11:14:21 +03:00
README.md initial commit from gigacoms 2026-07-15 11:14:21 +03:00
requirements.yml initial commit from gigacoms 2026-07-15 11:14:21 +03:00

Kubernetes Infrastructure

Корпоративный стандарт. Ansible-развёртывание кластера Kubernetes на Rocky Linux 9.
Локальный запуск через CLI и автоматизированный запуск через GitLab CI/CD.


Содержание


Инфраструктура

┌─────────────────────────────────────────────────────────────────┐
│  Operator workstation (manager_nodes)                           │
│  k8s-manager-01  10.203.0.92                                    │
│  kubectl · helm · k9s · kubectx/kubens                         │
│  Kubernetes Dashboard token → ~/.kube/dashboard-token          │
└────────────────────────┬────────────────────────────────────────┘
                         │ kubectl / helm (kubeconfig)
┌────────────────────────▼────────────────────────────────────────┐
│  Kubernetes cluster (k8s_cluster)                               │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  Control plane  k8s-master-01  10.203.0.97               │  │
│  │  kube-apiserver · etcd · controller-manager · scheduler  │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  Worker  k8s-worker-01  10.203.0.96                      │  │
│  │  kubelet · kube-proxy · Longhorn disk agent              │  │
│  │  Traefik DaemonSet                                       │  │
│  │    :10001 → kubernetes-dashboard (ClusterIP :443)        │  │
│  │    :10002 → longhorn-frontend    (ClusterIP :80)  [auth] │  │
│  │    :10003 → grafana               (ClusterIP :80)         │  │
│  │    :10005 → minio                (ClusterIP :9000)       │  │
│  │    :10006 → minio-console        (ClusterIP :9001)       │  │
│  │    :10103 → db/bgbilling-dev     (TCP :3306)             │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                 │
│  CNI: Flannel v0.26.1  │  Storage: Longhorn 1.7.2              │
└─────────────────────────────────────────────────────────────────┘

Узлы кластера

Hostname IP Группа инвентаря Роль
k8s-manager-01 10.203.0.92 manager_nodes Рабочая станция оператора
k8s-master-01 10.203.0.97 control_plane API server, etcd, scheduler
k8s-worker-01 10.203.0.96 workers Вычислительный узел
k8s-worker-02 10.203.0.212 workersgpu_workers GPU-узел (NVIDIA), Ollama-стек
k8s-worker-NN TBD workers Дополнительные вычислительные узлы

manager_nodesне Kubernetes-узел. Это рабочая станция оператора, с которой запускаются kubectl и helm.

Сервисы и точки доступа

Сервис URL Аутентификация
Kubernetes Dashboard http://10.203.0.96:10001 токен из ~/.kube/dashboard-token
Longhorn UI http://10.203.0.96:10002 BasicAuth (Secret traefik-auth-longhorn в ns traefik)
Grafana http://10.203.0.96:10003 логин Grafana (admin, пароль из Secret grafana-admin-secret)
MinIO S3 API http://10.203.0.96:10005 AWS Signature v4 (встроенная аутентификация MinIO)
MinIO Console http://10.203.0.96:10006 логин MinIO (rootUser из Secret minio-root-credentials)
MariaDB BGBilling 10.203.0.96:10103 TCP-проброс (без HTTP); аутентификация средствами MariaDB
Ollama Proxy (k8s_ai) http://10.203.0.96:10200 своя аутентификация приложения
Open WebUI (k8s_ai) http://10.203.0.96:10201 своя аутентификация приложения

Стек технологий

Компонент Версия Управляется
ОС Rocky Linux 9 Ansible
Kubernetes 1.33 (pkgs.k8s.io) Ansible → k8s_control_plane, k8s_worker
Container runtime containerd (Docker CE repo) Ansible
CNI Flannel v0.26.1 / Calico v3.27.0 Ansible → k8s_control_plane
Постоянное хранилище Longhorn 1.7.2 Ansible → longhorn_prereqs, longhorn
Ingress / proxy Traefik 32.1.0 Ansible → traefik
Kubernetes Dashboard v2.7.0 Ansible → k8s_manager
Объектное хранилище MinIO 5.4.0 (chart) Ansible → minio
Централизованные логи Loki 7.0.0 + Grafana Alloy 1.8.2 + Grafana 10.5.15 Ansible → logging
Шифрование Secret'ов Sealed Secrets 2.16.1 Ansible → sealed_secrets
GitOps Flux CD (latest stable) Ansible → flux
GitLab Agent agentk (latest stable) Ansible → gitlab_agent
Автоматизация Ansible (посм. requirements.yml)
CI/CD GitLab CI/CD .gitlab-ci.yml

Структура репозитория

.gitlab-ci.yml               — CI/CD-пайплайн (стадии validate → setup)
ansible.cfg                  — кэширование фактов, pipelining, YAML-вывод
requirements.yml             — коллекции Ansible Galaxy

inventory/prod/
  hosts.yml                  — статический список хостов (3 группы)
  group_vars/
    all.yml                  — SSH-учётные данные из переменных окружения
    k8s_cluster.yml          — общие переменные для control_plane + workers
    control_plane.yml        — k8s_version, CIDR, CNI
    workers.yml              — конфигурация дисков Longhorn
    manager_nodes.yml        — версии инструментов, часовой пояс
    traefik.yml              — traefik_port_map: таблица портов и backend-сервисов
    gpu_workers.yml          — node_labels, override дисков Longhorn для GPU-узлов

playbooks/                   — один плейбук = одна CI/CD-задача
  setup_manager.yml
  setup_control_plane.yml
  setup_worker_plane.yml
  setup_longhorn.yml
  setup_traefik.yml
  setup_flux.yml
  setup_gitlab_agent.yml
  setup_minio.yml
  setup_logging.yml
  setup_sealed_secrets.yml
  setup_gpu.yml

roles/
  k8s_manager/
  k8s_control_plane/
  k8s_worker/
  longhorn_prereqs/
  longhorn/          README.md  ← стандарт работы с хранилищем
  traefik/           README.md  ← стандарт публикации сервисов
  flux/              README.md  ← стандарт GitOps-деплоя приложений
  gitlab_agent/
  minio/             README.md  ← стандарт объектного хранилища S3
  logging/           README.md  ← стандарт централизованного сбора логов
  sealed_secrets/
  gpu_prereqs/       — nvidia-container-toolkit + containerd runtime на GPU-узле
  gpu_device_plugin/ — node label + NVIDIA k8s-device-plugin с manager-ноды
  gpu_model_storage/ README.md  ← стандарт локального RAID1-хранилища под модели

Роли и компоненты

k8s_control_plane · k8s_worker · k8s_manager

Базовые роли развёртывания кластера. Не имеют отдельного role-README — полное описание в разделе Архитектурные решения.

Роль Что делает Плейбук
k8s_control_plane containerd → kubelet → kubeadm init → CNI → kubeconfig setup_control_plane.yml
k8s_worker containerd → kubelet → kubeadm join setup_worker_plane.yml
k8s_manager kubectl / helm / k9s, Kubernetes Dashboard setup_manager.yml
Переменные k8s_control_plane
Переменная Значение по умолчанию Описание
k8s_version 1.33 Версия Kubernetes
pod_network_cidr 10.244.0.0/16 Сеть подов (Flannel)
service_cidr 10.96.0.0/12 Сеть сервисов
cni_plugin flannel flannel или calico
flannel_version v0.26.1 Версия манифеста Flannel
calico_version v3.27.0 Версия манифеста Calico
kubeconfig_fetch_to_managers true Копировать kubeconfig на manager-узел
Переменные k8s_worker
Переменная Значение по умолчанию Описание
k8s_version 1.33 Должна совпадать с control plane
worker_join_source_host первый хост control_plane Откуда читать join-команду
Переменные k8s_manager
Переменная Значение по умолчанию Описание
dashboard_insecure false HTTP-режим Dashboard (переопределяется в true в group_vars)
dashboard_manifest_url GitHub raw v2.7.0 Без зависимости от CDN
k8s_manager_upgrade_packages false Полное обновление ОС (opt-in)

gpu_prereqs · gpu_device_plugin

Подключение GPU-узла к кластеру. Драйвер NVIDIA ставится вручную заранее — роль его не устанавливает, только проверяет наличие (nvidia-smi). Запускается после setup_worker_plane.yml, отдельным плейбуком setup_gpu.yml, только для узлов группы gpu_workers (подгруппа workers).

Роль Запускается на Что делает
gpu_prereqs gpu_workers Проверка драйвера, установка nvidia-container-toolkit, nvidia-ctk runtime configure для containerd (default runtime)
gpu_device_plugin manager_nodes kubectl label node <host> gpu=nvidia для каждого узла gpu_workers; kubectl apply манифеста NVIDIA k8s-device-plugin
Переменные gpu_prereqs / gpu_device_plugin
Переменная Значение по умолчанию Описание
nvidia_container_toolkit_version "" (последняя) Версия пакета nvidia-container-toolkit
node_labels {gpu: nvidia}gpu_workers.yml) Лейблы, навешиваемые на узел после join
gpu_device_plugin_version v0.17.0 Тег релиза NVIDIA k8s-device-plugin
gpu_device_plugin_manifest_url GitHub raw для указанной версии Статический манифест DaemonSet

Порядок обязателен: setup_worker_plane.yml перезаписывает /etc/containerd/config.toml с нуля, поэтому gpu_prereqs (донастройка containerd под nvidia runtime) должна выполняться после, отдельным прогоном.


gpu_model_storage

Выделенный локальный RAID1-раздел под файлы моделей (Ollama) на GPU-узлах. gpu_workers исключены из Longhorn (longhorn_disks: []), а корневая ФС слишком маленькая (~30 ГБ) для хранения моделей — поэтому под них нарезается отдельный раздел из неразмеченного места на тех же зеркальных дисках, что и ОС. Запускается плейбуком setup_gpu_model_storage.yml, независимо от порядка kubeadm join.

Переменные gpu_model_storage
Переменная Значение по умолчанию Описание
gpu_model_storage_devices [] Диски-члены RAID1 (пусто = роль выключена); задаётся в gpu_workers.yml
gpu_model_storage_partition_size_gb 1000 Размер новой партиции на каждом диске, ГБ
gpu_model_storage_raid_device /dev/md1 Имя нового mdadm-массива
gpu_model_storage_mountpoint /mnt/ollama-models Точка монтирования
gpu_model_storage_fstype xfs Файловая система

Standard: roles/gpu_model_storage/README.md


longhorn_prereqs · longhorn

Двухэтапное развёртывание распределённого блочного хранилища.

Роль Запускается на Что делает
longhorn_prereqs workers iSCSI / NFS пакеты, модули ядра, SELinux CIL-политика, форматирование и монтирование дисков XFS
longhorn manager_nodes Аннотации узлов с disk-config JSON, helm upgrade --install longhorn

После установки доступен StorageClass longhorn (default). Приложения используют его через storageClassName: longhorn в PVC.

Переменные
Переменная Значение по умолчанию Описание
longhorn_chart_version 1.7.2 Версия Helm-чарта
longhorn_namespace longhorn-system Namespace
longhorn_default_replica_count 1 Реплик на том; поднять до 23 при добавлении worker-нод
longhorn_minimal_available_storage_percentage 10 Минимальный свободный % хранилища
longhorn_storage_over_provisioning_percentage 100 Коэффициент over-provisioning
longhorn_disks [{device: /dev/sdb, mountpoint: /mnt/longhorn-disk1}, ...] Диски для форматирования; переопределяется в hosts.yml

Стандарт работы с хранилищем (PVC, StatefulSet, реплики, бэкапы, диагностика):
roles/longhorn/README.md


traefik

HTTP/TCP port-proxy. DaemonSet на worker-нодах. Каждый сервис получает выделенный TCP-порт из диапазона 1000110999. Вся топология определяется через traefik_port_map в inventory/prod/group_vars/traefik.yml. Поддерживаются два режима маршрутизации: HTTP (IngressRoute, поле protocol не задаётся) и raw TCP (IngressRouteTCP с HostSNI("*"), protocol: tcp в port_map).

Задача Что делает
firewall.yml Открывает 1000010999/tcp; помещает flannel.1, cni0 в зону trusted
helm.yml helm upgrade --install traefik как DaemonSet с EntryPoint-ами из port_map
middleware.yml Применяет Middleware CRD (BasicAuth) для записей с basicauth.enabled: true
routes.yml Применяет IngressRoute (HTTP) или IngressRouteTCP (TCP) в зависимости от protocol в port_map
Переменные
Переменная Значение по умолчанию Описание
traefik_chart_version 32.1.0 Версия Helm-чарта
traefik_namespace traefik Namespace
traefik_port_map [] Список сервисов; переопределяется в group_vars/traefik.yml

Стандарт публикации сервисов (добавление порта, BasicAuth, диагностика, таблица активных портов):
roles/traefik/README.md


flux

Устанавливает Flux CD CLI и выполняет flux bootstrap gitlab, подключая кластер к fleet-репозиторию k8s-fleet на gitlab.gigacoms.info.

Задача Что делает
install.yml Flux CLI → /usr/local/bin/flux, bash-completion, flux check --pre
bootstrap.yml flux bootstrap gitlab — deploy key, Flux-контроллеры в flux-system, манифесты в clusters/production

Fleet-репозиторий: gitlab.gigacoms.info/k8s/k8s-fleet · путь: clusters/production

Переменные
Переменная Значение по умолчанию Описание
flux_version "" (latest stable) Версия Flux CLI
flux_gitlab_hostname gitlab.gigacoms.info Хост GitLab
flux_gitlab_owner k8s GitLab-группа fleet-репозитория
flux_gitlab_repository k8s-fleet Имя fleet-репозитория
flux_gitlab_branch main Ветка
flux_gitlab_path clusters/production Путь внутри репозитория
flux_gitlab_token из $GITLAB_FLUX_TOKEN GitLab PAT (scope: api)

Стандарт GitOps-деплоя приложений (структура fleet-repo, multi-branch, примеры с Longhorn и Traefik, CI/CD-шаблон):
roles/flux/README.md


minio

S3-совместимое объектное хранилище на базе MinIO. Запускается как standalone StatefulSet на worker-ноде, хранилище предоставляется отдельным StorageClass longhorn-minio (reclaimPolicy: Retain). Доступ — через Traefik на портах 10005 (S3 API) и 10006 (Console UI).

Задача Что делает
storageclass.yml Создаёт StorageClass longhorn-minio (Retain, numberOfReplicas=1)
namespace.yml Создаёт namespace minio
helm.yml helm upgrade --install minio minio/minio с values из шаблона

Предварительное условие: создать Secret minio-root-credentials вручную в namespace minio:

kubectl create secret generic minio-root-credentials \
  --from-literal=rootUser=minioadmin \
  --from-literal=rootPassword='СИЛЬНЫЙ_ПАРОЛЬ' \
  -n minio
Переменные
Переменная Значение по умолчанию Описание
minio_chart_version 5.4.0 Версия Helm-чарта minio/minio
minio_image_tag RELEASE.2025-04-22T22-12-26Z Тег образа MinIO
minio_namespace minio Namespace
minio_storage_class longhorn-minio StorageClass для PVC
minio_storage_size 1Ti Размер PVC
minio_console_url http://10.203.0.96:10006 URL Console (для MINIO_BROWSER_REDIRECT_URL)
minio_root_secret minio-root-credentials Имя Secret с rootUser/rootPassword
minio_buckets [backups, artifacts] Бакеты, создаваемые при старте

Стандарт объектного хранилища S3 (StorageClass, пользователи, lifecycle, интеграция с Loki, масштабирование):
roles/minio/README.md


logging

PLG-стек централизованного сбора логов: Loki (S3 backend → MinIO), Grafana Alloy (DaemonSet, замена EOL Promtail), Grafana (UI + алерты). Все три компонента разворачиваются в namespace monitoring из manager-узла через Helm.

Задача Что делает
namespace.yml Создаёт namespace monitoring
minio-user.yml Создаёт bucket loki-chunks, пользователя loki с политикой только на этот bucket, Secret loki-minio-secret
loki.yml helm upgrade --install loki в режиме single-binary с S3 backend (MinIO)
alloy.yml helm upgrade --install alloy как DaemonSet — сбор логов подов + journald на всех нодах
grafana.yml helm upgrade --install grafana с provisioning datasource Loki

Предварительное условие: создать Secret grafana-admin-secret вручную в namespace monitoring:

kubectl create namespace monitoring
kubectl create secret generic grafana-admin-secret \
  --from-literal=admin-user=admin \
  --from-literal=admin-password='СИЛЬНЫЙ_ПАРОЛЬ' \
  -n monitoring
Переменные
Переменная Значение по умолчанию Описание
loki_chart_version 7.0.0 Версия Helm-чарта grafana/loki
alloy_chart_version 1.8.2 Версия Helm-чарта grafana/alloy
grafana_chart_version 10.5.15 Версия Helm-чарта grafana/grafana
loki_minio_endpoint http://minio.minio.svc.cluster.local:9000 S3 endpoint для Loki
loki_minio_bucket loki-chunks Bucket для хранения чанков и индексов
loki_retention_days 31 Срок хранения логов в днях
loki_wal_storage_size 10Gi PVC для WAL/temp Loki (не логи — они в MinIO)
grafana_storage_size 5Gi PVC для базы данных Grafana
grafana_root_url http://10.203.0.96:10003 Внешний URL Grafana (Traefik)
grafana_admin_secret grafana-admin-secret Имя Secret с admin-user/admin-password

Стандарт централизованного сбора логов (архитектура PLG, масштабирование, переход на Graylog, диагностика):
roles/logging/README.md


sealed_secrets

Разворачивает Sealed Secrets — контроллер для безопасного хранения зашифрованных Secret'ов в Git. Запускается из manager-узла: контроллер — в kube-system через Helm, CLI kubeseal — на manager-узле.

Задача Что делает
cli.yml Скачивает kubeseal с GitHub Releases (версия из переменной или latest), идемпотентно
helm.yml helm upgrade --install sealed-secrets из bitnami-labs/sealed-secrets в kube-system

Использование после установки:

# Зашифровать Secret
kubectl create secret generic my-secret --from-literal=password=mysecret --dry-run=client -o yaml \
  | kubeseal --format yaml > my-sealedsecret.yaml

# Применить (можно коммитить в Git — зашифрован публичным ключом контроллера)
kubectl apply -f my-sealedsecret.yaml
Переменные
Переменная Значение по умолчанию Описание
sealed_secrets_chart_version 2.16.1 Версия Helm-чарта
sealed_secrets_namespace kube-system Namespace контроллера
sealed_secrets_cli_version "" (latest) Версия kubeseal CLI; пустая — берёт latest с GitHub

gitlab_agent

Устанавливает GitLab Agent for Kubernetes (agentk). Работает совместно с Flux: Flux деплоит, agentk даёт видимость в GitLab UI (Operate → Kubernetes clusters).

Agentk инициирует исходящее WebSocket-соединение к KAS — входящих портов не требует.

Задача Что делает
config.yml Клонирует fleet-repo, создаёт .gitlab/agents/production/config.yaml, коммитит и пушит при изменении
helm.yml helm upgrade --install gitlab-agent с токеном и KAS-адресом

Предварительное условие: зарегистрировать агент в GitLab UI → Operate → Kubernetes clusters → Connect a cluster → имя production → токен в CI/CD переменную GITLAB_AGENT_TOKEN.

Переменные
Переменная Значение по умолчанию Описание
gitlab_agent_hostname gitlab.gigacoms.info Хост GitLab
gitlab_agent_name production Имя агента (совпадает с именем в GitLab UI)
gitlab_agent_namespace gitlab-agent Namespace
gitlab_agent_kas_address wss://gitlab.gigacoms.info/-/kubernetes-agent/ KAS WebSocket-адрес
gitlab_agent_token из $GITLAB_AGENT_TOKEN Токен агента
gitlab_agent_ci_access_group k8s GitLab-группа с доступом к кластеру через CI

CI/CD-пайплайн

Стадии: validatesetup

Задача Триггер Описание
validate/lint каждый push / MR ansible-lint всех плейбуков и ролей
validate/syntax-check каждый push / MR Синтаксическая проверка всех плейбуков
setup/manager_nodes ручной, ветка main setup_manager.yml
setup/control_plane ручной, ветка main setup_control_plane.yml
setup/workers ручной, ветка main setup_worker_plane.yml
setup/longhorn ручной, ветка main setup_longhorn.yml
setup/traefik ручной, ветка main setup_traefik.yml
setup/flux ручной, ветка main setup_flux.yml
setup/gitlab_agent ручной, ветка main setup_gitlab_agent.yml
setup/minio ручной, ветка main setup_minio.yml
setup/logging ручной, ветка main setup_logging.yml
setup/sealed_secrets ручной, ветка main setup_sealed_secrets.yml
setup/gpu_workers ручной, ветка main setup_gpu.yml
setup/gpu_model_storage ручной, ветка main setup_gpu_model_storage.yml

resource_group: production — все setup-задачи сериализованы, одновременно выполняется только одна.

Переменные GitLab CI/CD (Settings → CI/CD → Variables)

Переменная Описание
ANSIBLE_SSH_USER SSH-пользователь для целевых серверов
ANSIBLE_SSHKEY_ID_RSA Содержимое приватного SSH-ключа (тип: File)
ANSIBLE_BECOME_PASS Пароль sudo
GITLAB_FLUX_TOKEN GitLab PAT (scope: api) для Flux bootstrap и клонирования fleet-repo
GITLAB_AGENT_TOKEN Токен GitLab Agent (получить в GitLab → Operate → Kubernetes clusters)
MINIO_ROOT_PASSWORD Пароль root-пользователя MinIO (используется при ручном создании Secret)
LOKI_MINIO_PASSWORD Пароль пользователя loki в MinIO (создаётся Ansible при запуске setup_logging)
GRAFANA_ADMIN_PASSWORD Пароль администратора Grafana (задаётся в Secret grafana-admin-secret вручную)

Порядок развёртывания

Для нового кластера — строго в следующем порядке:

1. setup:manager_nodes   — инструменты оператора
2. setup:control_plane   — Kubernetes, CNI, Dashboard
3. setup:workers         — подключение worker-нод
4. setup:longhorn        — хранилище (prereqs на workers → Helm с manager)
5. setup:traefik         — порт-прокси (firewall на workers → Helm с manager)
6. setup:minio           — объектное хранилище S3 (StorageClass + Helm с manager)
7. setup:flux            — GitOps bootstrap
8. setup:gitlab_agent    — видимость кластера в GitLab UI
9. setup:logging         — PLG-стек (Loki + Alloy + Grafana)
10. setup:sealed_secrets — контроллер Sealed Secrets + kubeseal CLI
11. setup:gpu_workers    — только для узлов gpu_workers, после их setup:workers

Перед запуском setup:minio создать Secret вручную: kubectl create secret generic minio-root-credentials --from-literal=rootUser=minioadmin --from-literal=rootPassword='...' -n minio

Перед запуском setup:logging создать Secret вручную: kubectl create namespace monitoring && kubectl create secret generic grafana-admin-secret --from-literal=admin-user=admin --from-literal=admin-password='...' -n monitoring


Архитектурные решения

kubeadm: version derive at runtime

kubernetesVersion в конфиге kubeadm определяется из kubeadm version -o short в runtime. Исключает ошибки несоответствия версий при изменении пакета без изменения переменной.

single-node: снятие control-plane taint

Taint node-role.kubernetes.io/control-plane:NoSchedule снимается автоматически. Dashboard и прочие workload'ы размещаются на master-узле при single-node-конфигурации.

kubeadm join: ежезапускная генерация токена

Join-токены истекают через 24 часа. Роль k8s_worker генерирует свежий токен на control plane при каждом запуске; шаг join идемпотентен — пропускается, если узел уже в кластере.

Longhorn: монтирование до аннотации

Диски форматируются как XFS и монтируются по UUID (nofail) до установки Longhorn. Longhorn обнаруживает диски через аннотации узлов — не через автоопределение. Это предотвращает переформатирование при повторном запуске плейбука.

Traefik: нет hostname, нет TLS

Каждый сервис получает выделенный порт (1000110999). Нет hostname-routing, нет TLS — клиент подключается по http://<worker-ip>:<port>. TLS терминируется на уровне Traefik при необходимости. Dashboard работает в HTTP-режиме (dashboard_insecure: true).

Flux + agentk: разделение ответственности

Ansible управляет ОС и кластером (установка, конфигурация нод). Flux управляет приложениями внутри кластера (деплой, обновление образов). agentk обеспечивает видимость состояния кластера в GitLab UI.

GPU-узлы: отдельный плейбук, вручную ставится только драйвер

gpu_workers — подгруппа workers. NVIDIA-драйвер ставится на сервер вручную и не управляется Ansible (роль gpu_prereqs лишь проверяет nvidia-smi и падает, если драйвера нет). setup_gpu.yml запускается отдельно и после setup_worker_plane.yml, так как роль k8s_worker перегенерирует /etc/containerd/config.toml с нуля и стёрла бы настройку nvidia runtime, если бы порядок был обратным. GPU-узлы исключены из дефолтной конфигурации Longhorn (longhorn_disks: [] в gpu_workers.yml) — это compute-нода, не storage-нода.

Firewalld и CNI-интерфейсы

Критично. flannel.1 и cni0 должны быть в зоне trusted на каждом узле кластера.

Без этого pod-to-pod-трафик от workers, приходящий на master, обрабатывается зоной internal — она пропускает только явно перечисленные порты. Любой порт нового сервиса молча отбрасывается, даже если порт открыт на уровне хоста.

При добавлении любой роли с firewalld — обязательно добавлять:

- name: Firewall | Trust CNI interfaces
  ansible.posix.firewalld:
    zone: trusted
    interface: "{{ item }}"
    permanent: true
    state: enabled
  loop:
    - flannel.1
    - cni0

Локальная разработка

# Установка коллекций Ansible Galaxy
ansible-galaxy collection install -r requirements.yml -p collections/ --force

# Проверка синтаксиса
ansible-playbook --syntax-check -i inventory/prod playbooks/setup_manager.yml

# Линтинг
ansible-lint playbooks/ roles/

# Dry-run (режим check + diff)
ansible-playbook -i inventory/prod playbooks/setup_control_plane.yml --check --diff

# Запуск только на одном хосте
ansible-playbook -i inventory/prod playbooks/setup_worker_plane.yml --limit k8s-worker-01

Добавление нового компонента

  1. Добавить хост в inventory/prod/hosts.yml (если новая нода)
  2. Создать inventory/prod/group_vars/<group>.yml (если новая группа)
  3. Создать роль roles/<role>/ со стандартной структурой
  4. Создать roles/<role>/README.md — подробный корп. стандарт использования компонента
  5. Создать playbooks/<operation>.yml
  6. Добавить CI/CD-задачу в .gitlab-ci.yml (stage: setup, resource_group: production)
  7. Обновить этот README: краткое описание роли + ссылка на roles/<role>/README.md
  8. Обновить CLAUDE.md: добавить роль в структуру репозитория и ключевые решения