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

Вступление

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

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

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

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

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

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

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

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

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

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

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

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

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

Для начала нам потребуется установить необходимые компоненты на наш сервер CentOS 7. В основном мы можем сделать это используя + yum + и + pip +.

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

sudo yum install epel-release

Теперь мы можем установить наши компоненты. Нам нужны библиотеки разработки и заголовки Python, менеджер пакетов Python + pip +, веб-сервер Nginx и обратный прокси-сервер. Нам также понадобится компилятор для моментального создания бинарного файла uWSGI:

sudo yum install python-pip python-devel nginx gcc

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

sudo pip install virtualenv

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

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

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

mkdir ~/myapp/

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

cd ~/myapp

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

virtualenv

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

source /bin/activate

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

()@:~/my_app$

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

deactivate

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

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

pip install uwsgi

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

uwsgi --version

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

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

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

  • Он должен предоставлять интерфейс через вызываемую функцию (функцию или другую языковую конструкцию, которая может быть вызвана)

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

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

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

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

nano ~//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 +. Как видите, он принимает два параметра.

Сначала мы назвали + environment +, потому что это будет словарь ключей-значений, подобный переменной окружения. Второй называется + 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 +:

изображение: https: //assets.digitalocean.com/articles/nginx_uwsgi_wsgi_1404/test_app.png [пример приложения wsgi]

Остановите сервер с помощью 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. Это безопаснее и быстрее.

Мы зададим имя нашего собственного пользователя для запуска сервера + uwsgi + и владения файлом сокета. Мы создадим каталог в + / run +, чтобы разместить файл сокета, чтобы и uWSGI, и Nginx могли получить к нему доступ. Мы будем называть сам сокет + myapp.sock +. Мы изменим права доступа на «664», чтобы Nginx мог писать в него (мы будем запускать uWSGI с группой + www-data +, которую использует Nginx. Мы также добавим опцию +uum +, которая удалит сокет после остановки процесса:

[uwsgi]
module = wsgi:application

master = true
processes = 5

uid =
socket = /run/uwsgi/myapp.sock
chown-socket = :nginx
chmod-socket = 660
vacuum = true

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

[uwsgi]
module = wsgi:application

master = true
processes = 5

uid =
socket = /run/uwsgi/myapp.sock
chown-socket = :nginx
chmod-socket = 660
vacuum = true

die-on-term = true

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

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

Мы можем запустить экземпляр uWSGI при загрузке, чтобы наше приложение всегда было доступно. Для этого мы можем создать системный файл systemd. Мы поместим это в каталог + / etc / systemd / system, который является лучшим местом для пользовательских файлов модулей. Мы будем называть файл модуля + uwsgi.service:

sudo nano /etc/systemd/system/uwsgi.service

Сначала мы начнем с раздела + [Unit] +, где мы можем шагать по нашим метаданным. Единственное, что мы здесь разместим, - это описание нашего сервиса:

[Unit]
Description=uWSGI instance to serve myapp

Далее мы откроем раздел + [Service] +. Поскольку мы используем виртуальную среду, наши команды запуска службы будут более сложными, чем обычно. Мы будем использовать команду + ExecStartPre +, чтобы убедиться, что наш каталог сокетов создан и принадлежит правильным сторонам. Это будет разрешено с ошибкой (путем добавления + - + после знака равенства), если они уже установлены. Это будет передано в один вызов + bash +.

Для фактической команды + ExecStart +, которая запустит uWSGI, мы также передадим фактические команды в + bash +. Это позволяет нам выполнять несколько разных команд, так как эта директива может запускать только одну команду (в данном случае + bash). Мы будем использовать это, чтобы перейти в каталог нашего приложения, активировать виртуальную среду и запустить uWSGI с файлом + .ini +, который мы создали:

[Unit]
Description=uWSGI instance to serve myapp

[Service]
ExecStartPre=-/usr/bin/bash -c 'mkdir -p /run/uwsgi; chown :nginx /run/uwsgi'
ExecStart=/usr/bin/bash -c 'cd /home//myapp; source myappenv/bin/activate; uwsgi --ini myapp.ini'

Теперь осталось только сформулировать раздел + [Install] +. Это определит, что произойдет, когда мы включим + модуль. По сути, он указывает, в каких состояниях устройство должно запускаться автоматически. Мы хотим указать, что при включении этот модуль должен запускаться всякий раз, когда сервер находится в многопользовательском режиме:

[Unit]
Description=uWSGI instance to serve myapp

[Service]
ExecStartPre=-/usr/bin/bash -c 'mkdir -p /run/uwsgi; chown :nginx /run/uwsgi'
ExecStart=/usr/bin/bash -c 'cd /home//myapp; source myappenv/bin/activate; uwsgi --ini myapp.ini'

[Install]
WantedBy=multi-user.target

После того как вы запишете вышеуказанную конфигурацию, сохраните и закройте файл.

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

sudo systemctl start uwsgi

Проверьте, что все началось без проблем, набрав:

systemctl status uwsgi

Если ошибок не было, включите службу, чтобы она запускалась при загрузке, набрав:

sudo systemctl enable uwsgi

Вы можете остановить сервис в любое время, набрав:

sudo systemctl stop uwsgi

Настройте Nginx на Proxy для uWSGI

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

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

Конфигурация Nginx, которую мы будем настраивать, предельно проста. Мы будем изменять существующий файл + nginx.conf + и добавлять новый блок сервера. Откройте файл с помощью + sudo + для редактирования:

sudo nano /etc/nginx/nginx.conf

Перед блоком сервера по умолчанию мы добавим ваш собственный блок сервера:

http {

   . . .

   include /etc/nginx/conf.d/*.conf;




   server {
       listen 80 default_server;
       server_name localhost;

       . . .

Блок, который мы создали, будет содержать конфигурацию нашего прокси-сервера uWSGI. Остальные элементы конфигурации ниже расположены внутри этого блока. Блок сервера должен прослушивать порт 80 и отвечать на доменное имя или IP-адрес вашего сервера:

server {
   listen 80;
   server_name ;
}

После этого мы можем открыть один блок локации, который будет обрабатывать все запросы. В этот блок мы включим параметры + uwsgi +, найденные в файле + / etc / nginx / uwsgi_params +, и передадим трафик в сокет, где прослушивает uWSGI:

server {
   listen 80;
   server_name ;

   location / {
       include uwsgi_params;
       uwsgi_pass unix:/run/uwsgi/myapp.sock;
   }
}

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

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

Вы можете проверить правильность конфигурации Nginx, набрав:

sudo nginx -t

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

sudo systemctl start nginx

Запустите Nginx при загрузке, включив сервис:

sudo systemctl enable nginx

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

изображение: https: //assets.digitalocean.com/articles/nginx_uwsgi_wsgi_1404/full_app.png [полное приложение WSGI]

Заключение

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

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

Related