9 сентября 2015 в 05:10

Мысли о развёртывании веб-приложений на тестовом сервере recovery mode

Предисловие


Нижеследующий текст − результат практического опыта и самообразовательных порывов человека, не имеющего систематического образования ни в одной из областей, о которых он (то есть я) берётся рассуждать. Поэтому заумные рассуждения здесь будут перемежаться банальностями. Бейте меня за первые и игнорируйте вторые. Для кого-то и они могут стать откровением.

Я постараюсь описать идеальные варианты настройки тестового веб-севера, хотя понимаю, какой бардак на них обычно творится. Буду ориентироваться на ситуацию, когда деплоить приходится часто, то есть на сервере живёт проект в стадии активной разработки либо несколько проектов на разных стадиях. Проектами занимаются разные разработчики или команды, поэтому проекты нужно изолировать друг от друга. Но сервер внутренний, поэтому такая степень изоляции и автоматизации процессов администрирования, как на серверах под сдачу в аренду, не нужна.

Основной упор я буду делать на применение разных версий Python в качестве языка поддерживаемых веб-приложений. Хотя многие вещи наверняка будут справедливы и для других языков, например, Ruby или Perl.

Взаимодействие программы на Python с веб-сервером происходит по протоколу WSGI (читается как “wiskey”), описанном в PEP 3333 и предшествовавшем ему документе, PEP 333.

Однозвенная архитектура


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

Самым популярным веб-сервером, работающим в таких условиях, настоящим «швейцарским армейским ножом» современного веба является Apache HTTPd (это название часто сокращают до “Apache”). Буду иметь в виду текущие версии Apache: 2.2 и 2.4.

Для подключения WSGI-приложений к Apache напрямую, без участия серверов второго звена, используется плагин (в терминологии Apache − модуль) mod_wsgi. WSGI − это продвинутый, Python-специфичный вариант протокола CGI. Технически интерпретатор Python (точнее, CPython) встроен в mod_wsgi как внешняя библиотека: статически или динамически − это определяется при сборке модуля. Понятно, что одновременно в mod_python может быть встроена только одна версия CPython, как и Apache может адресовать только один модуль mod_wsgi в одной инсталляции. Отсюда вытекает ограничение: в однозвенной архитектуре с mod_wsgi мы не можем использовать на сервере одновременно Python2 и Python3.

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

prefork


Упрощённо выполнение CGI-запроса веб-сервером в UNIX-подобной среде выглядит так: получив запрос, сервер порождает процесс CGI-приложения, передавая ему параметры запроса в переменных среды, а тело − в стандартном потоке ввода. Самые первые оптимизации были вызваны тем, что порождение процесса происходит достаточно долго, что увеличивает время ответа на запрос. Чтобы не тратить время на запуск CGI-программы (в нашем случае − интерпретатора Python, встроенного в mod_wsgi), достаточно запустить его заранее и держать в подвешенном состоянии, не закрывая поток ввода, до того момента, пока на сервер не придёт запрос. Если сервер может выполнять одновременно несколько потоков кода, то имеет смысл запустить несколько обработчиков CGI и следить за тем, чтобы по мере их завершения запускались новые. Количество (размер пула) работающих и ждущих обработчиков можно конфигурировать. Обработав один запрос, процесс-обработчик может принять следующий. Если же запросов стало слишком мало, сервер убьёт лишние простаивающие процессы. Также сервер убивает процессы в профилактических целях, после того, как количество обработанных ими запросов достигнет определённого (также настраиваемого) предела.

Именно так и работают модули mod_prefork и mod_itk. Последний для нас наиболее интересен, так как способен обслуживать каждый виртуальный хост от имени пользователя, установленного в настройках этого виртуального хоста. Но об этом чуть позже.

worker


Второй способ «списать» расходы на порождение CGI-процесса, а заодно и снизить общую нагрузку на сервер − использовать один процесс для одновременного обслуживания нескольких запросов, иными словами − в многопоточном (multiphreaded) режиме.

Описанную стратегию реализуют модули mpm_worker и mpm_event. Последний также разгружает основную нить обработки запроса от некоторых бухгалтерских действий и пока имеет статус экспериментального.

