# aero — Ansible-провижининг docker-хостов Ansible-роль `docker`, устанавливающая на сервер: - **Docker Engine** (`docker-ce`, `docker-ce-cli`); - **containerd**; - **docker compose** и (где есть) buildx; - обновление пакетов системы при старте. Ветка установки выбирается автоматически по пакетному менеджеру хоста: | Семейство | Пакетный менеджер | Пакеты | | --- | --- | --- | | Ubuntu / Debian | `apt` | `docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin` | | RedOS / RHEL | `dnf` | `docker-ce docker-ce-cli containerd docker-compose` | > Имена пакетов в RedOS отличаются (`containerd`, `docker-compose`), поэтому > списки разнесены по семействам в `roles/docker/defaults/main.yml`. ## Важно: где запускать **Ansible не работает как управляющий узел на нативном Windows** (нужен POSIX). Запуск — из **WSL** (Ubuntu). Управление окружением — через **uv**. ### Разовая настройка WSL ```bash # 1. uv внутри WSL curl -LsSf https://astral.sh/uv/install.sh | sh source ~/.local/bin/env # 2. Если DNS в WSL не резолвит (curl: Could not resolve host): echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf printf "\n[network]\ngenerateResolvConf = false\n" | sudo tee -a /etc/wsl.conf # 3. SSH-ключ в домашку WSL с правами 600 (на /mnt/c ssh отвергает ключ) mkdir -p ~/.ssh && chmod 700 ~/.ssh cp /mnt/c/Users/user/.ssh/local/id_ed25519 ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 ``` ## Запуск Из WSL, в каталоге проекта (`/mnt/c/.../iac/aero`). ### Установка с чистого листа — одной командой ```bash uv run poe install ``` Последовательность: `sync` → `galaxy` → **`provision`** (базовые пакеты + docker и запуск службы) → **`platform`** (деплой конфигурации + подъём подложки: k3s, gitea, vault, bootstrap Flux) → **`superuser`** (показать учётные данные администратора платформы). Приложения после этого разворачивает Flux сам. Перед запуском впишите хосты в [inventory.ini](inventory.ini) и убедитесь, что на control-node есть SSH-ключ и `~/.ssh/local/authorized_key.json` для входа в реестр. ### Пошагово / отдельные команды | Команда | Действие | | --- | --- | | `uv run poe ping` | проверка SSH + sudo | | `uv run poe provision` | базовые пакеты + docker/containerd + запуск службы | | `uv run poe deploy` | деплой конфигурации: файлы, `.env`, `docker login` | | `uv run poe platform` | деплой + поднять GitOps-подложку (k3s + gitea + vault + Flux) | | `uv run poe gen-env` | только сгенерировать пароли и `.env` | | `uv run poe login` | только `docker login` в реестр по ключу | | `uv run poe superuser` | показать логин+пароль администратора платформы | | `uv run poe dbsync` | досоздать базы, появившиеся после запуска СУБД | | `uv run poe reload` | перекатить нагрузки, конфигурация которых в ConfigMap | | `uv run poe flux-status` | состояние FluxCD в k3s | | `uv run poe vault-status` | состояние Vault (инициализирован / распечатан) | | `uv run poe check` / `deploy-check` | dry-run (`--check --diff`) | `docker compose` поднимает **только подложку** — k3s, gitea, vault и одноразовые установщики. Прикладные сервисы разворачивает Flux внутри k3s по манифестам из `apps/` и `infrastructure/`, и отдельной команды для них нет: достаточно запушить изменения в репозиторий контура. > `ANSIBLE_CONFIG` и `UV_LINK_MODE` заданы в `[tool.poe.env]` — на диске `C:` > (через `/mnt/c`) права `0777`, и ansible игнорирует `ansible.cfg` в > world-writable каталоге, поэтому путь к конфигу передаётся явно. ### GitOps-подложка контура (k3s + gitea + vault + FluxCD) Целевая схема контура — не «всё в compose», а **compose как подложка, k3s как среда выполнения**. Источником правды становится gitea внутри контура, а раскаткой занимается FluxCD. ``` docker compose k3s (3 ноды в контейнерах) ├── k3s-server ──► нода ┐ ├── k3s-worker-1..2 ──► ноды ├─► flux-system (controllers) ├── gitea (172.28.0.12) ◄────── ┘ и всё, что описано в clusters/aero: │ └── infra/iac.git istio, cert-manager, postgresql, minio, ├── vault (172.28.0.13) ◄─────────── rabbitmq, django, processing, workspaces, │ └── vault-init (unseal-цикл) measurements, bim ├── gitea-init ──► админ, организация, пустой репозиторий ├── flux-k8s-init ──► namespace flux-system + Service-мост до gitea └── flux-bootstrap ──► ставит Flux в k3s и привязывает к gitea ``` В compose — только это. Прикладные сервисы и их зависимости (СУБД, очереди, объектное хранилище) живут в k3s и описаны в `apps/` и `infrastructure/`. **Мост до gitea.** Контроллеры Flux работают внутри k3s, где резолвит CoreDNS, а имён compose-сети там нет. Поэтому `flux-k8s-init` заводит в namespace `flux-system` Service без селектора + Endpoints на статический IP gitea — тем же приёмом, каким заведён мост до Vault (`k3s/manifests/vault`). Источник в `GitRepository` указан именем `gitea.flux-system.svc.cluster.local`, а адрес задан ровно в одном месте — переменной `GITEA_BRIDGE_IP` (она же `ipv4_address` сервиса `gitea`). Контейнер `flux-bootstrap` резолвит то же имя через `extra_hosts` — иначе flux CLI не смог бы запушить манифесты. **Имена нод закреплены** через `hostname:` в compose. Это не косметика: k3s берёт имя ноды из hostname, иначе им становится ID контейнера. Local-path привязывает PV к ноде через `nodeAffinity` по имени, и после пересоздания контейнера тома «повисли» бы. ### Хранилище PVC `StorageClass local-path` (встроенный в k3s, он же default). Дефолтный путь провижинера `/var/lib/rancher/k3s/storage` перекрыт bind-mount'ом на хост — поэтому настраивать ConfigMap `local-path-config` не нужно: ``` {{ deploy_dir }}/k3s-storage/ ├── server/ ← PVC, севшие на k3s-server ├── worker-1/ └── worker-2/ ``` Файлы лежат на хосте обычными каталогами: переживают пересоздание контейнера k3s и `docker compose down -v`, бэкапятся штатными средствами. > **Следствие для stateful-сервисов.** Хранилище **node-local**: PVC привязан к > той ноде, где под запустился первым. postgresql, minio и rabbitmq уже работают > в k3s, поэтому их данные лежат в каталоге той ноды, куда сел под, — потеря > ноды делает том недоступным, а `kubectl delete pod` с последующим переездом > на другую ноду выглядит как «база опустела». Бэкап должен покрывать все > каталоги `k3s-storage/*`. ```bash uv run poe platform # поднять подложку uv run poe flux-status # убедиться, что Flux реконсилирует ``` Что происходит по шагам: 1. **Волна 1** — стартуют `k3s-server`, три воркера и `gitea`. Роль ждёт, пока k3s запишет `./k3s/kubeconfig.yaml` (и что файл непустой) — иначе `flux-bootstrap` смонтировал бы каталог вместо файла. 2. **`gitea-init`** — одноразовый: заводит администратора (`gitea admin user create`), организацию и пустой репозиторий через API. Идемпотентен. 3. **`flux-k8s-init`** — одноразовый: namespace `flux-system` и Service-мост до gitea (см. выше). Обязан отработать до bootstrap. 4. **`vault` + `vault-init`** — Vault на file-хранилище. `vault-init` живёт постоянно: инициализирует Vault одним ключом, распечатывает его и включает `kv-v2` на пути `secrets/` (тот же путь, что в k8s-контурах). После ребута хоста Vault поднимается запечатанным — цикл распечатывает его сам. 5. **`flux-bootstrap`** — одноразовый: `flux bootstrap git` ставит контроллеры в k3s и пушит их манифесты в gitea в `clusters/aero/flux-system`. > **FluxCD не работает в docker-compose** — это контроллеры Kubernetes. В compose > живёт только установщик; после его успеха Flux работает внутри k3s. Секреты подложки (`GITEA_ADMIN_PASSWORD`, `K3S_TOKEN`) генерируются так же, как остальные — в `aero/.secrets//`. Unseal-ключ и root-токен Vault лежат на отдельном docker-томе `sarex-vault-init` с правами `600` и **в git не попадают**. ### Развёртывание (роль `sarex_stack`) Копирует `docker-compose.yaml`, конфигурацию Vault и k3s-манифесты DNS-мостов в `/root/sarex`, рендерит `.env` из `.env.example` со **сгенерированными паролями** и доменами `*.sarex.local.lonsdaleites.ru`, логинится в реестр, кладёт секреты в Vault и поднимает подложку тремя волнами (`uv run poe platform`). Дальше за дело берётся Flux: базы, бакеты, очереди и сами приложения появляются в k3s из `apps/` и `infrastructure/`. ```bash uv run ansible-playbook deploy.yml --check --diff # dry-run uv run ansible-playbook deploy.yml # подготовка файлов ``` - Пароли генерируются один раз и персистятся на control-node в `aero/.secrets/` (gitignored) — повторный прогон не меняет `.env` (идемпотентно). - Только пароли и `.env`, без остального деплоя — отдельной командой: ```bash uv run poe gen-env # сгенерировать/обновить .env rm -rf .secrets/ && uv run poe gen-env # перегенерировать пароли заново ``` - Образы приложений приватные (`cr.yandex`), поэтому нужен `docker login` — его делает `uv run poe login`, а в кластер доступ приезжает секретами `regcred`/`yc-cr-auth` (`registry_secrets` в роли). - Домены нужно завести в DNS/hosts, чтобы они резолвились на хост. - TLS-сертификат выписывает cert-manager внутри кластера (`infrastructure/istio-config/aero`); самоподписанного сертификата на хосте больше нет — он был нужен верхнему nginx, а тот заменён istio ingressgateway. ### Galaxy-коллекции (опционально) Коллекции из `requirements.yml` (`community.docker` и др.) **не требуются** для работы роли — она использует только модули `ansible.builtin`. Ставятся при необходимости: ```bash uv run poe galaxy ``` ## Настройка Хосты — в [`inventory.ini`](inventory.ini) (группа `docker_hosts`). Root — через `sudo` (`become`). Если `sudo` с паролем — добавьте `--ask-become-pass`: ```bash uv run poe play -- --ask-become-pass ``` Переменные роли (`roles/docker/defaults/main.yml`): | Переменная | По умолчанию | Назначение | | --------------------------- | ------------ | -------------------------------------------- | | `docker_update_packages` | `true` | Обновлять пакеты при старте | | `docker_upgrade_dist` | `false` | Полный `dist-upgrade` (только Debian) | | `docker_service_enabled` | `true` | Автозапуск службы docker | | `docker_service_state` | `started` | Состояние службы после прогона | | `docker_users` | `[]` | Пользователи в группу `docker` (без sudo) | | `docker_redhat_add_ce_repo` | `false` | Внешний репозиторий Docker CE (RHEL; RedOS не нужен) | ## Структура ``` aero/ ├── pyproject.toml # окружение uv + poe-задачи ├── ansible.cfg # настройки ansible (inventory, become) ├── requirements.yml # galaxy-коллекции (опционально) ├── inventory.ini # хосты ├── site.yml # плейбук └── roles/docker/ ├── defaults/main.yml # переменные и списки пакетов по семействам ├── tasks/ │ ├── main.yml # диспетчер по пакетному менеджеру + общие шаги │ ├── debian.yml # ветка apt (Ubuntu/Debian) │ └── redhat.yml # ветка dnf (RedOS/RHEL) ├── handlers/main.yml # restart docker └── meta/main.yml # метаданные и зависимости ```