不知道为什么会发生这种情况。我有一个测试服务,每 10 秒将一些任意文本记录到标准输出。这是我的服务文件/etc/systemd/system/samplego.service
:
[Unit]
Description=Sample go app
After=network.target
[Service]
SyslogIdentifier=my-samplego
ExecStart=/opt/samplego/samplego
Restart=on-failure
[Install]
WantedBy=multi-user.target
这效果很好,我可以使用journalctl
.
但是,如果我更改例如中的设置/etc/systemd/journald.conf
,forwardtosyslog=yes
则问题,我的应用程序将停止日志记录,并且直到问题为止systemctl restart systemd-journald
我都看不到更新。重启后需要重新启动所有服务吗?journalctl -f
systemctl restart samplego
journald
答案1
20 世纪 90 年代中期 daemontools 的主要创新之一是可靠的记录。守护进程主管运行二守护者主要的守护进程和一个日志守护进程。它在调用任何一个之前打开一个管道,将管道的写入端作为主要的守护进程的标准输出和管道的读取端作为日志守护进程的标准输入。它还它本身在管道的两端保持打开的文件描述符。因此,如果日志守护程序死亡或终止,然后重新启动,写入管道的日志数据将被保留,并可供新重新启动的日志守护程序实例读取和记录,因为管道不会关闭。
systemd 设计者并没有从这个设计中吸取教训。
systemd
安排生成的服务进程(在配置的情况下)将其标准输出通过套接字发送到日志进程。但不像 daemontools 的情况那样,打开一个管道并安排每个进程继承管道的适当末端,而是systemd
使用 处AF_LOCAL
的流套接字/run/systemd/journal/stdout
。主进程作为客户端连接;日志进程作为服务器进行监听。
这意味着数据连接具有客户端-服务器套接字语义,而不是继承的管道语义。如果服务器挂掉,连接就会消失。所有缓冲的日志数据都会丢失,并且所有更远主守护程序写入的数据进入关闭的套接字并丢失。 (这就是为什么 systemd 有一个IgnoreSIGPIPE
默认设置为 true 的部分原因。将日志输出写入关闭的套接字还导致内核尝试终止正在写入日志的守护进程。)
因此,如果您终止该systemd-journald
进程,进程退出将关闭与其正在记录其输出的所有守护进程的套接字连接,并且它们的所有进一步输出都会丢失。无法从中恢复并将主进程的输出重新连接到日志进程。必须重新启动所有主进程,以便systemd
重新打开一个新的到(新创建的)日志服务器的客户端连接。
systemd 人员在 2016 年做出了一个解决方案。它涉及添加进程将自己选择的任意打开文件描述符推送到进程#1 中,并稍后拉取它们的功能。 systemd-journald
通过与正在记录的客户端的服务器端连接来执行此操作,以便它们在重新启动日志守护程序本身时保持打开状态。
这种机制的问题在于,它需要做更多的工作来确保其安全,而 systemd 人员在设计安全方面并没有良好的记录,仅今年的事件就证明了这一点。它需要更多的工作,因为WHO可以推什么和多少打开文件描述符到进程 #1 和多长时间需要进行访问控制和限制。否则,就有大量可能的漏洞。 (在 daemontools 设计中,服务管理器进程本身就是打开文件描述符的对象,因此它对为子进程保持打开的文件描述符拥有绝对控制权。它不能被欺骗、滥用或淹没。)
在 Laurent Bercot 的 s6 中,s6-svscan
使用主服务和日志服务之间的管道执行常见的 daemontools 操作。它还具有保持任意文件描述符打开的机制。这不是通过在进程 #1 中的服务管理器内以超级用户权限运行的代码来完成的,就像在 systemd 中一样。它是由一个完全非特权的进程完成的,该进程与服务管理器分开运行。
进一步阅读
- 迈克尔·比布尔 (2014-11-26)。重新启动日志会破坏其他服务。 Debian 错误 #771122。
- https://unix.stackexchange.com/a/332315/5132
- https://unix.stackexchange.com/a/294206/5132
- 洛朗·贝尔科特.
s6-fdholderd
。 s6。 skarnet.org。 - 斯蒂芬·赖克林 (2015-12-28)。支持日志中的配置重新加载。系统错误#2236。