Запуск aiohttp с помощью gunicorn и nginx

Инструкция с примерами и комментариями по запуску асинхронного веб-сервера aiohttp с помощью gunicorn за nginx.

Установка программ

Начать необходимо с установки необходимых программ. Примеры основаны на Ubuntu.

Установка актуальной версии nginx описана здесь.

Установка других пакетов:

$ sudo apt-get update
$ sudo apt-get install python3 virtualenv supervisor

Разворачивание проекта

Предположим, что проект уже готов и размещен в директории /home/user/aiohttp.

Код, на котором можно протестировать работу представлен ниже. Разместим его в файл app_test.py:

from aiohttp import web

def index(request):
    return web.Response(text="Welcome home!")

my_web_app = web.Application()
my_web_app.router.add_get('/', index)

Настроим Python

Работать будем через виртуальное окружение. Переходим в директорию проекта/home/user/aiohttp и создаем окружение командой:

$ cd /home/user/aiohttp
$ virtualenv env --no-site-packages -p python3

Активируем окружение и дальше работаем под ним:

$ source env/bin/activate

Теперь устанавливаем все необходимые пакеты:

$ pip install aiohttp

Установка и настройка Gunicorn

Устанавливаем командой:

$ pip install gunicorn

Запускаем:

$ gunicorn app_test:my_web_app --bind localhost:8081 --worker-class aiohttp.worker.GunicornWebWorker

Если ошибок нет, тогда можно проверить работу по адресу: http://localhost:8081

Теперь создадим файл конфигурации gunicorn.conf.py, разместим в нем необходимые настройки и поменяем строку запуска для его использования.

bind = "127.0.0.1:8081"
workers = 2
worker_class = "aiohttp.worker.GunicornWebWorker"

Здесь уже указано, что запросы будут обрабатываться двумя рабочими потоками. Если есть желание направить все через сокеты, тогда заменим bind на bind="unix:/tmp/apptest.sock".

Для отладки, в конфиг можно добавить reload=True, после чего gunicorn будет автоматически перезапускаться по факту изменения любого файла проекта.

Строка запуска примет вид:

$ gunicorn app_test:my_web_app -c gunicorn.conf.py

Настройка supervisor

Для работы gunicorn в режиме демона, атоматического запуска и перезапуска сервиса необходимо настроить supervisor. Программа написана на Python 2+ и поддерживает только 2 версию.

Есть другие альтернативы, такие как systemd, upstart, runit, но все они требуют более сложной настройки или наличия программ-зависимостей в системе.

Для проверки наличия установленного сервиса, запустим его командой (если ошибок нет, значит все в порядке):

$ sudo service supervisor restart

Файлы конфигурации лежат в директории /etc/supervisor/conf.d/, где создадим свой файл apptest.conf и добавим в него:

[program:apptest]
command=/home/user/aiohttp/env/bin/gunicorn main:application -c /home/user/aiohttp/gunicorn.conf.py
directory=/home/user/aiohttp
user=nobody
autostart=true
autorestart=true
#redirect_stderr=true

Если хотите все сообщения лить в лог ошибок, тогда включите redirect_stderr=true

Теперь прочитаем новый конфиг командой:

$ supervisorctl reread

Если мы видим наше приложение, тогда применим новую конфигурацию командой:

$ supervisorctl update

Посмотреть статус программ можно командой:

$ supervisorctl

Помощь по командам внутри supervisorctl доступна по команде help.

Если все сделано верно, тогда можно проверить работу по адресу: http://localhost:8081

Для настройки ведения логов необходимо добавить в конфиг supervisor следующие строки (затем перечитать конфиги, обновить и перезапустить приложение):

stderr_logfile=/var/log/supervisor/apptest.err.log
stdout_logfile=/var/log/supervisor/apptest.out.log

Перезапуск приложения из командной строки:

$ supervisorctl restart apptest

В Ubuntu 16+ есть проблема, из-за которой supervisor не запускается автоматически после перезагрузки. Для решения этой проблемы необходимо выполнить следующие команды:

$ systemctl enable supervisor
$ systemctl start supervisor

Настройка Nginx

Добавим конфиг сайта /etc/nginx/conf.d/apptest.conf с содержимым:

upstream apptest_server {
    # for UNIX domain socket setups
    server unix:/tmp/apptest.sock fail_timeout=0;

}
server {
    listen       80;
    server_name  apptest.local;

    # path for static files
    root /home/user/aiohttp/static;

    location / {
      # checks for static file, if not found proxy to app
      try_files $uri @proxy_to_app;
    }

    location @proxy_to_app {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://127.0.0.1:8081;
      #proxy_pass http://apptest_server;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

upstream apptest_server показан для примера, при желании завернуть все через сокеты, чтобы gunicorn не занимал портов.

Данные примеры будут работать только на Linux и Unix-подобных системах. На Windows это не повторить.

Напишите, нужен ли вообще этот асинхронный сервер?