Как обслуживать приложения Flask с помощью Gunicorn и Nginx на Ubuntu 22.04

A previous version of this tutorial was written by Kathleen Juell.

Введение

В этом руководстве вы создадите приложение на Python с использованием микрофреймворка Flask на Ubuntu 22.04. Большая часть этого руководства будет посвящена тому, как настроить сервер приложений Gunicorn и как запустить приложение, а также настроить Nginx для работы как обратного прокси-сервера.

Предварительные требования

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

  • Сервер с установленной Ubuntu 22.04 и обычным пользователем с правами sudo. Следуйте нашему руководству по начальной настройке сервера для руководства.

  • Установленный Nginx, следуя Шагам 1 и 2 из Как установить Nginx на Ubuntu 22.04.

  • Доменное имя настроено на ваш сервер. Вы можете приобрести его на Namecheap или получить бесплатно на Freenom. Вы можете узнать, как настроить домены на DigitalOcean, следуя соответствующей документации по доменам и DNS. Обязательно создайте следующие записи DNS:

    • Запись A с ваш_домен, указывающая на общедоступный IP-адрес вашего сервера.
    • Запись A с www.ваш_домен, указывающая на общедоступный IP-адрес вашего сервера.
  • Знакомство со спецификацией WSGI, которую сервер Gunicorn будет использовать для взаимодействия с вашим приложением Flask. Это обсуждение подробнее рассматривает WSGI.

Шаг 1 — Установка компонентов из репозиториев Ubuntu

Первым шагом будет установка всех необходимых компонентов из репозиториев Ubuntu. Это включает в себя pip, менеджер пакетов Python, который будет управлять компонентами Python. Вы также получите файлы разработки Python, необходимые для сборки некоторых компонентов Gunicorn.

Сначала обновите локальный индекс пакетов и установите пакеты, которые позволят вам создать ваше рабочее окружение Python. Среди них будут python3-pip, а также несколько других пакетов и инструментов разработки, необходимых для надежной программной среды:

  1. sudo apt update
  2. sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

После установки этих пакетов следующим шагом будет создание виртуальной среды для вашего проекта.

Шаг 2 — Создание виртуальной среды Python

Затем вы настроите виртуальное окружение, чтобы изолировать приложение Flask от других файлов Python на вашей системе.

Начните с установки пакета python3-venv, который установит модуль venv:

  1. sudo apt install python3-venv

Затем создайте родительский каталог для вашего проекта Flask. Перейдите в каталог с помощью команды cd после его создания:

  1. mkdir ~/myproject
  2. cd ~/myproject

Создайте виртуальное окружение для хранения требований вашего проекта Flask, набрав:

  1. python3 -m venv myprojectenv

Это установит локальную копию Python и pip в каталог с именем myprojectenv внутри вашего каталога проекта.

Перед установкой приложений в виртуальном окружении вам необходимо его активировать. Сделайте это, набрав:

  1. source myprojectenv/bin/activate

Ваш приглашение изменится, чтобы указать, что теперь вы работаете в виртуальном окружении. Оно будет выглядеть примерно так: (myprojectenv)user@host:~/myproject$.

Шаг 3 — Настройка приложения Flask

Теперь, когда вы находитесь в виртуальном окружении, вы можете установить Flask и Gunicorn и приступить к разработке вашего приложения.

Сначала установите wheel с локальным экземпляром pip, чтобы убедиться, что ваши пакеты установятся, даже если отсутствуют архивы wheel:

  1. pip install wheel

Примечание

Независимо от того, какую версию Python вы используете, когда виртуальное окружение активировано, вы должны использовать команду pip (а не pip3).

Затем установите Flask и Gunicorn:

  1. pip install gunicorn flask

Создание примерного приложения

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

Хотя ваше приложение может быть более сложным, мы создадим наше Flask-приложение в одном файле, называемом myproject.py:

  1. nano ~/myproject/myproject.py

