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

Вступление

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

Определения и концепции

Уточнение некоторых условий

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

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

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

  • uwsgi: быстрый двоичный протокол, реализованный сервером uWSGI для связи с более полнофункциональным веб-сервером. Этоwire protocol, а не транспортный протокол. Это предпочтительный способ общения с веб-серверами, которые передают запросы в uWSGI.

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

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

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

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

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

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

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

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

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

После завершения установки пакета вы получите доступ к диспетчеру пакетов Pythonpip. Мы можем использовать это для установки пакета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).

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

  • Приложение должно вызывать вызываемый веб-сервер со статусом HTTP и заголовками запроса.

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

nano ~/myapp/wsgi.py

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

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ["

Hello There!

"]

Приведенный выше код представляет собой полное приложение 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:

wsgi application example

Остановите сервер с помощью 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, от какого пользователя и группы будет запускаться процесс. Мы хотим запустить приложение под нашей собственной учетной записью (в этом руководстве мы используемdemo, но вы должны заменить своего собственного пользователя). Мы хотим установить группу для пользователя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 на Proxy для 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-адресу вашего сервера (без номера порта) и просмотреть настроенное вами приложение:

full WSGI app

Заключение

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

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

Related