Обзор методологии “The 12-Factor Applications”
Введение
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 я нашёл отличную инфографику, которая объясняет все эти принципы: