uWSGI
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 身份运行此命令。
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 服务器的后端。以下是配置示例。
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;
}
# ...
uwsgi_modifier1 参数列表。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
/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,使您的 application1 在 127.0.0.1 上提供服务。
运行 uWSGI
/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=yes在 Mailman 的 cgi 前端上不起作用!如果您想将其与 Mailman 配合使用,请删除此设置。 - 如果您想要进一步加固,建议使用命名空间 (namespaces)。您可以在 uWSGI 命名空间文档 中对该主题进行初步了解。
uWSGI 套接字的访问权限
uwsgi 中默认的(每个应用程序一个)套接字单元 (uwsgi@.socket) 允许系统上的任何用户进行读写访问。然而,systemd 允许更细粒度的访问管理(见 systemd.socket(5)),可以通过它使对 unix 套接字的访问更加严格。
通过在 /run 目录下创建一个 Web 应用特定的目录(需要预先使用 tmpfiles 创建 —— 参考 Web 应用程序包指南)并修改其 用户组 和文件 权限,该套接字将仅对 root 和 Web 服务器 可访问,并允许 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]。