Перейти к содержанию
Innovialabs

Методология Twelve-Factor App: принципы удобного деплоя приложений

Основы4 мин

Коротко

Twelve-Factor App — это двенадцать правил, которые делают приложение одинаково запускаемым в любом окружении: код в одном репозитории, конфиг в переменных окружения, процессы без состояния, логи в stdout и минимум различий между dev и prod.

Коротко: что такое Twelve-Factor App

Twelve-Factor App — набор из двенадцати принципов для веб-приложений и сервисов, которые развёртываются в облаке, в контейнерах или на обычных серверах. Цель одна: приложение должно собираться, настраиваться и запускаться одинаково везде — на ноутбуке разработчика, на staging и в production — и легко масштабироваться добавлением процессов.

Методология не привязана к языку или фреймворку. Её идеи лежат в основе Docker, Kubernetes и большинства PaaS-платформ.

Все двенадцать факторов

№ФакторСуть
1CodebaseОдна кодовая база в системе контроля версий, много деплоев
2DependenciesЗависимости объявлены явно и изолированы
3ConfigКонфигурация хранится в переменных окружения
4Backing servicesБД, кэш, очереди — подключаемые ресурсы по URL
5Build, release, runСборка, релиз и запуск строго разделены
6ProcessesПроцессы без состояния, ничего не храним локально
7Port bindingСервис сам слушает порт и экспортирует HTTP
8ConcurrencyМасштабирование запуском дополнительных процессов
9DisposabilityБыстрый старт и корректное завершение
10Dev/prod parityОкружения максимально похожи
11LogsЛоги — поток событий в stdout
12Admin processesРазовые задачи запускаются тем же кодом и окружением

Конфиг через переменные окружения

Всё, что меняется между окружениями — адрес БД, ключи API, флаги — живёт вне кода. Проверка простая: можно ли открыть репозиторий публично, не раскрыв ни одного секрета?

import os

DATABASE_URL = os.environ["DATABASE_URL"]
REDIS_URL = os.environ.get("REDIS_URL", "redis://localhost:6379/0")
DEBUG = os.environ.get("DEBUG", "false") == "true"

Локально значения можно держать в файле .env (добавленном в .gitignore), а на сервере — передавать через оркестратор или менеджер секретов. Избегайте файлов config.production.py с захардкоженными значениями: это и утечка секретов, и рост числа «особых» окружений.

Процессы без состояния

Процесс приложения не должен хранить ничего важного между запросами: сессии, загруженные файлы, кэш выносятся в Redis, базу данных или объектное хранилище.

  • Пользователь загрузил аватар — файл уходит в S3-совместимое хранилище, а не в папку uploads/ на диске.
  • Сессия хранится в Redis или в подписанной cookie, а не в памяти процесса.

Тогда любой экземпляр можно перезапустить или удалить, а нагрузку — распределить между несколькими копиями без «липких» сессий.

Логи как поток

Приложение не управляет файлами логов. Оно пишет события в stdout/stderr, а сбором, ротацией и хранением занимается среда: Docker, systemd, агент вроде Fluent Bit или Vector, система вроде Loki или ELK.

console.log(JSON.stringify({ level: "info", msg: "order created", orderId }));

Структурированный JSON удобно фильтровать и искать. Ротация файлов внутри контейнера, наоборот, приводит к потерянным логам и забитому диску.

Паритет dev и prod

Двенадцатифакторное приложение уменьшает три разрыва: по времени (код быстро доходит до production), по людям (разработчики участвуют в деплое) и по инструментам.

Типичная ошибка — SQLite локально и PostgreSQL на сервере. Мелкие различия в SQL всплывут только в production. Решение — поднимать локально те же сервисы тех же основных версий через Docker Compose.

Остальные факторы на практике

  • Зависимости: lock-файлы (package-lock.json, poetry.lock) в репозитории, никаких «глобально установленных» утилит.
  • Build, release, run: образ собирается один раз, получает тег, и именно он переходит из staging в production. Править код на сервере нельзя.
  • Port binding: приложение само поднимает HTTP-сервер на порту из PORT, а nginx или балансировщик стоят перед ним.
  • Disposability: обрабатывайте SIGTERM — дождитесь текущих запросов, закройте соединения и выходите.
  • Admin processes: миграции и скрипты запускаются как отдельный процесс из того же релиза, например docker compose run app python manage.py migrate.

Частые ошибки

  • Секреты в репозитории или в Docker-образе.
  • Файлы пользователей на локальном диске контейнера.
  • Разные версии БД в dev и prod.
  • Ручные правки на production-сервере в обход релиза.

Частые вопросы

Нет. Начните с самых полезных: конфиг в окружении, процессы без состояния, логи в stdout и одинаковые сервисы в dev и prod. Остальные факторы обычно подтягиваются, когда вы переходите на контейнеры и CI/CD.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.