Worker долгое время считался нестабильным и небезопасным − не сам по себе, а из-за того, что многие другие модули Apache не были приспособлены к многопоточному режиму работы или имели ошибки в его реализации. Но со временем эти проблемы были решены, и сейчас worker достаточно безопасно использовать в деле.

Надо сказать, что благодаря такой особенности CPython, как GIL, mod_wsgi достаточно легко был приспособлен к многопоточной работе, однако эта же особенность и не позволяет получить полную выгоду от многопоточности. GIL гарантирует, что в рамках одного процесса все потоки Python-кода будут исполняться последовательно квант за квантом, что создаёт только иллюзию одновременного исполнения. Но это не относится к C-коду, запускающему (или запускаемому из) Python, а разбор параметров запроса, отображение адреса на WSGI-обработчик и т. д. − всё это делается в C-коде. Соответственно, определённый выигрыш производительности от использования mpm_worker сайты, написанные на Python, всё же имеют.

Двухзвенная архитектура


Двухзвенная архитектура предполагает совместное использование веб-серверов разных уровней: внешнего (front end) и внутреннего (back end).

Внешний сервер служит посредником между пользователем и внутренними серверами при помощи “reverse proxy”-подобных протоколов и плагинов. Кроме того, внешний сервер занимается тем, для чего в 1990 году и создавался протокол HTTP: отдаёт клиенту статические файлы (assets). Хотя в последнее время эта функция в продакшн-среде часто передаётся сервисам CDN.

В качестве внешнего сервера можно использовать и Apache, но больше подходят на эту роль легковесные nginx или, в условиях ограниченных ресурсов, lighttpd. В данной роли от сервера требуются только быстрая реакция на запросы и скромность в отношении потребляемых ресурсов.

Внутренний сервер обычно зависит от архитектуры и языка реализации приложения. Python-приложения часто используют uWSGI, Green Unicorn или собственные серверы на основе многопоточных сетевых фреймворков типа twisted или Tornado. Многие веб-фреймворки также включают в себя веб-серверы, более или менее пригодные к работе в боевых условиях. Например, встроенный в Django веб-сервер предназначен исключительно для отладки, и должен заменяться в продакшне чем-то более серьёзным. Встроенный веб-сервер CherryPy, наоборот, оптимизирован под боевую нагрузку.

Особые требования к тестовому серверу (серверу CI)


Проблема прав доступа к файлам


Архитектура сети в POSIX-системах предполагает, что к портам 1-1024 имеет доступ только привилегированные пользователи. Порты 80 (HTTP) и 443 (HTTPS) попадают под это ограничение, поэтому родительский процесс веб-сервера (внешнего, если речь идёт о двухзвенной архитектуре), всегда имеет root-права.

Но CGI-скрипту нежелательно давать излишне широкие права по соображениям безопасности. Поэтому при работе со скриптами и данными веб-сервер всегда понижает привилегии до уровня обычного пользователя. Такой пользователь в Apache задаётся переменными среды APACHE_RUN_USER и APACHE_RUN_GROUP. В Debian и производных дистрибутивах этот пользователь имеет имя ‘www-data’. Для ещё большего усиления безопасности системы пользователь www-data, как и другие сервисные пользователи, не имеет оболочки и не может входить в систему.

Если веб-приложение устанавливается на сервер один раз и в дальнейшем не будет изменяться (за исключением разве что файлов, загружаемых пользователем), то логично будет зайти на сервер под пользователем с высокими привилегиями, развернуть файлы в соответствующий каталог (‘/var/www’ в Debian) и поменять владельца этих файлов:

# chown -R www-data:www-data /var/www/

Понятно, что для сервера разработки этот способ неудобен и вызывает опасения из-за постоянного использования привилегированного аккаунта. Вопрос об идеальных правах доступа к каталогам и данным веб-сервера с подробным ответом на него можно найти здесь. Статья попала в список каноничных вопросов на Serverfault, а это кое-что значит.

Суть статьи сводится к следующему:

  • если есть один ответственный за развёртывание сайта, нужно сделать его пользователем-владельцем всех файлов и каталогов сайта, а группой-владельцем сделать ‘www-data’, коды доступа же установить в 750 для каталогов (770 для каталогов, в которые пользователи сайта смогут загружать файлы) и 640 для файлов (660 для файлов, записываемых сервером). umask в стартап-скриптах нужно установить в 027,
  • если к сайту должны иметь доступ несколько человек, необходимо сделать владельцем пользователя root, группой-владельцем будет группа, в которой будут состоять все разработчики, а веб-сервер получит доступ к этим файлам за счёт младших трёх битов,
  • либо установить владельцем данных www-data, а группой-владельцем − группу разработчиков, но тогда нужно будет после создания каждого файла менять его владельца. Другим отвечающим упоминается возможность установить на каталоги биты suid и guid, чтобы владельцем созданного объекта становился не создатель, а владелец каталога, в котором был создан объект. Такой нестандартный способ использования этих битов был перенесён в Linux из *BSD, и я даже не уверен, сработает ли он, если файлы будут создаваться веб-сервером,
  • хотя в идеальном случае группе разработчиков стоит использовать средства автоматического развёртывания (Puppet, Chef) или CI-среду (Jenkins), чтобы свести ситуацию к одному ответственному пользователю, с той лишь разницей, что этим пользователем будет бот,
  • в итоге делается вывод, что полной изоляции виртуальных хостов при помощи только установки прав доступа добиться невозможно: уязвимость одного сайта даст возможность злоумышленнику как минимум прочесть код всех веб-приложений на данном сервере.


Что не упоминается в дискуссии, так это зубодробительная сложность всех этих манипуляций с chmod и chown, и, как следствие, высокая вероятность человеческих ошибок, приводящих к возникновению уязвимостей.

Всё становится намного проще и безопасней, если воспользоваться средствами разделения привилегий, имеющимися в Apache, либо перейти на двухзвенную архитектуру.

Средства разделения привилегий Apache


mpm_itk


Модуль mpm_itk даёт нам возможность самым простым и естественным способом определить, под каким пользователем будет выполняться наше веб-приложение:

<VirtualHost *:80>
    <IfModule mpm_itk_module>
        AssignUserID johnd developers
    </IfModule>

    …

</VirtualHost>


mod_suexec


Если по каким-то причинам мы вынуждены использовать другой мультипроцессорный модуль, то нам на помощь придёт mod_suexec. Его недостатком можно считать разве что относительную сложность в настройке, но это только в том случае, если мы хотим установить его из исходников. В дистрибутивах всё автоматизировано. А использование mod_suexec сводится опять-таки к одной директиве в конфигурации виртуального хоста:

<VirtualHost *:80>
    <IfModule mod_suexec.c>
        SuexecUserGroup johnd developers
    </IfModule>

    …

</VirtualHost>


Двухзвенная архитектура на тестовом сервере


Зачастую двухзвенная архитектура применяется для оптимизации производительности сервера. Разработчиков же больше волнует удобство развёртывания веб-приложения, нежели скорость его работы. Использование двухзвенной архитектуры на тестовом сервере также имеет определённые преимущества.

К преимуществам можно отнести то, что ответственность за установку и настройку внутреннего сервера несёт разработчик, он же определяет его оптимальные параметры и способ запуска. Внутренний сервер работает с привилегиями разработчика, и вопрос об оптимальной настройке прав доступа к программным файлам и каталогам проекта больше не стоит. Логи хоста также находятся в полном распоряжении разработчика. Веб-приложение может использовать ту версию Python, которая задана при помощи virtualenv, независимо от других проектов. Всё, о чём стоит позаботиться администратору сервера − каталоги со статикой и UNIX-сокет или непривилегированный порт, через который будут разговаривать внешний и внутренний серверы.

Разработчик также ответственен за автоматический запуск сервера и его бесперебойную работу. Если первое легко решается командой “crontab -e”, то второе можно обеспечить при помощи supervisor.

Перезагрузка веб-сервера


При использовании Apache как единственного сервера стоит добавить в настройки директиву “MaxRequestsPerChild 1”. С ней у разработчика не будет необходимости в перезагрузке сервера. На каждый запрос будет запускаться новая инстанция интерпретатора, следовательно, все изменения в коде сайта сразу же будут отражаться в его работе.

