Больше не нужно искать работу мечты — присоединяйтесь к команде Клауда

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

Команда Timeweb Cloud
Команда Timeweb Cloud
Наши инженеры, технические писатели, редакторы и маркетологи
06 мая 2022 г.
2486
8 минут чтения
Средний рейтинг статьи: 3.7

Материал посвящен использованию Flask в Ubuntu. В рамках статьи рассмотрим основы функционирования Gunicorn и Nginx для развертывания прокси под Frontend.

Обслуживание Приложений Flask С Помощью Nginx И Gunicorn В Ubuntu (1)

Подготовка к проекту

Нам понадобится хост с предустановленной Ubuntu. Желательно создать на сервере обычного пользователя с правами sudo. Например, арендуйте готовую к эксплуатации удаленную машину у провайдера Timeweb Cloud.

Также понадобится:

  1. Инсталлировать веб-сервер Nginx.
  2. Зарегистрировать домен.
  3. Настроить записи DNS:
    • А с указанием публичного IP в графе your_domain.
    • А аналогично, но с добавлением www.

Желательно иметь представление о спецификации WSGI. Это поможет понять, как Gunicorn обменивается данными и командами с приложениями Flask.

Инсталлируем компоненты

В рамках подготовки к работе постановим менеджер pip, с его помощью мы будем манипулировать компонентами Python. Плюс скачаем файлы, требуемые для создания ряда элементов Gunicorn. Но предварительно скачаем обновления пакетов и установим python3-pip, он нужен для организации среды разработки:

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

Зададим настройки виртуальной среде Python

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

sudo apt install python3-venv

Вторым этапом сделаем родительскую директорию, где будем хранить весь проект Flask. И сразу перейдем в нее:

mkdir ~/myproject
cd ~/myproject

Третьим этапом организуем непосредственно виртуальную среду Python:

python3 -m venv myprojectenv

Все используемые впоследствии файлы будут сохраняться в папке myprojectenv. Остается провести активацию:

source myprojectenv/bin/activate

Результат будет заметен по изменившемуся виду командной строки. Она примет такой вид:

(myprojectenv)user@host:~/myproject$

Настроим приложение Flask

Обновим pip:

pip install -U pip

Следом загрузим Flask и Gunicorn:

pip install gunicorn flask

Создадим свой модуль

Теперь применим Flask для создания простейшей программы. Но важно учитывать, что данную микроструктуру чаще берут в качестве дополнительного модуля, т.к. в ней минимум инструментов из тех, что обычно актуальны при веб-разработке. В качестве примера создадим простой вариант под названием myproject.py:

nano ~/myproject/myproject.py

В указанный файл мы сохраним код программы. При запуске будет импортирован Flask и создан экземпляр объекта. Например, вот так:

from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
    return "<h >Hello!<h1>"
if __name__ == "__main__":
  app.run(host='0.0.0.0')

После внесения изменений закройте файл с сохранением. Теперь проверим работоспособность кода, но сначала настроим брандмауэр, чтобы тот разрешал трафик по порту 5000.

sudo ufw allow 5000

Теперь система готова к тесту программного модуля Flask:

python myproject.py

Теперь откроем наш домен по IP с указанием порта:

http://your_server_ip:5000

В окне браузера увидим «Hello!». Завершим сессию и отключим сервер нажатием комбинации <Ctrl+C> (в терминале).

Сделаем точку входа WSGI

Переходим к созданию файла, который послужит нам точкой входа в разрабатываемую программу. Он «подскажет» серверу Gunicorn, каким образом обмениваться данными с ним.

nano ~/myproject/wsgi.py

Перенесем экземпляр Flask в только что созданный файл, после чего откроем его:

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

По итогу закроем его.

Настроим Gunicorn

Перед продолжением работы убедимся, что сервер Gunicorn способен правильно обрабатывать нашу программу. Выполняется это передачей наименования точки входа без расширения и имени вызываемого элемента. Возьмем, например, wsgi:app. Еще в команде понадобится указать номер порта с интерфейсом:

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

Перейдем в браузер и введем IP нашего сервера с портом под номером 5000:

http://your_server_ip:5000

Программа выдаст сообщение «Hello!». Завершим работу нажатием кнопок <Ctrl + C> в окне терминала. И отключим виртуальную среду:

deactivate

После этого все команды Python будут иметь отношение только к общей системной среде. Если работать с виртуальной придется постоянно, есть смысл запускать ее автоматически вместе с операционкой. С этой целью сгенерируем файл автозагрузки. Сначала создадим *.service и разместим в папке /etc/systemd/system:

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

Первоначально [Unit], куда обычно вносят метаданные и зависимости, внесем описание службы и зададим условие инициализировать «виртуалку» после подтверждения наличия связи:

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

