30
12 факторов архитектуры приложения Interlabs 01.07.2015 1 / 30

12 факторов архитектуры приложения

  • View
    116

  • Download
    6

Embed Size (px)

Citation preview

12 факторов архитектурыприложения

Interlabs

01.07.2015

1 / 30

О чем речь

• как упорядочить структуру приложения• как свести воедино веб-часть и фоновые сценарии• как сделать приложение более надежным• как сделать приложение масштабируемым• какие рекомендации могут помочь вне зависимости отсредства реализации

http://12factor.net

2 / 30

12 факторов1 Единая кодовая база2 Явные локализованные зависимости3 Конфигурация в переменных окружения4 Сервисы как подключаемые ресурсы5 Отдельные стадии сборки и запуска6 Приложение = набор процессов7 Экспорт сервисов через привязку портов8 Масштабирование через процессы9 Одноразовость процесса10 Идентичность сред разработки и выполнения11 Журнал как поток сообщений12 Административные процессы

3 / 30

1. Единая кодовая база

• код приложения хранится в системе контроля версий• одно приложение – один репозиторий• общий код для нескольких приложений – зависимость, всвою очередь соответствующая 12 факторам

• репозиторий и код едины для различных вариантовразвертывания приложения

• различные варианты развертывания могут соответствоватьразличным веткам репозитория

На практике: ничего нового, используем контроль версий.

4 / 30

2. Явные локализованныезависимости

• зависимости должны быть так или иначе явнопродекларированы

• зависимости от системных пакетов должны бытьминимизированы, все что может быть установленолокально для приложения, должно быть установленолокально

• в идеале все внешние программы, вызываемыеприложением, должны поставляться с приложением(например, используя контейнер)

5 / 30

Зависимости: практика

• мы практически не используем внешние PHP библиотеки• полная контейнеризация приложений когда-нибудь будетвозможна, но не сейчас

• используем стандартный формат модуля нашейплатформы, явно декларируем зависимости в Makefile

• используем внешние программы установленные в рамкахсистемы/контейнера

• используем симлинки как основной инструментсвязывания с зависимостями

6 / 30

3. Конфигурация в переменныхокружения

• информация, специфичная для развертывания, никогда нехранится в коде (иначе противоречие с первым фактором)

• различные варианты конфигурации в отдельных файлахусложняет либо развертывание, либо код

• все параметры конфигурации, относящиеся к средезапуска – из переменных окружения

• все это не относится к конфигурации, описывающейструктуру приложения и не меняющуюся в зависимости отсреды запуска

7 / 30

Конфигурация: практикаРабота с переменными окружения встроена в DI-контейнер:

1 $container = new Container();2 $container->with(App::DATABASE, function ($c) {3 return new Connection(4 $c[’DATABASE_DSN’],5 $c[’DATABASE_USER’],6 $c[’DATABASE_PASS’]7 );8 });

Объект соединения создается с параметрами подключения,определенными в окружении.

1 export DATABASE_DSN=’mysql:host=localhost;dbname=test’2 export DATABASE_USER=test3 export DATABASE_PASSW=test

8 / 30

Конфигурация: практика

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

• конфигурация гарантированно не попадает в репозиторий,не нужно хранить примеры файлов конфигурации

• определение переменных: уровень системы, уровеньпрограммы-супервизора, конфигурация веб-сервера и т.д.

9 / 30

4. Сервисы как подключаемыересурсы

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

• работа cо сторонним сервисом не зависит от конкретногорасположения сервиса

• код не должен содержать специфики, связанной срасположением (например локальным) того или иногосервиса

• в разных средах выполнения различные ресурсы,определяемые конфигурацией в соответствии с пунктом 3

10 / 30

Ресурсы: практика

MySQL удовлетворяет принципуSMTP есть проблемы (зависимость от локального

sendmail)Sphinx файлы индексов нужно выносить за пределы

файловой системы приложения

Главное – при использовании любого стороннего сервисапомнить, что он может быть развернут как локально, так иудаленно, не рассчитывать на локальную установку

11 / 30

5. Отдельные стадии сборки изапуска

• сборка формирует из репозитория запускаемые артефакты• публикация устанавливает результат сборки в окружение сопределенной конфигураций в соответствии с пунктом 3

• код запускается в новом окружении• запускаемый код не содержит элементов, необходимыхтолько на этапе сборки и проходит все необходимые фазыоптимизации

12 / 30

Сборка: практика

• разделяем фазы сборки и запуска на уровне файловойструктуры проекта

• автоматически формируем структуру запускаемого образана этапе разработки при помощи симлинков

• на этапе публикации формируем полный образ,содержащий все необходимое путем разрешения всехсимлинков

• образы различных версий могут быть опубликованынезависимо с последующим атомарным переключениемрелиза

13 / 30

6. Приложение = наборпроцессов

• приложение выполняется как набор процессов• чем проще процессы, тем лучше• все состояние системы – в стороннем сервисе хранения,процессы ничего не сохраняют

• веб-приложение – процесс(ы) выполняемыевеб-сервером, но не только

• процессы, выполняющие фоновую обработку• процессы, запускаемые периодически и т.д.

Самый важный принцип, должен быть номер один.

14 / 30

Процессы: практика

Supervisor: A Process Control System

http://supervisord.org

15 / 30

supervisord• на первоначальном этапе используем для управленияфоновыми процессами

• в дальнейшем и для процессов веб-части тоже• supervisord.conf – описание всех процессов

1 [supervisord]2 logfile=/dev/null

3 [program:api-worker]4 command=api-worker5 autostart=true6 autorestart=true7 stdout_logfile=/dev/df/18 stdout_logfile_maxbytes=09 process_name=%(program_name)s-%(process_num)01d

