Выкладка python-проектов с помощью pip и wheel

в 7:53, , рубрики: deploy, deployment, pip, python, wheel, Веб-разработка, Песочница, метки: , , , ,

Привет!

В этом посте я буду говорить о выкладке Python-проектов: о том как положить на сервер код и все требуемые сторонние модули. Многие из нас сталкивались с проблемой развертки проекта на боевой машине, но на хабре об этом мало пишут; я хочу поделиться своим опытом.

image

Проблема

Каждый запускает проект по разному; для полноты картины коротко опишу что использую я.

  1. pip и virtualenv для локальной установки пакетов
  2. uwsgi прослойка между nginx и web приложением
  3. supervisor для запуска нужных процессов: uwsgi, celery, tornado и т.д.
  4. nginx запускается под root и смотрит в даль

В наших проектах мы частенько используем модули, которые в какой то мере написаны на C и требуют компиляции. Тут и начинаются неудобства: на сервере нужно установить компилятор и еще кучу dev пакетов, чтобы скомпилировать зависимости вашего проекта (таких как simplejson или lxml). Первое, что приходит в голову — это взять и скомпилировать локально, а потом просто скопировать на сервер. А что тут такого? И там и там x86_64. Но как оказалось бинарная совместимость это шаткое понятие в Linux системах. Я конечно догадываюсь почему это так, но слабоват чтобы об этом рассуждать. Короче, то что вы скомпилируете у себя на Ubuntu не обязательно будет работать на Debian. Если у вас куча серверов, можно выделить один для сборки пакетов или поднять идентичную виртуальную машину локально и компилировать там.

При выкладке проекта на боевой сервер есть два варианта: компилировать код на сервере или делать сборку пакетов самому. Лично меня этот скудный выбор расстраивает. Напрашивается вопрос, а почему авторы модулей или PyPi не делает сборку пакетов для разных платформ, есть же бинарные яйца(binary eggs)? Они это делали, и вы даже можете устанавливать их с помощью easy_install, но эта попытка провалилась, так как нет гарантий, то что эти бинарники будут работать у вас на сервере даже с той же версией Python и архитектурой. И наверное на PyPi не у всех пакетов, требующих компиляции, есть бинарные сборки. Кстати, pip не поддерживает бинарные яйца именно по этой причине. А компилировать код на сервере это какой-то больной подход, особенно если серверов для выкладки у вас много, да даже работая за ноутбуком этот процесс просто выбешывает. Все должно быть как то проще: просто скачал и установил.

Попытка №1

Когда я занялся этим вопросом в первый раз, я пошел во все тяжкие. Я поднимал свой Debian репозиторий и собирал проект в нативные deb-пакеты. Все было вполне автоматизировано: я написал скрипты для автоматического запуска проекта; на fabric написал скрипт для сборки deb-пакетов и выкладки проекта на сервер. Проект разбивался на три разных deb-пакета: код проекта, virtualenv со всеми модулями, конфигурации (dev, prod). Установка и запуск на сервере заключалась в том, чтобы выполнить одну команду:

sudo apt-get install my-project-venv my-project-dev-conf my-project

Это все конечно здорово, но очень надоедало то, что при обновлении одного модуля в virtualenv приходилось компилировать все модули, чтобы собрать deb-пакет. А еще там было немало танцев с бубном, чтобы сделать непереносимую virtualenv переносимой: переписывать путь в строке !#python во всех файлах в папке bin, удалять все pyc-файлы, удалять все ссылки и устанавливать все пакеты в папке src в папку site-packages. Чтобы проработать эту махину по выкладке, мне пришлось потратить кучу времени и теперь как-то не очевидно, что я сделал свою жизнь проще.

Попытка №2

Во второй раз, я решил что лучше собирать все нужные модули в отдельные бинарные пакеты. Наткнулся на относительно новый проект под названием wheel и решил попробовать.

Wheel — это альтернатива бинарным яйцам; автор старается делать все по последним веяниям, и не так давно его PEP был принят. Из отличий примечательно, что wheel это формат установки, а не импортируемое. Еще wheel хороший помощник при работе на локальной машине: можно скачать и скомпилировать все часто используемые пакеты в одну папку, потом при создании новой виртуальной среды устанавливать пакеты оттуда за O(1).

Теперь для выкладки проекта я делаю следующее:

  1. Поднял свой индекс пакетов: на github есть немало проектов, которые позволяют поднять свой приватный pypi, я использую localshop, потому что в нем есть возможность ограничить доступ на скачивание.
  2. Все зависимости (из requires.txt) собираю в формате wheel и заливаю в свой индекс: для этого мне пришлось немного дописать localshop, так как он не поддерживал этот формат.
  3. Для того чтобы не устанавливать git на сервере, проект так же пакую и кладу в свой индекс пакетов.

На стороне сервера остается только установить и запустить. Это выглядит примерно так:

virtualenv myproject
. myproject/bin/activate
# проект установится и подтянет все зависимости
pip install --use-wheel myproject==0.1.1
myproject init
myproject runserver 0.0.0.0:8000

Запуск под uwsgi:

pip install --use-wheel uwsgi
uwsgi --module=myproject.wsgi --home=myproject ....

Запуск supervisor:

pip install --use-wheel supervisor
supervisor -c supervisor.conf -j supervisor.pid

Я пишу на Python 2.7.3, который стоит по умолчанию на моей Ubuntu, а на серверах стоит Debian и Python версии 2.6. Конечно же между ними есть разница, например: форматирование строк c помощью format нет на Python 2.6. Пытаться поставить 2.7.3 из дистрибутива не лучшая идея, легче скомпилировать Python и в этом нам хороший помощник — проект pythonbrew.

Лень — двигатель прогресса! Все мы программисты лентяи(особенно те, кто пишут на Python и Ruby) и, сталкиваясь с неудобствами, мы хотим сделать себе жизнь проще. А как делаете выкладку вы?

Ссылки

lucumr.pocoo.org/2012/6/22/hate-hate-hate-everywhere/
github.com/pypa/pip/issues/492
hynek.me/articles/python-app-deployment-with-native-packages/
crate.io/packages/wheel
www.python.org/dev/peps/pep-0427/
bitbucket.org/dholth/wheel
wheel.readthedocs.org/en/latest/story.html
github.com/mvantellingen/localshop
github.com/utahta/pythonbrew

Автор: yun_man_ger

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js