Перейти к основному содержимому

Много проектов. Одна структура. Никакого хаоса.

Оглавление

Когда проектов становится больше десяти, начинаются вопросы:

  • Где лежит конфиг для этого сервиса?
  • Какую переменную окружения нужно задать при деплое?
  • Почему один контейнер «ест» всю память, а другой молчит при падении?
  • Как быстро развернуть всё это на новой машине?

Ответ: единая структура, единые правила, единый подход.


🗂 Единая структура репозиториев
#

Каждый активный проект следует одному шаблону:

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: 10m

Health 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, один подход

🎯 Философия в трёх принципах
#

  1. Воспроизводимость
    Конфиг, который работает у меня, должен работать и у тебя. Никаких «а у меня работает».

  2. Прозрачность
    Код открыт, документация рядом, переменные вынесены. Нет магии - есть понятные шаги.

  3. Прагматизм
    Инструменты выбираются по задаче, а не по хайпу. Если работает - не трогай. Если не работает - чини или меняй.


🗂 Архив - это не провал, а ступенька
#

В папке .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: что делает, как запустить, какие порты

Ссылки
#

Здесь пока нет статей.