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

Вступление

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

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

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

  • Сервер с установленной Ubuntu 18.04 и пользователем без права root с привилегиями sudo. Следуйте нашему руководству по первоначальной настройке сервера для получения рекомендаций.
  • Nginx установлен, следуя шагам 1 и 2 Как установить Nginx на Ubuntu 18.04.
  • Доменное имя, настроенное для указания на ваш сервер. Вы можете приобрести его на Namecheap или получить бесплатно на Freenom. Вы можете узнать, как указать домены на DigitalOcean, изучив соответствующую документацию о доменах и DNS. Обязательно создайте следующие записи DNS:

    • Запись A с ваш_домен, указывающая на публичный IP-адрес вашего сервера.
    • Запись A с www.ваш_домен, указывающая на публичный IP-адрес вашего сервера.
  • Знакомство с uWSGI, нашим сервером приложений, и спецификацией WSGI. This discussion of definitions and concepts goes over both in detail.

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

Нашим первым шагом будет установка всех необходимых нам компонентов из репозиториев Ubuntu. Мы установим pip, менеджер пакетов Python, для управления нашими компонентами Python. Мы также получим файлы разработки Python, необходимые для создания uWSGI.

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

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

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

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

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

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

sudo apt install python3-venv 

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

mkdir ~/myproject 
cd ~/myproject 

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

python3.6 -m venv myprojectenv 

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

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

source myprojectenv/bin/activate 

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

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

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

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

pip install wheel 

Примечание

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

Следующим шагом установим Flask и uWSGI: 

pip install uwsgi flask 

Создание образца приложения

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

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

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:  

sudo ufw allow 5000 

Сейчас вы можете протестировать свое приложение Flask, набрав текст:  

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

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

Flask sample app

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

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

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

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

nano ~/myproject/wsgi.py 

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

~/myproject/wsgi.py

from myproject import app

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

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

Шаг 4 - Настройка uWSGI

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

Тестирование обслуживания uWSGI

Давайте проверим, что uWSGI может обслуживать наше приложение.

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

Давайте также укажем сокет, чтобы он был запущен на общедоступном интерфейсе, а также протокол, чтобы он использовал HTTP вместо бинарного протокола uwsgi. Мы будем использовать тот же номер порта, 5000, который мы открыли ранее:  

uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi:app 

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

http://your_server_ip:5000

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

Flask sample app

Убедившись, что он работает правильно, нажмите CTRL-C в окне терминала.

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

deactivate 

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

Создание файла конфигурации uWSGI

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

Поместим этот файл в каталог нашего проекта и назовем его myproject.ini:

nano ~/myproject/myproject.ini 

Внутри мы начнем с заголовка [uwsgi], чтобы uWSGI знал, что нужно применить настройки. Мы укажем две вещи: сам модуль, ссылаясь на файл wsgi.py за вычетом расширения, и вызываемый модуль внутри файла, app:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

Следующим шагом мы скажем uWSGI запуститься в режиме мастера и породить пять рабочих процессов для обслуживания реальных запросов:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

master = true
processes = 5

Во время тестирования вы открыли uWSGI на сетевом порту. Однако вы собираетесь использовать Nginx для обработки реальных клиентских соединений, который затем будет передавать запросы uWSGI. Поскольку эти компоненты работают на одном компьютере, предпочтительнее использовать сокет Unix, поскольку он быстрее и безопаснее. Давайте назовем сокет myproject.sock и поместим его в эту директорию.

Давайте также изменим разрешения на сокет. Позже мы передадим группе Nginx право собственности на процесс uWSGI, поэтому нам нужно убедиться, что владелец сокета может читать информацию из него и записывать в него. Мы также очистим сокет после остановки процесса, добавив опцию vacuum:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

master = true
processes = 5

socket = myproject.sock
chmod-socket = 660
vacuum = true

Последнее, что мы сделаем, это установим опцию die-on-term. Это поможет убедиться, что система init и uWSGI имеют одинаковые предположения о том, что означает каждый сигнал процесса. Установка этого параметра выравнивает два компонента системы, реализуя ожидаемое поведение:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

