Введение
В этом руководстве мы настроим простое приложение 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, менеджер пакетов Python pip
и веб-сервер и обратный прокси Nginx.
sudo apt-get update
sudo apt-get install python-dev python-pip nginx
Как только установка пакета завершится, у вас будет доступ к менеджеру пакетов Python pip
. Мы можем использовать его для установки пакета virtualenv
, который мы будем использовать для изоляции среды Python нашего приложения от других, которые могут существовать в системе:
sudo pip install virtualenv
После этого мы можем начать создавать общую структуру для нашего приложения. Мы создадим виртуальную среду, о которой упоминалось выше, и установим сервер приложений uWSGI в этой среде.
Настройка каталога приложения и виртуальной среды
Мы начнем с создания папки для нашего приложения. Это может содержать вложенную папку, содержащую фактический код приложения в более полном виде. Для наших целей этот каталог просто будет содержать нашу виртуальную среду и точку входа WSGI:
mkdir ~/myapp/
Затем перейдите в этот каталог, чтобы мы могли настроить среду для нашего приложения:
cd ~/myapp
Создайте виртуальную среду с помощью команды virtualenv
. Мы назовем ее myappenv
для простоты:
virtualenv myappenv
A new Python environment will be set up under a directory called myappenv
. We can activate this environment by typing:
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, обратите внимание на отступы:
Вышеуказанный код составляет полное приложение 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’у о том, какому пользователю и группе следует запускать процесс. Мы хотим запустить приложение под нашей учетной записью (в этом руководстве мы используем 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 для проксирования к 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 глобально, а не в виртуальной среде, в зависимости от ваших потребностей. Компоненты достаточно гибкие, поэтому вы должны сможете настроить их конфигурацию для различных сценариев.