Запуск Flask приложения c uWSGI, virtualenv и nginx

В мире python существует довольно много различных приложений "супервизоров" предназначенных для запуска веб-приложений: uWSGI, gunicorn, gevent, twisted web и т.д. Однако, пожалуй, наиболее производительным и гибким супервизором является uWSGI. В этом посте я расскажу как запустить ваше Flask приложение под uWSGI. Конечно же, настроим nginx для проксирования запросов и отдачи статического контента.

Предисловие

Мы создадим простейшее web-приложение на Flask. Само приложение будет запускать uWSGI, запросы на который будет проксировать nginx. Приложение будет называться flask-uwsgi. Запомните это название, мы далее будем его часто использовать для имени пользователя и директорий.

Есть два основных способа запуска приложения под uWSGI: используя версию uWSGI из репозиториев дистрибутива, и второй - это установка uWSGI в виртуальное окружение. Лично я предпочитаю устанавливать uWSGI в виртуальное окружение - есть возможность контролировать версию uWSGI, хранить его конфигурацию в системе контроля версий да, и, как правило, в pip версии более "свежие". Первый вариант проще, и в целом подойдёт большинству людей. В этом посте я опишу оба варианта.

Подготовка

Для начала установим пакеты, которые нам понадобятся вне зависимости, от выбранного способа. В Ubuntu:

sudo apt-get install nginx python3 virtualenv

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

sudo mkdir /srv/flask-uwsgi/

Запускать любое веб-приложение лучше под отдельным пользователем. Создадим его:

sudo useradd -s /bin/false -d /srv/flask-uwsgi flask-uwsgi

Обратите внимание, что мы установили командную оболочку пользователя в /bin/false - то есть по сути отключили ему возможность как либо войти в систему. Сделано это опять же из соображений безопасности. Используя su и имея права супер-пользователя в системе мы сможем войти под этим пользователем и настроить всё для запуска приложения.

Дадим права на директорию /srv/flask-uwsgi созданному пользователю:

sudo chown -Rc flask-uwsgi:www-data /srv/flask-uwsgi
sudo chmod -R 0755 /srv/flask-uwsgi

Командами выше мы:

  • владельцем директории назначили созданного ранее пользователя. Этот пользователь имеет все права на директорию (читать/создавать/удалять);
  • группой-владельцем директории назначили www-data - это группа создается при установке nginx, и с правами этой группы он запускается. Таким образом nginx сможет получить доступ к директории и корректно отдавать статику;
  • группе www-data и всем остальным пользователям дали возможность исключительно читать файлы из директории;

Создаем приложение и его виртуальное окружение

Войдём под пользователем flask-uwsgi которого ранее создали:

sudo su - flask-uwsgi -s /bin/bash

Обратите внимание, что мы сразу окажемся в директории /srv/flask-uwsgi так как задали её в качестве домашней директории для пользователя.

Создадим виртуальное окружение для нашего flask приложения:

virtualenv -p python3 venv

Активируем виртуальное окружение и установим в нём Flask:

source venv/bin/activate
pip install Flask

Теперь создадим файл app.py в котором будет наше наипростейшее приложение:

from flask import Flask

def create_app():
    app = Flask(__name__)

    @app.route("/")
    def index():
        return "Hello world!"

    return app

if __name__ == "__main__":
    app = create_app()
    app.run()

Обратите внимание, что мы не указываем каких либо параметров в вызове метода run(): запущенный веб-сервер будет "слушать" на стандарном порту и локальном хосте (127.0.0.1:5000).

Так же нам необходимо создать директорию для статики - несмотря на то, что мы не используем никакой статики в нашем простом приложении, в конфигурации nginx мы её указываем, и если директорию не создать, то nginx не запустится:

mkdir static

И выходим из под пользователя:

exit

Настройка nginx

Создадим файл конфигурации nginx. В большинстве дистрибутивов принятно, что файлы с конфигурацией виртуальных хостов помещаются в /etc/nginx/site-available, и создаются символические ссылки на них в директории /etc/nginx/site-enabled. Это позволяет отключать виртуальные хосты не удаляя их конфигурацию. Мы поступим так же (в других ваших проектах тоже стоит так делать, если вы до этого делали как-то иначе).

