翻译
Running a Flask Application as a Service with Systemd
在服务器上部署应用程序时,须要确保应用程序不间断地运行。如果应用程序解体,则心愿它主动重启,如果服务器断电,则心愿该应用程序在复原电源后立刻启动。基本上,您须要的是监督应用程序并在发现不再运行时将其重启。
在以前的教程中,我向你展现了如何应用 supervisord(一种用 Python 编写的第三方实用程序)实现此性能。明天,我将向您展现基于 systemd 的相似解决方案,它是许多 Linux 发行版中的本地组件,包含 Debian 衍生产品(如 Ubuntu)和 RedHat 衍生产品(如 Fedora 和 CentOS)。
应用 Systemd 配置服务
Systemd 通过称为 unit
的实体进行配置。有几种类型的 unit,包含服务,套接字,设施,计时器等。对于服务,unit 配置文件必须具备.service 扩展名。在上面,你能够看到服务 unit 配置文件的根本构造:
[Unit]
Description=<a description of your application>
After=network.target
[Service]
User=<username>
WorkingDirectory=<path to your app>
ExecStart=<app start command>
Restart=always
[Install]
WantedBy=multi-user.target
[Unit]
局部是所有类型的 unit 配置文件的公共局部。它用于配置对于 unit 和任何依赖项的个别信息,这些信息有助于零碎确定启动程序。在我的模板中,我增加了服务的形容,并且我还指定我心愿我的应用程序在网络子系统初始化后启动,因为它是一个 web 应用程序。
[Service]
局部蕴含了特定于你的应用程序的详细信息。我应用最常见的选项来定义运行服务的用户、起始目录和执行命令。Restart
选项通知 systemd,除了在系统启动时启动服务外,如果应用程序退出,我还心愿重新启动它。这样能够解决解体或其余可能导致过程完结的意外问题。
最初,[Install]
局部将配置启用该 unit 的形式和工夫。通过增加 WantedBy=multi-user.target
行我通知 systemd 在零碎以多用户模式运行时激活这个 unit,这是 Unix 服务器在运行时的失常模式。如果你想理解更多对于多用户模式的细节,请参阅对于 Unix runlevels 的探讨。
unit 配置文件增加到 /etc/systemd/system 目录中,供 systemd 查看。每次增加或批改单元文件时,必须通知 systemd 刷新其配置:
$ sudo systemctl daemon-reload
而后,您能够应用 systemctl <action> <service-name>
命令启动、进行、重新启动或取得服务状态:
$ sudo systemctl start <service-name>
$ sudo systemctl stop <service-name>
$ sudo systemctl restart <service-name>
$ sudo systemctl status <service-name>
留神: 能够应用 service <service-name> <action> 命令来治理服务,而不是应用 systemctl。在大多数发行版中,service 命令映射到 systemctl 并给出雷同的后果。
为 Flask 应用程序编写零碎配置文件
如果你想为你本人的应用程序创立一个 systemd 服务文件,只须要应用下面的模板并填写 Description
, User
, WorkingDirectory
和 ExecStart
即可。
作为一个例子,假如我想在 Linux 服务器上部署 Flask Mega-Tutorial 中提到的 microblog 应用程序,然而我想应用 systemd 来监督这个 process,而不是应用 supervisord。
作为你的参考,这里是我在教程中应用的 supervisord 配置文件:
[program:microblog]
command=/home/ubuntu/microblog/venv/bin/gunicorn -b localhost:8000 -w 4 microblog:app
directory=/home/ubuntu/microblog
user=ubuntu
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
systemd 的等效单元配置文件将写入 /etc/systemd/system/microblog.service 中,并将具备以下内容:
[Unit]
Description=Microblog web application
After=network.target
[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/microblog
ExecStart=/home/ubuntu/microblog/venv/bin/gunicorn -b localhost:8000 -w 4 microblog:app
Restart=always
[Install]
WantedBy=multi-user.target
请留神,启动命令如何达到虚拟环境外部以获取可执行 gunicorn
。这等效于激活虚拟环境,而后在没有门路的状况下运行 gunicorn,然而这样做的益处是能够在单个命令中实现。
将这个文件增加到你的零碎后,你能够应用以下命令启动服务:
$ sudo systemctl daemon-reload
$ sudo systemctl start microblog
环境变量
如果 Flask 应用程序心愿提前设置一个或多个环境变量,那么能够将它们增加到服务文件中。例如,如果须要设置 FLASK_CONFIG
和 DATABASE_URL
变量,能够应用 Environment 选项定义它们如下:
[Unit]
Description=Microblog web application
After=network.target
[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/microblog
Environment=FLASK_CONFIG=production
Environment=DATABASE_URL=sqlite:////path/to/the/database.sqlite
ExecStart=/home/ubuntu/microblog/venv/bin/gunicorn -b localhost:8000 -w 4 microblog:app
Restart=always
[Install]
WantedBy=multi-user.target
请留神,如果你遵循我的教程格调,并为环境变量应用.env 文件,则无需通过 systemd 服务文件增加它们。实际上,我更喜爱通过.env 文件解决环境,因为这是一种实用于开发和生产的对立办法。
拜访日志
Systemd 有一个称为 journal 的日志记录子系统,由 journald 守护过程实现,它收集所有正在运行的 Systemd 单元的日志。能够应用 journalctl 实用工具查看日记的内容。上面是一些常见日志拜访命令的示例。
查看 microblog 服务的日志:
$ journalctl -u microblog
查看 microblog 服务的最初 25 个日志条目:
$ journalctl -u microblog -n 25
跟踪 microblog 服务的日志:
$ journalctl -u microblog -f
还有更多的选项可用。运行 journalctl --help
查看更残缺的选项摘要。
高级用法: 应用 Systemd 的运行 Worker Pools
如果你应用 Celery 运行后盾过程,则将上述解决方案扩大到实用于你的 workers 是很简略,因为 Celery 容许你应用单个命令启动 worker 过程池。这实际上与解决带有多个 worker 的 gunicorn 的形式雷同,因而您要做的就是创立第二个.service 文件来治理 Celery 主过程,该文件又将治理 worker。
然而,如果你读到了我的 Flask Mega-Tutorial 的最初几章,你就会晓得我曾经引入了一个基于 RQ 的工作队列来执行后台任务。应用 RQ 时,您必须独自启动 workers,没有主流程能够为你治理 workers pool。这是我在教程中应用 supervisor 治理 RQ workers 的办法:
[program:microblog-tasks]
command=/home/ubuntu/microblog/venv/bin/rq worker microblog-tasks
numprocs=1
directory=/home/ubuntu/microblog
user=ubuntu
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
在这里,numprocs 参数使你能够依据须要启动任意数量的 worker。通过此参数,supervisor 将从单个配置文件启动并监督指定数量的实例。
可怜的是,在 systemd 中没有 numprocs 选项,因而这种类型的服务须要不同的解决方案。最简略的办法是为每个工作实例创立一个独自的服务文件,然而这样做会很麻烦。相同,我要做的是将服务文件创建为模板,可用于启动所有这些雷同的实例:
[Unit]
Description=Microblog task worker %I
After=network.target
[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/microblog
ExecStart=/home/ubuntu/microblog/venv/bin/rq worker microblog-tasks
Restart=always
[Install]
WantedBy=multi-user.target
你可能在此文件中留神到的奇怪的事件是,我在服务形容中增加了 %I。这是服务参数,一个要传递给每个实例的数字。在形容中蕴含这个 %I 将帮忙我辨认实例,因为来自 systemd 命令的所有输入都将替换为实例号。对于这种特定的状况,我实际上并不需要应用此参数,然而在其余字段中蕴含 %I 是很常见的,比方必要时应用 start 命令。
与惯例服务文件的另一个区别是,我将应用 /etc/systemd/system/microblog-tasks@.service
这个名称来编写此服务文件。文件名中的 @示意这是一个模板,因而在它前面将有一个参数来标识从中衍生出的每个实例。我将应用实例编号作为参数,因而该服务的不同实例将在 systemd 中被称为 microblog-tasks@1
, microblog-tasks@2
等。
当初,我能够在 bash 中应用大括号扩大来启动四个 worker:
$ sudo systemctl daemon-reload
$ sudo systemctl start microblog-tasks@{1..4}
$ sudo systemctl status microblog-tasks@{1..4}
如果你想独自解决一个实例,你也能够这样做:
$ sudo systemctl restart microblog-tasks@3
这简直和单个 supervisord 配置一样不便,然而有一个毛病,当你想对所有工作程序执行操作时,必须在命令中包含 {1..4} 范畴。
要将整个 worker pool 真正视为一个实体,我能够创立一个新的 systemd target,这是另一种类型的 unit。而后,我能够将所有实例映射到该指标,当我要对组的所有成员执行操作时,这将容许我援用该指标。让咱们从新指标的 unit 配置文件开始,我将其命名为 /etc/systemd/system/microblog-tasks.target:
[Unit]
Description=Microblog RQ worker pool
[Install]
WantedBy=multi-user.target
除了形容之外,惟一须要的定义是对 multi-user.target 的依赖,就像你记得的那样,multi-user.target 是定义上上述所有单元文件的指标。
当初,我能够更新服务文件模板以援用新指标,因为对原始 multi-user.target 的可传递援用,最终等同于新指标。
[Unit]
Description=Microblog task worker %I
After=network.target
[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/microblog
ExecStart=/home/ubuntu/microblog/venv/bin/rq worker microblog-tasks
Restart=always
[Install]
WantedBy=microblog-tasks.target
当初零碎能够应用以下命令重新配置,应用新的设置:
$ sudo systemctl daemon-reload
$ sudo systemctl disable microblog-tasks@{1..4}
$ sudo systemctl enable microblog-tasks@{1..4}
必须应用 disable
和 enable
命令,以强制 systemd 为 worker 工作删除旧指标并利用新指标。当初 woker pool 能够应用 target 来解决:
$ sudo systemctl restart microblog-tasks.target
如果之后你决定减少第 5 个 worker,你能够这样做:
$ sudo systemctl enable microblog-tasks@5
$ sudo systemctl start microblog-tasks.target
当然,你也能够缩小 worker。上面是如何缩小 worker 4 和 5:
$ sudo systemctl stop microblog-tasks@{4..5}
$ sudo systemctl disable microblog-tasks@{4..5}
在这一点上,我认为这个解决方案在方便性和功能性方面超过了 supervisor 的 numprocs
命令,因为我不仅能够管制整个 worker 过程,而且能够增加和删除 worker,而不用编辑任何配置文件!