Запуск 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 и подстроить под свои нужды.