90 lines
7.2 KiB
Markdown
90 lines
7.2 KiB
Markdown
# Развёртывание платформы
|
||
|
||
Одна VM, systemd, бинари артефактами CI (`PLATFORM_DIRECTION.md` §3). Не Kubernetes: дети-`tmctl`
|
||
живут часами и держат эксклюзивный лок на файлах книги на локальном диске — оркестратор, способный
|
||
переселить под посреди прогона, этой нагрузке враждебен.
|
||
|
||
## Файлы
|
||
|
||
- `tmplatformd.service` — юнит контрольной панели. Тело юнита проверено `systemd-analyze verify`
|
||
(systemd 259) — exit 0, без замечаний. ⚠ Проверять надо с ПОДСТАВЛЕННЫМ существующим `ExecStart=`:
|
||
дословно юнит даёт exit 1, потому что `verify` проверяет и наличие бинаря, а `/usr/local/bin/tmplatformd`
|
||
на стенде нет. ⚠ **юнит целиком под systemd не запускался** — нет sudo и нет бинаря на стенде.
|
||
|
||
Отдельные свойства песочницы при этом проверены ЖИВЫМ прогоном в пользовательском systemd 259
|
||
(`systemd-run --user --wait`), а не вычитаны из доки:
|
||
`ProtectSystem=strict` + `ReadWritePaths=` на несуществующем пути → **`226/NAMESPACE`, юнит не
|
||
стартует**; тот же путь созданным → `0/SUCCESS`; префикс `-` на несуществующем → `0/SUCCESS`
|
||
(строка игнорируется — потому мы её и не префиксуем);
|
||
`ProtectHome=yes` → `/home` пуст, чтение `/home/<user>` даёт `Permission denied`;
|
||
`ProtectHome=tmpfs` + `BindPaths=<каталог>` → каталог виден. Ресурсные потолки
|
||
(`MemoryMax=80%`, `OOMPolicy=continue`) живьём не проверялись — вывод из
|
||
`systemd.resource-control(5)`/`systemd.service(5)`.
|
||
|
||
## Откат релиза: не ниже версии 5
|
||
|
||
`goose down` до версии 4 и ниже НЕ РАБОТАЕТ на живой базе: down-путь `00005` восстанавливает
|
||
`users_email_key` и `email NOT NULL`, а обе формы нарушают строки, которые пишет боевой код
|
||
(неподтверждённая личность даёт `email = NULL`; один адрес законно принадлежит двум аккаунтам).
|
||
Откат транзакционный, поэтому падение ничего не портит — но планировать откат ниже 5 нельзя,
|
||
план отката — накатить вперёд. Разбор: `docs/STACK_DECISIONS.md` §8.
|
||
|
||
## Что юнит закрывает содержательно
|
||
|
||
- **PD-13 (осиротевшие процессы движка).** Каждый `tmctl` живёт в cgroup ЭТОГО юнита, поэтому падение
|
||
или рестарт платформы не оставляет прогон без присмотра. Обычную остановку делает супервизор
|
||
(группа процессов, `internal/ingest/procgroup_unix.go`); cgroup — это ответ на случай, когда
|
||
супервизора уже нет, чтобы попросить.
|
||
- **`TimeoutStopSec=90`** больше, чем дренаж платформы (15 с) плюс grace движка (30 с). Меньше —
|
||
и systemd прибьёт `tmctl` посреди остановки, оставив лок проекта.
|
||
- **Секреты через `LoadCredential=`,** а не через окружение: переменная окружения видна в
|
||
`/proc/<pid>/environ` и наследуется каждым ребёнком-`tmctl`. Конфиг читает `*_FILE` первым.
|
||
|
||
## Установка (набросок, исполняется владельцем)
|
||
|
||
```sh
|
||
useradd --system --home-dir /srv/textmachine tmplatform
|
||
# ⚠ Каталог создаём ЯВНО: --system не создаёт домашний каталог, а `ReadWritePaths=/srv/textmachine`
|
||
# при `ProtectSystem=strict` на несуществующем пути валит сборку mount-namespace — юнит не стартует
|
||
# вовсе (PD-91, systemd.exec(5)). Это первая команда, которую пропускают, читая набросок сверху вниз.
|
||
install -d -m0750 -o tmplatform -g tmplatform /srv/textmachine
|
||
install -D -m0755 tmplatformd /usr/local/bin/tmplatformd
|
||
install -D -m0755 tmplatformctl /usr/local/bin/tmplatformctl
|
||
install -d -m0700 -o root -g root /etc/tmplatform
|
||
printf '%s' 'postgres://...' > /etc/tmplatform/dsn && chmod 0400 /etc/tmplatform/dsn
|
||
printf '%s' '<oauth client secret>' > /etc/tmplatform/oidc_client_secret && chmod 0400 /etc/tmplatform/oidc_client_secret
|
||
```
|
||
|
||
`/etc/tmplatform/env` — несекретное окружение:
|
||
|
||
```
|
||
TM_PLATFORM_ADDR=127.0.0.1:8080
|
||
TM_PLATFORM_TRUSTED_ORIGINS=https://app.example.org
|
||
TM_PLATFORM_OIDC_ISSUER=https://accounts.google.com
|
||
TM_PLATFORM_OIDC_CLIENT_ID=...
|
||
TM_PLATFORM_OIDC_REDIRECT_URL=https://app.example.org/auth/callback
|
||
TM_PLATFORM_AFTER_LOGIN=/library
|
||
TM_PLATFORM_SIGNUP_GRANT_USD=5
|
||
```
|
||
|
||
Миграции выкатываются ОДИН раз, не каждой репликой: `TM_PLATFORM_MIGRATE=1 tmplatformd` разово
|
||
либо отдельный шаг деплоя. `goose` держит advisory-лок, так что параллельный запуск не гонка,
|
||
но и не норма.
|
||
|
||
## Где на сервере лежат книги
|
||
|
||
`/srv/textmachine` — корень библиотеки НА СЕРВЕРЕ. Решение владельца «книги живут в `~/books`»
|
||
относится к машине разработки: под этим юнитом домашние каталоги недоступны вовсе
|
||
(`ProtectHome=yes` подставляет пустой `/home` и детям-`tmctl` тоже), поэтому библиотека под
|
||
домашним каталогом на сервере просто не откроется — не «сработает медленнее», а не найдётся.
|
||
Оператору, которому это нужно, юнит называет ровно две строки замены (`ProtectHome=tmpfs` +
|
||
`BindPaths=`); других изменений не требуется.
|
||
|
||
⚠ Выбирающего путь кода ещё нет: `Supervisor.Workdir` задаёт вызывающий, а вызывающий — воркер,
|
||
которого нет (строка 103 единого бэклога). Когда он появится, корень становится настройкой, и её
|
||
дефолт — этот каталог.
|
||
|
||
## Чего здесь ещё нет
|
||
|
||
TLS и домен (перед юнитом предполагается edge-прокси), ограничитель соединений на edge,
|
||
ротация логов, бэкап Postgres. Всё это — работа с первым реальным деплоем, не раньше.
|