No description
Find a file
mrwho 12bcf77d62 style(helm): convert inline JSON ConfigMaps to YAML format
Replace inline JSON string definitions with native YAML syntax for Kubernetes resource manifests in helm.yml, improving readability and maintainability while preserving identical resource configurations.
2026-07-19 18:40:02 +03:00
inventory/prod refactor(infra): restructure inventory and update dependencies 2026-07-16 21:28:44 +03:00
playbooks style(helm): convert inline JSON ConfigMaps to YAML format 2026-07-19 18:40:02 +03:00
research initial commit from gigacoms 2026-07-15 11:14:21 +03:00
roles style(helm): convert inline JSON ConfigMaps to YAML format 2026-07-19 18:40:02 +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: добавить роль в структуру репозитория и ключевые решения