Django.fun

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

Introduction

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

Prerequisites

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

  • Сервер с установленной 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.

Step 1 — Installing the Components from the Ubuntu Repositories

Нашим первым шагом будет установка всех необходимых нам компонентов из репозиториев 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 

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

Step 2 — Creating a Python Virtual Environment

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

Start by installing the python3-venv package, which will install the venv module:  

sudo apt install python3-venv 

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

mkdir ~/myproject 
cd ~/myproject 

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

python3.6 -m venv myprojectenv 

This will install a local copy of Python and pip into a directory called myprojectenv within your project directory.

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

source myprojectenv/bin/activate 

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

Step 3 — Setting Up a Flask Application

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

First, let’s install wheel with the local instance of pip to ensure that our packages will install even if they are missing wheel archives:  

pip install wheel 

Примечание

Regardless of which version of Python you are using, when the virtual environment is activated, you should use the pip command (not pip3).

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

pip install uwsgi flask 

Creating a Sample App

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

While your application might be more complex, we’ll create our Flask app in a single file, called 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) 

Visit your server’s IP address followed by :5000 in your web browser:

http://your_server_ip:5000

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

Flask sample app

When you are finished, hit CTRL-C in your terminal window to stop the Flask development server.

Creating the WSGI Entry Point

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

Let’s call the file wsgi.py:  

nano ~/myproject/wsgi.py 

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

~/myproject/wsgi.py

from myproject import app

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

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

Step 4 — Configuring uWSGI

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

Testing uWSGI Serving

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

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

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

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

Visit your server’s IP address with :5000 appended to the end in your web browser again:

http://your_server_ip:5000

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

Flask sample app

When you have confirmed that it’s functioning properly, press CTRL-C in your terminal window.

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

deactivate 

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

Creating a uWSGI Configuration File

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

Let’s place that file in our project directory and call it 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.

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

Step 5 — Creating a systemd Unit File

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

Create a unit file ending in .service within the /etc/systemd/system directory to begin:  

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 

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

Step 6 — Configuring Nginx to Proxy Requests

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

Begin by creating a new server block configuration file in Nginx’s sites-available directory. Let’s call this myproject to keep in line with the rest of the guide:  

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;
    }
}

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

To enable the Nginx server block configuration you’ve just created, link the file to the sites-enabled directory:  

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: checks the Nginx error logs.
  • sudo less /var/log/nginx/access.log: checks the Nginx access logs.
  • sudo journalctl -u nginx: checks the Nginx process logs.
  • sudo journalctl -u myproject: checks your Flask app’s uWSGI logs.

Step 7 — Securing the Application

Чтобы обеспечить безопасность трафика на вашем сервере, давайте получим 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 

You’ll need to press ENTER to accept.

Next, install Certbot’s Nginx package with apt:  

sudo apt install python-certbot-nginx 

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

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

This runs certbot with the --nginx plugin, using -d to specify the names we’d like the certificate to be valid for.

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

If that’s successful, certbot will ask how you’d like to configure your HTTPS settings.  

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' 

To verify the configuration, let’s navigate once again to your domain, using https://:

https://your_domain

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

Conclusion

В этом руководстве вы создали и обеспечили безопасность простого приложения 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

Поделитесь с другими: