Введение

The twelve-factor apps - это набор принципов по построению веб приложений и сервисов, выпущенный компанией Heroku в 2011 году. Эти принципы позволяют создать приложение, которое будет легко масштабировать, удобно развёртывать, организовывть разные среды и т.д.

В оригинале каждый принцип занимает одну-две страницы текста. В этом обзоре я постараюсь кратко сформулировать основные моменты из всех двенадцати принципов.

У многих терминов, использованных в оригинале, нет устоявшихся переводов на русский язык, поэтому вместе с переводами терминов я буду приводить их оригинальные названия.

Принципы

№1. Codebase

One codebase tracked in revision control, many deploys

Кодовая база приложения должна находиться в репозитории (repo) системы контроля версий. Разные приложения не должны использовать одну и ту же кодовую базу. Общий код должен быть выделен в библиотеки и подключаться как зависимость. У одного приложения может быть много развёртываний (deploys). Развёртывание - это работающий экземпляр приложения. Все развёртывания должны иметь общую кодовую базу, но могут отличаться конкретными версиями кода.

№2. Dependencies

Explicitly declare and isolate dependencies

Приложение не должно полагаться на наличие необходимых ему модулей в системе. Все необходимые библиотеки должны быть явно описаны в виде манифеста (dependency declaration manifest). Необходимо использовать инструмент для изоляции зависимостей (dependency isolation) для того, чтобы избежать появления неявных зависимостей из окружающей системы. Пример: Gemfile - манифест, описывающий зависимости, bundler - инструмент для их изоляции. Бонусом явного описания зависимостей является упрощение развёртывания приложения для разработчика.

№3. Config

Store config in the environment

Конфигурация (config) - это то, что может меняться между разными развёртываниями (пароли для подключения к базам данных и сервисам, хосты и т.д.). Хранение конфигурации в коде - нарушение принципов отделения кода от конфигурации. Приложения хранят конфигурацию в переменных окружения (environment variables, env vars), которые можно легко менять в разных развёртыванияних без изменения кода.

№4. Backing services

Treat backing services as attached resources

Вспомогательный сервис (backing service) - это любой сервис, который используется приложением в процессе его работы (например, MySQL, RabbitMQ, Memcached и т.д.). Код приложения не делает различий между локальными и удалёнными сервисами: оба воспринимаются как ресурс (resource), параметры доступа к которому хранятся в конфигурации. Каждый сервис трактуется как ресурс. Например, два инстанса шардированной базы данных будут двумя отдельными ресурсами.

№5. Build, release, run

Strictly separate build and run stages

Кодовая база трансформируется в развёртывание с помощью трёх стадий:

  • стадию сборки (build stage), которая превращает код конкретной версии в исполняемый модуль, известный как сборка(build);
  • стадию релиза (release stage), которая объединяет сборку с текущей конфигурацией развёртывания;
  • стадию запуска (run stage, альтернативное название - runtime), на которой приложение запускается как один или несколько процессов.

Приложения строго разделяют эти стадии. Все сборки являются неизменяемыми и у каждой должен быть уникальный идентификатор (обычно время или возрастающее число).

№6. Processes

Execute the app as one or more stateless processes

Приложение выполняется в виде одного или нескольких процессов. Процессы приложения должны быть без состояния (stateless) и не должны иметь разделяемых данных (share-nothing). Все данные, которые нужно сохранить между запусками, должны храниться в стороннем сервисе (обычно, базе данных).

№7. Port binding

Export services via port binding

Приложение должно не требовать наличия какого-то сервера в среде исполнения (например, Apache для выполнения PHP как его модуля), а быть быть полностью self-contained. Для этого приложение занимает порт (port binding) и обслуживает запросы, приходящие на этот порт. Обычно это делается включением в список зависимостей библиотеки сервера (например, Tornado для Python).

Помните также, что приложение в целом может быть вспомогательным сервисом для других приложений.

№8. Concurrency

Scale out via the process model

В приложении у каждого типа работы должен быть свой тип процесса (process type). Например, HTTP-запросы могут обслуживаться web процессами, а длительные фоновые задачи выполняться рабочими процессами (worker processes).

Эта модель будет очень полезна, когда придёт время масштабирования (scale out). Отсутствие разделяемых данных (share-nothing) и горизонтальное разделение процессов (horizontally partitionable nature) у приложений означает, что добавление - это простая операция.

№9. Disposability

Maximize robustness with fast startup and graceful shutdown

Процессы приложения должны стремиться к минимальному времени запуска, т.к. это даёт большую гибкость в процессе релиза и масштабирования (scaling up).

Процессы приложения должны завершаться корректно при получении сигнала SIGTERM, а также быть устойчивыми к внезапной смерти (sudden death) при отказе аппаратного обеспечения и другим неожиданным проблемам.

№10. Dev/prod parity

Keep development, staging, and production as similar as possible

Приложения спроектированы для непрерывного развёртывания (continuous deployment) с уменьшением разницы между средами разработки (development) и работы приложения (production).

Пути уменьшения разницы между средами:

  • по времени: код, написанный разработчиком, будет развёрнут в среде работы приложения через несколько часов или даже минут;
  • в персонале: разработчик, написавший код, участвует в его развёртывании и наблюдает за его поведением в среде работы приложения;
  • в инструментах: делать окружение разработки и работы приложения максимально похожими (не стоит использовать в разработке легковесные вспомогательные сервисы вместо реальных и надеяться, что адаптеры корректно скроют все различия).

№11. Logs

Treat logs as event streams

Логи (logs) - это поток событий, упорядоченных по времени возникновения. Само приложение должно не заниматься правилами аггрегации логов, а писать их в stdout. При работе над приложением разработчик может сразу увидеть этот поток событий, а в среде исполнения логи могут быть аггрегированы и храниться долгое время для последующего анализа.

№12. Admin processes

Run admin/management tasks as one-off processes

При работе приложения часто бавают нужны одноразовые административные процессы (one-off admin processes): миграции базы данных, скрипты заполнения или исправления данных и т.д. Такие процессы должны работать в том же окружении, что и обычные долгоживущие процессы приложения, так же должны проходить стадию релиза, храниться в той же кодовой базе и использовать ту же конфигурацию.

Дополнительные материалы

На DZone я нашёл отличную инфографику, которая объясняет все эти принципы:

Инфографика the 12-Factor App от DZone