В блоке [Service] укажем аккаунт и группу, от имени которых будет запускаться процесс.

[Unit]
Description=Gunicorn instance to serve myproject
After=network.target
[Service]
User=username
Group=www-data

Следующим шагом укажем переменную PATH, чтобы при активизации система сразу «понимала», где брать требуемые файлы (в «виртуалке»).

[Unit]
Description=Gunicorn instance to serve myproject
After=network.target
[Service]
User=username
Group=www-data
WorkingDirectory=/home/username/myproject
Environment="PATH=/home/username/myproject/myprojectenv/bin"
ExecStart=/home/username/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

Следует учитывать, что для systemd нужно указывать полный путь к файлу Gunicorn, размещенному в «виртуалке».

В итоге добавим раздел [Install] и внесем в него:

[Unit]
Description=Gunicorn instance to serve myproject
After=network.target
[Service]
User=username
Group=www-data
WorkingDirectory=/home/username/myproject
Environment="PATH=/home/username/myproject/myprojectenv/bin"
ExecStart=/home/username/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app
[Install]
WantedBy=multi-user.target

Все, файл systemd подготовлен. Закройте его с сохранением изменений. И можно запускать службу Guricorn (сразу настроим ее загрузку вместе с операционкой):

sudo systemctl start myproject
sudo systemctl enable myproject

Остается проверить ее текущее состояние:

sudo systemctl status myproject

Настроим Nginx

Переходим к работе с веб-сервером Nginx (Gunicorn не используется в качестве фронтенд-сервера, и обычно настраивается реверс-прокси. В нашем случае это будет Nginx). Сначала создадим файл с конфигурацией в директории с sites-available, назовем его myproject.

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

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

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

Следом добавим location, где укажем proxy_params, определяющий параметры настраиваемого прокси.

server {
    listen 80;
    server_name your_domain www.your_domain;

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

Закроем файл с сохранением изменений. И применим новую конфигурацию путем привязки к директории sites-enabled.

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

Также добавим пользователя www-data в группу текущего пользователя. Для этого выполним:

sudo usermod -a -G ${USER} www-data

Проверим конфигурацию Nginx:

sudo nginx -t

И если все в порядке, перезапустим его:

sudo nginx -s reload

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

sudo ufw delete allow 5000
sudo ufw allow 'Nginx Full'

Попробуйте зайти на наш сервер из браузера: http://your_domain. Программа выдаст сообщение «Hello!»

Защитим наше решение

Последняя задача в этом материале – установка SSL сертификата для защиты трафика. Мы рекомендуем получить бесплатный SSL Let’s Encrypt с помощью Certbot. Рекомендуемый способ установки Certbot — через snap, который уже предустановлен в Ubuntu. 

Подробную инструкцию по выпуску SSL можно найти в официальной документации Certbot.

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

sudo snap install core; sudo snap refresh core

Если ранее вы устанавливали какие-то пакеты Certbot с помощью пакетного менеджера apt, удалите их командой:

sudo apt-get remove certbot

Установите Certbot через snap:

sudo snap install --classic certbot

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

sudo ln -s /snap/bin/certbot /usr/bin/certbot

Выполните команду ниже, чтобы получить сертификат и автоматически внести необходимые изменений в конфигурацию Nginx:

sudo certbot --nginx

Certbot будет автоматически обновлять сертификат. Вы можете протестировать обновление, запустив команду:

sudo certbot renew --dry-run

После проверьте открытие домена https://your_domain в браузере. Помимо традиционного результата в виде сообщения «Hello!», должен появиться замочек, подтверждающий защиту сайта.

Выводы

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

Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
06 мая 2022 г.
2486
8 минут чтения
Средний рейтинг статьи: 3.7
Комментарии 13
Ser.Makarov
Ser.Makarov
22.05.2024, 12:20

Здраствуйте. Столкнулся с проблемой исчезновения файла myproject.sock после выполнения команды sudo systemctl restart myproject Поддержка тактично "послала", возможно тут подскажете в чем может быть причина. Или, возможно кто-то сталкивался с аналогичной проблемой и смог ее решить... Все сделано 1:1 как в статье

Вообще было бы неплохо добавить описание решение возможных проблем в статье.

Timeweb
Timeweb
28.05.2024, 11:08

Добрый день! Сходу не удалось определить, в чем может быть проблема, ошибок в статье не нашли. Можете сообщить номер тикета? Попробуем разобраться.

Ser.Makarov
Ser.Makarov
03.06.2024, 13:07

Тикет №8804094 и Тикет №8923593 Только я уверен что это не решит проблемы, поскольку меня уже отфутболили, дважды!

Timeweb
Timeweb
10.06.2024, 12:38

Добрый день! Коллеги подготовили 2 способа, которые возможно помогут решить вашу проблему.

Важная информация! Поднимать сервис необходимо не через root-пользователя. Нужно создать нового пользователя и поднимать проект в его домашней директории /home/user. Также, устанавливаем следующие права на файлы:

sudo chown -R username:www-data /home/username/myproject
sudo chmod -R 775 /home/username/myproject

1 способ. Удаление файла myproject.sock Количество workers равно количеству ядер на сервере. Добавляем в файл /etc/systemd/system/myproject.service следующую строку, которая удаляет myproject.sock перед запуском сервиса: ExecStartPre=/bin/rm -f /home/username/myproject/myproject.sock После этого обновляем файл. 3.png А после перезагружаем процесс systemd:

sudo systemctl daemon-reload
sudo systemctl restart myproject

2 способ. Переход с unix-сокета на TCP-сокет Меняем строку ExecStart на следующую: ExecStart=/home/username/myproject/myprojectenv/bin/gunicorn -- workers 3 --bind 0.0.0.0:5000 wsgi:app Unix socket заменяется на 0.0.0.0:5000. 2.PNG А так выглядит конфигурация Nginx: 1.png Примечание: везде замените username на имя пользователя!

Toy Krutoy
Toy Krutoy
14.05.2024, 08:40

Проделал все шаги. Через 5000 порт при ручном запуске с активированной виртуалкой все работает. Запустить работу через домен не удалось. Вместе с поддержкой TimeWeb Cloud бились пол-дня и имеем: 502 Bad Gateway

Вот что написала поддержка:

> Проблема возникает из-за того, что nginx не может подключиться к socket-файлу. Вам необходимо сделать так, чтобы ваше приложение его запускало от имени и группы www-data, чтобы nginx имел доступ к нему. По возможности, рекомендую его перенести в /var/run для более удобной работы.

Можете подсказать как решить проблему?

Toy Krutoy
Toy Krutoy
14.05.2024, 10:25

Нашел решение в статье про Django: для решения проблемы нужно добавить пользователя www-data в группу текущего пользователя: sudo usermod -a -G ${USER} www-data Добавьте куда-нибудь в статью, наверное )