Код приложения будет находиться в этом файле. Он будет импортировать Flask и создавать объект Flask. Вы можете использовать его, чтобы определить функции, которые должны выполняться, когда запрашивается определенный маршрут:

~/myproject/myproject.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'>Hello There!</h1>"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

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

Если вы следовали руководству по начальной настройке сервера, у вас должен быть включен брандмауэр UFW. Чтобы протестировать приложение, вам нужно разрешить доступ к порту 5000:

  1. sudo ufw allow 5000

Теперь вы можете протестировать свое Flask-приложение, набрав:

  1. python myproject.py

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

Output
* Serving Flask app "myproject" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

Посетите IP-адрес вашего сервера, за которым следует :5000, в вашем веб-браузере:

http://your_server_ip:5000

Вы должны увидеть что-то вроде этого:

Когда закончите, нажмите CTRL-C в вашем терминальном окне, чтобы остановить сервер разработки Flask.

Создание точки входа WSGI

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

Назовите файл wsgi.py:

  1. nano ~/myproject/wsgi.py

В этом файле импортируйте экземпляр Flask из нашего приложения, а затем запустите его:

~/myproject/wsgi.py
from myproject import app

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

Сохраните и закройте файл, когда закончите.

Шаг 4 — Настройка Gunicorn

Теперь ваше приложение написано с установленной точкой входа. Теперь можно перейти к настройке Gunicorn.

Прежде чем продолжить, проверьте, что Gunicorn может правильно обслуживать приложение.

Вы можете сделать это, передав ему имя точки входа приложения. Это формируется как имя модуля (без расширения .py), плюс имя вызываемой функции внутри приложения. В этом случае это wsgi:app.

Также укажите интерфейс и порт для привязки с помощью аргумента 0.0.0.0:5000, чтобы приложение запускалось на общедоступном интерфейсе:

  1. cd ~/myproject
  2. gunicorn --bind 0.0.0.0:5000 wsgi:app

Вы должны увидеть вывод подобный следующему:

Output
[2020-05-20 14:13:00 +0000] [46419] [INFO] Starting gunicorn 20.0.4 [2020-05-20 14:13:00 +0000] [46419] [INFO] Listening at: http://0.0.0.0:5000 (46419) [2020-05-20 14:13:00 +0000] [46419] [INFO] Using worker: sync [2020-05-20 14:13:00 +0000] [46421] [INFO] Booting worker with pid: 46421

Посетите IP-адрес вашего сервера с добавлением :5000 в конце веб-браузера снова:

http://your_server_ip:5000

Вы должны увидеть вывод вашего приложения:

Когда вы подтвердите, что он работает должным образом, нажмите CTRL-C в окне терминала.

Когда вы закончите использовать виртуальное окружение, вы можете его деактивировать:

  1. deactivate

Теперь любые команды Python будут снова использовать системное окружение Python.

Далее создайте файл службы systemd. Создание файла службы systemd позволит инициализационной системе Ubuntu автоматически запускать Gunicorn и обслуживать приложение Flask при каждой загрузке сервера.

Создайте файл юнита, заканчивающийся на .service внутри каталога /etc/systemd/system, чтобы начать:

  1. sudo nano /etc/systemd/system/myproject.service

Внутри вы начнете с раздела [Unit], который используется для указания метаданных и зависимостей. Добавьте здесь описание вашей службы и скажите инициализационной системе запускать это только после достижения цели сети:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

Затем добавьте раздел [Service]. Здесь будет указан пользователь и группа, от имени которых вы хотите запустить процесс. Предоставьте вашей обычной учетной записи владение процессом, поскольку она владеет всеми соответствующими файлами. Также предоставьте групповое владение группе www-data, чтобы Nginx мог легко общаться с процессами Gunicorn. Не забудьте заменить здесь имя пользователя вашим именем пользователя:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data

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

  • Запустить 3 рабочих процесса (однако вы можете отрегулировать это по необходимости)
  • Создать и привязать к файлу сокета Unix, myproject.sock, в пределах нашего каталога проекта. Мы установим значение маски доступа 007, чтобы файл сокета создавался с доступом только для владельца и группы, ограничивая доступ для других пользователей
  • Указать имя файла точки входа WSGI, а также вызываемый Python-объект в этом файле (wsgi:app)

