Как я раскладываю сервисы по docker compose
Когда сервисов на коробке становится больше трёх, наступает момент истины: либо у тебя есть система, либо у тебя бардак, в котором ты через полгода не вспомнишь, где чьи данные. У меня бардак уже был, поэтому в этот раз я завёл правило и держусь за него зубами.
Правило ровно одно: один docker compose стек = одна папка в /app/<сервис>. Всё, что относится к сервису, живёт внутри этой папки и нигде больше.
Как выглядит на диске
/app
├── writefreely/
│ ├── compose.yml
│ ├── .env
│ └── data/
├── nextcloud/
│ ├── compose.yml
│ ├── .env
│ └── data/
└── gitea/
├── compose.yml
├── .env
└── data/
Никаких именованных докер-томов, разбросанных в недрах /var/lib/docker, которые потом фиг найдёшь. Все данные я монтирую относительными путями внутрь папки сервиса — в подкаталог data/. Хочу посмотреть, что сервис нажил — иду в его папку, и там всё.
Из чего состоит стек
Типичный compose.yml у меня выглядит так:
services:
writefreely:
image: writeas/writefreely:latest
container_name: writefreely
restart: unless-stopped
env_file: .env
ports:
- "127.0.0.1:8080:8080"
volumes:
- ./data:/data
Несколько вещей, которые я делаю всегда и осознанно:
restart: unless-stoppedна каждом сервисе. После ребута коробки (или скачка питания) всё поднимается само. Но если я сам остановил контейнер руками — он остаётся остановленным и не воскресает у меня за спиной.alwaysведёт себя навязчивее, поэтому именноunless-stopped.- Все секреты — в
.env, не вcompose.yml. Пароли БД, токены, ключи. Самcompose.ymlтогда не стыдно показать кому угодно. - Порты вешаю на
127.0.0.1, наружу публикую отдельно через единую точку входа. Внутрь докера снаружи напрямую никто не лезет.
.env рядом, и в нём примерно вот это:
# /app/writefreely/.env
DOMAIN=blog.local
DATABASE_PASSWORD=...
Восстанавливаемость — ради неё всё и затевалось
Главная мысль всей этой раскладки: коробку я должен уметь собрать заново с нуля за вечер. Не «вспомнить, как было», а буквально развернуть.
Для этого мне нужны две вещи: сами compose.yml с .env (это рецепт) и data/ (это содержимое). Рецепты весят копейки и меняются редко, поэтому я их версионирую и складываю отдельно. Грубо — собрать все compose-файлы в одно место:
mkdir -p ~/server-config/app
rsync -av --include='*/' \
--include='compose.yml' --include='.env' \
--exclude='*' \
/app/ ~/server-config/app/
Эту папку ~/server-config я кладу в git и в бэкап. Получается, что вся конфигурация сервера — это десяток текстовых файлов, которые читаются глазами и восстанавливаются за минуты.
Данные из data/ — отдельная история, они большие и про них будет отдельный разговор про бэкапы. Но рецепт от содержимого я держу раздельно сознательно: потерять конфиг и потерять данные — это две очень разные катастрофы, и готовиться к ним надо по-разному.
Скучно? Скучно. Но именно скучные системы переживают переезд на новое железо без седых волос.