Timeweb
Timeweb
14.05.2024, 11:00

Добавили :blue_heart:

Toy Krutoy
Toy Krutoy
12.05.2024, 09:10

Добрый день! Начинаете с User=username (пятая строка в примере кода): В блоке [Service] укажем аккаунт и группу, от имени которых будет запускаться процесс.

[Unit]
Description=Gunicorn instance to serve myproject
After=network.target
[Service]
User=username
Group=www-data

А затем продолжаете с User=user: Следует учитывать, что для systemd нужно указывать полный путь к файлу Gunicorn, размещенному в «виртуалке».

В итоге добавим раздел [install] и внесем в него:

[Unit]
Description=Gunicorn instance to serve myproject
After=network.target
[Service]
User=user
Group=www-data
WorkingDirectory=/home/user/myproject
Environment=&#34;PATH=/home/user/myproject/myprojectenv/bin&#34;
ExecStart=/home/user/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app
[Install]
WantedBy=multi-user.target

Наверное где-то неправильно, или так и нужно?

Timeweb
Timeweb
13.05.2024, 06:44

Добрый день! Да, это ошибка, везде должен быть один и тот же пользователь. Спасибо, что сообщили :blue_heart: Мы исправили в статье.

Toy Krutoy
Toy Krutoy
14.05.2024, 05:18

Немного недоисправляли: 7, 8, 9 строчки тоже нужно user заменить на username

[Unit]
Description=Gunicorn instance to serve myproject
After=network.target
[Service]
User=username
Group=www-data
WorkingDirectory=/home/user/myproject
Environment=&#34;PATH=/home/user/myproject/myprojectenv/bin&#34;
ExecStart=/home/user/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app
[Install]
WantedBy=multi-user.target
Timeweb
Timeweb
14.05.2024, 05:23

Действительно. Спасибо :)

Alex Sergeev
Alex Sergeev
21.04.2024, 16:12

здравствуйте! проблемка, сайт поднялся но на публичном ip ничего, что делать?

Timeweb
Timeweb
23.04.2024, 05:18

Добрый день! В статье мы описывали размещение сайта на домене, но если вы хотите, чтобы сайт был доступен по публичному IP сервера, попробуйте указать в конфигурации Nginx listen ip_адрес_сервера:80 вместо listen 80:

server {
    listen ip_адрес_сервера:80;
    server_name your_domain www.your_domain;
}

После чего перезапустите Nginx:

sudo nginx -s reload