uWSGI

来自 ArchWiki

uWSGI 是一个用纯 C 语言编写的快速、自愈和对开发者/系统管理员友好的应用容器服务器。

还有用 Python 编写的替代品,例如 gunicorn

安装

安装 uwsgi 软件包。插件需要单独安装(它们的软件包名称以 uwsgi-plugin- 开头)。

配置

由 uWSGI 提供的 Web 应用程序/etc/uwsgi/ 中配置,其中每个应用程序都需要自己的配置文件(ini 样式)。详细信息可以在 uWSGI 文档中找到。

或者,您可以在 Emperor 模式下运行 uWSGI(在 /etc/uwsgi/emperor.ini 中配置)。它允许单个 uWSGI 实例使用单个主 Supervisor(称为 Emperor)运行一组不同的应用程序(称为 Vassal)。

注意: 插件必须在使用其选项之前显式加载,否则选项将无法识别。这可以通过 --plugins 命令行选项或配置文件中的 plugins 变量来完成。

Web 应用程序

uWSGI 支持多种不同的语言,因此也支持许多 Web 应用程序。作为示例,假设已存在配置文件 /etc/uwsgi/example.ini,并且已预先安装了您的 Web 应用程序所需的插件。

Python

以下是 Python 应用程序的简单示例。

/etc/uwsgi/example.ini
[uwsgi]
chdir = /srv/http/example
module = example
plugins = python

也可以使用以下语法单独运行 uWSGI,例如

$ uwsgi --socket 127.0.0.1:3031 --plugin python2 --wsgi-file ~/foo.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191 --uid --gid

您应该避免以 root 身份运行此命令。

注意: 请注意正在使用的操作模式,在没有 --lazy-apps 的情况下进行预派生可能会导致不明显的行为。默认情况下,Python 插件不会初始化 GIL。这意味着您的应用程序生成的线程将不会运行。如果您需要线程,请记住使用 enable-threads 启用它们。在多线程模式下(使用 threads 选项)运行 uWSGI 将自动启用线程支持。这种“奇怪”的默认行为是出于性能原因,这没什么可耻的。[1]

PHP

以下是基于 PHP 的网站的简单示例。

/etc/uwsgi/example.ini
[uwsgi]
; maximum number of worker processes
processes = 4
; the user and group id of the process once it’s started
uid = http
gid = http
socket = /run/uwsgi/%n.sock
master = true
chdir = /srv/http/%n
; php
plugins = php
; jail our php environment
php-docroot = /srv/http/%n
php-index = index.php
; clear environment on exit
vacuum = true

Web 服务器

uWSGI 可以是许多支持访问转发的 Web 服务器的后端。以下是一些配置示例。

注意: 建议通读 uWSGI 文档,以从性能和安全角度理解配置。

Nginx

nginx 可以根据您的 Web 应用程序将访问重定向到 unix 套接字或端口(在 localhost 或远程计算机上)。

/etc/nginx/example.conf
# ...
# forward all access to / towards 
location / {
  root /usr/share/nginx/html;
  index index.html index.htm;
  include uwsgi_params;
  # this is the correct uwsgi_modifier1 parameter for a php based application
  uwsgi_modifier1 14;
  # uncomment the following if you want to use the unix socket instead
  # uwsgi_pass unix:/var/run/uwsgi/example.sock;
  # access is redirected to localhost:3031
  uwsgi_pass 127.0.0.1:3031;
}
# ...
提示: 请查看 文档,了解适合您的 Web 应用程序的 uwsgi_modifier1 参数列表。

Nginx (在 chroot 中)

注意: 本节假设您已按照 Nginx#在 chroot 中安装 中的描述部署了 Nginx。假设 Nginx chroot 位于 /srv/http 中。

首先创建 ini 文件,该文件将指向您的应用程序

/etc/uwsgi/application1.ini
[uwsgi]
chroot = /srv/http
chdir = /www/application1
wsgi-file = application1.py
plugins = python
socket = /run/application1.sock
uid = http
gid = http
threads = 2
stats = 127.0.0.1:9191
vacuum = true

由于我们正在 chroot 到 /srv/http,因此上述配置将导致创建以下 unix 套接字 /srv/http/run/application1.sock

注意
  • 您的应用程序必须放置在 /srv/http/www/application1 中,然后才能启动服务。根据配置,您的应用程序可能会被缓存,因此您可能需要在修改后重新启动服务。
  • 如果您正在部署 python 应用程序,您可能需要复制标准 python 库 - 如果您在 python 3 下开发,则可以从 /usr/lib/python3.6 复制到 /srv/http/lib/python3.6

您可以尝试运行以下命令

# cp -r -p /usr/lib/python3.6 /srv/http/lib
# cp -r -p /usr/lib/*python*so /srv/http/lib

您将需要在您的服务文件中禁用通知

/etc/systemd/system/multi-user.target.wants/uwsgi\@application1.service
[Unit]
Description=uWSGI service unit

[Service]
PIDFile=/run/%I.pid
RemainAfterExit=yes
ExecStart=/usr/bin/uwsgi --ini /etc/uwsgi/%I.ini
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -INT $MAINPID
Restart=always
StandardError=syslog
KillSignal=SIGQUIT

[Install]
WantedBy=multi-user.target
注意: PID 文件将在 /run 而不是 /srv/http/run 中创建

修改后,请确保 重新加载 以合并新的或更改的单元。

然后,您可以自由地 启用启动 uwsgi@application1.service

