Много проектов. Одна структура. Никакого хаоса.
Оглавление
Когда проектов становится больше десяти, начинаются вопросы:
- Где лежит конфиг для этого сервиса?
- Какую переменную окружения нужно задать при деплое?
- Почему один контейнер «ест» всю память, а другой молчит при падении?
- Как быстро развернуть всё это на новой машине?
Ответ: единая структура, единые правила, единый подход.
🗂 Единая структура репозиториев#
Каждый активный проект следует одному шаблону:
project-name/
├── compose.yaml # Единый формат: сервисы, сети, volumes, лимиты
├── stack.env # Переменные окружения (в .gitignore)
├── README.md # Кратко: что делает, как запустить, какие порты
├── .github/
│ └── workflows/ # CI: сборка, тесты, деплой (если нужно)
└── (опционально)
├── Dockerfile # Если нужен кастомный образ
├── config/ # Конфиги приложений (не секреты)
└── scripts/ # Утилиты: бэкап, миграция, проверкаПочему это работает:
- ✅ Новый проект - копируешь шаблон, меняешь имя, настраиваешь
- ✅ Знакомый формат - не нужно гадать, где искать конфиг
- ✅ Документация рядом с кодом - всегда актуальна
⚙️ Единые правила для всех контейнеров#
Неважно, запускаешь ты Minecraft-сервер или Grafana - следующие параметры есть везде:
Ресурсные лимиты (предотвращение «голодания»)#
services:
my-service:
cpus: "1.0"
mem_limit: 1g
pids_limit: 100Безопасность по умолчанию#
security_opt:
- no-new-privileges:true
- seccomp:unconfined # только если действительно нужноЛоги с ротацией#
logging:
driver: json-file
options:
max-file: "3"
max-size: 10mHealth check для наблюдаемости#
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40sКорректная остановка#
stop_signal: SIGTERM
stop_grace_period: 30sРезультат: любой сервис ведёт себя предсказуемо. Упал - перезапустился. Уперся в лимит - видно сразу. Логи не заполняют диск.
🔐 Конфигурация через .env, а не хардкод#
# compose.yaml
services:
app:
env_file: [stack.env]
image: ${REGISTRY:-ghcr.io}/potatoenergy/app:${TAG:-latest}# stack.env (в .gitignore)
REGISTRY=ghcr.io/potatoenergy
TAG=2026.04.15
APP_SECRET=${APP_SECRET} # подставляется из окружения хостаПреимущества:
- ✅ Секреты не в репозитории
- ✅ Один файл - легко ротировать, легко аудировать
- ✅ Деплой на dev/stage/prod - разные
.env, одинcompose.yaml
🌐 Сети: явные контракты вместо default#
networks:
prometheus:
external: true
name: prometheus
traefik:
external: true
name: traefik
services:
grafana:
networks: [prometheus, traefik] # видит метрики + веб-трафик
loki:
networks: [prometheus] # только метрики, нет публичного доступаЗачем:
- Сервисы находят друг друга по имени (
http://loki:3100) - Публичный доступ - только через те сервисы, что явно подключены к
traefik - Легко добавить новый сервис в мониторинг: подключил к
prometheus- и он уже виден
🔄 Деплой: одна команда для всего#
# Клонировать стек
git clone https://github.com/potatoenergy/grafana-stack.git
cd grafana-stack
# Заполнить переменные
cp stack.env.example stack.env
# (отредактировать stack.env)
# Запустить
docker compose up -d
# Проверить статус
docker compose psДля обновления:
git pull
docker compose pull
docker compose up -dДля отката:
git checkout <previous-commit>
docker compose up -d📊 Что это даёт на практике#
| Метрика | Было (разрозненные конфиги) | Стало (единый подход) |
|---|---|---|
| Время деплоя нового сервиса | 30–60 мин (поиск конфигов, настройка) | 5–10 мин (клон + .env + up -d) |
| Диагностика проблемы | Лезть в 5+ репозиториев, гадать | Один compose.yaml, стандартные логи |
| Масштабирование | Ручная правка каждого сервиса | Шаблон + переменные |
| Онбординг нового участника | Неделя на изучение «как у нас» | Один README, один подход |
🎯 Философия в трёх принципах#
Воспроизводимость
Конфиг, который работает у меня, должен работать и у тебя. Никаких «а у меня работает».Прозрачность
Код открыт, документация рядом, переменные вынесены. Нет магии - есть понятные шаги.Прагматизм
Инструменты выбираются по задаче, а не по хайпу. Если работает - не трогай. Если не работает - чини или меняй.
🗂 Архив - это не провал, а ступенька#
В папке .old/ лежат проекты, которые:
- ✅ Были полезным экспериментом, но устарели архитектурно
- ✅ Заменены более совершенным решением
- ✅ Были форками для тестирования гипотез
Архивация ≠ удаление. Код остаётся доступным, история коммитов сохраняется, лицензия действует. Разница лишь в статусе: «не поддерживается активно».
Не бойся архивировать. Бойся останавливаться.
🔮 Что впереди#
- Унификация шаблонов: один
compose.template.yamlдля всех новых проектов - Авто-генерация README: из
compose.yaml+.env.example - CI-аудит: автоматическая проверка конфигов на безопасность и полноту
- «Картофельный индекс»: агрегатор самохостинг-решений с проверкой на воспроизводимость
🗂 Чеклист для нового проекта#
- Создал репозиторий по шаблону:
compose.yaml,stack.env,README.md - Задал ресурсные лимиты:
cpus,mem_limit,pids_limit - Добавил
security_opt: [no-new-privileges:true] - Настроил лог-ротацию:
max-file,max-size - Прописал health check с адекватными интервалами
- Вынес секреты в
.env, добавил его в.gitignore - Подключил сервис к нужным внешним сетям (
prometheus,traefik) - Написал README: что делает, как запустить, какие порты
Ссылки#
Здесь пока нет статей.