iac/aero/README.md

253 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
и запуск службы) → **`stack`** (деплой конфигурации + `docker compose up -d`
сервисов приложения) → **`superuser`** (генерация и создание Django-админа).
Перед запуском впишите хосты в [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 stack` | деплой + поднять стек (`docker compose up -d`) |
| `uv run poe gen-env` | только сгенерировать пароли и `.env` |
| `uv run poe login` | только `docker login` в реестр по ключу |
| `uv run poe superuser` | сгенерировать логин+пароль и создать Django-суперпользователя |
| `uv run poe flux-status` | состояние FluxCD в k3s |
| `uv run poe vault-status` | состояние Vault (инициализирован / распечатан) |
| `uv run poe check` / `deploy-check` | dry-run (`--check --diff`) |
Поднимаемые сервисы задаются в `sarex_services` (роль `sarex_stack`) —
`postgres(+init)`, `redis`, `rabbitmq`, `minio(+init)`, `measurements`,
`backend`, `celery`, `frontend`, `nginx` (k3s/gitea/processing не поднимаются).
> `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 (4 ноды в контейнерах)
├── k3s-server ──► нода ┐
├── k3s-worker-1..3 ──► ноды ├─► flux-system (controllers)
├── gitea (172.28.0.12) ◄────── ┘ и всё, что опишем в clusters/aero
│ └── infra/iac.git
├── vault (+init)
├── flux-k8s-init ──► namespace flux-system + Service-мост до gitea
└── flux-bootstrap ──► ставит Flux в k3s и привязывает к gitea
```
**Мост до gitea.** Контроллеры Flux работают внутри k3s, где резолвит CoreDNS,
а имён compose-сети там нет. Поэтому `flux-k8s-init` заводит в namespace
`flux-system` Service без селектора + Endpoints на статический IP gitea — тем же
приёмом, что `bridge-postgres`/`bridge-minio` для processing. Источник в
`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/
└── worker-3/
```
Файлы лежат на хосте обычными каталогами: переживают пересоздание контейнера
k3s и `docker compose down -v`, бэкапятся штатными средствами.
> **Следствие для stateful-сервисов.** Хранилище **node-local**: PVC привязан к
> той ноде, где под запустился первым. При переезде postgres/redis/rabbitmq/minio
> в k3s их нужно явно прибивать к конкретной ноде (`nodeSelector`), иначе данные
> размажутся по четырём каталогам, а потеря ноды сделает том недоступным.
> Бэкап должен покрывать все каталоги `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/<host>/`. Unseal-ключ и root-токен Vault лежат на
отдельном docker-томе `sarex-vault-init` с правами `600` и **в git не попадают**.
#### Границы текущего этапа
Сделана только подложка. Осознанно **не** сделано:
- Репозиторий в gitea содержит лишь `clusters/aero/flux-system` — то, что запушил
bootstrap. Наполнение (`apps/`, `infrastructure/`) и перенос прикладных сервисов
из compose в k3s — следующий этап.
- Vault поднят и распечатан, но **kubernetes auth не настроен**: для него нужен
Vault Agent Injector внутри k3s, а он приедет уже через Flux. Пока в Vault
можно только класть секреты, поды их ещё не читают.
- Прикладной стек (`sarex_services`) не тронут и продолжает работать в compose.
### Развёртывание sarex-стека (роль `sarex_stack`)
Копирует `docker-compose.yaml`, `nginx/templates` и `backend/uwsgi.ini` в
`/root/sarex`, рендерит `.env` из `.env.example` со **сгенерированными паролями**
и доменами `*.sarex.local.lonsdaleites.ru`, генерирует самоподписанный
TLS-сертификат на все три домена (`nginx/certs/selfsigned.{crt,key}`), логинится
в реестр и (при `sarex_compose_up=true`) поднимает сервисы приложения.
Роли/базы и MinIO-бакеты создаются одноразовыми `postgres-init`/`minio-init`.
```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/<host> && uv run poe gen-env # перегенерировать пароли заново
```
- Стек по умолчанию **не поднимается** (`sarex_compose_up: false`). Образы —
приватные (`cr.yandex`), поэтому сперва `docker login cr.yandex` на хосте,
затем `cd /root/sarex && docker compose up -d` (или прогон с
`-e sarex_compose_up=true`).
- Домены нужно завести в DNS/hosts, чтобы они резолвились на хост.
### 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 # метаданные и зависимости
```