编辑 /srv/http/etc/nginx/nginx.conf 并在其中添加新的 server 部分,该部分至少应包含以下内容

/srv/http/etc/nginx/nginx.conf
...
    server
    {
        listen       80;
        server_name  127.0.0.1;
        location /
        {
            root   /www/application1;
            include uwsgi_params;
            uwsgi_pass unix:/run/application1.sock;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html
        {
            root   /usr/share/nginx/html;
        }
    }
...

确保现在 重启 nginx.service 以使您的 application1127.0.0.1 上提供服务。

运行 uWSGI

注意: 这假设使用的 Web 应用程序已正确配置,正在由您的 Web 服务器提供服务,Web 服务器将其重定向到它正在使用的套接字或端口,并且已在 /etc/uwsgi/ 中配置。为简单起见,配置文件名应仅包含字母数字字符和 _,以避免转义问题,请参阅 systemd.unit(5) § STRING ESCAPING FOR INCLUSION IN UNIT NAMES

如果您计划一直使用 Web 应用程序(而无需按需激活),则可以简单地 启动启用 uwsgi@example

如果您计划按需启动 Web 应用程序,则可以 启动启用 uwsgi@example.socket

要使用 Emperor 模式,请 启动启用 emperor.uwsgi.service

要使用此模式的套接字激活,请 启动启用 emperor.uwsgi.socket

技巧和窍门

uWSGI 提供的一些功能无法通过使用 官方存储库中提供的 systemd 服务文件来访问。以下部分解释了对它们的更改。有关更多信息,请参见 [2]

套接字激活

使用套接字激活,您希望

  • 将您的 Web 服务器定向到 unix 套接字,从而启动运行应用程序的 uWSGI 实例
  • 您很可能希望在某个空闲时间后由 uWSGI 关闭应用程序
  • 您希望您的 Web 服务器能够在再次访问应用程序时重新启动它

uWSGI 提供了设置,您可以使用这些设置让实例关闭应用程序

/etc/uwsgi/example.ini
[uwsgi]
# ...

# set idle time in seconds
idle = 600
# kill the application after idle time was reached
die-on-idle = true

# ...

但是,当前的 uwsgi@.service 文件不允许这样做,因为 systemd 将非零退出代码视为失败,从而将单元标记为失败,并且 Restart=always 指令使空闲时间后关闭变得无用。解决此问题的方法是将 uWSGI 在自行关闭应用程序后可能提供的退出代码添加到 systemd 将视为成功的列表中,方法是使用 SuccessExitStatus 指令(有关更多信息,请参见 [3])。

/etc/systemd/system/uwsgi-socket@.service
[Unit]
Description=uWSGI service unit

[Service]
ExecStart=/usr/bin/uwsgi --ini /etc/uwsgi/%I.ini
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -INT $MAINPID
Type=notify
SuccessExitStatus=15 17 29 30
StandardError=syslog
NotifyAccess=all
KillSignal=SIGQUIT

[Install]
WantedBy=multi-user.target

这将允许使用 kill-after-idle 功能进行正确的套接字激活。

强化 uWSGI 服务

Web 应用程序暴露在野外,并且根据其质量和底层语言的安全性,有些应用程序的运行比其他应用程序更危险。处理可能不安全的 Web 应用程序的一个好方法是将其隔离。 systemd 具有一些可以使用的功能。请看以下示例(有关更多信息,请参见 systemd.exec(5)[4]

/etc/systemd/system/uwsgi-secure@.service
[Unit]
Description=uWSGI service unit

[Service]
ExecStart=/usr/bin/uwsgi --ini /etc/uwsgi/%I.ini
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -INT $MAINPID
Type=notify
SuccessExitStatus=15 17 29 30
StandardError=syslog
NotifyAccess=all
KillSignal=SIGQUIT
PrivateDevices=yes
PrivateTmp=yes
ProtectSystem=full
ReadWritePaths=/etc/webapps /var/lib/
ProtectHome=yes
NoNewPrivileges=yes

[Install]
WantedBy=multi-user.target
注意
  • 使用 NoNewPrivileges=yes 不适用于 Mailman 的 cgi 前端!如果您想将其与它结合使用,请删除此设置。
  • 如果您想要进一步强化,建议使用命名空间。您可以在 uWSGI 命名空间文档中初步了解该主题。

uWSGI 套接字的可访问性

uwsgi 中的默认(每个应用程序)套接字单元 (uwsgi@.socket) 允许系统上的任何用户进行读写访问。但是,systemd 允许更精细的访问管理(请参见 systemd.socket(5)),通过它可以使对 unix 套接字的访问更具限制性。

通过在 /run 下的 Web 应用程序特定目录下方创建它(需要事先使用 tmpfiles 创建 - 有关参考,请参见 Web 应用程序软件包指南)并修改其 和文件权限,套接字仅对 rootWeb 服务器 可访问,并允许 Web 应用程序以其自己的用户身份运行

/etc/systemd/system/uwsgi-secure@.socket
[Unit]
Description=Socket for uWSGI %I

[Socket]
ListenStream=/run/%I/%I.sock
SocketGroup=http
SocketMode=0660

[Install]
WantedBy=sockets.target

故障排除

Apache httpd

AH00957: uwsgi: 尝试连接到 127.0.0.1:0 (*) 失败

默认的 uWSGI 端口 (3031) (目前?) 不适用于 Apache httpd 服务器。请参阅 [5] 了解详细信息。

参见