Разумеется, запрет на вторичное использование обработчика запроса снизит производительность сервера, но иначе придётся перезагружать сервер при каждом изменении кода. Есть два способа перезагрузить Apache: WSGI-специфический и общий.

  • WSGI-специфический способ заключается в обновлении даты редактирования WSGI-скрипта. Это можно сделать командой touch. Этот метод работает только тогда, когда mod_wsgi настроен на работу в daemon mode. (В общем случае крайне желательно настроить mod_wsgi на работу в daemon mode, хотя по умолчанию этот режим выключен.)
  • Общий метод − команда “apachectl restart” или аналогичная − требует прав root. Разумеется, можно обойтись соответствующей настройкой sudo, но использование sudo само по себе сложнее и опаснее, чем это может показаться. Поэтому такой способ использовать нежелательно.


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

Выводы


Двухзвенная архитектура веб-сервера является оптимальной для тестового сервера, используемого несколькими разработчиками или командами разработчиков внутри одного предприятия. Она освободит администратора сервера от большого количества рутины, разработчикам даст больше свободы в выборе средств реализации своих проектов, позволит более эффективно использовать аппаратные ресурсы. В то же время удобство разработки иногда может достигаться ценой понижения производительности. Но даже если организация настолько стеснена в средствах, что вынуждена располагать тестовые и боевые проекты на одном сервере, безопасность и достаточно хороший уровень производительности вполне достижимы и в этом случае.
Python-программист
Самое читаемое Разработка

