Данное руководство
HOWTO
было проверено при создании реального сервера. Все инструкции и файлы конфигурации рабочие. Ошибки могут быть только в путях, если пользователь будет менять оригинальные пути на свои (например, где-то создал/вставил не ту папку, где-то забыл что-то поменять и т.д.). Будьте внимательны!
Материал будет похож на HOWTO
с примерами конфигов. Предварительные требования для создание связки Flask + Nginx + Gunicorn + Gevent - это работающее приложение Flask, построенное в виде пакета на локальном компьютере, сервер VDS с установленной OS Ubuntu или Debian с доступом по ssh от пользователя root
(или пользователя, имеющего привилегии sudo
). Для тестирования подойдет виртуальная машина развернутая на QEMU-KVM
или virtualbox
.
Немного статистики. Крупный популярный блог, исходный код которого правильно написан на Flask при работе в вышеуказанной связке и при параметрах VDS: 1 CPU, 1 Гб RAM и HDD на NVMe без особой напруги может выдержать до 25 тыс. уникальных посетителей в сутки (при интенсивности кликов до 45 тыс. сутки) + к этому суточная обработка контента сайта поисковыми роботами около 10 - 15 тыс. заходов в сутки. При этом загрузка сервера не будет превышать 65 - 70% (и все это без использования приложением кэша, дополнительной оптимизации Nginx и использовании БД MariaDB на настройках по умолчанию). Дополнительно можно посмотреть последний раздел материала "Асинхронность в веб-приложении на Flask в Python.".
Допустим, что локальное приложение Flask имеет следующую структуру и лежит в каталоге flask-app
:
/flask-app (папка с проектом) ... (вспомогательные файлы и папки проекта) /www (само приложение) __init__.py /static /views /templates ...
Flask
+Nginx
+Gunicorn
+Gevent
.Подключаемся к VDS по ssh и сначала обновим локальный индекс пакетов и установленную ОС, затем установим пакеты, которые позволят создать виртуальную среду Python. Так же установим несколько пакетов и средств разработки, необходимые для создания некоторых компонентов gunicorn
. Некоторые пакеты могут быть уже установлены, это зависит от провайдера, у которого арендуется VDS.
Откроем терминал подключенный к VDS (в данном случае от пользователя root
):
# обновляем индекс пакетов $ apt update # обновляем ОС $ apt upgrade # ставим необходимые пакеты $ apt install build-essential libssl-dev libffi-dev rsync python3-dev # следующие 2 пакета нужны для создания виртуальной # среды из Python, поставляемого с системой, если # будете компилировать Python, то их можно не ставить $ apt install python3-pip python3-venv # rsync - для синхронизации проекта # supervisor - для запуска проекта и поддержки в рабочем состоянии # nginx - легкий прокси сервер $ apt install rsync supervisor nginx
Внимание! Если приложение использует базу данных, то на этом этапе нужно установить необходимые пакеты, залить и настроить БД к использованию. Настройка какой-то конкретной БД здесь рассматриваться не будет.
Настроим виртуальное окружение, чтобы изолировать приложение Flask от других файлов Python в системе. Операционные системы Ubuntu и Debian уже поставляются с определенной версией Python3, и если приложению Flask требуется другая версия, то нужную версию Python3 можно скомпилировать из исходников. Для простоты, в виртуальную среду установим системную версию Python3, т.е. версию, установленную по умолчанию в системе.
# создаем каталоги $ mkdir -p /var/www/venv # устанавливаем системный python3 в виртуальное окружение $ python3 -m venv /var/www/venv --prompt VENV # активируем виртуальную среду $ source /var/www/venv/bin/activate (VENV) :~$
Первым делом нужно обновить менеджер пакетов pip
в только что установленном виртуальном окружении, а затем установить зависимости приложения Flask, предварительно скопировав файл зависимостей requirements.txt
на VDS, например, командой Linux scp
в директорию /var/www
. Файл зависимостей можно получить при помощи менеджера пакетов pip
, например запустив команду python3 -m pip freeze > requirements.txt
в активированной локальной виртуальной среде разработки.
# обновляем pip (VENV) :~$ python3 -m pip install -U pip # теперь ставим зависимости приложения (VENV) :~$ python3 -m pip install -r /var/www/requirements.txt
Далее ставим пакет gunicorn
с поддержкой gevent
.
(VENV) :~$ python3 -m pip install -U gunicorn[gevent]
Скопируем приложение Flask с локального компьютера на VDS в папку /var/www/flask-app
, которая будет располагаться рядом с виртуальной средой Python3. Почему рядом с виртуальным окружением, а не в него. Это делается для удобства. Например, приложение Flask перешло на новую версию Python3, который обеспечивает бОльшую производительность или в процессе рефакторинга внедрены новые функции из более новой версии Python3. В этом случае на VDS компилируем нужную версию Python3 из исходников, ставим опять рядом новое виртуальное окружение, в реальном времени (без остановки сервера) в настройках supervisor
переключаем приложение на новое окружение. Затем синхронизируем новую версию приложения с VDS и перезапускаем supervisor
. Поле проверки работоспособности удаляем старое окружение.
Приступим к синхронизации файлов приложения. Открываем терминал на локальной машине, где разрабатывалось приложение Flask. Копировать файлы на VDS будем утилитой rsync
(она должна быть установлена). При синхронизации файлов приложения можно дополнительно исключить такие файлы как .git
и т.д., добавив опцию --exclude 'file-or-dir-name'
# копируем файлы приложения на VDS по сети утилитой `rsync` $ rsync -r /local/path/to/flask-app root@10.10.10.10:/var/www/
Для справки: адрес удаленного сервера VDS записывается в формате:
имя_пользователя@ip_vds:/папка/на/vds
После синхронизации файлов приложения Flask, переключаем терминал на VDS и внутри папки с проектом создадим пустой файл настроек gunicorn.py
и папку для логов gunicorn
и supervisor
:
# пустой файл настроек `gunicorn` $ touch /var/www/flask-app/gunicorn.py # папка для логов, если нет $ mkdir /var/www/flask-app/logs
В итоге, на VDS должна получится следующая структура каталогов.
/www /venv (виртуальное окружение) /flask-app (папка с проектом) gunicorn.py (пустой файл настроек) /logs (папка для логов) /www (само приложение) __init__.py /static /views /templates ...
Здесь самое главное структура, чтобы gunicorn
без проблем подхватывал приложение Flask (имена папок могут быть любыми).
gunicorn
+ gevent
.Открываем файл /var/www/flask-app/gunicorn.py
и вставляем конфигурацию gunicorn
(все настройки прокомментированы).
# файл `/var/www/flask-app/gunicorn.py` import os.path import multiprocessing # определяем папку в которой лежит файл `gunicorn.py` BASEDIR = os.path.abspath(os.path.dirname(__file__)) # для связи с nginx будем использовать сокет # сокеты быстрее чем порты bind = f"unix://{BASEDIR}/logs/app.sock" # для обеспечения безопасности, установим # пользователя и группу под которыми # будет работать приложение Flask user = "www-data" group = "www-data" # выставляем уровень логирования `gunicorn` # логи бывают "debug", "info", "warning", "error", "critical" loglevel = "warning" # папка с ошибками запуска `gunicor`, сюда же # будут валиться ошибки в коде приложения Flask errorlog = f"{BASEDIR}/logs/gunicor.log" ######### WORKERS ######### # Кол-во воркеров вычисляется по формуле workers = multiprocessing.cpu_count() * 2 + 1 # для воркеров используем класс 'gevent' worker_class = 'gevent' # на каждого воркера закладываем по 2-5 асинхронных потоков, # в зависимости от "тяжести" приложения (подбирается опытным путем) worker_connections = workers * 3 # перезапускаем воркера, который не отвечает более 30 сек. timeout = 30 # ждем окончания запросов на соединении 15 секунд keepalive = 15
Меняем владельца приложения на www-data
(пользователь и группа):
# меняем владельца приложения $ chown -R www-data:www-data /var/www/flask-app # меняем права доступа к приложению $ chmod -R 0754 /var/www/flask-app
supervisor
.Создадим пустой файл конфигурации supervisor
для запуска приложения flask-app
.
$ touch /etc/supervisor/conf.d/flask-app.conf
Открываем файл /etc/supervisor/conf.d/flask-app.conf
и добавляем конфигурацию (все настройки детально прокомментированы).
# файл `/etc/supervisor/conf.d/flask-app.conf` # указываем имя приложения [program:flask-app] # папка с виртуальным окружением environment=PYTHONPATH=/var/www/venv/bin/ # команда запуска приложения command=/var/www/venv/bin/gunicorn -c /var/www/flask-app/gunicorn.py www:app # папка в которой лежит приложение directory = /var/www/flask-app/ # пользователь и группа от которых # будет работать приложение user=www-data group=www-data # логи запуска приложения stderr_logfile = /var/www/flask-app/logs/supervisor.log # автоматический старт приложения при сбое autostart=true autorestart=true
Обновляем конфигурацию supervisor
и пробуем запустить приложение командами (если приложение использует базу данных, то она должна быть настроена):
# обновляем конфигурацию $ supervisorctl update # заходим в CLI супервизора $ supervisorctl # должно появиться имя приложения flask-app # запускаем приложение supervisor> start flask-app flask-app: started # выходим из CLI supervisor> quit
Если приложение НЕ запустилось, то смотрим логи /var/www/flask-app/logs/supervisor.log
и /var/www/flask-app/logs/gunicorn.log
. Скорее всего, причина может быть в правах доступа или что-то напутано с путями. Если приложение запустилось, то появится файл сокета /var/www/flask-app/logs/app.sock
.
nginx
.Здесь будут рассмотрены только те настройки nginx
, которые отвечают за обслуживание приложения Flask. Рассмотрение других настроек оптимизации работы прокси-сервера nginx
не входят в тему данного материала (и вообще - это отдельная песня).
Создаем на VDS пустые файлы конфигурации nginx
для обслуживания приложения Flask:
# пустой файл конфигурации в папке `sites-available` touch /etc/nginx/sites-available/flask-app.ru # ссылка в папке `sites-enabled` на пустой файл конфигурации ln -s /etc/nginx/sites-available/flask-app.ru /etc/nginx/sites-enabled/flask-app.ru
Открываем пустой файл /etc/nginx/sites-available/flask-app.ru
и добавляем следующую конфигурацию nginx
.
Внимание! Nginx не поддерживает комментарии в конфигурационных файлах на кириллице, следовательно их необходимо убрать, иначе будут сыпаться ошибки.
# файл /etc/nginx/sites-available/flask-app.ru server { listen 80; server_name www.flask-app.ru; # перенаправление на домен без www # полезно для SEO оптимизации return 301 $scheme://flask-app.ru$request_uri; } server { listen 80; # настройка домена без www server_name flask-app.ru; # отдаем обслуживание статики NGINX # тем самым облегчаем работу Python location ^~ /static/ { root /var/www/flask-app/www; # кэшируем статические файлы на клиенте expires 7d; add_header Cache-Control "public"; # не пишем логи доступа к статике # тем самым облегчая работу HDD access_log off; log_not_found off; } # обслуживание самого приложения Flask location / { # подключаем параметры прокси-сервера, они находятся в файле # `/etc/nginx/proxy_params`, если нужно что-то оптимизировать include proxy_params; # проксируем запросы к приложению через сокет proxy_pass http://unix:/var/www/flask-app/logs/app.sock; } }
Далее проверяем правильность конфигурации nginx
:
$ nginx -t # nginx: the configuration file /etc/nginx/nginx.conf syntax is ok # nginx: configuration file /etc/nginx/nginx.conf test is successful
Если ошибок не обнаружено, то перезапустим процесс nginx
для чтения новой конфигурации:
$ systemctl restart nginx
Так как домен flask-app.ru
не делегирован, а проверить работу приложение в браузере по протоколу http
хочется, необходимо в файле hosts
локального компьютера прописать соответствие ip-адреса сервера VDS и домена flask-app.ru
. Другими словами, нужна следующая запись в конце файла hosts
:
... # ip_адрес_VDS domen www.domen 10.10.10.10 flask-app.ru www.flask-app.ru
После этого приложение Flask должно открыться в браузере по ссылке http://flask-app.ru
.
Если будут обнаружены любые ошибки, проверяем следующие файлы, расположенные на VDS:
/var/log/nginx/error.log
- журнал ошибок Nginx;/var/log/nginx/error.log
- журнал доступа Nginx;$ journalctl -u nginx
- журнал процессов Nginx;/var/www/flask-app/logs/supervisor.log
- журнал ошибок Supervisor;/var/www/flask-app/logs/gunicorn.log
- журнал ошибок Gunicorn, а также сюда попадают ошибки в коде приложения Flask. Примечание: Данное руководство было опробовано при разворачивании реального сервера. Все инструкции и файлы конфигурации рабочие и не зависят от версии операционной системы. Ошибки могут быть только в путях, если пользователь будет менять оригинальные пути на свои (например, где-то неправильно папку создал/вставил, где-то забыл что-то поменять и т.д.)
Что можно сделать еще?
https
.Чтобы обеспечить защиту трафика сервера, необходимо получить сертификат SSL для домена (перед этим домен должен быть делегирован). Этого можно добиться несколькими способами, в том числе получить бесплатный сертификат от Let’s Encrypt (в связи с санкциями, в России данная опция может быть не актуальна).
Добавим хранилище Certbot на VDS (для Ubuntu и Debian работает одинаково):
# нужно будет нажать ENTER для подтверждения $ add-apt-repository ppa:certbot/certbot # установим пакет Certbot Nginx $ apt install python-certbot-nginx
Пакет Certbot предоставляет широкий выбор способов получения сертификатов SSL с помощью плагинов. Плагин Nginx изменит конфигурацию Nginx и перезагрузит ее, когда это потребуется. Для использования этого плагина используем следующую команду:
# необходимо подставить свои доменные имена $ certbot --nginx -d flask-app.ru -d www.flask-app.ru
Эта команда запускает certbot
с плагином --nginx
, опция -d
служит для указания доменных имен, для которых получаем сертификат.
Если это первый запуск certbot
, то будет предложено указать адрес эл. почты и принять условия обслуживания. После этого certbot
свяжется с сервером Let’s Encrypt и отправит запрос с целью подтвердить, что вы контролируете домен, для которого запрашивается сертификат. Если контроль доменного имени будет подтвержден, то certbot
запросит предпочитаемый вариант настройки HTTPS.
После выбора желаемых настроек нужно нажать ENTER. Конфигурация будет обновлена, а Nginx перезапустится с новыми настройками. Затем certbot
завершит работу и выведет сообщение, указывающее место хранения сертификатов на VDS-сервере.
Все, теперь приложение будет работать по протоколу https
.