Systemd требует указания полного пути к исполняемому файлу Gunicorn, который установлен в вашем виртуальном окружении.

Не забудьте заменить имя пользователя и пути проекта на свои:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

Наконец, добавьте секцию [Install]. Это скажет systemd, к чему следует привязать эту службу, если вы разрешите ей запускаться при загрузке. Вы захотите, чтобы эта служба запускалась, когда обычная многопользовательская система будет работать:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

С этим ваш файл службы systemd завершен. Сохраните его и закройте.

Теперь вы можете запустить созданную вами службу Gunicorn и разрешить ее запуск при загрузке:

  1. sudo systemctl start myproject
  2. sudo systemctl enable myproject

Давайте проверим статус:

  1. sudo systemctl status myproject

Вы должны увидеть вывод подобный следующему:

Output
● myproject.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2022-05-10 19:40:41 UTC; 9s ago Main PID: 17300 (gunicorn) Tasks: 4 (limit: 2327) Memory: 56.0M CPU: 514ms CGroup: /system.slice/myproject.service ├─17300 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─17301 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─17302 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app └─17303 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app May 10 19:40:41 r systemd[1]: Started Gunicorn instance to serve myproject. . . .

Если вы видите какие-либо ошибки, обязательно исправьте их, прежде чем продолжить обучение.

Шаг 5 — Настройка Nginx для проксирования запросов

Ваш сервер приложений Gunicorn должен быть запущен и ожидать запросов на сокетном файле в каталоге проекта. Теперь вы можете настроить Nginx для передачи веб-запросов на этот сокет, внесши небольшие изменения в его конфигурационный файл.

Начните с создания нового конфигурационного файла серверного блока в каталоге sites-available Nginx. Назовите его myproject, чтобы соответствовать остальной части руководства:

  1. sudo nano /etc/nginx/sites-available/myproject

Откройте серверный блок и скажите Nginx слушать порт по умолчанию 80. Также скажите ему использовать этот блок для запросов к доменному имени нашего сервера:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;
}

Затем добавьте блок location, который соответствует каждому запросу. В этом блоке вы будете включать файл proxy_params, который определяет некоторые общие параметры прокси, которые необходимо установить. Затем вы передадите запросы на сокет, который вы определили с помощью директивы proxy_pass:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/sammy/myproject/myproject.sock;
    }
}

Сохраните и закройте файл, когда закончите.

Чтобы включить только что созданную конфигурацию серверного блока Nginx, создайте ссылку на этот файл в каталоге sites-enabled:

  1. sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

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

  1. sudo nginx -t

Если это возвращается без указания каких-либо проблем, перезапустите процесс Nginx, чтобы прочитать новую конфигурацию:

  1. sudo systemctl restart nginx

Наконец, снова настройте брандмауэр. Вам больше не нужен доступ через порт 5000, поэтому вы можете удалить это правило. Затем вы можете разрешить полный доступ к серверу Nginx:

  1. sudo ufw delete allow 5000
  2. sudo ufw allow 'Nginx Full'

Теперь вы должны иметь возможность перейти на доменное имя вашего сервера в веб-браузере:

http://your_domain

Вы должны увидеть вывод вашего приложения:

Примечание: Вы получите ошибку HTTP 502 шлюза, если Nginx не сможет получить доступ к сокетному файлу gunicorn. Обычно это происходит из-за того, что домашний каталог пользователя не позволяет другим пользователям получать доступ к файлам внутри него.

