systemd vs supervisord

过去咱们项目组的应用都是用 supervisord 托管的。最近由于某些因素,没法使用 supervisord,所以考虑改用 systemd。
做为主流 Linux 发行版的默认选项,以前多多少少用过一点 systemd。不过此次须要上生产环境,因此抽空深刻研究一番。html

为何要用 supervisord?程序员

  1. 实现进程的分组管理,好比支持一同启动/中止多个生产者/消费者实例。docker

  2. 进程崩溃的时候能够重启工具

要想改用 systemd,须要看下 systemd 如何应对这两个问题。
(如无指明,在本文中,supervisord 的配置项在 [program:x] 下面,而 systemd 的配置项则位于 [Service]spa

进程控制

不管 supervisord 仍是 systemd,都采用 ini 做为配置文件的格式。跟 supervisord 不一样的是,systemd 每一个程序都要单独开一个 unit 文件。
supervisord 能够同时启动/中止配置文件中因此的进程(或者某个进程组配置中的进程)。systemd 能够用依赖来实现这一点。下面例子中,咱们
就建立了一个能够同时管理的进程组:pwa

; group.target
[Unit]
Description=Application
Wants=prog1.service prog2.service
; prog1.service
[Unit]
Description=prog1
PartOf=group.target

[Service]
ExecStart=/usr/bin/prog1
Restart=on-failure
; prog2.service
[Unit]
Description=prog2
PartOf=group.target

[Service]
ExecStart=/usr/bin/prog2
Restart=on-failure

systemctl start group.targetprog1prog2 也会带起来。systemctl restart group.targetprog1prog2 也会跟着重启。设计

相对来讲,supervisord 的作法更加直观一些。rest

若是要更改 supervisord 的配置文件,supervisord 须要运行 supervisorctl reread 才会生效。
而 systemd 则须要 systemctl daemon-reload。半斤八两吧。
不过 supervisord 有一个好。若是你不知道哪些程序的配置改变了,简单地执行 supervisorctl update,全部涉及的进程都会被重启。
而 systemd 貌似作不到这一点。日志

systemd 能够指定 stop 操做时能够选择的命令(ExecStop=)。另外它还提供了 ExecReload=,能够自定义调用 systemctl reload xxx 从新读取程序配置文件时的操做。
supervisord 不支持 reload 指定进程。同时对于 stop 操做,它只容许你选择要发送哪一种信号...code

supervisord 的 stopwaitsecs 能够控制 stop 操做后等待程序退出的耐心(以秒衡量)。待给定的耐心都消耗完毕后,supervisord 才会痛下杀手,发送 SIGKILL。
systemd 对应的配置项是 TimeoutStopSec=。systemd 会给多一次机会,在下最后通牒以前,会先发送 SIGTERM,过另外一个 TimeoutStopSec 以后才发送 SIGKILL。

进程重启

为了不在 supervisord 和 systemd 两套术语间迷糊,请容许我抛弃全部术语,用本身的话描述。
life-cycle

从上图能够看到,进程在 RUNNING 以前,会有一个 STARTING 的过程。在 STARTING 过程当中,进程可能会读取配置文件,进行初始化。
这一过程当中的错误处理,跟 RUNNING 状态的应当有所不一样。
以上是 supervisord 的想法。
因此 supervisord 提供了单独的 startretries 配置项,用来配置 STARTING 阶段的重启次数。
systemd 对此没有特殊处理。

一个程序,从 RUNNING 到 EXITED,有两种可能:正常退出或异常退出…(废话)
这两种状况,是经过配置的退出码来区分的。对于 supervisord,这个配置项是 exitcodes。systemd 则经过 SuccessExitStatus 来控制。
有趣的是,exitcodes 的默认值是 0,2,不知道为什么它会认为 2 也是正常的退出码。

若是配置了 autorestart = true,只要程序退出,supervisord 都会把它启动起来。相对的,若是配置的是 autorestart = unexpected,则只有
异常退出才会重启。这两个选项,在 systemd 里对应 Restart=alwaysRestart=on-failure。systemd 还提供了 Restart=on-success(只有正常
退出才重启)和 Restart=on-abort(只有收到异常信号才重启)。

对于重启次数,supervisord 没有做限定。由于重启一个程序时,supervisord 会先让它处于 STARTING 状态。这个状态的持续时间,是由配置项
中的 startsecs 决定的,默认 1 秒。若是是不可恢复的错误,程序就不可能成功进入到 RUNNING 状态。固然也许存在这样的状况,程序运行 1 秒
后,就会崩溃。那么它就会陷于不停重启的无间地狱。

systemd 对此一如既往,提供了 N 多选项以供采用。你能够用 RestartSec 控制每次重启的间隔,能够用 StartLimitIntervalStartLimitBurst 设定
给定周期内可以重启的次数。好比指定 StartLimitInterval=1sStartLimitBurst=3,就能够实现跟 supervisord 一致的默认重启策略。

比较完最基本的两种功能,让咱们继续看看,二者在一些小细节上的对对碰。

控制实例数

supervisord 能够用 numprocs 来控制单个程序对应的实例数。systemd 也能够作到这一点,虽然有点麻烦(某种意义上,更增强大)。
systemd 会把以 @ 结尾的 service 文件看成模板,在运行时根据给定的参数展开成多个实例。

具体实现方式见:http://0pointer.de/blog/proje...

日志

supervisord 可以重定向被托管的程序的 stdout 和 stderr 到日志文件中,并提供日志切割服务。systemd 也支持这一点,尽管它的实现有很大的不一样。

根据鄙人的经验,基于按期检查的日志切割服务,不是个好的选择。
一旦赶上突发高峰,有可能会出现日志没法及时切割的状况;而调小检查间隔,大部分状况下都在无心义地空转。(说的就是你,logroated)
好在不管是 supervisord,仍是 systemd,提供的切割服务都是实时的。每当写入内容会超过上限时,就会自动切割。

systemd 的日志服务是经过 journald 组件实现的。你能够在 /etc/systemd/journald.conf 中配置它。
journald 默认的日志存储形式是 Storage=auto。这个选项比较奇妙,若是你建立了 /var/log/journal 文件夹,那么它就会把日志写到这个文件夹下。不然不进行持久化。

持久化后的日志是这个样子的:

/var/log/journal/c4010ceea79847afbedecb60a775db96/
├── system.journal
├── user-1000.journal
└── user-65534.journal

第一次看到这样的目录结构,说不定你会大吃一惊。journald 设计者脑洞不是通常的大。从这个结构上,根本看不出应用日志在哪里嘛。
不,彻底没有这样的必要,由于全部的程序的日志都会写到一块去。不分彼此,全变成一团浆糊。随便一提,日志默认都是压缩的。

要看日志,你得用 journalctl。好比看 prog1.service 的日志,须要 journalctl -u prog1.service。要看特定时期的日志,须要 journalctl --since $timestamp --until $timestamp

这么前卫的设计我可接受无能。这种 journalctl 控制一切的方式,致使 systemd 日志没法集成到传统的日志收集工具中。
程序员工具箱中各类 text base 处理工具,对此也大眼瞪小眼,只能对着 journalctl 低声下气,接受对方的小脾气。

journald 提供了三个配置项,RuntimeMaxFileSize=RuntimeMaxFiles=。顾名思义,就是单个日志文件大小和容许的日志数。
另外,RuntimeMaxUse=RuntimeKeepFree= 能够控制总大小的上限。

supervisord 在这方面作的要好得多。经过 stdout/stderr_logfile_maxbytesstdout/stderr_logfile_backups,你能够规划每个程序的日志文件的切割粒度。
不一样程序的日志不会挤一块儿,产生日志少的程序也不会被产生日志多的程序干扰。

systemd vs supervisord

除了以上几点外,还有一些没有具体提到的功能。
好比 supervisord 经过 priority 配置进程启动顺序,以及 systemd 对应的 Before/After 依赖机制。
好比 supervisord 的 events 功能,和与之相对应的 systemd 的 notify 机制。
好比 supervisord 能够管理 fastcgi(真有人这么作吗)。
好比 systemd 提供的基于 cgroup 的资源限制。
因为没有使用经验,对这些功能就不做一一比较了。

是时候结题了。systemd 和 supervisord 各有长短,不存在哪一方绝对的碾压。systemd 跟 Linux 紧密结合,所需的依赖少,其提供的保障天然比 supervisord 更可靠。然而在强大的能力背后,也有配置复杂、不易上手等问题。supervisord 偏于应用层,却所以有独特的用武之地。举个例子,许多人会往 docker 打包里面封入一份 supervisord,让它来作 PID 1,以此稍微加强下健壮性。换 systemd 作一样的事,就像用园艺剪刀裁纸,即便可以顺利完成,也不免事倍功半。毕竟这样的方式跟 systemd 的设计是背道而驰的。

相关文章
相关标签/搜索