Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
fe024b3b12
|
|||
|
8378f0edb0
|
|||
|
48737c1b6d
|
|||
|
600a30ec11
|
|||
|
670947fcdf
|
|||
|
3545905cbd
|
|||
|
893996f0c9
|
@@ -0,0 +1,82 @@
|
|||||||
|
# Алерты на проблемные контейнеры
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
Случай с wakapi: при старте упали миграции, контейнер встал в restart-loop и
|
||||||
|
несколько дней крутился по кругу — никто не узнал. Из этого две проблемы:
|
||||||
|
|
||||||
|
1. Контейнеры могут бесконечно перезапускаться при ошибке.
|
||||||
|
2. Нет алертов о таких ситуациях.
|
||||||
|
|
||||||
|
## Что есть и что использовать
|
||||||
|
|
||||||
|
- **Netdata** — Docker-collector через cgroups + Docker API: состояние,
|
||||||
|
restart count, healthcheck status. Алерты в `health.d/*.conf`, нотификации
|
||||||
|
через `health_alarm_notify.conf` (Telegram/Discord/email/ntfy).
|
||||||
|
- **Dozzle** — только для просмотра логов после факта, нормальных алертов нет.
|
||||||
|
- **Caddy** — мог бы участвовать в healthcheck снаружи, но это отдельный слой.
|
||||||
|
|
||||||
|
## План — три слоя
|
||||||
|
|
||||||
|
### 1. Healthchecks в compose (фундамент)
|
||||||
|
|
||||||
|
Без них Docker считает контейнер «running», пока процесс жив, — wakapi с
|
||||||
|
падающими миграциями этому условию удовлетворял. Добавить в каждый
|
||||||
|
`docker-compose.yml.j2`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-q", "-O-", "http://localhost:PORT/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 60s # окно на миграции — failed-проверки до истечения не считаются
|
||||||
|
```
|
||||||
|
|
||||||
|
`start_period` — ключевая штука для случая wakapi: даём миграциям отработать,
|
||||||
|
до его истечения healthcheck не убивает контейнер.
|
||||||
|
|
||||||
|
### 2. Алерты через Netdata (главное)
|
||||||
|
|
||||||
|
Два разных сигнала:
|
||||||
|
|
||||||
|
- **Restart loop** — алерт на `docker.container_state` или счётчик
|
||||||
|
перезапусков (растёт > N за M минут). Это и есть «контейнер крутится по
|
||||||
|
кругу».
|
||||||
|
- **Unhealthy** — после healthcheck выше алерт на
|
||||||
|
`docker.container_health_status != healthy` дольше M минут.
|
||||||
|
|
||||||
|
Канал нотификаций: один, проще всего Telegram-бот. Настройка в
|
||||||
|
`health_alarm_notify.conf`.
|
||||||
|
|
||||||
|
### 3. Restart policy — что менять (или не менять)
|
||||||
|
|
||||||
|
Скорее **оставить `unless-stopped`**. Альтернативы и их минусы:
|
||||||
|
|
||||||
|
- `on-failure:5` — Docker сам остановит после 5 попыток. Минус: после ребута
|
||||||
|
сервера сервис не поднимется (только `always`/`unless-stopped` встают на
|
||||||
|
старте докера). Серьёзный регресс для домашнего сервера.
|
||||||
|
- Внешний sidecar, слушающий `docker events` и останавливающий контейнер
|
||||||
|
после N рестартов в окне — лишняя сложность ради того, что уже сделает
|
||||||
|
алерт.
|
||||||
|
|
||||||
|
Лучше: алерт пришёл → решаем вручную, остановить или чинить.
|
||||||
|
|
||||||
|
## Опционально (вне netdata)
|
||||||
|
|
||||||
|
- **Uptime Kuma** — внешний HTTP-чек по публичным URL. Ловит случаи, когда
|
||||||
|
контейнер «здоров», но прокся/DNS/Caddy сломаны. Свои нотификации, дашборд.
|
||||||
|
Не дублирует netdata, проверяет с другой стороны.
|
||||||
|
|
||||||
|
## Шаги при реализации
|
||||||
|
|
||||||
|
1. Добавить healthcheck + start_period в compose-шаблоны (начать с wakapi,
|
||||||
|
потом по списку).
|
||||||
|
2. Проверить, что netdata собирает Docker-метрики (collector включён).
|
||||||
|
3. Настроить один канал нотификаций (Telegram/ntfy/email — выбрать).
|
||||||
|
4. Написать пару алертов: restart-loop и unhealthy.
|
||||||
|
|
||||||
|
## Открытые вопросы
|
||||||
|
|
||||||
|
- Какой канал нотификаций использовать.
|
||||||
|
- Добавлять ли Uptime Kuma сразу или потом.
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
# Gitea runner on-demand в Yandex Cloud
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
В YC планируется развернуть self-hosted раннер для Gitea Actions. Сборки —
|
||||||
|
несколько раз в неделю, в среднем ~10 в неделю по ~5 минут. ВМ 24/7 даёт
|
||||||
|
утилизацию в районе 1%, остальное оплачивается впустую.
|
||||||
|
|
||||||
|
Цель — раннер активен только во время сборки и небольшого окна простоя
|
||||||
|
после, без ручных команд от разработчика.
|
||||||
|
|
||||||
|
## Архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
push → Gitea ──webhook──► Cloud Function ──Compute API──► ВМ (раннер)
|
||||||
|
(HMAC validate, │
|
||||||
|
start logic) ▼
|
||||||
|
act_runner (docker)
|
||||||
|
probe + decide
|
||||||
|
│
|
||||||
|
└─REST self-stop
|
||||||
|
```
|
||||||
|
|
||||||
|
Без API Gateway: функция публикуется напрямую через свой HTTPS-эндпоинт
|
||||||
|
`https://functions.yandexcloud.net/<id>`. Этот URL вписывается в Gitea
|
||||||
|
webhook. Аутентификация — HMAC-SHA256 в заголовке `X-Gitea-Signature`,
|
||||||
|
проверяется внутри функции.
|
||||||
|
|
||||||
|
Поток событий:
|
||||||
|
|
||||||
|
1. Push в Gitea → System Webhook на URL функции.
|
||||||
|
2. Функция валидирует HMAC, читает state ВМ, действует по стейт-машине
|
||||||
|
(см. ниже).
|
||||||
|
3. ВМ стартует, docker поднимает контейнер `act_runner`, тот подключается
|
||||||
|
к Gitea и забирает джобу.
|
||||||
|
4. На ВМ работают probe (раз в 30 сек собирает телеметрию) и decide
|
||||||
|
(раз в 1 мин принимает решение).
|
||||||
|
5. После idle-окна decide дёргает Compute REST API на gas самой себя.
|
||||||
|
|
||||||
|
## Cloud-side
|
||||||
|
|
||||||
|
### Ресурсы в YC
|
||||||
|
|
||||||
|
- Один фолдер на старте — общий с Gitea-сервером. Принятый риск: SA
|
||||||
|
самогашения формально может остановить любую ВМ в фолдере. Перенос в
|
||||||
|
отдельный фолдер — миграция на потом.
|
||||||
|
- Два сервисных аккаунта:
|
||||||
|
- `runner-self-stop` (привязан к ВМ): `compute.instances.stop`,
|
||||||
|
`compute.instances.get`.
|
||||||
|
- `runner-starter-fn` (привязан к функции): `compute.instances.start`,
|
||||||
|
`compute.instances.get`.
|
||||||
|
- Cloud Function `runner-starter`, runtime Python 3.11, 256 MB, timeout
|
||||||
|
30 сек. Публичный HTTPS-эндпоинт включён.
|
||||||
|
- Алерт Cloud Monitoring: `compute.instance.status = RUNNING` дольше 24 ч
|
||||||
|
подряд → нотификация (канал — на этапе внедрения).
|
||||||
|
|
||||||
|
### Bootstrap-скрипты
|
||||||
|
|
||||||
|
```
|
||||||
|
scripts/
|
||||||
|
├── runner-starter/ # код Cloud Function
|
||||||
|
│ ├── handler.py # webhook → start, стейт-машина
|
||||||
|
│ └── requirements.txt
|
||||||
|
├── runner_bootstrap.py # one-time: создать SA, ВМ, функцию, алерт
|
||||||
|
└── runner_deploy_function.py # обновить версию функции (yc CLI)
|
||||||
|
```
|
||||||
|
|
||||||
|
Скрипты на Python поверх `yc` CLI (через `subprocess`). Идемпотентность —
|
||||||
|
проверкой существования ресурсов перед созданием. Terraform не вводим:
|
||||||
|
ресурсов мало, оверкилл.
|
||||||
|
|
||||||
|
### Стейт-машина функции
|
||||||
|
|
||||||
|
| State ВМ | Действие |
|
||||||
|
| ------------------------------------- | ------------------------------------------------------- |
|
||||||
|
| `RUNNING`, `STARTING`, `RESTARTING` | 200, ничего не делаем |
|
||||||
|
| `STOPPED` | `instances:start` → 200 |
|
||||||
|
| `STOPPING` | poll до `STOPPED` (до 25 сек), затем `start` → 200 |
|
||||||
|
| `PROVISIONING`, `UPDATING` | 503 (временное состояние, retry клиентом) |
|
||||||
|
| `ERROR`, `CRASHED` | 500 + лог ошибки (нужен человек) |
|
||||||
|
| `DELETING`, `DELETED` | 500 + лог ошибки (что-то очень не так) |
|
||||||
|
|
||||||
|
## Host-side
|
||||||
|
|
||||||
|
### ВМ
|
||||||
|
|
||||||
|
- 2 vCPU (100%), 4 GB RAM, 25 GB network-hdd.
|
||||||
|
- Ubuntu 22.04 LTS.
|
||||||
|
- Без публичного IP при возможности (все исходящие к Gitea — через NAT
|
||||||
|
или внутренний адрес).
|
||||||
|
- Привязан SA `runner-self-stop`.
|
||||||
|
- Регистрация в Gitea Actions делается **один раз** при первой настройке.
|
||||||
|
Registration token берётся в Site Admin → Actions → Runners, кладётся
|
||||||
|
в Vault. Плейбук проверяет наличие `.runner` файла на ВМ; если есть —
|
||||||
|
пропускает регистрацию.
|
||||||
|
|
||||||
|
### Плейбук `playbook-gitea-runner.yml`
|
||||||
|
|
||||||
|
Стандартная структура проекта:
|
||||||
|
|
||||||
|
- `roles/owner` — пользователь `gitea-runner` (uid/gid выделить
|
||||||
|
отдельные, в группе `docker`).
|
||||||
|
- `files/gitea-runner/`:
|
||||||
|
- `docker-compose.template.yml` — `act_runner` в docker
|
||||||
|
(`gitea/act_runner:<pinned>`), `restart: unless-stopped`, mount
|
||||||
|
docker socket для запуска job-контейнеров.
|
||||||
|
- `act-runner-config.template.yaml` — конфиг раннера.
|
||||||
|
- `runner-probe.template.py` + `runner-probe.template.service` +
|
||||||
|
`runner-probe.template.timer`.
|
||||||
|
- `runner-decide.template.py` + `runner-decide.template.service` +
|
||||||
|
`runner-decide.template.timer`.
|
||||||
|
- `samples-logrotate.template.conf` — ротация `samples.log`.
|
||||||
|
|
||||||
|
Расширения шаблонов — `.template.<ext>`, не `.j2` (соглашение проекта).
|
||||||
|
|
||||||
|
### Раннер в docker
|
||||||
|
|
||||||
|
`act_runner` стартует через `docker compose up -d` под пользователем
|
||||||
|
`gitea-runner`. Поскольку `restart: unless-stopped`, дополнительный
|
||||||
|
systemd-юнит для самого раннера не нужен — после `Start` ВМ docker
|
||||||
|
поднимет контейнер автоматически.
|
||||||
|
|
||||||
|
Идентификатор контейнера фиксированный (`gitea_runner_app`), чтобы probe
|
||||||
|
мог исключать его из счёта.
|
||||||
|
|
||||||
|
### Probe и decide
|
||||||
|
|
||||||
|
Два независимых юнита, телеметрия — append-only лог.
|
||||||
|
|
||||||
|
`runner-probe` (timer раз в 30 сек):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# pseudocode
|
||||||
|
busy_count=$(docker ps -q | grep -v <runner_container_id> | wc -l)
|
||||||
|
state=$([ "$busy_count" -gt 0 ] && echo busy || echo idle)
|
||||||
|
echo "$(date -u +%FT%TZ) $state containers=$busy_count" \
|
||||||
|
>> /var/lib/runner-idle/samples.log
|
||||||
|
```
|
||||||
|
|
||||||
|
В реальной реализации — Python, фильтр по docker SDK или по результату
|
||||||
|
`docker ps --format '{{.Names}}'`.
|
||||||
|
|
||||||
|
`runner-decide` (timer раз в 1 мин):
|
||||||
|
|
||||||
|
1. Читает хвост `samples.log`.
|
||||||
|
2. Находит `last_busy_at` — timestamp последней `busy`-строки.
|
||||||
|
3. Находит `last_sample_at` — timestamp последней любой строки.
|
||||||
|
4. Логика:
|
||||||
|
- `now - last_sample_at > STALE_THRESHOLD` (5 мин) → **probe сломан**,
|
||||||
|
не гасим, логируем error. Алерт CM поймает по uptime.
|
||||||
|
- `now - last_busy_at > IDLE_THRESHOLD` (10 мин) → `instances:stop`
|
||||||
|
через REST.
|
||||||
|
- Иначе → ничего.
|
||||||
|
|
||||||
|
Параметры (`IDLE_THRESHOLD`, `STALE_THRESHOLD`) — переменные в шаблоне,
|
||||||
|
тюнятся по эксплуатации.
|
||||||
|
|
||||||
|
### Самогашение через REST
|
||||||
|
|
||||||
|
Без `yc` CLI. Decide-скрипт получает IAM-токен из metadata-сервиса и
|
||||||
|
дёргает Compute REST:
|
||||||
|
|
||||||
|
```python
|
||||||
|
TOKEN_URL = "http://169.254.169.254/computeMetadata/v1/instance/" \
|
||||||
|
"service-accounts/default/token"
|
||||||
|
ID_URL = "http://169.254.169.254/computeMetadata/v1/instance/id"
|
||||||
|
HEADERS = {"Metadata-Flavor": "Google"}
|
||||||
|
|
||||||
|
token = requests.get(TOKEN_URL, headers=HEADERS).json()["access_token"]
|
||||||
|
instance_id = requests.get(ID_URL, headers=HEADERS).text
|
||||||
|
requests.post(
|
||||||
|
f"https://compute.api.cloud.yandex.net/compute/v1/instances/{instance_id}:stop",
|
||||||
|
headers={"Authorization": f"Bearer {token}"},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Никаких файлов с SA-key, никаких зависимостей сверх `python3 +
|
||||||
|
requests`.
|
||||||
|
|
||||||
|
## Страховки от зависшей ВМ
|
||||||
|
|
||||||
|
Главный failure mode — probe или decide молча сломались, ВМ работает 24/7.
|
||||||
|
|
||||||
|
Слой 1 — soft idle-stop в decide. Нормальная работа.
|
||||||
|
|
||||||
|
Слой 2 — probe-staleness в decide. Если `samples.log` не обновляется
|
||||||
|
дольше `STALE_THRESHOLD` — логируем error, **не гасим** (мог идти
|
||||||
|
длинный билд). Полагаемся на слой 3.
|
||||||
|
|
||||||
|
Слой 3 — внешний алерт через Cloud Monitoring на uptime ВМ > 24 ч. Не
|
||||||
|
дёргает остановку, только нотификация. Порог высокий, чтобы дни активной
|
||||||
|
отладки не триггерили его. Если фактически висит сутки — это сигнал
|
||||||
|
смотреть руками.
|
||||||
|
|
||||||
|
Hard-cap по uptime внутри decide **не делаем**: ломает кейс «активно
|
||||||
|
тестирую несколько часов подряд», когда busy-сэмплы есть и логика идёт
|
||||||
|
правильно.
|
||||||
|
|
||||||
|
## Секреты (Vault, `vars/secrets.yml`)
|
||||||
|
|
||||||
|
| Ключ | Назначение |
|
||||||
|
| --------------------------------- | ----------------------------------------------------- |
|
||||||
|
| `gitea_runner_registration_token` | одноразовый токен для `act_runner register` |
|
||||||
|
| `gitea_webhook_secret` | общий с функцией HMAC-секрет для webhook |
|
||||||
|
| `yc_runner_folder_id` | в каком фолдере живёт ВМ |
|
||||||
|
| `yc_runner_instance_id` | ID ВМ (заполняется после bootstrap) |
|
||||||
|
| `yc_runner_function_url` | URL функции для webhook (заполняется после bootstrap) |
|
||||||
|
|
||||||
|
## invoke-таски
|
||||||
|
|
||||||
|
| Таск | Что делает |
|
||||||
|
| ----------------------------- | ----------------------------------------------------------------------------- |
|
||||||
|
| `inv runner-bootstrap` | one-time: создаёт SA, ВМ, функцию, алерт. Идемпотентен. |
|
||||||
|
| `inv runner-deploy-function` | заливает новую версию `runner-starter`. |
|
||||||
|
| `inv runner-pl` | up → `ansible-playbook playbook-gitea-runner.yml` → down. С `try/finally`. |
|
||||||
|
| `inv runner-up` / `down` | ручной старт/стоп ВМ для дебага. |
|
||||||
|
| `inv runner-status` | state ВМ + хвост `samples.log` (через ssh). |
|
||||||
|
| `inv runner-ssh` | ssh на ВМ, поднимает её при необходимости. |
|
||||||
|
|
||||||
|
`runner-pl` — основной таск, единственный «штатный» путь обновления
|
||||||
|
конфига ВМ. Если плейбук падает посередине, `finally` всё равно гасит ВМ
|
||||||
|
(idle-watch её и так бы погасил, но явное лучше).
|
||||||
|
|
||||||
|
## Стоимость
|
||||||
|
|
||||||
|
Базовая ставка YC (USD, после повышения 1 мая 2026): vCPU 100% =
|
||||||
|
$0.010164/ч, RAM = $0.002705/ГБ·ч, network-hdd = $0.0000356/ГБ·ч.
|
||||||
|
|
||||||
|
Профиль: 10,75 ч активной ВМ в месяц.
|
||||||
|
|
||||||
|
| Конфиг (2 vCPU 100%, 4 GB RAM, 25 GB HDD) | $/мес |
|
||||||
|
| ----------------------------------------- | ----- |
|
||||||
|
| Compute (vCPU + RAM) при 10,75 ч | ~0.33 |
|
||||||
|
| Disk (HDD, 24/7) | ~0.64 |
|
||||||
|
| Cloud Function, Monitoring | 0.00 |
|
||||||
|
| **Итого** | **~1.0** |
|
||||||
|
|
||||||
|
Сравнение: эта же ВМ в режиме 24/7 ≈ $23/мес. Экономия — порядка 95%.
|
||||||
|
|
||||||
|
Дальнейшая оптимизация — диск (15 GB вместо 25, ещё ~$0.25/мес). Делать
|
||||||
|
не сейчас.
|
||||||
|
|
||||||
|
## Принятые риски
|
||||||
|
|
||||||
|
- **Общий фолдер с другими ВМ.** SA `runner-self-stop` теоретически
|
||||||
|
может погасить и Gitea-сервер, если тот переедет в YC рядом. Митигация
|
||||||
|
при появлении такой ВМ — перенос в отдельный фолдер.
|
||||||
|
- **Холодный старт ~60 сек.** Дизайн заявляет 40, реальность ближе к
|
||||||
|
60 (Ubuntu boot + docker pull + act_runner connect). Документируем как
|
||||||
|
«нормальная задержка первой джобы».
|
||||||
|
- **Регистрационный токен утерян.** При пересоздании диска ВМ нужен
|
||||||
|
новый токен из Gitea UI. Документируем процесс. Раз в годы — терпимо.
|
||||||
|
- **Probe сломан, ВМ висит.** Поймает алерт CM, ручное расследование.
|
||||||
|
|
||||||
|
## План внедрения
|
||||||
|
|
||||||
|
1. Создать в YC: 2 SA, ВМ, дисковый ресурс. Через `inv
|
||||||
|
runner-bootstrap` или вручную через консоль (выбираем по желанию на
|
||||||
|
этапе реализации).
|
||||||
|
2. Прогнать `inv runner-pl` на свежесозданной ВМ. С временно
|
||||||
|
уменьшенным `IDLE_THRESHOLD` (2 мин вместо 10) — чтобы тестировать
|
||||||
|
гашение быстро.
|
||||||
|
3. Зарегистрировать раннер в Gitea руками: получить registration token,
|
||||||
|
положить в Vault, повторить `inv runner-pl`.
|
||||||
|
4. Проверить, что раннер появился в Gitea UI и забирает тестовую джобу.
|
||||||
|
5. Проверить idle-watch: дать ВМ постоять, убедиться, что гасится.
|
||||||
|
6. Создать функцию `runner-starter` через `inv runner-deploy-function`.
|
||||||
|
Проверить ручным `yc serverless function invoke`.
|
||||||
|
7. Прописать System Webhook в Gitea на URL функции, секрет совпадает с
|
||||||
|
Vault.
|
||||||
|
8. Тестовый push → end-to-end проверка.
|
||||||
|
9. Поднять `IDLE_THRESHOLD` обратно до 10 мин.
|
||||||
|
10. Настроить алерт Cloud Monitoring на uptime > 24 ч.
|
||||||
|
11. Неделя наблюдения: лог функции, samples.log, uptime ВМ, счёт.
|
||||||
|
|
||||||
|
## Открытые вопросы
|
||||||
|
|
||||||
|
- **Канал нотификаций** для алерта Cloud Monitoring (Telegram, ntfy,
|
||||||
|
email) — выбрать на этапе настройки.
|
||||||
|
- **Тип executor** в act_runner — docker (по умолчанию) или host. Ходим
|
||||||
|
через docker, host-executor пока не обсуждается.
|
||||||
|
- **Webhook на pull request** — нужно или только push? По умолчанию
|
||||||
|
только push. Расширим, если возникнет PR-flow.
|
||||||
|
- **Перенос ВМ в отдельный фолдер** — когда в общем появится вторая
|
||||||
|
ВМ. Пока не критично.
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# Журнал миграции в Timeweb
|
||||||
|
|
||||||
|
Хронология фактического переезда. План и архитектурные решения —
|
||||||
|
в [timeweb.md](timeweb.md). Здесь только то, что реально сделано,
|
||||||
|
с датами.
|
||||||
|
|
||||||
|
Новые записи — сверху.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 4 — условное монтирование внешнего диска (2026-05-22, выполнено)
|
||||||
|
|
||||||
|
Задача `Mount external storages` в `playbook-system.yml` теперь
|
||||||
|
выполняется только при включённом флаге `mount_external_storage`
|
||||||
|
(default `false`). Сам UUID диска оставлен захардкоженным в
|
||||||
|
плейбуке — параметризовать не стали, потому что для Timeweb (фаза 1)
|
||||||
|
монтирование вообще не нужно, а для фазы 2 пока неизвестно, какой
|
||||||
|
UUID получится у второго диска.
|
||||||
|
|
||||||
|
Изменения:
|
||||||
|
|
||||||
|
- `playbook-system.yml` — у задачи mount добавлен
|
||||||
|
`when: mount_external_storage | default(false) | bool`.
|
||||||
|
- `production.yml` (инвентарь YC) — у хоста `server` добавлен
|
||||||
|
`mount_external_storage: true`, чтобы текущее поведение
|
||||||
|
сохранилось.
|
||||||
|
|
||||||
|
В будущем `timeweb.yml` просто не будет задавать эту переменную —
|
||||||
|
mount пропустится, `/mnt/applications` останется обычной директорией
|
||||||
|
на системном диске.
|
||||||
|
|
||||||
|
На фазе 2 (подключение медленного диска в Timeweb) UUID в
|
||||||
|
`playbook-system.yml` придётся поменять и включить флаг — это
|
||||||
|
осознанный шаг, не автоматизировано.
|
||||||
|
|
||||||
|
Проверено прогоном `inv pl -- system` на текущем сервере (Yandex
|
||||||
|
Cloud) — задача mount по-прежнему выполняется, `/mnt/applications`
|
||||||
|
смонтирован, изменений нет.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 3 — переключение auth на cr.yandex (2026-05-22, выполнено)
|
||||||
|
|
||||||
|
Заменена аутентификация в Yandex Container Registry с YC-metadata
|
||||||
|
service на OAuth-token из vault.
|
||||||
|
|
||||||
|
Изменения:
|
||||||
|
|
||||||
|
- `files/yandex-docker-registry-auth.sh` — **удалён**.
|
||||||
|
- `playbook-homepage.yml` — задача `ansible.builtin.script:
|
||||||
|
yandex-docker-registry-auth.sh` заменена на
|
||||||
|
`community.docker.docker_login` с `username: oauth`, `password:
|
||||||
|
"{{ yc_oauth_token }}"`.
|
||||||
|
- `playbook-transcriber.yml` — то же самое.
|
||||||
|
|
||||||
|
Локальные push-плейбуки (`playbook-homepage-registry.yml`,
|
||||||
|
`playbook-transcriber-registry.yml`) не трогал — там нет auth-задачи
|
||||||
|
в принципе, локальный docker аутентифицируется вручную
|
||||||
|
(`yc container registry configure-docker` или `docker login`).
|
||||||
|
Если позже захочется унифицировать — можно добавить тот же
|
||||||
|
`docker_login` с `delegate_to: 127.0.0.1`.
|
||||||
|
|
||||||
|
Проверено прогоном `inv pl -- homepage` и `inv pl -- transcriber` на
|
||||||
|
текущем сервере (Yandex Cloud) — ошибок нет, контейнеры работают.
|
||||||
|
Значит и на Timeweb заработает (единственная разница — исходящий IP,
|
||||||
|
а OAuth-токен в YC принимается извне).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 2 — OAuth-token для cr.yandex (2026-05-22, выполнено)
|
||||||
|
|
||||||
|
В `vars/secrets.yml` добавлена (или обновлена) переменная
|
||||||
|
`yc_oauth_token` со свежим OAuth-токеном Яндекса. Токен будет
|
||||||
|
использоваться для логина в `cr.yandex` с новой машины Timeweb
|
||||||
|
(вместо текущего скрипта `files/yandex-docker-registry-auth.sh`,
|
||||||
|
который завязан на YC metadata service `169.254.169.254` и
|
||||||
|
работает только внутри YC).
|
||||||
|
|
||||||
|
Сам код переключения на `community.docker.docker_login` пока не
|
||||||
|
вносится — это следующая итерация. Сейчас токен просто положен в
|
||||||
|
vault, чтобы не делать этого в день cutover'а под прессом.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 1 — снижение TTL DNS (2026-05-22, выполнено)
|
||||||
|
|
||||||
|
В админке Yandex 360 для зоны `vakhrushev.me` уменьшен TTL
|
||||||
|
A-записей с **21 600 с (6 ч)** до **1 200 с (20 мин)**. Это даёт
|
||||||
|
запас по времени на распространение изменений после смены IP в
|
||||||
|
день cutover'а — старые кэширующие резолверы перестанут отдавать
|
||||||
|
старый адрес максимум через 20 минут (вместо 6 часов).
|
||||||
|
|
||||||
|
Делается **заранее**, потому что само снижение TTL тоже
|
||||||
|
распространяется по кэшам по правилам старого TTL — то есть после
|
||||||
|
правки нужно подождать ≥ 6 часов, чтобы новое значение TTL само
|
||||||
|
успело прижиться. Раньше cutover'а нужно сделать с большим
|
||||||
|
запасом — день в день не сработает.
|
||||||
@@ -0,0 +1,532 @@
|
|||||||
|
# Миграция сервера в Timeweb
|
||||||
|
|
||||||
|
## Контекст и цели
|
||||||
|
|
||||||
|
Сервер `rivendell-v2` переезжает с виртуальной машины в Yandex Cloud
|
||||||
|
(`158.160.46.255`) на VPS в Timeweb.
|
||||||
|
|
||||||
|
### Причины переезда
|
||||||
|
|
||||||
|
1. **Высокая стоимость.** Тариф в Yandex Cloud обходится в ≈ 2 900 ₽/мес
|
||||||
|
за конфигурацию, которая в Timeweb стоит ≈ 2 000 ₽/мес и при этом
|
||||||
|
мощнее по всем параметрам (см. сравнение ниже).
|
||||||
|
2. **Упор в потолок RAM.** Текущий сервер уже использует ≈ 80 %
|
||||||
|
доступной памяти на штатной нагрузке (см.
|
||||||
|
`project_server_specs`). Любой всплеск (миграции БД, индексация
|
||||||
|
в Outline, бэкап с restic) — и приложения начинают конкурировать
|
||||||
|
за память, появляются OOM-риски. Дальше расти на этом тарифе
|
||||||
|
некуда без значительного увеличения цены.
|
||||||
|
3. **Медленные диски.** Из-за высокой стоимости в YC приходится
|
||||||
|
использовать дешёвый HDD-том вместо SSD/NVMe — это заметно
|
||||||
|
снижает отзывчивость приложений (особенно Gitea, Outline,
|
||||||
|
тёплый старт контейнеров, рестики check/forget). На Timeweb за
|
||||||
|
меньшие деньги получаем NVMe.
|
||||||
|
|
||||||
|
Переезд решает все три проблемы одновременно: дешевле, больше
|
||||||
|
RAM, быстрее диск.
|
||||||
|
|
||||||
|
### Сравнение тарифов
|
||||||
|
|
||||||
|
| Параметр | Yandex Cloud | Timeweb Cloud VPS |
|
||||||
|
| -------------- | ----------------------------------------- | ------------------ |
|
||||||
|
| CPU | Intel Cascade Lake, vCPU 2, гарантия 50 % | 4 × 3.3 ГГц |
|
||||||
|
| RAM | 4 ГБ | 8 ГБ |
|
||||||
|
| Диск | 120 ГБ HDD | 80 ГБ NVMe |
|
||||||
|
| Публичный IP | да | да |
|
||||||
|
| **Цена/месяц** | **2 887 ₽** | **1 980 ₽** |
|
||||||
|
|
||||||
|
Итого: **−907 ₽/мес (≈ −31 %)**, при этом **×2 RAM** (закрывает
|
||||||
|
причину 2), **×2 ядер**, гарантия CPU 100 % вместо 50 %,
|
||||||
|
**NVMe вместо HDD** (закрывает причину 3). Минус — диск меньше
|
||||||
|
(80 ГБ против 120 ГБ HDD), что и стало основанием для фазы 2 с
|
||||||
|
подключением второго «холодного» диска под крупные данные.
|
||||||
|
|
||||||
|
Переезжает **только compute** (VM с приложениями). Остальные сервисы
|
||||||
|
Yandex Cloud остаются на месте и продолжают использоваться с новой
|
||||||
|
машины:
|
||||||
|
|
||||||
|
- **Container Registry** — `cr.yandex/crplfk0168i4o8kd7ade` для образов
|
||||||
|
`homepage-nginx` и `transcriber`.
|
||||||
|
- **Object Storage (S3)** — restic-репозиторий `yandex_cloud_s3`.
|
||||||
|
- **Postbox SMTP** — `postbox.cloud.yandex.net` (gitea, gramps, wakapi,
|
||||||
|
outline, authelia, apprise).
|
||||||
|
- **Yandex 360 / DNS-зона** `vakhrushev.me` — там же управляются записи
|
||||||
|
и почтовый домен.
|
||||||
|
|
||||||
|
Параметры даунтайма — мягкие, это личная машина. Стратегия — «cold
|
||||||
|
cutover»: остановить сервисы на источнике, раскатать ansible на
|
||||||
|
target без запуска приложений, перенести данные с сохранением
|
||||||
|
uid/gid, запустить сервисы на target, переключить DNS.
|
||||||
|
|
||||||
|
Конфигурация target — Cloud VPS Timeweb с одним диском **80 ГБ** на
|
||||||
|
первой фазе. Позднее (отдельной фазой) будет подключён второй
|
||||||
|
«медленный» диск под крупные данные (`calibre`, бэкапы, возможно
|
||||||
|
`outline`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> Фактическое выполнение переезда — в отдельном файле
|
||||||
|
> [timeweb-migration-log.md](timeweb-migration-log.md). Здесь только
|
||||||
|
> план и архитектурные решения.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Инвентаризация YC-зависимостей в коде
|
||||||
|
|
||||||
|
| Компонент | Где | Что делать при переезде |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `production.yml` | `ansible_host: 158.160.46.255`, `ansible_user: major` | Заменить на новый IP/пользователя Timeweb |
|
||||||
|
| `files/yandex-docker-registry-auth.sh` | Логин в `cr.yandex` через **YC metadata service** (`169.254.169.254`) | **Не работает вне YC.** Перейти на static OAuth-token / IAM-token (новый скрипт + секрет в vault) |
|
||||||
|
| `playbook-system.yml` (mount-storage) | UUID `3942bffd-…` монтируется в `/mnt/applications` | Фаза 1: отключить mount или сделать UUID переменной vault. Фаза 2 (после подключения медленного диска): включить заново с новым UUID |
|
||||||
|
| `files/backups/config.template.toml` | `[storage.yandex_cloud_s3]` + `AWS_*` ключи | **Не меняем.** Тот же бакет/ключи продолжают работать. Меняется только `host_name` (для подписи снапшотов и нотификаций) — он уже шаблонится |
|
||||||
|
| SMTP (`postbox_host/port/user/pass`) | gitea, gramps, wakapi, outline, authelia, apprise | **Не меняем.** Postbox SMTP доступен извне YC по тем же credentials |
|
||||||
|
| `files/backups/rclone.template.conf` (`pr86keedav`) | WebDAV-копия restic — внешний сервис | **Не меняем** |
|
||||||
|
| Caddy `tls anwinged@ya.ru` | ACME | Не меняется, ACME перевыпустит сертификаты после смены IP |
|
||||||
|
|
||||||
|
Никаких других hardcoded YC-эндпоинтов в плейбуках / шаблонах нет —
|
||||||
|
SSH, ufw, fail2ban, docker, eget, restic, Caddy полностью переносимы.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## UID / GID — критично для rsync
|
||||||
|
|
||||||
|
UID/GID каждого приложения зафиксированы в плейбуках и в
|
||||||
|
`vars/homepage.yml` / `vars/transcriber.yml`. Роль `owner` создаёт
|
||||||
|
группы и пользователей **с явно указанными gid/uid**
|
||||||
|
(`roles/owner/tasks/main.yml`). Это значит:
|
||||||
|
|
||||||
|
- Если на новой машине **сначала** раскатать все плейбуки (без
|
||||||
|
запуска приложений), пользователи получатся с теми же uid/gid.
|
||||||
|
- Тогда `rsync -aAX` (с сохранением owner) корректно ляжет на target.
|
||||||
|
- Дополнительный maping uid не нужен.
|
||||||
|
|
||||||
|
Список приложений с uid/gid (для сверки и для документации):
|
||||||
|
|
||||||
|
```
|
||||||
|
caddyproxy 1010 / 1011
|
||||||
|
authelia 1011 / 1012
|
||||||
|
netdata 1012 / 1013
|
||||||
|
miniflux 1013 / 1014
|
||||||
|
rssbridge 1014 / 1015
|
||||||
|
wakapi 1015 / 1016
|
||||||
|
dozzle 1016 / 1017
|
||||||
|
transcriber 1017 / 1018
|
||||||
|
wanderer 1018 / 1019
|
||||||
|
memos 1019 / 1020
|
||||||
|
gitea 1005 / 1006
|
||||||
|
outline 1007 / 1008
|
||||||
|
homepage 1008 / 1009
|
||||||
|
gramps 1009 / 1010
|
||||||
|
calibre 1102 / 1102
|
||||||
|
remembos 1103 / 1103
|
||||||
|
apprise 1104 / 1104
|
||||||
|
tuwunel 1105 / 1105
|
||||||
|
goaccess 1106 / 1106
|
||||||
|
```
|
||||||
|
|
||||||
|
(Возможные пересечения uid одного приложения и gid другого
|
||||||
|
существуют, но Linux держит их в разных пространствах имён — не
|
||||||
|
страшно.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Подготовка кода проекта
|
||||||
|
|
||||||
|
Делается **до** аренды Timeweb-машины, отдельным PR (или сериями
|
||||||
|
коммитов на отдельной ветке). Цель — чтобы тот же ansible
|
||||||
|
работал и на источнике, и на target без условных хаков.
|
||||||
|
|
||||||
|
### 1. Заменить YC-specific docker registry auth
|
||||||
|
|
||||||
|
`files/yandex-docker-registry-auth.sh` сейчас использует metadata
|
||||||
|
service (`169.254.169.254`). Это работает только внутри YC VM,
|
||||||
|
поэтому на Timeweb его надо заменить.
|
||||||
|
|
||||||
|
**Решение — OAuth-token Яндекса.** Простой и достаточный для
|
||||||
|
домашнего сервера механизм:
|
||||||
|
|
||||||
|
1. Получить OAuth-token в кабинете Яндекса:
|
||||||
|
<https://oauth.yandex.ru/authorize?response_type=token&client_id=1a6990aa636648e9b2ef855fa7bec2fb>
|
||||||
|
(стандартный client_id для `yc` CLI, токен с правом доступа к
|
||||||
|
Container Registry).
|
||||||
|
2. Положить в `vars/secrets.yml` как `yc_oauth_token` (vault).
|
||||||
|
3. Переписать `files/yandex-docker-registry-auth.sh` как шаблон
|
||||||
|
(`.template.sh`) и рендерить через `ansible.builtin.template`
|
||||||
|
вместо `script:`. Скрипт сводится к:
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
echo "{{ yc_oauth_token }}" | \
|
||||||
|
docker login --username oauth --password-stdin cr.yandex
|
||||||
|
```
|
||||||
|
Альтернатива — не рендерить, а передавать токен в скрипт
|
||||||
|
аргументом или через переменную окружения, чтобы не светить его
|
||||||
|
в системе.
|
||||||
|
4. В `playbook-homepage.yml` и `playbook-transcriber.yml` поменять
|
||||||
|
`ansible.builtin.script:` на `ansible.builtin.template:` +
|
||||||
|
`ansible.builtin.command:` (либо использовать модуль
|
||||||
|
`community.docker.docker_login` напрямую с `username: oauth`,
|
||||||
|
`password: "{{ yc_oauth_token }}"` — это самый чистый вариант,
|
||||||
|
тогда отдельный скрипт вообще не нужен).
|
||||||
|
5. То же самое — для локальных push-плейбуков
|
||||||
|
`playbook-homepage-registry.yml` и
|
||||||
|
`playbook-transcriber-registry.yml`.
|
||||||
|
|
||||||
|
Рекомендую вариант с `community.docker.docker_login` — это убирает
|
||||||
|
shell-скрипт целиком и сильно проще.
|
||||||
|
|
||||||
|
Минусы OAuth-token: токен живёт долго и даёт доступ ко всему
|
||||||
|
аккаунту Яндекса. Для личного сервера приемлемо; если позже
|
||||||
|
захочется минимизировать blast radius — заменить на IAM-key
|
||||||
|
сервисного аккаунта (отдельная итерация после миграции).
|
||||||
|
|
||||||
|
Затронутые места: `files/yandex-docker-registry-auth.sh` (удалить
|
||||||
|
или переписать), `playbook-homepage.yml`, `playbook-transcriber.yml`,
|
||||||
|
`playbook-homepage-registry.yml`, `playbook-transcriber-registry.yml`,
|
||||||
|
`vars/secrets.yml` (новый ключ `yc_oauth_token`).
|
||||||
|
|
||||||
|
### 2. Сделать опциональным монтирование внешнего диска
|
||||||
|
|
||||||
|
Сейчас `playbook-system.yml` жёстко монтирует UUID `3942bffd-…` в
|
||||||
|
`/mnt/applications`. На Timeweb этого диска нет.
|
||||||
|
|
||||||
|
Минимальная правка — вытащить UUID в переменную (`storage_uuid`) и
|
||||||
|
обернуть mount-задачу `when: storage_uuid is defined`. В
|
||||||
|
`vars/secrets.yml` или `vars/vars.yml` для текущего сервера задать
|
||||||
|
UUID, для Timeweb (фаза 1) — не задавать. На фазе 2 (когда придёт
|
||||||
|
медленный диск) — задать новый UUID.
|
||||||
|
|
||||||
|
Альтернатива: вынести параметры в инвентарь
|
||||||
|
(`production.yml` → `host_vars/server.yml`).
|
||||||
|
|
||||||
|
При этом сама директория `/mnt/applications` должна создаваться в
|
||||||
|
любом случае — playbook уже это делает, надо лишь убедиться, что
|
||||||
|
задача «Create directory for mount» не зависит от mount-задачи.
|
||||||
|
|
||||||
|
### 3. Параметризовать инвентарь
|
||||||
|
|
||||||
|
На время перехода — **два отдельных файла**: текущий
|
||||||
|
`production.yml` остаётся как есть, рядом появляется новый
|
||||||
|
`timeweb.yml` с настройками Timeweb-машины. Все ansible-команды
|
||||||
|
во время миграции явно указывают `-i timeweb.yml`. После того, как
|
||||||
|
переезд закончен и старая машина выключена — `production.yml`
|
||||||
|
просто удаляется, `timeweb.yml` переименовывается в
|
||||||
|
`production.yml`.
|
||||||
|
|
||||||
|
`tasks.py` использует `yq` для извлечения `ansible_host` / `ansible_user`
|
||||||
|
из инвентаря (`_yq(".ungrouped.hosts.server…")`) — путь к файлу
|
||||||
|
зашит константой `HOSTS_FILE = "production.yml"`. Варианты:
|
||||||
|
|
||||||
|
- На время миграции временно поменять `HOSTS_FILE = "timeweb.yml"`
|
||||||
|
в локальном коммите (или через env override), потом откатить — после
|
||||||
|
переименования всё снова работает.
|
||||||
|
- Принять, что `inv ssh / zj / btop / login` работают только с
|
||||||
|
активным сервером (тем, что в `production.yml`), а к старой
|
||||||
|
машине во время миграции ходим напрямую через `ssh
|
||||||
|
major@158.160.46.255`.
|
||||||
|
|
||||||
|
Первый вариант чище. Достаточно одной строчки правки.
|
||||||
|
|
||||||
|
### 4. Прочее
|
||||||
|
|
||||||
|
- `README.md` — обновить инструкцию по DNS и упомянуть Timeweb.
|
||||||
|
- Удалить (или пометить deprecated) yandex-метаданные в комментариях
|
||||||
|
`yandex-docker-registry-auth.sh`.
|
||||||
|
- Проверить, что у всех application-плейбуков задача с
|
||||||
|
`community.docker.docker_compose_v2: state: present` помечена
|
||||||
|
тегом `run-app` — это позволит раскатывать `--skip-tags run-app`
|
||||||
|
для подготовки target без запуска контейнеров. Сейчас тег `run-app`
|
||||||
|
есть в большинстве плейбуков, но надо пройтись и убедиться, что
|
||||||
|
он покрывает **все** контейнеры (включая calibre, dozzle,
|
||||||
|
remembos, transcriber, tuwunel, wanderer, memos).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Подготовка target-машины
|
||||||
|
|
||||||
|
1. Заказать Cloud VPS в Timeweb:
|
||||||
|
- Ubuntu LTS (та же мажорная версия, что и сейчас — упростит
|
||||||
|
совместимость пакетов).
|
||||||
|
- 4 GB RAM (текущий лимит ≈ 3.8 GiB, см. `project_server_specs`),
|
||||||
|
можно взять чуть с запасом — 4–6 GB, иначе netdata + tuwunel +
|
||||||
|
outline начнут давить.
|
||||||
|
- 2 vCPU.
|
||||||
|
- SSD 80 ГБ.
|
||||||
|
- Снять/настроить firewall провайдера (или отключить, т.к. у нас
|
||||||
|
свой ufw).
|
||||||
|
|
||||||
|
2. Создать пользователя с правами sudo (аналог `major`), залить
|
||||||
|
свой SSH-ключ.
|
||||||
|
|
||||||
|
3. Добавить хост в инвентарь как `server` (или временный
|
||||||
|
`timeweb`), убедиться, что `ansible -m ping` отвечает.
|
||||||
|
|
||||||
|
4. Снизить TTL DNS-записей в Yandex 360 до 60–300 секунд **за
|
||||||
|
~24–48 часов** до cutover.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cutover (план дня X)
|
||||||
|
|
||||||
|
Предусловия: код выкатан, target-машина пингуется по ansible, TTL
|
||||||
|
DNS снижены.
|
||||||
|
|
||||||
|
### Шаг 1. Финальный бэкап на источнике
|
||||||
|
|
||||||
|
```bash
|
||||||
|
inv ssh
|
||||||
|
sudo /usr/local/sbin/backup-all.py 2>&1 | tee /tmp/final-backup.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Убедиться, что в логе все приложения отработали успешно и в S3
|
||||||
|
появился свежий restic-snapshot (на случай отката или потери
|
||||||
|
данных при rsync).
|
||||||
|
|
||||||
|
### Шаг 2. Остановить все приложения на источнике
|
||||||
|
|
||||||
|
Аккуратно остановить контейнеры каждого приложения (через
|
||||||
|
`docker compose down` от соответствующего пользователя или одним
|
||||||
|
проходом):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
inv ssh
|
||||||
|
for user in caddyproxy authelia netdata miniflux rssbridge wakapi \
|
||||||
|
dozzle transcriber wanderer memos gitea outline homepage gramps \
|
||||||
|
calibre remembos apprise tuwunel goaccess; do
|
||||||
|
sudo -iu "$user" bash -c "cd /mnt/applications/$user && docker compose down"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
(Можно завести вспомогательный плейбук `playbook-shutdown-all.yml`,
|
||||||
|
если такое будет часто.)
|
||||||
|
|
||||||
|
Проверить `docker ps`, что пусто. Снять флаги cron на бэкап (чтобы
|
||||||
|
финальный backup не побежал во время миграции):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl stop cron
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 3. Раскатать инфраструктуру на target БЕЗ запуска приложений
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1) системная база
|
||||||
|
uv run ansible-playbook -i timeweb.yml --diff playbook-all-setup.yml
|
||||||
|
|
||||||
|
# 2) приложения (создаём пользователей, каталоги, конфиги,
|
||||||
|
# но НЕ запускаем контейнеры)
|
||||||
|
uv run ansible-playbook -i timeweb.yml --diff \
|
||||||
|
--skip-tags run-app \
|
||||||
|
playbook-all-applications.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Цель — после этого на target есть:
|
||||||
|
|
||||||
|
- Корректные uid/gid для всех приложений.
|
||||||
|
- Каталоги `/mnt/applications/<app>/{data,config,backups}`.
|
||||||
|
- Шаблоны `docker-compose.yml` и application-конфиги — отрендерены
|
||||||
|
и лежат на месте.
|
||||||
|
- Docker и сети созданы.
|
||||||
|
- ufw настроен, fail2ban работает.
|
||||||
|
|
||||||
|
### Шаг 4. Перенос данных
|
||||||
|
|
||||||
|
Два варианта.
|
||||||
|
|
||||||
|
**Вариант A — rsync напрямую (быстрее).** С target-машины тянем
|
||||||
|
данные со старой:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo rsync -aAX --info=progress2 --delete \
|
||||||
|
--exclude='lost+found' \
|
||||||
|
major@158.160.46.255:/mnt/applications/ \
|
||||||
|
/mnt/applications/
|
||||||
|
```
|
||||||
|
|
||||||
|
`-aAX` сохраняет ACL/xattrs и uid/gid (численные значения).
|
||||||
|
|
||||||
|
Каждое приложение можно тянуть отдельно — удобнее наблюдать
|
||||||
|
прогресс и можно частично пересинхронизировать в случае ошибок.
|
||||||
|
|
||||||
|
**Вариант B — restore из restic.** Если по сети источник недоступен
|
||||||
|
(например, IP уже закрыли) или хочется проверить, что бэкапы вообще
|
||||||
|
рабочие — восстанавливаемся из YC S3:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo /usr/local/sbin/restic-shell.sh
|
||||||
|
restic restore latest --target /mnt/applications --path /mnt/applications
|
||||||
|
```
|
||||||
|
|
||||||
|
Рекомендую **A с фолбэком на B**: rsync быстрее и точнее (с
|
||||||
|
точностью до секунды), restic держим как страховку.
|
||||||
|
|
||||||
|
### Шаг 5. Запуск приложений на target
|
||||||
|
|
||||||
|
Раскатываем application-плейбуки ещё раз — теперь без `--skip-tags`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run ansible-playbook -i timeweb.yml --diff \
|
||||||
|
playbook-all-applications.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Этот же запуск проверит идемпотентность шаблонов (не должно быть
|
||||||
|
diff'ов кроме docker-up).
|
||||||
|
|
||||||
|
После старта — проверить:
|
||||||
|
|
||||||
|
- `docker ps` — все контейнеры в healthy.
|
||||||
|
- Локально (по IP) `curl http://<target-ip>` — Caddy отвечает (на
|
||||||
|
редирект, т.к. сертификаты ещё не выпущены под этим IP).
|
||||||
|
- Логи Caddy — выпуск сертификатов запустится после смены DNS, не
|
||||||
|
раньше. Это нормально.
|
||||||
|
|
||||||
|
### Шаг 6. Переключение DNS
|
||||||
|
|
||||||
|
В Yandex 360 admin (`admin.yandex.ru/domains/vakhrushev.me`)
|
||||||
|
поменять A-записи для всех subdomain'ов на новый IP. Перечень
|
||||||
|
поддоменов (из `Caddyfile.template`):
|
||||||
|
|
||||||
|
```
|
||||||
|
vakhrushev.me (apex)
|
||||||
|
matrix.vakhrushev.me
|
||||||
|
auth.vakhrushev.me
|
||||||
|
status.vakhrushev.me
|
||||||
|
git.vakhrushev.me
|
||||||
|
outline.vakhrushev.me
|
||||||
|
gramps.vakhrushev.me
|
||||||
|
miniflux.vakhrushev.me
|
||||||
|
wakapi.vakhrushev.me
|
||||||
|
wanderer.vakhrushev.me
|
||||||
|
memos.vakhrushev.me
|
||||||
|
remembos.vakhrushev.me
|
||||||
|
calibre.vakhrushev.me
|
||||||
|
wanderbase.vakhrushev.me
|
||||||
|
rssbridge.vakhrushev.me
|
||||||
|
dozzle.vakhrushev.me
|
||||||
|
goaccess.vakhrushev.me
|
||||||
|
```
|
||||||
|
|
||||||
|
После смены — подождать пока TTL разойдётся, проверить через
|
||||||
|
`dig +short <hostname>` с независимой машины.
|
||||||
|
|
||||||
|
Caddy сам пойдёт за сертификатами Let's Encrypt — следить за его
|
||||||
|
логами (`docker logs caddyproxy_app -f`).
|
||||||
|
|
||||||
|
### Шаг 7. Проверка после cutover
|
||||||
|
|
||||||
|
Чеклист (примерно по приоритету):
|
||||||
|
|
||||||
|
- [ ] `vakhrushev.me` отвечает 200, отдаёт homepage.
|
||||||
|
- [ ] `auth.vakhrushev.me` — Authelia, можно залогиниться.
|
||||||
|
- [ ] `git.vakhrushev.me` — Gitea, репозитории на месте, ssh-доступ
|
||||||
|
(порт 2222 в ufw уже открыт).
|
||||||
|
- [ ] `outline.vakhrushev.me` — открывается, документы на месте.
|
||||||
|
- [ ] `matrix.vakhrushev.me` — Tuwunel/Element подключается;
|
||||||
|
federation проверяется через
|
||||||
|
<https://federationtester.matrix.org/>.
|
||||||
|
- [ ] `miniflux.vakhrushev.me`, `wakapi.vakhrushev.me`,
|
||||||
|
`memos.vakhrushev.me`, `gramps.vakhrushev.me`,
|
||||||
|
`remembos.vakhrushev.me`, `wanderer.vakhrushev.me`,
|
||||||
|
`calibre.vakhrushev.me`, `rssbridge.vakhrushev.me`,
|
||||||
|
`dozzle.vakhrushev.me`, `goaccess.vakhrushev.me` —
|
||||||
|
открываются, данные на месте.
|
||||||
|
- [ ] Netdata `status.vakhrushev.me` — собирает метрики.
|
||||||
|
- [ ] Backup-cron — следующий запуск (1:00) проходит успешно,
|
||||||
|
приходит уведомление в apprise.
|
||||||
|
- [ ] SMTP — отправить тестовое письмо из gitea/authelia (триггер
|
||||||
|
reset password).
|
||||||
|
- [ ] Container Registry — `docker pull cr.yandex/...` на новой
|
||||||
|
машине проходит (это значит, что наша новая аутентификация
|
||||||
|
через OAuth/IAM работает).
|
||||||
|
|
||||||
|
### Шаг 8. Заморозка источника
|
||||||
|
|
||||||
|
Когда всё подтверждено стабильным (≥ 24 часа):
|
||||||
|
|
||||||
|
- Остановить и выключить старую VM в YC.
|
||||||
|
- Подождать неделю-две на случай отката.
|
||||||
|
- Удалить VM и связанные ресурсы (только compute! S3-бакет с
|
||||||
|
restic-бэкапами и Container Registry **остаются**).
|
||||||
|
- Удалить `production.yml`, переименовать `timeweb.yml` →
|
||||||
|
`production.yml`, откатить временную правку `HOSTS_FILE` в
|
||||||
|
`tasks.py` (теперь снова `production.yml`). Закоммитить.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 2: подключение медленного диска
|
||||||
|
|
||||||
|
После того как Timeweb-сервер стабилен:
|
||||||
|
|
||||||
|
1. Заказать дополнительный «холодный» диск в Timeweb, прицепить
|
||||||
|
к VPS.
|
||||||
|
2. Узнать UUID нового устройства (`lsblk -f`).
|
||||||
|
3. Решить, куда монтировать — варианты:
|
||||||
|
- Сохранить текущую схему (`/mnt/applications` на медленном
|
||||||
|
диске целиком). Минус: всё IO приложений уходит на медленный
|
||||||
|
диск.
|
||||||
|
- **Лучше:** оставить `/mnt/applications` на быстром SSD,
|
||||||
|
медленный смонтировать как `/mnt/cold` и под calibre/большие
|
||||||
|
бэкапы делать bind-mount или поменять `data_dir` у нужных
|
||||||
|
приложений.
|
||||||
|
4. Восстановить в `playbook-system.yml` mount-задачу с новым
|
||||||
|
UUID (через переменную, заведённую на фазе 1).
|
||||||
|
5. Прогнать `inv pl -- system` с тегом `mount-storage`.
|
||||||
|
6. Переехать на холодный диск только большие данные. Для calibre
|
||||||
|
это означает остановить контейнер, `rsync` библиотеки книг,
|
||||||
|
поправить `data_dir` в `vars`, запустить.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что НЕ менять во время миграции
|
||||||
|
|
||||||
|
Чтобы не накапливать изменения в одном переезде:
|
||||||
|
|
||||||
|
- Версии docker-образов всех приложений — те же, что в источнике.
|
||||||
|
- Конфиги приложений — без правок.
|
||||||
|
- Restic snapshot policy.
|
||||||
|
- Apprise/notification каналы.
|
||||||
|
|
||||||
|
Любые улучшения (healthchecks из `docs/drafts/alerts.md`,
|
||||||
|
gitea runner и т.п.) — отдельным циклом после миграции.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Откат
|
||||||
|
|
||||||
|
Если на target что-то критично сломалось:
|
||||||
|
|
||||||
|
1. DNS возвращаем обратно на старый IP.
|
||||||
|
2. Старая VM в YC жива и заглушена → стартуем её, поднимаем
|
||||||
|
сервисы (`docker compose up -d` под каждым пользователем).
|
||||||
|
3. Изучаем, в чём дело на target, лечим, повторяем cutover.
|
||||||
|
|
||||||
|
Поэтому шаг «Заморозка источника» отделён от «удаления» — у нас
|
||||||
|
есть «горячее запасное» как минимум на пару дней.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Открытые вопросы
|
||||||
|
|
||||||
|
На текущей итерации — нет, все ключевые развилки закрыты:
|
||||||
|
|
||||||
|
- ~~Auth для cr.yandex~~ → OAuth-token Яндекса (`yc_oauth_token` в
|
||||||
|
vault, `community.docker.docker_login` в плейбуках).
|
||||||
|
- ~~Инвентарь~~ → два отдельных файла, после cutover `timeweb.yml`
|
||||||
|
переименовывается в `production.yml`.
|
||||||
|
- ~~Регион/TZ Timeweb~~ → совпадает с текущим.
|
||||||
|
- ~~IP-whitelist в конфигах~~ → отсутствует, смена IP безопасна.
|
||||||
|
- ~~Объём данных vs 80 ГБ~~ → 22 ГБ всего, из них calibre 16 ГБ;
|
||||||
|
с запасом влезает в фазе 1, второй диск не на критическом пути.
|
||||||
|
|
||||||
|
Возможные вопросы по ходу реализации (выяснятся в процессе):
|
||||||
|
|
||||||
|
- Конкретная процедура получения OAuth-token Яндекса (через
|
||||||
|
`oauth.yandex.ru` или через `yc` CLI).
|
||||||
|
- Поведение Caddy при первом выпуске сертификатов после смены DNS —
|
||||||
|
убедиться, что rate-limit Let's Encrypt не упрётся (≈ 17
|
||||||
|
поддоменов выпускаются сразу, лимит LE — 50 сертификатов в неделю
|
||||||
|
на registered domain, запас есть).
|
||||||
|
- Federation Matrix после смены IP — обычно достаточно того, что
|
||||||
|
apex `vakhrushev.me` отдаёт `.well-known/matrix/server`, но
|
||||||
|
стоит проверить через `federationtester.matrix.org` сразу после
|
||||||
|
cutover.
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
# Must be executed for every user
|
|
||||||
# See https://cloud.yandex.ru/docs/container-registry/tutorials/run-docker-on-vm#run
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
curl --silent --show-error -H Metadata-Flavor:Google 169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token | \
|
|
||||||
cut -f1 -d',' | \
|
|
||||||
cut -f2 -d':' | \
|
|
||||||
tr -d '"' | \
|
|
||||||
docker login --username iam --password-stdin cr.yandex
|
|
||||||
@@ -26,9 +26,11 @@
|
|||||||
loop:
|
loop:
|
||||||
- "{{ base_dir }}"
|
- "{{ base_dir }}"
|
||||||
|
|
||||||
- name: "Login to yandex docker registry."
|
- name: "Login to Yandex Container Registry"
|
||||||
ansible.builtin.script:
|
community.docker.docker_login:
|
||||||
cmd: "files/yandex-docker-registry-auth.sh"
|
registry_url: "cr.yandex"
|
||||||
|
username: "oauth"
|
||||||
|
password: "{{ yc_oauth_token }}"
|
||||||
|
|
||||||
- name: "Copy docker compose file"
|
- name: "Copy docker compose file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
|
|||||||
@@ -54,5 +54,6 @@
|
|||||||
src: 'UUID=3942bffd-8328-4536-8e88-07926fb17d17'
|
src: 'UUID=3942bffd-8328-4536-8e88-07926fb17d17'
|
||||||
fstype: ext4
|
fstype: ext4
|
||||||
state: mounted
|
state: mounted
|
||||||
|
when: mount_external_storage | default(false) | bool
|
||||||
tags:
|
tags:
|
||||||
- mount-storage
|
- mount-storage
|
||||||
|
|||||||
@@ -38,9 +38,11 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
|
|
||||||
- name: "Login to yandex docker registry."
|
- name: "Login to Yandex Container Registry"
|
||||||
ansible.builtin.script:
|
community.docker.docker_login:
|
||||||
cmd: "files/yandex-docker-registry-auth.sh"
|
registry_url: "cr.yandex"
|
||||||
|
username: "oauth"
|
||||||
|
password: "{{ yc_oauth_token }}"
|
||||||
|
|
||||||
- name: "Copy docker compose file"
|
- name: "Copy docker compose file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ ungrouped:
|
|||||||
ansible_host: "158.160.46.255"
|
ansible_host: "158.160.46.255"
|
||||||
ansible_user: "major"
|
ansible_user: "major"
|
||||||
ansible_become: true
|
ansible_become: true
|
||||||
|
mount_external_storage: true
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ def pl(ctx: Context) -> None:
|
|||||||
raise Exit("Укажи хотя бы один плейбук: inv pl -- <name> [name ...]", code=1)
|
raise Exit("Укажи хотя бы один плейбук: inv pl -- <name> [name ...]", code=1)
|
||||||
playbooks = [_resolve_playbook(name) for name in names]
|
playbooks = [_resolve_playbook(name) for name in names]
|
||||||
ctx.run(
|
ctx.run(
|
||||||
f"uv run ansible-playbook -i production.yml --diff {' '.join(playbooks)}",
|
f"uv run ansible-playbook -i {HOSTS_FILE} --diff {' '.join(playbooks)}",
|
||||||
pty=True,
|
pty=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+162
-174
@@ -1,175 +1,163 @@
|
|||||||
$ANSIBLE_VAULT;1.1;AES256
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
30613937343031343632383733623435366535373231316163393436363636656462326262383565
|
37316466386337633433633632333065613137643963356437653030653433353464666636383238
|
||||||
3032663665323131626263356531633934326639636231620a363635376263333438336331343366
|
6131616231356633383839653530666333343666316363630a323736656238336633336363656634
|
||||||
36386337323165333861633062656433313062343764636138663533333639316336306230653732
|
35633031623730613966343533393033386538346162333061613135353063323533336135376337
|
||||||
3331336137616263630a306135333566646434663231383138363966386661643836626561376338
|
6262346462636361370a356336616330323738383535356238383238656366303833313137613461
|
||||||
33636362323937386664646630383062613535666431393634316337626564613733313861386238
|
39613266616632376433636263653332363661373832316538343530353963363762356534373264
|
||||||
39316263666662633066633836366236346431313531656339613566303962656165396662326563
|
38316134643632386564626662616235316563343833646164336439623035303838316630393030
|
||||||
65623036333932393739646162353836646562643866396263386232633933326538316637656365
|
32636566663835626362666235363432316430633139333935383031303564353633323835616530
|
||||||
38656562383861613030306635613236646235613436316635386531656666363738396461313263
|
39353336366634356530393339633836613563666435323533323731666666656339313431613161
|
||||||
30653934366537303133613962653137633131323431396266646339376339623034373963666438
|
63386639663635393339643965643664303130633234333130663336623738313665366630316539
|
||||||
66383464303431353962323032316533613138383831343036383230303931326433396333623935
|
39373930316434636261646431633666653066373632396136313639633266383036643739323936
|
||||||
37396432376637373135666236333332383262323931616432343665653836626265376632643765
|
63343836666263623966333364616230393338386439616264616437373739373366326430363038
|
||||||
33303835393863333334653664613337343063313362363136383234666335636565383237656639
|
61333237323139336236333366666164313364366663333335646139353061323362313465613865
|
||||||
34323839613765626231303230616661626530633530333165373535663139643339656438396237
|
32376234323936336466356239666162323330323133616166333733613566636635303965316665
|
||||||
36656134643636643733363336343739616532666130393863666665393138383261353730626565
|
61616135333964613862303432323032313533353132303762396539323033636636643539663334
|
||||||
38306133343463306462656534326431623238336562653433316233383861303032393437316336
|
36663866313130303531333961373337316462376564396639656265393663373230623937376337
|
||||||
32353338613639653735393239333235633565636563313933333763323339656237326162316465
|
34656339323864633462633030333730613765653631636233356535643333356661333331303935
|
||||||
66313034306263343462376632303539656533353265336366613338326439323732623438626162
|
33646364636434336162356535393162353533636262333735616463623432653933316230306261
|
||||||
39393937613836656236383030343436303632363330313734333665643365666138633034323462
|
38353766363130333963613830643735663930353162343665323435613435326638363162356438
|
||||||
66376333386237383666623434636662363338626538353933636632646236393630343739636666
|
35666666343165323561396336646264613633323066613665346535313165306435666463353030
|
||||||
37666531633839363365633863646530396432613166313035353638313463373338313139616133
|
33626661326536646264343531653834616563316533386539656431353165633161666366386433
|
||||||
62383234356665333132613664383931316238353863306538343831363233303862383737373939
|
66623662303636393732643330333664373935313932306232306433653038623461393030306562
|
||||||
37303430303766343366633536643139363366663734326162366434333165613033653666383337
|
66663637636534393631633939363037303331383166343561373163666263323563313636326538
|
||||||
62626538316463343466613065326666396266643661656164376336336532666134613663623163
|
31656132376636653336353666316639623230613563333363653965333435323231303161333231
|
||||||
64316335633839356231393130343938613334393737666663363662356466326235666561653239
|
65353539633132373036346233626533356139373938646130376437626532383864623138613463
|
||||||
39386635616165633063383032666366383861333038373636613663613461316433633562623664
|
65373530343563613633323439636638316330663639306438323761306238616539353262326166
|
||||||
65373536663230356632663133356639323838653431333836376330316162633261333934363335
|
61323239666661366665343736323834633065306237643538383238616563353366343936663835
|
||||||
34383937343063303835626435316534356239316230326566383036646237336238623036323161
|
66313066653832636534346136373962653136303435623365376235626463366633623561313231
|
||||||
62326264636130323965313866616631663039623431363139363462663435323866393437373566
|
66343861363336303136666136363531386166316631643565383835333261356237373736386638
|
||||||
37353463353731303434303435353061633531663464656336306439373238633038343237313133
|
32653565623034303939383134613235353237393035336662303432653734636630373235303733
|
||||||
34333463626261333038363438343034373335346332316430376436656331626664376664323037
|
37303234333764303863323733356363353864323038393762373339303239656566363966363832
|
||||||
32393631663434383265326231353035356333343739386132326435653438306136373237396539
|
66373965333362383031636330613637656436653365303930316233396431323338636239333930
|
||||||
33613462623562343966343933363037326234323836363636313938666534333337646139326533
|
62353637333562343534663964636235623363313463613135373639653536636339376435313662
|
||||||
61633666623936646366643336333339303633643230393465623031643963643635313264353236
|
31616465643730336562373463353965616263366137316430303666346532633735333661616337
|
||||||
65313631663430336262326463663938386630363464386230383766376363373235366438393635
|
31303361633437663065326666633437383534366639396661623336643565383336623232666563
|
||||||
65313232383334666263626662646264393565326164613364313138303638653333653963316561
|
64376136303637393330363263666631336461396163373932346138653362326163666331336434
|
||||||
34346464653637376433356335663930396432386238366132393562393162353235393438633533
|
66356232666439633132363537626436613064663562333536303238313834303666363830353632
|
||||||
62656533316431666463633530653832356263653030326366663932306662613465643638313633
|
33633633373738383165363135323232313735393335646238326261643765613061623264343261
|
||||||
62396562616463313066343832316238386234343537346436623039643132393562303130613331
|
37663531396165653838346431666534636365373563366636383165376137396537353031653261
|
||||||
38653261353132633036623138643338366534396237613333333765653436363032616235373035
|
62393437633735366433613166356337303564313538633933633465363331316332656262303439
|
||||||
35313966623531373636363638383862333935353931653861663966643531383335653739356565
|
62383535383666383535326538356431313365663937313065363138636336666631643737343634
|
||||||
31373937396234616135653765643131666530383030343064366531336135366265633232653433
|
61396664383539316261386235643430383436396638303737366632306538636133666239616162
|
||||||
65396566626232633831343734353432633462343336616135373861303836613463393736306133
|
33373066623365353832666139633436343664333434393933646265323132616665623938643061
|
||||||
64663531643630326432376235386433623365373163366663623632333531623863623663643434
|
32663763316639333036343333373632633431373038326666323036333664333332313731313865
|
||||||
36646134623665633531643732663137613862343666613139336231646564363266343935653263
|
33363638616661363763313263623230663735383834353835613861373063666538363764333433
|
||||||
66653366666635666535636637626134363633336233613732656166373063333237323465616434
|
65316333643937366632313335643364333736633936316132313534373862646365323037363963
|
||||||
39623238636235333866666536346430373735323530633133663937636366663530326465386161
|
63353237373730623533633961323038616532643034643939353034383036326461383566346230
|
||||||
39346466386133656633373438333133303566363233626238366133636333656462373065613863
|
61363561666265393536323164323462356237373764306632383034646665613130373030383336
|
||||||
65363439383163323332383931663833303234326132343462333835323664363461656566393065
|
39353235353036323964356139363530636364346666663331643736306361653966636164613830
|
||||||
38646261323336316239363465343238643132306235613031626438323838653066376561626661
|
65353664643733383737663766613965616334356561376538623763613331343334613532386562
|
||||||
34356530666665323230646436633935343861323638656638323163306236393865366630636236
|
33623564636233663234313631623734333934303439383662336665623936643039623039303962
|
||||||
62386161646131623738333664636361396239643666323837646332383538623734386531313664
|
63336433333766376130356339303239626536383364373063663435653236386139613366323938
|
||||||
65313632343365393130643137353735666565663030383231616231313237323866386336316361
|
34653161313061333837373164653238663539326562633237616134623366643039373033353331
|
||||||
66656165643261653464316639613635323531306362353164373531326461666437303434346233
|
31633436323463623661656636383964643362613130336537653731646239313365633662656132
|
||||||
31383864346233313633353065343236633636386138323761666662373564623234613965323131
|
35393262613830323964346662666664346664316662623865663236643065333533313135643063
|
||||||
36313861316563333262306434663265313237626631396561303236343330633738356666633663
|
37343461316564316666343465303539383463613663306136653465383336353232366238316332
|
||||||
61313663336237653361383963333764336137396666613634313036373564353564643334623363
|
31313837323533313863333565643762653362633231333436396238666562363139626336666532
|
||||||
62353531306532323664376363383938646536393339346666656339393230613362666337663861
|
38326331653736393031636264316330643532353862366235613334383266613238326431343066
|
||||||
37633633653463343430666634643863383438633933343839663865616136363538643061343437
|
33666566613061363465376637636636626432383132636330343736653034643965396566373064
|
||||||
34613037353835613866303230303162396531626663616164343263633261363335313936666339
|
37303830393531383131646161613335666631633535383739626130366464313930343262393533
|
||||||
63383533616530356262363838636466333038656339316364626263383731313464313734613630
|
66373137303531623663643339643233373737303831323735623561656165383034373131386639
|
||||||
62646266666136616632636161363631623362346230643134663664396565323932343462383661
|
32333838663264366661616262373439323433386538373261373465343163326438623962333566
|
||||||
38303663653262333236613833396237663834333139316666343065396137306562613265343863
|
30376463646631663330366364626133386131313261396434656336636566323234633864323731
|
||||||
65323065663862636230636664623132306231366462346432343030376236346465663831623537
|
61393864666537656338646462353764356131303836333762333932643130346439356432323231
|
||||||
63633231333165613731626137656539366131633364623661616136616434306563656139346137
|
65373866393263313236353937363031366661666664346362313466356366663336383761623637
|
||||||
63313032343161623235306230633361666163623061333738383135636664623438323238663631
|
66343938303839663630363562353634353938643638373335636530323430656336303963666662
|
||||||
37613964643931323432353431306564393639386437666539376238643065343738313265373661
|
63643031396133646531316162323836616461373863643463663132383764363034663562363365
|
||||||
61303764646463326632653335323432646436353765633862623838386337623464333839643833
|
37383234663939646539643562376532323936616264326533333637323633373330643162623537
|
||||||
39383961666234363638323735636231623962666461373435633631323530643237656464396465
|
63626138616165343938643237313562616234303665303635653830343735326661666235643333
|
||||||
66623431393461613634373237646636333965396435663563363161626666356638366462373261
|
36623333343365613161663663356566656533363736313231336536386339326336373730643630
|
||||||
37633238323135666136623663653665303832656437663536383236313334313461353032663933
|
61386139313064376161303066323162316564313761303734376663653464323138303339323763
|
||||||
63643164363664663939613635373362376162336262653332663936313737396130366330656532
|
35373930366262373936356464613731396136613634663562373737663739613562373333386265
|
||||||
31653463383132643262613839613962663836376463343661393736633633396164643264653431
|
35323161316633373430633833663266633030613430613635356135393161653836366464326236
|
||||||
30663732303236653165386537653432656266363239373030333630353661666636303730373937
|
63373738366661333964623962363035636131656539613139373138643839643138373564333430
|
||||||
65363237366333376133306437376534636133356238326461333762326563386265363636323831
|
34623932396134343762393334373033663132306636633864643235373861386633366639316566
|
||||||
39343665386262336265383865343563343832623766656534306661326462333561373835366631
|
66623439613730353237366366663161363030646663323836653638383330323164333461666334
|
||||||
39636361623831623533353962633363393531313530363833613962616331653565633733303964
|
62333662643631373361643263646537323239643234346333333261623933626166333630356662
|
||||||
32393433303938323566646264323761633035653231353761643261663839313665663434643834
|
61333362313230343134343331303031303634383335313339303363383930373136333336313062
|
||||||
65356432393431336235306437643861653437643362363839623634333835376636623664616139
|
39626532646365663734313462343766333962333266363965373630363831626633623830633733
|
||||||
66376562633232636431626436653161333137633466313433663433383230636337653535643430
|
37633666663361343639366663313237633161353062396337393138613261383935353730353932
|
||||||
61613032656135323765613837626266313632353661346636643866613138303930346563623738
|
32383066386636346433306662326130633662363135313331383530346632356333363238383266
|
||||||
35613831623565353432336338373465303437623234313736353661353430656661366365373230
|
61303138636664303637326137376638333266343132356237383432613238333632356163643232
|
||||||
33646134356661616164303865623464306339653439613365626261323237623135346537393535
|
32333966383164343064643934333765386136323736303138326635663561343964393934316433
|
||||||
62393465343134626333333462316331656134383362383031353863316632393061333933336362
|
37663763653034363039636436346630303664346237616433376462303463633930383430646430
|
||||||
36326662363833303436663166383365346433323866346462663261333330656666663162383564
|
38343762663635373033373135326361393935386566363031366630366561663431333461663332
|
||||||
35336438643064313833393638323864343237616163383033313966303262326135323335353931
|
62393538306163386139656431353235343265373033663530306163316464373432353661343439
|
||||||
66333938393264323533353231303935346661653835386262306133393065356535643835663665
|
35356634323966646263613937663234613431316333656330343765316163626339376430343064
|
||||||
38363930356530366135313734306464623739376438613430373634396339393864396264303135
|
30363335633337623139343464656431353738623230633137633130353430373435303963663333
|
||||||
61356333636236326566386264353930626564636438616265353939383733663837313233356363
|
66646563626336393538613134636162643864316163626361663663313131343063643336396532
|
||||||
63643835393437336366313030303864306536666638623430356263336234646462383666316431
|
36333336386130653537653637653163343031376436383763386163376336623837336365616530
|
||||||
36313464346266646438383762313138376338323537386635636561656662306533316362396162
|
66386262363633353465663664326537343037383838333863366662306131303364626538623763
|
||||||
38326165633532623933376165643861323735353831363264376162316561613038633961333337
|
30376132613861636539393138326131356333613837336365383862383362303233633333396238
|
||||||
30646461636332623466643033633764333330353832616365376633643263336131313733653139
|
34663163363234363564336338656366313931353262303032343334303864386337356437633361
|
||||||
39646239366261366465333962643565636430393464613866613038333636393362383636343534
|
39383366386334303039313439376236663533633035326662626466333539626261643466653966
|
||||||
61323830616234633364346131336630393965373730343464366166376232346464636263323639
|
66383862663635346539396139323138613264373436653338653136366135613962373562373932
|
||||||
63336464623733363139366665336131653163613833383261376138373032666663356637383832
|
62316134636636373264623365613636373132396433373630643531633536333130646130373963
|
||||||
32313130633363346435383638616236633761616166663339316437353938636636613530383836
|
33356538316334336236346532356263363933633933376165666563313161343638656365373137
|
||||||
64623661366130656439306266343435396334383564353466663339383862313733313931383463
|
61656438313063363661316537336538646233326564633934303766383738343130373737653961
|
||||||
64323237656361383262343735366562623965356636343963363966616333313333646233373464
|
32326462626435343363326366663364376431613837646365383933396437383138333665383832
|
||||||
33383939386262663730316333616663636161356463396362643237356532386162363131626461
|
33376563363466333637656233643137333636326535653335393931653063623937373836313863
|
||||||
37323965313063623463356133626531393339336535303562343530316663613639646531323136
|
30363238356466363461313633306565646336633366346461333032643635366336646365643761
|
||||||
30353732646237623264653963373863363965326338666264306562373932393333633639396131
|
37363433623037333838663731376663306134363665383835333164643131646632393639363538
|
||||||
34303764396330326165636264313532393961303038623031336631653831323337306261333630
|
32656539643461653861376436396130316166616265336433356663343235333763636564326464
|
||||||
37333964376533636132303335653935343932373330373632626235356437636165623436383036
|
32663530333030306130366532636439353264303137613366353266363664346433666565303135
|
||||||
38313565373561393834316532333930356135623439373161643063643738353031353565396330
|
34343835623936643631643166636339306231323138356565373663636665303264653161613539
|
||||||
37656162346433326638353439613666336534336562623633643230636134383931653538616665
|
65336230633035336464373431663130383131376430306136333634376435393933623238653136
|
||||||
32393432383265613237323138386361353934373965306462393666616532653563626232643035
|
61633138653339336631313932353264366334616630363464373131376538303630663566653061
|
||||||
61643732376434633537633663633130313437656166333239633533393334373163333566343430
|
30386665383864383838613835613337633937616437376363626263376634643061393937653430
|
||||||
38633165353637306237316436663235633162353132646562353638333038663636323465633632
|
38626539383634616536396665396262633666376438363930383635353936623534353131666333
|
||||||
34643037623634643534663366633133363030323966313065353333633636646636306565333238
|
31616536383662663666333265623032613936383063303636366362346337333337633739393665
|
||||||
39336662626138306464613461343762316533656433626165323764616535623539336439396663
|
62306639376466636161333233656239313964373239336134646434613166646534656466356431
|
||||||
63393365626235613063613934306132333162646237316364306637346136623061363236383765
|
36626162383866643633323938646130366261373366353764646234653236323166363463366435
|
||||||
37353138363337346530626563366136333635663863313038643537366237633362343136396664
|
65636137643639353039616138383265633832373864643438366333393031326363333461376138
|
||||||
61623237353433333238633163636565386134356565303763336238636366316330666339383365
|
65303838626435656634626236663338316632613664646463306561663532353062643637663866
|
||||||
30323235356633656362353738393234616435663333613364316539636430623262643162313337
|
32663236633630383365663235313337373837396531326532333632343736343035643733333262
|
||||||
36323466303832336530336566343731306362333862663537613339663562623739343636613162
|
61633532323330623338393838626333323932376630316239303335313765343039313332316336
|
||||||
36343563373665376565366266343461643562636630623166626165636337613931653338633862
|
32303638356539333136353163396536313830333264613266303139376465613166363730343635
|
||||||
65393138353661656265666335343263333063653430326532663839383433643966363639643636
|
37623532356135613831363137356238373863653933366532346532363130386363616133373764
|
||||||
61376365363538636235666235623638376334363265626136313536353637386564303936636263
|
62353066376631343934356337326465613436363361646431373962656639393566323731316136
|
||||||
32306239306339656238393864666135613663366332666135663461353366313833376430376263
|
30313730386438663261636562643061333664303636373732663464356530336333383739313566
|
||||||
62613163303964333735396338373737653837666435656130376435376434356462383264636561
|
35616566663065313732656532346330636335383664383061323131306530623632353132326432
|
||||||
34353563316132336663316166663832383939333634316562383634383838336531313731613666
|
34313365396430396338363063376534363332306432313531303261353736353138383733346363
|
||||||
62643231636266353935343539366465376139643834306261623738313432306133653461383738
|
37373063666630633632613039353763626165613038616464363333383662356363373133623261
|
||||||
39396332373364353833626661333634346131396337636235653431616336393666373231383030
|
61316130393165633434623334616239666137653831636131393732363330363039306666346363
|
||||||
32306466613136346265653038636537646330643337663863383562323638616661333037323232
|
38353832356138383732306533646133663865663062663862663232323038383532616230386265
|
||||||
35613138363330353533643064613366343339343032373737306364353135353334336666663732
|
38626535313931386533613936343637373064666663336436326464313834333334366264336130
|
||||||
36343963613636376561666266623537316432666161326331383761323437383738373762643937
|
37346438383739313735636139343638373063306162313230366364316136333137653939313032
|
||||||
30623737643239326261343939663065643265653363633661376265626637643336613635393335
|
63303035663465653062613764333434323234323332316365333165383533396430316537623164
|
||||||
65373565333936333431656331633039323135336236656337343532643939386338663239393065
|
37356138623233656436663738393966346662646339626137663533616234653535663663383738
|
||||||
65666536333732646235633762633032393463663334616165333834653938346230316236353839
|
37323564653664653530653533396332333664636261306339663830373730613832363565613363
|
||||||
34396362386265646261373561636230363962663433303535373035346334353932643365383763
|
63333635646339656535366365633638393164663562633562383162373932306233313164386331
|
||||||
32333239613961346466356562376663613062373162666264633636323833323263333765616563
|
37613036323664316237336130366138333863316563666337333464383932353363306231336338
|
||||||
39646530343962353362363634336336323463623137646531373362353832343335366461646535
|
65393537636162316637343262313333386532616632376563333039386131336230316266396265
|
||||||
38653735316536396438613866326438363036653833366636626130323437623366373833366165
|
34626630633038623462346365316438316238663163346136663436393736623464643961303339
|
||||||
30303636666263323062343931306435363961643838636163366433376436303231316338613034
|
35613565393633373861626239663563336633643565383833373632353737393838666161633462
|
||||||
37393631363632383461373566306365306631396335633432383939336332626237653462393136
|
36316634313530663032366430656131373838616161613265633936633930646137646661653738
|
||||||
34316636643464363634366535333463326533333564633163363062666463343731396231656234
|
31356266666162643736363538646464363164633434643161643239333336326262386135633165
|
||||||
39346333303465363037313063373366373439306333636465636366666437326362626264653033
|
65656334313632336138393364356664386237313936626333616638663666396530623464613665
|
||||||
64613062343538303931646630373565663530336133633032366331626536353237336235633636
|
36636537396565343864353465633638613439663663633766356331333036313130383432366136
|
||||||
63366639366439386530303966323563323862383865356630313636333333393464653762626634
|
66383333643938613831386631386537353166653561376161633431306531343033646233343731
|
||||||
65366231613661313233626239303035323666346236636362393036353839333636343434646266
|
34363164316463633133616230316433373966343031353733343762643032356264646631323236
|
||||||
65653039353966616361363335346565383863616161316134383365616636333732653233383261
|
33393231333330303339613634643333346134643035653330343230663035343230626465386339
|
||||||
65616664343830353861616666616237313532363334653430313437313535666436383338396363
|
36323030633037363462343130343333313538393163393263633339323133656239366161323837
|
||||||
33313436363061306431366332373936633034393733646137636338336431333033343532613531
|
30383231323061323538393063316666303534663664623464383634613834326237363532353165
|
||||||
32613839393232646565663931303530376432376337613762346230646366613935383234313666
|
30356561643738373537343831663461623839303363366266356663666664623030333936656436
|
||||||
61643339353933336434666466623133336637343534303737366162316561366632333335663233
|
39616261626464343039623161343234626566623537396264366439666438343739306361383666
|
||||||
34643036326630306632353438643666623939393033646238353261386231626634303266303530
|
33326433383830313933383030393235306261323462323265623862363861653331643736363336
|
||||||
64643436653234616332623835333165626135613465346162393335353133356233666536313632
|
36373561363566346232613631653464613034653438343462383432643962626665303634646230
|
||||||
65616135666533343839666132623639343565303436623162383738353633613864356535646365
|
63656261613463623466393064623534306363663835616138626332356430633165363366343461
|
||||||
32373337393936393830666365383462333437373539666633386361373135333163393334303235
|
66623133666461313934346365623434653433373734376632616435386636613166633935376337
|
||||||
33663631386566356366666132616265373533373561616564343538303432346562356234336663
|
61326437333132306637663436663930396461393131643766643362316434346438316630343833
|
||||||
32623866396434326264636539323132613239343938353739376539383139313833376563623434
|
31343638383339646436363238393634636363303861383266313638623663303732333231316366
|
||||||
62636334326234313230666662396561393130396137306437393334323561356435343866386636
|
37653861663737656631666535383366643265373135396236393936353264613765656534313430
|
||||||
32656337656439653830653365313031326562643437376538316561653963643232353434313538
|
39663431623737346462303035613162393939623239326232326330636562326236323830613238
|
||||||
64373638616133666463393462643465306565646136643862363162643638343565316139626539
|
32303263333864616434316361646162623163306262646131346135613162353833366537666532
|
||||||
64643939383936313035323936656438313039376635383733633032613165343130663930323166
|
39326430306331663736363365386264653634613530376639313336613939363135656638353632
|
||||||
37343261333332663863366533386335373962323163616564376434636361356438393035656533
|
31656430306363396366306539626436356436306233303032623265356337303666363965353964
|
||||||
30383139323931306232353664636662313036643431663536353035356139643761613235663837
|
62373031336438326635386632313365336236303737373832366363333634383835306231623065
|
||||||
37613133363433356536316466343237613131386536356234343135323861396130663464323236
|
62646236643264373036303465663838366565616530306463613563613463323735646430663566
|
||||||
63636563633031396465663563366263373938373531336239323138653531386535643332653736
|
34356263353637386163316639636334616663303935666530396333653633343861393335313966
|
||||||
61396432356161643663623130656632633862333861656464613432623732656465376236313437
|
36383139383835343937393934316330623338646663636466396436353739303464313161376138
|
||||||
65386630633036636663303633636134343739366562643062343030383138653466326636366266
|
61663033623031653361643735366562393431643236636531323061303033313966633838646237
|
||||||
32633239343039633636313837643432333238366533393061646237626130303934356438633936
|
3831653036383565396363663066316432636537376430376165
|
||||||
38366265656365333338363431643432633463313438633361333764653637623964363732303737
|
|
||||||
61343137653930353361653364656233343166633162313964306531383834356237343031396137
|
|
||||||
34366135623530366164646532643636346233353563333031343931643037613463613639356238
|
|
||||||
36306235336562333935643035313934366339623365616661616461653832336137336464393662
|
|
||||||
38363433646139646633353162616661323433636531393339643562373538616430363061366330
|
|
||||||
35333138613136323865346462653761666534343538313033663835653631363631623532663133
|
|
||||||
64383135326333626438363066633366316364643332623030653230353861633837646362626333
|
|
||||||
38363236616265313638626263316164323563616237653465353031353734333032323761393761
|
|
||||||
31333331643161396338653330653537353634306139656536363665643437633433666236356334
|
|
||||||
64386566623836306666653766626465646664303231613062663862613565393364303233333636
|
|
||||||
61633631643437373235636133333832646463366633353939383834373362633539333766303661
|
|
||||||
3132333365333061633665366432346636646564313437333061
|
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
---
|
---
|
||||||
|
host_name: "rivendell"
|
||||||
|
primary_user: "major"
|
||||||
|
primary_user_uid: 1000
|
||||||
|
primary_user_gid: 1001
|
||||||
|
|
||||||
|
# Directory for all user binaries and scripts
|
||||||
|
bin_prefix: "/usr/local/bin"
|
||||||
|
|
||||||
|
# External disk for application data
|
||||||
|
application_dir: "/mnt/applications"
|
||||||
|
|
||||||
apprise_external_port: 8000
|
apprise_external_port: 8000
|
||||||
apprise_external_url: "http://127.0.0.1:{{ apprise_external_port }}"
|
apprise_external_url: "http://127.0.0.1:{{ apprise_external_port }}"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user