16 / 30

supervisord: полезные свойства

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

• поддерживает [include] – итоговую конфигурациюпроцесса можно строить из описаний процессовотдельных функциональных модулей

• можно доопределить свойства процесса, определяемогово включаемом файле

17 / 30

supervisord: fcgiВстроенная поддержка fcgi:

1 [fcgi-program:php-cgi]2 socket=tcp://127.0.0.1:500003 command=/usr/bin/php-cgi -b 127.0.0.1:500004 numprocs=15 priority=9996 process_name=%(program_name)s_%(process_num)02d7 autorestart=true8 autostart=true9 startsecs=1

10 startretries=311 stopsignal=QUIT12 stopwaitsecs=10

Порождение процессов php-cgi – возможны варианты.

18 / 30

7. экспорт сервисов черезпривязку портов

• приложение самодостаточно и предоставляет сервис черезопределенный порт

• веб-приложение предоставляет HTTP-сервис черезнепривилегированный порт

• порт может быть использован при разработке безнеобходимости использовать веб-сервер спривилегированным портом

• mod_http is so last century• в production роутинг запросов через HTTP-frontend• HTTP - только один из вариантов

19 / 30

Порты: PHP• встроенный сервер годится для отладки, противоречит п.10• supervisord + php-cgi + внешний nginx

1 map $http_host $id {2 hostnames;3 domain1 50000; domain2 50001;4 }5 server {6 listen 80 default_server;7 root /srv/www/$id;8 location / { try_files $uri $uri/ /index.php; }9 location ~ \.php$ {

10 fastcgi_pass 127.0.0.1:$id;11 fastcgi_index index.php;12 fastcgi_param SCRIPT_FILENAME \13 $document_root$fastcgi_script_name;14 include fastcgi_params;15 }

20 / 30

9. Масштабирование черезпроцессы

• масштабирование приложения с помощью моделипроцессов

• в случае PHP - вообще единственно возможный вариант• приложение = набор процессов различных типов• количество однотипных процессов можно менять взависимости от нагрузки

• процессы не пытаются реализовать полноценный демон,делегирую все необходимое системе запуска (supervisor)

• процессы должны допускать параллельную работу,перезапуск в случае сбоя и т.д.

21 / 30

Масштабирование: практикаВ пределах одного сервера:

• supervisord:

1 [program:api-worker]2 command=api-worker3 numprocs=34 process_name=%(program_name)s_%(process_num)02d

• GNU parallel (http://gnu.org/software/parallel/)

1 inotifywait -q -m -r -e MOVED_TO -e CLOSE_WRITE \2 --format %w%f my_dir \3 | parallel -u worker-script

Если скрипт простой и использует стандартные средства(окружение, обработчики сигналов и т.д.), его легчераспараллелить.

22 / 30

10. Одноразовость процесса

• процессы быстро запускаются и быстро завершаются• процессы корректно обрабатывают сигналы, в частностиSIGTERM

• процессы должны корректно отрабатывать ситуациювнезапной остановки

• процесс может быть быстро перезапущен супервизором вслучае возникновения проблемы

23 / 30

Одноразовость: практика• обрабатываем сигналы

1 declare(ticks=1); # важно!2 include(__DIR__ . ’/../lib/autoload.php’);3 use imp\std\os\Process;4 Process::main(5 $app = new Application(),6 [7 SIGINT => [ $app, ’stop’ ],8 SIGTERM => [ $app, ’stop’ ]9 ]

10 );

• стараемся свести функциональность к обработкеминимально возможной единицы данных

• допускаем возможность перезапуска процесса междуотдельными задачами обработки

• допускаем параллельный запуск однотипных процессов24 / 30

11. Идентичность средразработки и выполнения

• среды выполнения и разработки максимально идентичны• разработку и развертывание выполняют одни и те же люди• частая публикация изменений

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

25 / 30

11. Журнал как поток сообщений

• все процессы пишут лог в stdout• журналирование в едином легко обрабатываемомформате, например JSON

• каждое сообщение позволяет идентифицировать процесс,который его сформировал

• stdout всей группы процессов формирует единый поток• поток может быть при необходимости перенаправлен,обработан внешними инструментами и т.д.

26 / 30

Журналирование: практикаЖурналирование в JSON поддерживается фреймворком,добавляем в сообщение идентификаторы процесса и типапроцесса:

1 use imp\cli\Application as CLIApplication;2 use imp\log\JSONLogger;3 use imp\std\IO;4 use imp\std\os\Process;

5 $application = new CLIApplication();6 $application->onLog(new JSONLogger(7 IO::stdout(),8 [ ’sid’ => $this->sid, ’pid’ => Process::PID() ]9 ));

10 $application->log(Log::INFO, ’application started’);

27 / 30

12. Административные процессы

• типовые административные задачи реализуются какотдельные процессы

• административные процессы запускаются в той же средевыполнения, что и длительно выполняющиеся процессыприложения

• административные задачи входят в состав приложения ииспользуют общий код с основными процессами

28 / 30

Итого• приложение = веб-часть + фоновые процессы• максимально простая логика отдельных процессов• делегирование управления процессами супервизору• процессы не должны быть привязаны к состоянию,состояние – во внешнем хранилище

• конфигурация – свойство окружения, а не состоянияпроцесса

• веб-приложение – сервис, экспортирующий http-порт• журнал = поток сообщений от всех процессов приложения

http://12factor.net

29 / 30

Что читать

• http://12factor.net: методология• http://habrahabr.ru/post/258739/: русский перевод• http://supervisord.org: документация по supervisor• http://www.gnu.opg/software/parallel/man.html:документация по GNU parallel с примерами

30 / 30