containerd:systemd 配置文件分析

858 阅读4分钟

systemd的配置文件 设置了systemd如何控制containerd:

[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target

[Service]
#uncomment to enable the experimental sbservice (sandboxed) version of containerd/cri integration
#Environment="ENABLE_CRI_SANDBOXES=sandboxed"
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd

Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity
LimitNOFILE=infinity
# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity
OOMScoreAdjust=-999

[Install]
WantedBy=multi-user.target
  • 文件开头注释:版权信息,授权许可Apache License 2.0。
  • [Unit] 部分:同上,定义描述、文档链接、启动顺序。
  • [Service] 部分:
    • ENABLE_CRI_SANDBOXES:使用 experimental sbservice 版本的 containerd/cri 集成,这是一个可选环境变量。
    • ExecStartPre:同上,加载 overlay 模块。
    • ExecStart:启动 containerd 二进制文件。
    • Type=notify: systemd 启动 containerd 后会通知我们。
    • Delegate=yes:同上,允许非特权进程启动。
    • KillMode=process:同上,kill 整个进程树。
    • Restart=always:一定会重启。
    • RestartSec=5:重启间隔 5 秒。
    • LimitNPROC/LimitCORE/LimitNOFILE/TasksMax:同上,资源限制,确保 containerd 可以正常运行。
    • OOMScoreAdjust=-999:OOM 优先级,-999 表示最低。
  • [Install] 部分:同上,需要由 multi-user.target 启动。

KillMode=process

systemd KillMode 配置详解

systemd 服务单元文件中有一个很重要的配置选项 KillMode。它指定了当需要停止某个服务时,systemd 应该如何向该服务进程发送停止信号。理解不同 KillMode 选项的作用,可以帮助我们为服务编写出更加健壮的 systemd 服务单元文件。

KillMode=process

这是最常用的配置选项。

  • 它指定 systemd 应该首先向服务进程的父进程(服务的主进程)发送 SIGTERM 信号。systemd 会等待一定时间(默认 5s),
  • 如果服务仍未停止,则向所有子进程也发送 SIGTERM 信号。
  • 如果服务最终没有完全停止,systemd 会发送 SIGKILL 信号强制停止服务。

这种模式允许服务正常关闭并清理状态,但也可以强制将其停止,非常灵活。大多数服务都适用这种模式。

KillMode=control-group

systemd 会向控制组发送 SIGTERM 信号,以优雅地停止服务。这需要服务进程加入一个控制组,并在接收到组信号后进行关闭。

KillMode=mixed

systemd 首先向主进程发送 SIGTERM 信号,然后立即向子进程发送 SIGKILL 信号强制停止。这可避免子进程无法正确关闭的情况。

KillMode=none

systemd 不会自动发送任何信号。服务需要自身实现关闭逻辑。这种模式可最大限度延长服务的关闭时间,但也要求服务本身处理得当。

其他模式

还有 KillMode=control-group-strictKillMode=process-strict 等模式,这里不再详述。 。为服务选择合适的 KillMode 配置,在它无法正常工作时也可以被正确关闭,这在服务开发中非常关键。

Type=notify

systemd notify 机制

systemd 的 notify 机制用于服务程序通知 systemd 自身的运行状态。这使得 systemd 可以更准确地跟踪服务的运行状况。 notify 机制主要有三种方式,调用sd_notify()函数,这是最简单的方式。

服务程序直接调用 libsystemd 提供的 sd_notify() 函数,向 systemd 发送通知消息。

例如,在服务启动后调用:

#include <systemd/sd-daemon.h>

sd_notify(0, "READY=1");

这样 systemd 就会收到"READY=1"的通知消息,知道服务已启动成功

containerd 如何通知systemd ?

  • cmd/containerd/command/notify_linux.go
import (
	"context"

	sd "github.com/coreos/go-systemd/v22/daemon"

	"github.com/containerd/containerd/log"
)

// notifyReady notifies systemd that the daemon is ready to serve requests
func notifyReady(ctx context.Context) error {
	return sdNotify(ctx, sd.SdNotifyReady) // READY=1
}

// notifyStopping notifies systemd that the daemon is about to be stopped
func notifyStopping(ctx context.Context) error {
	return sdNotify(ctx, sd.SdNotifyStopping)
}

func sdNotify(ctx context.Context, state string) error {
	notified, err := sd.SdNotify(false, state)
	entry := log.G(ctx)
	if err != nil {
		entry = entry.WithError(err)
	}
	entry.WithField("notified", notified).
		WithField("state", state).
		Debug("sd notification")

	return err
}

  • 它导入 github.com/coreos/go-systemd/v22/daemon 这个包

  • 调用 daemon.SdNotify() 函数,该函数内部会调用 C 库 libsystemd 的 sd_notify() 函数

  • sd_notify() 发送 "READY=1" 消息,通知 systemd 服务已启动成功

所以 containerd 通过调用 go-systemd 这个 Go 语言包,实现了使用 sd_notify() 机制来通知 systemd 自己的运行状态。

OOMScoreAdjust

OOMScoreAdjust 选项用于设置服务的 OOM(内存溢出)优先级。它的值范围是 -1000 到 1000,默认为 0。

  • 数值越大,该服务被 OOM 杀死的优先级越高。
  • 数值越小,该服务被 OOM 杀死的优先级越低。
  • -1000 表示 disable,即该服务不会被 OOM 杀死。

设置 containerd 的 OOMScoreAdjust=-999,就是希望它被 OOM 杀死的优先级尽可能低,使其能够尽可能长时间运行。因为 containerd 作为容器运行时,其正常运行对系统非常重要。

OOM(Out of memory)是系统内存耗尽的情况。当系统内存不足以继续为请求服务时,内核的 OOM 杀手会选择某些进程并杀死它们以释放内存,这就是 OOM 杀死。