Обслуживание приложений Flask с помощью uWSGI и Nginx в Ubuntu 18.04

Данный мануал поможет настроить простое приложение Python в микрофреймворке Flask на сервере Ubuntu 18.04. Также оно охватывает настройку сервера приложений uWSGI и обратного прокси-сервера Nginx.

Требования

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

Для начала нужно установить все зависимости из репозиториев: pip (пакетный менеджер Python) и инструменты разработки.

Обновите индекс пакетов и установите все пакеты, необходимые для создания среды Python:

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

2: Создание виртуальной среды

Теперь нужно изолировать приложение Flask от других файлов Python. Для этого используется виртуальная среда. Чтобы создать такую среду, установите инструмент venv:

sudo apt install python3-venv

Теперь нужно создать и открыть родительский каталог проекта Flask:

mkdir ~/myproject
cd ~/myproject

Создайте виртуальную среду для хранения файлов проекта 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.

pip install uwsgi flask

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

Теперь создайте простое приложение Flask.

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

Для примера создайте приложение, состоящее из одного файла, myproject.py:

nano ~/myproject/myproject.py

В этом файле будет храниться код приложения. Сюда нужно импортировать Flask и определить объект Flask. Так можно задать функции, которые нужно выполнить при запросе определённых маршрутов.

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

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

* 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://server_domain_or_IP:5000

На экране появится:

Hello There!

Чтобы остановить сервер разработки, нажмите CTRL-C в окне терминала.

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

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

Создайте файл wsgi.py:

nano ~/myproject/wsgi.py

Этот файл очень прост; импортируйте Flask и запустите его:

from myproject import app
if __name__ == "__main__":
app.run()

Сохраните и закройте файл.

4: Настройка и тестирование сервера uWSGI

Итак, приложение готово.

Теперь нужно убедиться, что uWSGI может обслуживать страницы приложения.

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

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

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

Откройте в браузере домен или IP-адрес сервера, указав порт :5000.

http://server_domain_or_IP:5000

На странице должно появиться:

Hello There!

Теперь можно остановить сервер приложений, нажав в окне терминала CTRL-C.

Приложение готово, можно отключить виртуальную среду:

deactivate

Теперь все операции будут выполняться в общей среде Python.

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

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

Создайте файл myproject.ini в каталоге проекта:

nano ~/myproject/myproject.ini

Добавьте раздел [uwsgi], в нём укажите точку входа:

[uwsgi]
module = wsgi:app

Затем настройте запуск uWSGI в режиме master. При этом будет запускаться пять рабочих процессов.

[uwsgi]
module = wsgi:app
master = true
processes = 5

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

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

[uwsgi]
module = wsgi:app
master = true
processes = 5
socket = myproject.sock
chmod-socket = 660
vacuum = true

В конец файла нужно добавить опцию die-on-term, благодаря которой система инициализации и uWSGI одинаково воспринимают сигналы процессов.

[uwsgi]
module = wsgi:app
master = true
processes = 5
socket = myproject.sock
chmod-socket = 660
vacuum = true
die-on-term = true

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

5: Создание юнит-файла systemd

После этого нужно создать файл сервиса для системы инициализации systemd. Такой файл позволяет системе инициализации автоматически запускать uWSGI и обслуживать приложение Flask.

Создайте юнит-файл с расширением .service в каталоге /etc/systemd/system:

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

Добавьте раздел [Unit], который определяет метаданные и зависимости приложения. Внесите в него описание сервиса.

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

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

[Unit]
Description=uWSGI instance to serve myproject
After=network.target
[Service]
User=8host
Group=www-data

Далее нужно установить переменную PATH, чтобы система инициализации знала, где искать исполняемые файлы (в виртуальной среде). Systemd требует полного пути к исполняемым файлам uWSGI в виртуальной среде.

Примечание: Не забудьте заменить условные данные своими.

[Unit]
Description=uWSGI instance to serve myproject
After=network.target
[Service]
User=8host
Group=www-data
WorkingDirectory=/home/8host/myproject
Environment="PATH=/home/8host/myproject/myprojectenv/bin"
ExecStart=/home/8host/myproject/myprojectenv/bin/uwsgi --ini myproject.ini

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

[Unit]
Description=uWSGI instance to serve myproject
After=network.target
[Service]
User=8host
Group=www-data
WorkingDirectory=/home/8host/myproject
Environment="PATH=/home/8host/myproject/myprojectenv/bin"
ExecStart=/home/8host/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
[Install]
WantedBy=multi-user.target

Cервис systemd готов. Сохраните и закройте файл.

Запустите сервис uWSGI и включите его автозапуск.

sudo systemctl start myproject
sudo systemctl enable myproject

Проверьте состояние сервиса:

sudo systemctl status myproject

Вы получите такой вывод:

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/8host/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
├─30378 /home/8host/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
├─30379 /home/8host/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
├─30380 /home/8host/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
├─30381 /home/8host/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
└─30382 /home/8host/myproject/myprojectenv/bin/uwsgi --ini myproject.ini

Если в этом выводе вы видите ошибки, исправьте их, прежде чем продолжить.

6: Настройка обратного прокси-сервера Nginx

Теперь приложение uWSGI готово. Настройте Nginx для передачи запросов на сокет с помощью протокола uwsgi.

Создайте новый блок server в каталоге sites-available:

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

Добавьте блок server. Задайте порт, который должен прослушивать веб-сервер (в данном случае – стандартный порт 80) и укажите доменное имя или IP в директиве server_name.

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

Затем нужно добавить блок location, который будет содержать файл uwsgi_params (в нем хранятся параметры uWSGI). Отправьте запрос настроенному сокету при помощи директивы uwsgi_pass:

server {
listen 80;
server_name your_domain www.your_domain;
location / {
include uwsgi_params;
uwsgi_pass unix:/home/8host/myproject/myproject.sock;
}
}

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

Чтобы включить блок server, нужно создать симлинк в каталог 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

На странице появится:

Hello There!

Если вы получили ошибку, проверьте файлы:

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

Читайте также:

Мы получим бесплатный доверенный сертификат от сервиса Let’s Encrypt с помощью клиента Certbot. Установите его:

sudo add-apt-repository ppa:certbot/certbot

Чтобы подтвердить установку, нажмите Enter.

Затем установите пакет Certbot для Nginx:

sudo apt install python-certbot-nginx

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

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

Команда запустит certbot с плагином —nginx, а опция –d задаст домен.

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

Если все прошло успешно, certbot спросит, нужно ли настроить HTTPS:

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 выведет сообщение о том, что процесс прошел успешно, и скажет, где найти сертификаты:

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://your_domain

Заключение

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

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

Tags: , , , , , , , , ,