Создадим файл /etc/nginx/sites-available/flask-uwsgi.conf:

sudo vim /etc/nginx/sites-available/flask-uwsgi.conf

С таким содержимым:

upstream uwsgi_flask_upstream {
    server unix:/tmp/flask-uwsgi.sock;
}

server {
    listen 80;
    server_tokens off;
    server_name .example.com;

     location / {
         include uwsgi_params;
         uwsgi_pass uwsgi_flask_upstream;
     }

     location /static {
         root /srv/flask-uwsgi;
     }
}

Nginx так же будет раздавать статические файлы вашего веб-приложения. Пожалуй, лучше чем nginx с этим не справится ни один другой веб-сервер. :)

Вся коммуникация между nginx и uWSGI будет происходит по протоколу uWSGI через unix-сокет - это наиболее производительный способ коммуникации приложений в Linux.

Активируем наш виртуальный хост:

sudo ln -s /etc/nginx/sites-available/flask-uwsgi.conf /etc/nginx/sites-enabled/

Проверим, что наш конфигурационный файл в порядке:

sudo nginx -t

Результат выполнения команды должен быть примерно такой:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Если всё в порядке, то скажем nginx перечитать конфигурацию:

sudo systemctl reload nginx

uWSGI

uWSGI из репозитория дистрибутива

Установим uWSGI из репозитория. Для этого под обычным пользователем выполним:

sudo apt-get install uwsgi uwsgi-plugin-python3

Мейнтейнеры ubuntu собрали и настроили uWSGI аналогично nginx: конфигурации приложений хранятся в директории /etc/uwsgi/apps-available. Для активации конфигурации необходимо создать символическую ссылку в директории /etc/uwsgi/apps-enabled.

Создадим файл конфигурации нашего приложения. Создадаём файл /etc/uwsgi/apps-available/flask-uwsgi.ini с таким содержимым:

[uwsgi]
# Задаем переменную
base = /srv/flask-uwsgi

# Имя модуля и приложения, которое запустит uWSGI
module = app:app

# Директория где находится приложение
chdir = %(base)
# Путь до директории с виртуальным окружением приложения
home = %(base)/venv

master = true
# Количество процессов uWSGI
processes = 5

# Указываем пользователя и группу под которым запускать приложение
uid = flask-uwsgi
gid = www-data
# Указываем где создавать файл-сокет
socket = /tmp/flask-uwsgi.sock

# Удаляем временные файлы uWSGI при выключении
vacuum = true

Часть настроек представленные в файле, уже определены в /etc/init.d/uwsgi. В частности gid, master и socket. Это означает, что их можно не добавлять в файл, но лично я предпочитаю видеть сразу все настройки, чем держать в голове что они ещё где-то определены.

Далее создадим символическую ссылку на файл конфигурации который мы создали таким образом активируя конфигурацию.

sudo ln -s /etc/uwsgi/apps-available/flask-uwsgi.ini /etc/uwsgi/apps-enabled/

И перезапускаем uWSGI:

sudo systemctl restart uwsgi

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

sudo systemctl enable uwsgi

uWSGI в виртуальном окружении

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

sudo apt-get install python3-dev build-essential

Снова войдём под пользователем которого мы создали для запуска приложения:

sudo su - flask-uwsgi -s /bin/bash

Установим uWSGI в виртуальном окружении:

source venv/bin/activate
pip install uwsgi

Создадим файл uwsgi.ini, но уже в директории с проектом:

[uwsgi]
# Имя модуля и приложения, которое запустит uWSGI
module = app:app

master = true
# Количество процессов uWSGI
processes = 5

# Указываем где создавать файл-сокет
socket = /tmp/flask-uwsgi.sock
# Указываем пользователя и группу для сокет-файла
chmod-socket = 660
chown-socket = flask-uwsgi:www-data
# Указываем пользователя и группу под которыми выполнять приложение
uid = flask-uwsgi
gid = www-data

# Удаляем временные файлы uWSGI при выключении
vacuum = true
# При закрытии терминала - завершать работу
# Необходимо для корректного запуска с помощью systemd
die-on-term = true

Выходим из под пользователя flask-uwsgi:

exit

Под обычным пользователем создадим файл /etc/systemd/system/flask-uwsgi.service с таким содержимым:

[Unit]
Description=uWSGI instance to serve flask-uwsgi project
After=network.target

[Service]
User=flask-uwsgi
Group=www-data
WorkingDirectory=/srv/flask-uwsgi
Environment="PATH=/srv/flask-uwsgi/venv/bin"
ExecStart=/srv/flask-uwsgi/venv/bin/uwsgi --ini uwsgi.ini

[Install]
WantedBy=multi-user.target

Таким образом мы создадим сервис systemd которым можно управлять - запускать, останавливать, перезапускать и смотреть его статус. Так же он автоматически будет запущен вместе с системой при загрузке. Запустим сервис:

sudo systemctl start flask-uwsgi

Проверим, что всё запустилось:

superuser@yakkety64:~# sudo systemctl status flask-uwsgi
 flask-uwsgi.service - uWSGI instance to serve flask-uwsgi project
   Loaded: loaded (/etc/systemd/system/flask-uwsgi.service; disabled; vendor preset: enabled)
   Active: active (running) since Mon 2017-01-02 07:51:49 UTC; 1h 50min ago
 Main PID: 22955 (uwsgi)
    Tasks: 6 (limit: 4915)
   CGroup: /system.slice/flask-uwsgi.service
           ├─22955 /srv/flask-uwsgi/venv/bin/uwsgi --ini uwsgi.ini
           ├─22957 /srv/flask-uwsgi/venv/bin/uwsgi --ini uwsgi.ini
           ├─22958 /srv/flask-uwsgi/venv/bin/uwsgi --ini uwsgi.ini
           ├─22959 /srv/flask-uwsgi/venv/bin/uwsgi --ini uwsgi.ini
           ├─22960 /srv/flask-uwsgi/venv/bin/uwsgi --ini uwsgi.ini
           └─22961 /srv/flask-uwsgi/venv/bin/uwsgi --ini uwsgi.ini

Jan 02 07:51:49 yakkety64 uwsgi[22955]: WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x55a660ad49d0 pid
Jan 02 07:51:49 yakkety64 uwsgi[22955]: *** uWSGI is running in multiple interpreter mode ***
Jan 02 07:51:49 yakkety64 uwsgi[22955]: spawned uWSGI master process (pid: 22955)
Jan 02 07:51:49 yakkety64 uwsgi[22955]: spawned uWSGI worker 1 (pid: 22957, cores: 1)
Jan 02 07:51:49 yakkety64 uwsgi[22955]: spawned uWSGI worker 2 (pid: 22958, cores: 1)
Jan 02 07:51:49 yakkety64 uwsgi[22955]: spawned uWSGI worker 3 (pid: 22959, cores: 1)
Jan 02 07:51:49 yakkety64 uwsgi[22955]: spawned uWSGI worker 4 (pid: 22960, cores: 1)
Jan 02 07:51:49 yakkety64 uwsgi[22955]: spawned uWSGI worker 5 (pid: 22961, cores: 1)

И если всё в порядке, то разрешаем сервису запускаться вместе с системой при загрузке:

sudo systemctl enable flask-uwsgi

Заключение

В этой статье мы рассмотрели два самых распространенных варианта запуска веб-приложения написанного на Flask под uWSGI. Конечно же, конфигурация uWSGI представленная в посте является довольно простой, но подойдёт для старта особенно тем, кто не знаком с uWSGI. Для нагруженных крупных проектов я настоятельно рекомендую ознакомиться с документацией uwsgi и подстроить под свои нужды.


Понравилась статья? Поделись с друзьями!




Комментарии на этом сайте требуют включенного Javascript в вашем браузере. Вероятно, ваш браузер не поддерживает Javascript, или он был отключен по каким-то причинам. Если вы хотите прокомментировать пост, или просто почитать комментарии, то пожалуйста, включите Javascript или попробуйте открыть эту страницу другим браузером.