master = true
processes = 5

socket = myproject.sock
chmod-socket = 660
vacuum = true

die-on-term = true

Вы, возможно, заметили, что мы не указали протокол, как это было в командной строке. Это потому, что по умолчанию uWSGI использует протокол uwsgi, быстрый двоичный протокол, предназначенный для связи с другими серверами. Nginx может использовать этот протокол как родной, поэтому лучше использовать его, чем принудительно обмениваться данными по HTTP.

Когда вы закончите, сохраните и закройте файл.

Шаг 5 - Создание файла модуля systemd

Следующим шагом будет создание файла единицы службы systemd. Создание файла systemd unit позволит системе init Ubuntu автоматически запускать uWSGI и обслуживать приложение Flask при каждой загрузке сервера.

Для начала создайте файл модуля с расширением .service в каталоге /etc/systemd/system:

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

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

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

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

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data

Следующее, давайте определим рабочий каталог и установим переменную окружения PATH, чтобы система init знала, что исполняемые файлы для процесса находятся в нашей виртуальной среде. Давайте также укажем команду для запуска службы. Systemd требует, чтобы мы указали полный путь к исполняемому файлу uWSGI, который установлен в нашей виртуальной среде. Мы передадим имя конфигурационного файла .ini, который мы создали в каталоге нашего проекта.

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

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI 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/uwsgi --ini myproject.ini

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

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI 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/uwsgi --ini myproject.ini

[Install]
WantedBy=multi-user.target

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

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

sudo systemctl start myproject 
sudo systemctl enable myproject 

Давайте проверим состояние дел:  

sudo systemctl status myproject 

Вы должны увидеть результат, подобный этому:  

Output

● myproject.service - uWSGI instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2018-07-13 14:28:39 UTC; 46s ago Main PID: 30360 (uwsgi) Tasks: 6 (limit: 1153) CGroup: /system.slice/myproject.service ├─30360 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini ├─30378 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini ├─30379 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini ├─30380 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini ├─30381 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini └─30382 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini 

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

Шаг 6 - Настройка Nginx для запросов прокси

Наш сервер приложений uWSGI теперь должен быть запущен и ожидать запросов на сокете в директории проекта. Давайте настроим Nginx на передачу веб-запросов на этот сокет с использованием протокола uwsgi.

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

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

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

/etc/nginx/sites-available/myproject

server {
    listen 80;
    server_name your_domain www.your_domain;
}

Следующим шагом добавим блок расположения, который соответствует каждому запросу. Внутри этого блока мы включим файл uwsgi_params, который определяет некоторые общие параметры uWSGI, которые необходимо установить. Затем мы передадим запросы на сокет, который мы определили с помощью директивы uwsgi_pass:

/etc/nginx/sites-available/myproject

server {
    listen 80;
    server_name your_domain www.your_domain;

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/home/sammy/myproject/myproject.sock;
    }
}

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

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

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

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

sudo nginx -t 

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

sudo systemctl restart nginx 

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

sudo ufw delete allow 5000 

sudo ufw allow 'Nginx Full' 

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

http://your_domain

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

Flask sample app

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

  • 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:проверяет журналы uWSGI вашего приложения Flask.

Шаг 7 - Защита приложения

Чтобы обеспечить безопасность трафика на вашем сервере, давайте получим SSL-сертификат для вашего домена. Существует несколько способов сделать это, включая получение бесплатного сертификата от Let's Encrypt, генерацию самоподписанного сертификата или покупку сертификата у другого поставщика и настройку Nginx на его использование, следуя шагам со 2 по 6 из How to Create a Self-signed SSL Certificate for Nginx in Ubuntu 18.04. В целях целесообразности мы остановимся на первом варианте.

Сначала добавьте репозиторий Certbot Ubuntu:  

sudo add-apt-repository ppa:certbot/certbot 

Вам нужно будет нажать ENTER, чтобы принять.

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

sudo apt install python-certbot-nginx 

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

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 2018-07-23. 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:  

sudo ufw delete allow 'Nginx HTTP' 

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

https://your_domain

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

Заключение

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

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

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

Вернуться на верх