Если ваш сокетный файл называется /home/sammy/myproject/myproject.sock, убедитесь, что /home/sammy имеет как минимум права 0755. Вы можете использовать инструмент, такой как chmod, чтобы изменить права, например, так:

  1. sudo chmod 755 /home/sammy

Затем перезагрузите страницу, чтобы увидеть, исчезла ли ошибка HTTP 502.

Если вы столкнетесь с какими-либо ошибками, попробуйте проверить следующее:

  • sudo less /var/log/nginx/error.log: проверяет журналы ошибок Nginx.
  • sudo less /var/log/nginx/access.log: проверяет журналы доступа Nginx.
  • sudo journalctl -u nginx: проверяет журналы процесса Nginx.
  • sudo journalctl -u myproject: проверяет журналы Gunicorn вашего приложения Flask.

Шаг 6 — Защита приложения

Чтобы обеспечить безопасность трафика на ваш сервер, давайте получим SSL-сертификат для вашего домена. Существует несколько способов сделать это, включая получение бесплатного сертификата от Let’s Encrypt, создание самоподписанного сертификата или покупку у другого провайдера и настройку Nginx для его использования, следуя Шагам с 2 по 6 из Как создать самоподписанный SSL-сертификат для Nginx в Ubuntu 22.04. Мы будем использовать первый вариант (Let’s Encrypt) для экспедиции.

Установите пакет Nginx Certbot с помощью apt:

  1. sudo apt install python3-certbot-nginx

Certbot предоставляет различные способы получения SSL-сертификатов через плагины. Плагин Nginx будет заботиться о перенастройке Nginx и перезагрузке конфигурации при необходимости. Чтобы использовать этот плагин, введите следующее:

  1. sudo certbot --nginx -d your_domain -d www.your_domain

Это запускает certbot с плагином --nginx, используя -d для указания имен, для которых мы хотели бы, чтобы сертификат был действителен.

Если это ваше первое запуск certbot, вам будет предложено ввести адрес электронной почты и согласиться с условиями обслуживания. После этого certbot свяжется с сервером Let’s Encrypt, затем выполнит проверку, чтобы убедиться, что вы контролируете домен, для которого запрашивается сертификат.

Если это проходит успешно, certbot спросит, как вы хотели бы настроить ваши HTTPS-настройки:

Output
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. ------------------------------------------------------------------------------- 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Выберите свой вариант, затем нажмите ENTER. Конфигурация будет обновлена, и Nginx перезагрузится, чтобы применить новые настройки. certbot завершит процесс сообщением о том, что он прошел успешно, и укажет местоположение ваших сертификатов:

Output
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain/privkey.pem Your cert will expire on 2020-08-18. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

Если вы следовали инструкциям по установке Nginx в предварительных требованиях, вам больше не понадобится избыточное разрешение профиля HTTP:

  1. sudo ufw delete allow 'Nginx HTTP'

Для проверки конфигурации снова перейдите на ваш домен, используя https://:

https://your_domain

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

Заключение

В этом руководстве вы создали и обеспечили безопасность простого приложения Flask виртуальной средой Python. Вы создали точку входа WSGI, чтобы любой сервер приложений, поддерживающий WSGI, мог взаимодействовать с ним, а затем настроили сервер приложений Gunicorn для обеспечения этой функции. После этого вы создали файл службы systemd для автоматического запуска сервера приложений при загрузке. Вы также создали блок сервера Nginx, который направляет трафик веб-клиента на сервер приложений, перенаправляя внешние запросы, и обеспечили безопасность трафика к вашему серверу с помощью Let’s Encrypt.

Flask – это очень простой, но крайне гибкий фреймворк, предназначенный для предоставления вашим приложениям функциональности, не ограничивая слишком сильно их структуру и дизайн. Вы можете использовать общий стек, описанный в этом руководстве, чтобы обслуживать созданные вами приложения Flask.

Source:
https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-22-04