uWSGI

来自 ArchWiki
(重定向自 Uwsgi)

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

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

安装

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

配置

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

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

注意: 插件必须先显式加载才能使用其选项,否则这些选项将无法识别。这可以通过 --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 套接字或端口(在本地主机或远程计算机上)。

/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#Installation in a 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,以便在 127.0.0.1 上提供你的 application1

运行 uWSGI

注意: 这假设所使用的 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@.socket) 在 uwsgi 中允许系统上的任何用户进行读取和写入访问。但是,systemd 允许更精细的访问管理(参见 systemd.socket(5)),通过它可以更严格地限制对 unix 套接字的访问。

通过在 /run 下的 webapp 特定目录(需要预先使用 tmpfiles 创建 - 有关参考,请参阅 Web application package guidelines)下创建它,并修改其 和文件 权限,该套接字仅对 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]

参见