Поиск по сайту:

Как настроить uWSGI и Nginx для обслуживания приложений Python в Ubuntu 14.04


Введение

В этом руководстве мы будем настраивать простое приложение WSGI, обслуживаемое uWSGI. Мы будем использовать веб-сервер Nginx в качестве обратного прокси-сервера для сервера приложений, чтобы обеспечить более надежную обработку соединений. Мы будем устанавливать и настраивать эти компоненты на сервере Ubuntu 14.04.

Определения и понятия

Уточнение некоторых терминов

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

  • WSGI: спецификация Python, определяющая стандартный интерфейс для связи между приложением или инфраструктурой и приложением/веб-сервером. Это было создано для того, чтобы упростить и стандартизировать связь между этими компонентами для согласованности и взаимозаменяемости. По сути, это определяет интерфейс API, который можно использовать поверх других протоколов.
  • uWSGI: контейнер сервера приложений, предназначенный для предоставления полного стека для разработки и развертывания веб-приложений и служб. Основным компонентом является сервер приложений, который может обрабатывать приложения на разных языках. Он взаимодействует с приложением, используя методы, определенные спецификацией WSGI, и с другими веб-серверами по множеству других протоколов. Это часть, которая переводит запросы от обычного веб-сервера в формат, который может обрабатывать приложение.
  • uwsgi: быстрый двоичный протокол, реализованный сервером uWSGI для связи с более полнофункциональным веб-сервером. Это проводной протокол, а не транспортный протокол. Это предпочтительный способ взаимодействия с веб-серверами, которые передают запросы к uWSGI через прокси.

Требования к приложению WSGI

Спецификация WSGI определяет интерфейс между веб-сервером и частями стека приложений. В этом контексте «веб-сервер» относится к серверу uWSGI, который отвечает за преобразование клиентских запросов в приложение с использованием спецификации WSGI. Это упрощает связь и создает слабо связанные компоненты, так что вы можете легко поменять любую сторону без особых проблем. .

Веб-сервер (uWSGI) должен иметь возможность отправлять запросы в приложение, запуская определенный «вызываемый объект». Вызываемый объект — это просто точка входа в приложение, где веб-сервер может вызывать функцию с некоторыми параметрами. Ожидаемые параметры представляют собой словарь переменных среды и вызываемый объект, предоставляемый компонентом веб-сервера (uWSGI).

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

С компонентом «веб-сервер» этого взаимодействия, предоставляемым uWSGI в этом случае, нам нужно будет только убедиться, что наши приложения обладают описанными выше качествами. Мы также настроим Nginx для обработки фактических клиентских запросов и передачи их на uWSGI. сервер.

Установите компоненты

Для начала нам нужно установить необходимые компоненты на наш сервер Ubuntu 14.04. В основном мы можем сделать это с помощью apt и pip.

Сначала обновите индекс пакета apt, а затем установите библиотеки и заголовочные файлы для разработки Python, менеджер пакетов pip Python, а также веб-сервер Nginx и обратный прокси-сервер:

sudo apt-get update
sudo apt-get install python-dev python-pip nginx

После завершения установки пакета у вас будет доступ к диспетчеру пакетов pip Python. Мы можем использовать это для установки пакета virtualenv, который мы будем использовать для изоляции среды Python нашего приложения от любых других, которые могут существовать в системе:

sudo pip install virtualenv

Как только это будет завершено, мы можем приступить к созданию общей структуры нашего приложения. Мы создадим описанную выше виртуальную среду и установим в ней сервер приложений uWSGI.

Настройте каталог приложений и Virtualenv

Мы начнем с создания папки для нашего приложения. Это может содержать вложенную папку, содержащую фактический код приложения в более полном приложении. Для наших целей этот каталог будет просто содержать нашу виртуальную среду и нашу точку входа WSGI:

mkdir ~/myapp/

Затем перейдите в каталог, чтобы мы могли настроить среду для нашего приложения:

cd ~/myapp

Создайте виртуальную среду с помощью команды virtualenv. Мы назовем это myappenv для простоты:

virtualenv myappenv

Новая среда Python будет настроена в каталоге с именем myappenv. Мы можем активировать эту среду, набрав:

source myappenv/bin/activate

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