Комментарии (27)

  • 0
    Я постараюсь описать идеальные варианты настройки тестового веб-севера, хотя понимаю, какой бардак на них обычно творится.

    Тестовый стенд не должен быть идеальным, он должен архитектурно соответствовать продакту. А маленький срачЪ в qa env — всегда соответствует большой «помойке» на проде.

    Да и вообще, вы всю дорогу тёплое и мягкое путаете.
    Если есть несколько независимых команд, то нужно поставть балансировщик, прописать upstream's, и для особо буйных команд сливать реквесты а) в контейнеры или б) на выделенные хосты, а уже там должны сидеть ваши *cgi и ci-agent, да билдить и управлять всей хурмой из vcs's.

    И отдельным пунктом хотелось бы отметить, что virtualenv дальше компа разработчика уходить не должен. Надо стороннюю библиотеку на сервере — соберите пакет и используйте локальную репу для деплоя, а с setuptools, pip и gcc обращаться надо осторожно, а лучше вообще не дружить.
    • 0
      Про контейнеры понимаю, но вот про

      virtualenv дальше компа разработчика уходить не должен

      как-то первый раз слышу. И вообще, часто встречаю virtualenv в продакшне, и мне он там вполне нравится.

      Не могли бы вы обосновать этот пункт поподробнее или ссылочкой кинуть?
      • +2
        А теперь добавьте туда ruby с его менеджером пакетов, nodejs, и еще пяток интерпретаторов, накатите по 10 — 15 пакетов в каждом, а потом попробуйте понять, что у вас лежит на сервере. И как его собрать заного. Все что запускается на сервере должно устанавливаться из пакетов, иначе вы получаете помойку, которая не может поддерживаться без вечно устаревающей документации и человека обладающего божественным знанием как его собрать.
        • 0
          Не понимаю, чем знание
          pip install -r requirements
          bower install
          

          божественнее, чем, скажем
          sudo dpkg --set-selections < ~/Package.list
          sudo dselect
          

          А если к этому добавить ещё и сборку в пакеты всех библиотек, которые есть в PyPy/Registry, но нет в репо целевой ОС (целевых ОС), управление частным репо, управление ключами… Оно действительно вам надо?
          • +1
            Разница в том, что с dpkg вы используете бинарные пакеты, собранные один раз в правильном окружении, а pip install вызывает gcc и билдит модули при каждом деплое.

            Кейсов тут несколько:
            1. Если вы захардкорили версии зависимостей во избежании dependencies hell, то никто вам не гарантирует, что данный конкретный пакет будет доступен всегда. В один их тех самых доджливых вечеров в четверг, pip вам скажет, что нужного пакета нет.
            2. Вы уверены, что все -devel файлы, которые используюся при компиляции — идентичны? И что unexpected behaviour на callback внешней функции в продакте не настигнет вас через 2 месяца после того самого дождливого четверга, а вы как на зло с семьей посредине средиземного моря на лайнере?
            3. Компилятора на сервере быть не должно.

            Да, поддерживать все это в приличном состоянии можно, стóит это неимоверных усилий, но моментально рассыпается под шаловливыми руками отдельно взятых личностей. Такие дела, и всё это уже сто раз проходили в связке perl + cpan.
            • 0
              pip install может не вызывать gcc каждый раз, а ставить пакеты из wheel-кэша. Wheels можно собирать и на другом сервере.

              Хотя я ещё не настолько параноик, чтобы удалять gcc с сервера. Я вообще не помню ни одной массово эксплуатируемой уязвимости с участием gcc.
            • 0
              А dpkg не скажет? Pip хотя бы может тянуть из репа, который может быть вашим, и расположен в том месте, за которое можете сами ручаться.

              Компилятора на сервере быть не должно из-за того, что злоумышленник придет и скомпилирует что-нибудь? Можно и статические библиотеки принести.
          • 0
            А потом вам понадобится срочно еще какая-то либа на racket, для какого-то скрипта, немножко php что в пакетах не было, и еще что-нибудь, главное чтоб у него свой менеджер пакетов был, а может и инсталлятор в виде скрипта, в хомяк аль в opt положит мину.
            В документацию записать забудут сей факт, человечек который это сделал станет не доступен.

            И однажды вам понадобится развернуть сервис на новом железе (и очень срочно).
            Развернете вы (debian | red-hat | sled), накатите пакеты менеджером, потом pip, потом bower, в общем все по доке.
            Вот только не получится каменный цветочек. А начальство шумит, пальчиком грозится. И в думу вы погрузитесь, печальную. ))

            И не вспоминайте про chef, puppet, salt, это по сути один из способов документирования ваших сервисов.
            • 0
              Решение этой проблемы, если я вас правильно понял − создать собственный deb-репозиторий и добиться того, чтобы проект можно было развернуть только при помощи этого репозитория, без использования иных пакетных менеджеров, кроме apt?

              Спасибо, но я с тем же успехом упакую проект целиком, со всеми незадокументированными вовремя какашками, в wheel. Он гарантированно развернётся. Правда, обновления безопасности для компонентов я не получу. Но это в любом случае требует разработчика. Просто wheel-репозиторий требует Python-разработчика, а переупаковка всего проекта или библиотек, которых нет в системном репо или которые в нём устарели, в deb-пакеты требует Python-разработчика, умеющего dpkg-buildpackage и прочую чорную магию. Либо выделить отдельную человекоединицу для упаковки пакетов, а этого далеко не всякий бюджет выдержит. Проще обойтись толковым пайтонистом кмк.

              Правда, тут ниже мне уже советуют использовать Docker. Я так понимаю, что установив проект со всеми его сюрпризами в контейнер LXC, можно при помощи Docker сохранить снимок этого контейнера и деплоить проект из этого снимка. Я не знаю подводных камней этого процесса, но выглядит всяко проще, чем создавать deb-пакет на каждый чих.
              • 0
                Не люблю магию, github.com/jordansissel/fpm, нарисуйте скрипты чтоб билдить все что вам нужно, привяжите их к бил серверу, артефакты автоматом на репозиторий. Добавьте puppet, salt, chef, bash, docker и тд для развертывания инфраструктуры целиком.

                Как то так. Это в общем одна из задач системного администратора обеспечивающего поддержку разработки.

                Менеджеров пакетов под разные языки куча, бывает что сервис работает, написан например на erlang, а разработчика нет. rebar, pecl, pip, lein, gem, cabal, stack и тд. С тем же pip я последний раз сталкивался 2 года назад, с rebar 4 года назад, lein вчера, gem пару месяцев назад, cabal — неделю назад. У каждого языка своя инфраструктура, вы их все тащите на прод? В общем это дело хозяйское, но мой опыт в администрировании склоняет меня в сторону пакетирования. Это потом сильно проще, и обеспечивает заменяемость людей.
                • 0
                  github.com/jordansissel/fpm

                  Спасибо за наводку, не знал про эту штуку. Надо будет иметь в виду. Наверное, я изменю своё мнение насчёт сборки OS-targeted пакетов.
    • 0
      Про virtualenv — согласен. Но на дворе 2015, девелоперы привыкли, а у OPs появился докер. Это снимает проблему «чёрт знает что у нас требует хрен знает чего».
  • +2
    В качестве внешнего сервера можно использовать и Apache, но больше подходят на эту роль легковесные nginx

    Тогда зачем вы рассматриваете монстра apache со стариком mod_wsgi?
    • 0
      Ну, я его особо и не рассматриваю в роли фронтенда. Хотя, с другой стороны, чем не фронтенд? Монструозность Apache сильно преувеличена, зато в его конфигурацию намного проще въехать, чем в конфиг того же nginx.
      • 0
        зато в его конфигурацию намного проще въехать, чем в конфиг того же nginx.

        Не согласен, вы просто с ним не разобрались, что может быть проще?
        пример конфига
        upstream myappbackend {
                    server 127.0.0.1:14001  max_fails=3     fail_timeout=1s;
                    server 127.0.0.1:14002  max_fails=3     fail_timeout=1s;
                    server 127.0.0.1:14003  max_fails=3     fail_timeout=1s;
                    server 127.0.0.1:14004  max_fails=3     fail_timeout=1s;
            }
        
            server {
                    listen                                          4.5.6.7:80;
                    server_name                                     example.com;
        
                    access_log      /var/log/nginx/myapp.log  main;
        
        
                    location / {
                            proxy_set_header                Host            $host;
                            proxy_set_header                X-Real-Ip       $remote_addr;
                            proxy_pass                      http://myappbackend/;
                    }
            }      
        

        А насчет монструозности, можете посмотреть например это.
        Примеров в сети хватает для любого python-приложения, у вас оно кстати на каком фреймворке?
        • 0
          что может быть проще?

          Вы правы, наверное. Дело привычки.

          Насчёт монструозности − там по ссылке автор приводит зачем-то конфигурацию железа, но вообще ничего не пишет про конфигурацию Apache и nginx. Скорее всего, он действительно сравнивает nginx с mpm-prefork, на что ему и попеняли в комментариях.

          Вот, на мой взгляд, несколько более корректное сравнение. В нём, по крайней мере, говорится о том, какой модуль использовался. Стабильный mpm-event и nginx идут ноздря в ноздрю.

          Фреймворк − чаще всего Django.
          • 0
            Ну на вкус и цвет… У меня apache ассоциируется только почему-то с php.
            У tornado, например, есть замечательный балансировщик, который сам разбрасывает запросы по инстансам и ядрам, а как будет вести себя apache — мне очень интересно, если учесть, что apache обрабатывает каждый запрос в отдельном процессе/потоке, в отличие от nginx (поправьте если не прав).
            Использование apache только как прокси для python-приложения скажется на производительности, — на пиках вы это увидите. В nginx вы эту проблему легко решите масштабированием (как простой пример, дополнительными upstream'ами на других серверах).

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

              Уже нет. Как сейчас помню:
              • в версии 2.0 код, отвечающий за запуск обработчиков запросов, выделили в отдельные модули (mpm_*),
              • в 2.2 появился мультипотоковый обработчик mpm_worker,
              • в 2.4 стабилизировался mpm_event, который на том графике внизу, рядом с nginx.
              • 0
                Ваша ссылочка на Performance от 2012 года, да и моя не надолго от вас ушла.
                Свежих тестов не нашел, самому гонять — не до этого, да и уже не мой профиль.Так что останемся при своих, на днях еще буду шерстить, если что сюда отпишусь.
                Для затравки http://habrahabr.ru/post/210950/
                В любом случае, спасибо за стью, плюс.
                • 0
                  Здорово, отписывайтесь. А то и отдельную статью пишите.
  • 0
    -
  • +3
    А не проще ли будет взять какой-нибудь Docker? Тогда и окружение будет стандартизированным везде, и на dev и на prod.
    • 0
      Docker, как я понимаю, оперирует содержимым контейнеров LXC или OpenVZ? Возможно, стоит попробовать. Либо написать свои скрипты для деплоя в контейнеры.

      Но беспокоит вопрос использования дискового пространства. Серверные диски желательно экономить.
      • 0
        docker как раз для вашего случая
      • +1
        Не совсем так. Напилите kvm и поиграйтеь с докер — поймёте в чём «прикол»!
      • 0
        Докер использует copy-on-write на файловых системах, можно развернуть 100 одинаковых контейнеров и места они будут занимать как один.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.