Remove duplicated helm install tasks from main.yml and centralize them in helm.yml, simplifying the role structure and reducing redundancy by consolidating OCI registry and local chart installation paths into a single file. |
||
|---|---|---|
| inventory/prod | ||
| playbooks | ||
| research | ||
| roles | ||
| .ansible-lint | ||
| .gitignore | ||
| .gitlab-ci.yml | ||
| ansible.cfg | ||
| README.md | ||
| requirements.yml | ||
Kubernetes Infrastructure
Корпоративный стандарт. Ansible-развёртывание кластера Kubernetes на Rocky Linux 9.
Локальный запуск через CLI и автоматизированный запуск через GitLab CI/CD.
Содержание
- Инфраструктура
- Стек технологий
- Структура репозитория
- Роли и компоненты
-
- Стандарт работы с хранилищем
roles/longhorn/README.md
- Стандарт работы с хранилищем
-
- Стандарт публикации сервисов
roles/traefik/README.md
- Стандарт публикации сервисов
-
- Стандарт GitOps-деплоя приложений
roles/flux/README.md
- Стандарт GitOps-деплоя приложений
-
- Стандарт объектного хранилища S3
roles/minio/README.md
- Стандарт объектного хранилища S3
-
- Стандарт централизованного сбора логов
roles/logging/README.md
- Стандарт централизованного сбора логов
-
- Sealed Secrets (kubeseal)
- 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 | workers → gpu_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 |
Реплик на том; поднять до 2–3 при добавлении 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-порт из диапазона 10001–10999. Вся топология определяется через traefik_port_map в inventory/prod/group_vars/traefik.yml. Поддерживаются два режима маршрутизации: HTTP (IngressRoute, поле protocol не задаётся) и raw TCP (IngressRouteTCP с HostSNI("*"), protocol: tcp в port_map).
| Задача | Что делает |
|---|---|
firewall.yml |
Открывает 10000–10999/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-пайплайн
Стадии: validate → setup
| Задача | Триггер | Описание |
|---|---|---|
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
Каждый сервис получает выделенный порт (10001–10999). Нет 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
Добавление нового компонента
- Добавить хост в
inventory/prod/hosts.yml(если новая нода) - Создать
inventory/prod/group_vars/<group>.yml(если новая группа) - Создать роль
roles/<role>/со стандартной структурой - Создать
roles/<role>/README.md— подробный корп. стандарт использования компонента - Создать
playbooks/<operation>.yml - Добавить CI/CD-задачу в
.gitlab-ci.yml(stage: setup,resource_group: production) - Обновить этот README: краткое описание роли + ссылка на
roles/<role>/README.md - Обновить
CLAUDE.md: добавить роль в структуру репозитория и ключевые решения