(myappenv)username@host:~/my_app$

Если вы хотите покинуть эту среду в любое время, вы можете просто ввести:

deactivate

Если вы деактивировали свою среду, повторно активируйте ее, чтобы продолжить работу с руководством.

Если эта среда активна, все установленные пакеты Python будут содержаться в этой иерархии каталогов. Они не будут мешать системной среде Python. Имея это в виду, теперь мы можем установить сервер uWSGI в нашу среду, используя pip. Пакет для этого называется uwsgi (это по-прежнему сервер uWSGI, а не протокол uwsgi):

pip install uwsgi

Вы можете убедиться, что он теперь доступен, набрав:

uwsgi --version

Если он возвращает номер версии, сервер uWSGI доступен для использования.

Создайте приложение WSGI

Далее мы создадим невероятно простое приложение WSGI, используя требования спецификации WSGI, которые мы обсуждали ранее. Повторим еще раз: компонент приложения, который мы должны предоставить, должен иметь следующие свойства:

  • Он должен предоставлять интерфейс через вызываемый объект (функцию или другую языковую конструкцию, которую можно вызвать)
  • Вызываемый объект должен принимать в качестве параметров словарь, содержащий пары ключ-значение, подобные переменным среды, и вызываемый объект, доступный на сервере (uWSGI).
  • Вызываемый объект приложения должен возвращать итерируемый объект, который создаст тело для отправки клиенту.
  • Приложение должно вызывать callable веб-сервера с HTTP-статусом и заголовками запроса.

Мы напишем наше приложение в файле с именем wsgi.py в каталоге нашего приложения:

nano ~/myapp/wsgi.py

Внутри этого файла мы создадим самое простое WSGI-совместимое приложение, какое только сможем. Как и во всем коде Python, обязательно обратите внимание на отступы:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ["<h1 style='color:blue'>Hello There!</h1>"]

Приведенный выше код представляет собой законченное приложение WSGI. По умолчанию uWSGI будет искать вызываемый объект с именем application, поэтому мы назвали нашу функцию application. Как видите, он принимает два параметра.

Первый мы назвали environ, потому что это будет словарь ключ-значение, похожий на переменную окружения. Второй называется start_response и представляет собой имя, которое приложение будет использовать внутри приложения для ссылки на отправляемый вызываемый веб-сервер (uWSGI). Оба имени параметра были выбраны просто из-за их использования в примеры в спецификации PEP 333, которая определяет взаимодействия WSGI.

Наше приложение должно принять эту информацию и сделать две вещи. Во-первых, он должен вызвать вызываемый объект, который он получил, с кодом состояния HTTP и любыми заголовками, которые он хочет отправить обратно. В этом случае мы отправляем ответ «200 OK» и устанавливаем для заголовка Content-Type значение text/html.

Во-вторых, он должен вернуться с итерируемым объектом для использования в качестве тела ответа. Здесь мы только что использовали список, содержащий одну строку HTML. Строки также являются итерируемыми, но внутри списка uWSGI сможет обработать всю строку за одну итерацию.

В реальном сценарии этот файл, скорее всего, будет использоваться как ссылка на остальную часть кода вашего приложения. Например, проекты Django по умолчанию включают файл wsgi.py, который переводит запросы с веб-сервера (uWSGI) в приложение (Django). Упрощенный интерфейс WSGI остается неизменным независимо от того, насколько сложен фактический код приложения. Это одна из сильных сторон интерфейса.

Сохраните и закройте файл, когда закончите.

Чтобы протестировать код, мы можем запустить uWSGI. Мы скажем ему пока использовать HTTP и прослушивать порт 8080. Мы передадим ему имя скрипта (суффикс удален):

uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi

Теперь, если вы посетите IP-адрес или доменное имя вашего сервера в своем веб-браузере, а затем :8080, вы должны увидеть текст заголовка первого уровня, который мы передали в качестве тела в нашем wsgi.py файл:

Остановите сервер с помощью CTRL-C, когда убедитесь, что это работает.

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

deactivate

Настройте файл конфигурации uWSGI

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

Чтобы продолжить именование, которое мы использовали до сих пор, мы назовем файл myapp.ini и поместим его в папку нашего приложения:

nano ~/myapp/myapp.ini

Внутри нам нужно создать раздел с именем [uwsgi]. В этом разделе будут жить все наши элементы конфигурации. Начнем с определения нашего приложения. Сервер uWSGI должен знать, где находится вызываемый объект приложения. Мы можем указать файл и функцию внутри:

[uwsgi]
module = wsgi:application

Мы хотим пометить исходный процесс uwsgi как главный, а затем создать ряд рабочих процессов. Начнем с пяти рабочих:

[uwsgi]
module = wsgi:application

master = true
processes = 5

На самом деле мы собираемся изменить протокол, который uWSGI использует для связи с внешним миром. Когда мы тестировали наше приложение, мы указали --protocol=http, чтобы мы могли видеть его из веб-браузера. Поскольку мы будем настраивать Nginx в качестве обратного прокси-сервера перед uWSGI, мы можем это изменить. Nginx реализует механизм проксирования uwsgi, который представляет собой быстрый двоичный протокол, который uWSGI может использовать для взаимодействия с другими серверами. Протокол uwsgi на самом деле является протоколом uWSGI по умолчанию, поэтому, просто опуская спецификацию протокола, он возвращается к uwsgi.

Поскольку мы разрабатываем эту конфигурацию для использования с Nginx, мы также собираемся отказаться от использования сетевого порта и вместо этого использовать сокет Unix. Это более безопасно и быстро. Сокет будет создан в текущем каталоге, если мы используем относительный путь. Мы назовем его myapp.sock. Мы изменим разрешения на «664», чтобы Nginx мог писать в него (мы будем запускать uWSGI с группой www-data, которую использует Nginx. Мы также добавим vacuum , который удалит сокет, когда процесс остановится:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

Нам нужен последний вариант, так как мы будем создавать файл Upstart для запуска нашего приложения при загрузке. У Upstart и uWSGI разные представления о том, что сигнал SIGTERM должен делать с приложением. Чтобы устранить это несоответствие, чтобы процессы могли обрабатываться с помощью Upstart, как и ожидалось, нам просто нужно добавить параметр с именем die-on-term, чтобы uWSGI убивал процесс, а не перезагружал его:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

die-on-term = true

Сохраните и закройте файл, когда закончите. Этот файл конфигурации теперь настроен для использования со сценарием Upstart.

Создайте файл Upstart для управления приложением

Мы можем запустить экземпляр uWSGI при загрузке, чтобы наше приложение всегда было доступно. Мы поместим это в каталог /etc/init, который проверяет Upstart. Мы будем называть это myapp.conf:

sudo nano /etc/init/myapp.conf

Во-первых, мы можем начать с описания службы и выбрать уровни запуска системы, на которых она должна запускаться автоматически. Стандартные пользовательские уровни запуска — от 2 до 5. Мы укажем Upstart остановить службу, когда она находится на любом уровне выполнения за пределами этой группы (например, когда система перезагружается или находится в однопользовательском режиме):

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

Далее Upstart сообщит, от имени какого пользователя и группы запустить процесс. Мы хотим запустить приложение под своей учетной записью (в этом руководстве мы используем демо, но вы должны указать своего пользователя). Мы хотим установить группу для пользователя www-data, который, однако, использует Nginx. Это необходимо, потому что веб-сервер должен иметь возможность читать и писать в сокет, который создаст наш файл .ini:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

Далее мы запустим фактические команды для запуска uWSGI. Поскольку мы установили uWSGI в виртуальную среду, у нас есть дополнительная работа. Мы могли бы просто указать полный путь к исполняемому файлу uWSGI, но вместо этого мы активируем виртуальную среду. Это было бы проще, если бы мы полагались на дополнительное программное обеспечение, установленное в среде.

Для этого мы будем использовать блок script. Внутри мы перейдем в каталог нашего приложения, активируем виртуальную среду (мы должны использовать . в сценариях вместо source) и запустим экземпляр uWSGI, указывающий на наш < файл.ini:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

script
    cd /home/demo/myapp
    . myappenv/bin/activate
    uwsgi --ini myapp.ini
end script

На этом наш сценарий Upstart завершен. Сохраните и закройте файл, когда закончите.

Теперь мы можем запустить службу, набрав:

sudo start myapp

Мы можем убедиться, что он был запущен, набрав:

ps aux | grep myapp
demo   14618  0.0  0.5  35868  5996 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14619  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14620  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14621  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14622  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14623  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   15520  0.0  0.0  11740   936 pts/0    S+   15:53   0:00 grep --color=auto myapp

Это запустится автоматически при загрузке. Вы можете остановить службу в любое время, набрав:

sudo stop myapp

Настройте Nginx на прокси для uWSGI

На данный момент у нас есть приложение WSGI, и мы убедились, что uWSGI может его читать и обслуживать. Мы создали файл конфигурации и скрипт Upstart. Наш процесс uWSGI будет прослушивать сокет и обмениваться данными с использованием протокола uwsgi.

Теперь мы находимся в той точке, где мы можем работать над настройкой Nginx в качестве обратного прокси. Nginx может использовать прокси с использованием протокола uwsgi для связи с uWSGI. Это более быстрый протокол, чем HTTP, и он будет работать лучше.

Конфигурация Nginx, которую мы будем настраивать, чрезвычайно проста. Создайте новый файл в каталоге sites-available в иерархии конфигурации Nginx. Мы назовем наш файл myapp, чтобы он соответствовал имени приложения, которое мы использовали:

sudo nano /etc/nginx/sites-available/myapp

В этом файле мы можем указать номер порта и доменное имя, на которое должен отвечать этот серверный блок. В нашем случае мы будем использовать порт 80 по умолчанию:

server {
    listen 80;
    server_name server_domain_or_IP;
}

Поскольку мы хотим отправлять все запросы на этот домен или IP-адрес нашему приложению WSGI, мы создадим единый блок местоположения для запросов, начинающихся с /, который должен соответствовать всем. Внутри мы будем использовать директиву include для включения ряда параметров с разумными значениями по умолчанию из файла в нашем каталоге конфигурации Nginx. Файл, содержащий их, называется uwsgi_params. После этого мы будем передавать трафик на наш экземпляр uWSGI по протоколу uwsgi. Мы будем использовать сокет unix, который мы настроили ранее:

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/home/demo/myapp/myapp.sock;
    }
}

