跳转至内容

uWSGI

来自 ArchWiki

uWSGI 是一个快速、具有自我修复能力且对开发人员/系统管理员友好的应用程序容器服务器,完全由 C 语言编写。

也有一些用 Python 编写的替代方案,例如 gunicorn

安装

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

配置

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

或者,您可以使用 Emperor 模式 运行 uWSGI(在 /etc/uwsgi/emperor.ini 中配置)。它允许单个 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 的情况下进行 preforking 可能会导致非直观的行为。默认情况下,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

修改后,请确保 重新加载 (reload) 以纳入新的或更改的单元。

然后您可以自由地 启用 (enable)启动 (start) 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;
        }
    }
...

现在请确保 重启 (restart) 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]

套接字激活 (Socket activation)

使用套接字激活,您希望

  • 将您的 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 将非零退出代码视为失败,从而将单元标记为 failed,此外 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 应用程序的一个好方法是将它们隔离(jail)。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=yesMailman 的 cgi 前端上不起作用!如果您想将其与 Mailman 配合使用,请删除此设置。
  • 如果您想要进一步加固,建议使用命名空间 (namespaces)。您可以在 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: attempt to connect to 127.0.0.1:0 (*) failed

默认的 uWSGI 端口 (3031)(目前?)与 Apache httpd 服务器不兼容。详情请参阅 [5]

参见

© . This site is unofficial and not affiliated with Arch Linux.

Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.