uWSGI

uWSGI

Web Server Gateway Interface

Мы не будем подробно рассматривать что такое uWSGI. Цель статьи - запустить сайт написанный на python (Flask). Также подразумевается, что nginx уже установлен и активно используется.

На тему uWSGI написано много статей. Схема ниже показывает главную суть — реализация протокола WSGI для обеспечения взаимодействия между веб-сервером и конечными приложениями.

Web Server Gateway Interface

uWSGI, как и др. пакеты, можно установить из deb-пакетов:

sudo apt update
sudo apt install nginx uwsgi uwsgi-plugin-python

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

sudo systemctl enable uwsgi
service uwsgi status

Также можно произвести установку в виде модуля python. Примерно с 22.04 версии в Ubuntu введен запрет на установку python-модулей без активации виртуального окружения. Но, для того чтобы не использовать --break-system-packages, появилась утилита pipx:

sudo apt install pipx

pipx install uwsgi
sudo pipx ensurepath --global
command -v uwsgi >/dev/null 2>&1 || sudo ln -s ~/.local/bin/uwsgi /usr/bin/uwsgi && uwsgi --version

Если вы выберете этот способ, то для автоматического запуска uWSGI прийдется создать:

systemd unit

sudo touch /etc/systemd/system/myapp.service
sudo mcedit /etc/systemd/system/myapp.service
    # /etc/systemd/system/uwsgi.service
    [Unit]
    Description=uWSGI instance for myapp
    After=network.target

    [Service]
    User=www-data
    Group=www-data
    WorkingDirectory=/var/www/domain-or-subdomain
    ExecStart=/var/www/domain-or-subdomain/uwsgi --ini uwsgi.ini
    Restart=always
    KillSignal=SIGQUIT
    Type=notify
    NotifyAccess=all

    [Install]
    WantedBy=multi-user.target

После внесения изменений ниже нужно будет сделать enable/start для одноименного сервиса — myapp

Продолжаем. Создаем директории и конфиги:

touch /etc/nginx/sites-available/domain-or-subdomain.conf
ln -s /etc/nginx/sites-available/domain-or-subdomain.conf /etc/nginx/sites-enabled/

mkdir -p /var/www/domain-or-subdomain
touch /var/www/domain-or-subdomain/myapp.py 
touch /var/www/domain-or-subdomain/uwsgi.ini

sudo mkdir -p /var/log/uwsgi/ && touch /var/log/uwsgi/myapp.log
sudo chgrp -R www-data /var/log/uwsgi/myapp.log
sudo chmod g+rw /var/log/uwsgi/myapp.log

Содержимое конфига nginx:

server {
    listen 80;
    server_name your_domain.com;

    root /var/www/domain-or-subdomain;
    #index index.html index.htm;

    # Обслуживание статических файлов напрямую через Nginx
    location /static/ {
        alias /path/to/your/www-root/static/;
    }

    # Проксирование всех остальных запросов к uWSGI
    location / {
        include uwsgi_params;
        uwsgi_pass unix:/run/uwsgi/app/myapp/socket;

        uwsgi_param Host $host;
        uwsgi_param X-Real-IP $remote_addr;
        uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
        uwsgi_param X-Forwarded-Proto $scheme;
    }

    # Статистика веб-сервера (только для localhost)
    location /uwsgi-stats {
        allow 127.0.0.1;
        deny all;
        uwsgi_pass 127.0.0.1:9191;
    }

    location ~* \.(jpg|jpeg|gif|png|svg|webp)$ {
    expires 30d;
    add_header X-Cache-Status "Browser Cache";
}

    access_log /var/log/nginx/myapp_access.log;
    error_log /var/log/nginx/myapp_error.log;
}

Содержимое конфига uwsgi.ini:

[uwsgi]
uid = www-data
gid = www-data

chdir = /var/www/domain-or-subdomain
module = myapp:app
plugins = python3

# Включение мастер-процесса
master = true
processes = 4
enable-threads = true
threads = 2
max-requests = 6000
no-orphans = true
die-on-term = true

# Сокет для связи с Nginx (используется протокол uwsgi)
socket = unix:/run/uwsgi/app/myapp/socket
# Права на сокет (чтобы Nginx мог писать)
chmod-socket = 660
# Очистка сокета при остановке
vacuum = true

# Отключение логирования для статики
static-skip-ext = .jpg,.jpeg,.png,.gif,.ico,.css,.js

# Graceful restart при изменении кода
py-autoreload = 1

stats = 127.0.0.1:9191
memory-report = true

Сам скрипт, который будет отвечать на запросы:

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-

def app(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"<h1>Hello Master!</h1>"]

Теперь можно проверить работу сервера:

cd /var/www/domain-or-subdomain
sudo chown -R www-data:www-data /var/www/domain-or-subdomain
# sudo запустит сервис по днужным пользователем, если без sudo, то запуск произойдет от вашего имени
sudo uwsgi --http-socket :8080 --plugin python3 --module myapp:app 

Сайт должен отдавать контент по адресу: http://localhost:8080/ Если это так, то можно переходить к тестированию боевой схемы с nginx

Применяем новые конфигурации:

sudo nginx -t && sudo systemctl reload nginx
sudo uwsgi --ini /var/www/domain-or-subdomain/uwsgi.ini

Результат работы скрипта будет досткпен по доменному имени, который прописан в nginx. Но перестанет после перезагрузки системы. Давайте пропишем его как автозапускаемый сервис:

sudo mv /var/www/domain-or-subdomain/uwsgi.ini /etc/uwsgi/apps-available/myapp.ini
sudo ln -s /etc/uwsgi/apps-available/myapp.ini /etc/uwsgi/apps-enabled/myapp.ini

Применяем изменения и смотрим информацию о нашем приложении:

sudo systemctl restart uwsgi
sudo service uwsgi status myapp

Наслаждаемся результатом!

См. также