Собственно, это все, что нам нужно для простого приложения. Есть некоторые улучшения, которые могут быть сделаны для более полного приложения. Например, мы можем определить ряд восходящих серверов uWSGI за пределами этого блока, а затем передать их ему. Мы могли бы включить еще несколько параметров uWSGI. Мы также можем обрабатывать любые статические файлы из Nginx напрямую и передавать экземпляру uWSGI только динамические запросы.

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

Включите конфигурацию сервера, которую мы только что сделали, связав ее с каталогом sites-enabled:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled

Проверьте файл конфигурации на наличие синтаксических ошибок:

sudo service nginx configtest

Если он сообщает, что проблем не обнаружено, перезапустите сервер, чтобы изменения вступили в силу:

sudo service nginx restart

После перезапуска Nginx вы сможете перейти к доменному имени или IP-адресу вашего сервера (без номера порта) и увидеть настроенное вами приложение:

Заключение

Если вы дошли до этого момента, значит, вы создали простое приложение WSGI и имеете некоторое представление о том, как нужно разрабатывать более сложные приложения. Мы установили контейнер/сервер приложения uWSGI в специально созданную виртуальную среду для обслуживания нашего приложения. Мы сделали файл конфигурации и скрипт Upstart для автоматизации этого процесса. Перед сервером uWSGI мы настроили обратный прокси-сервер Nginx, который может взаимодействовать с процессом uWSGI, используя проводной протокол uwsgi.

Вы можете легко увидеть, как это можно расширить при настройке реальной производственной среды. Например, uWSGI имеет возможность управлять несколькими приложениями, используя так называемый «императорский режим». Вы можете расширить конфигурацию Nginx для балансировки нагрузки между экземплярами uWSGI или для обработки статических файлов для вашего приложения. При обслуживании нескольких приложений это может быть в ваших интересах установить uWSGI глобально, а не в виртуальной среде, в зависимости от ваших потребностей.Все компоненты довольно гибкие, поэтому вы сможете настроить их конфигурацию для соответствия множеству различных сценариев.