什么原因会导致 systemd 服务停止并最终取消作业?

什么原因会导致 systemd 服务停止并最终取消作业?

有时,当停止 systemd 服务时systemctl stop test-server会失败,并提示该作业已被取消:

Unable to stop service test-server: Job for test-server.service canceled.

什么原因可能导致服务停止被取消?

注意:停止实际上是从 Ansible 剧本发起的,但看不出其有何关联。

答案1

systemd 中的每个单元在内部都有一个作业槽,并且每次只能为该单元安装一个作业。作业通常封装单元的状态更改请求,但其效果因单元类型而异。在服务中,它们可能会发起状态更改请求,但即使您取消已安装的作业(或取消并替换为另一种作业类型,这将使​​另一个作业保持等待状态,直到该操作完成,因为 unit_start/stop 函数在内部也可以决定某个作业何时可运行),该操作也可能运行。

举例来说,如果您有一个需要很长时间的停止操作,那么在停止作业运行时调用 start 将使用默认作业模式 (replace) 取消已安装/正在运行的停止作业,并在单元的作业槽中安装启动作业。由于 unit_stop 先前已启动到停用的转换(以及映射到服务内部子状态的任何内容 - stop、stop-sigterm、stop-sigkill、stop-final、stop-final-sigterm、stop-final-sigkill),unit_start 现在将返回 -EAGAIN,这会导致 systemd 将启动作业置于 JOB_WAITING 状态,并且在下一次状态更改时,它将被添加到运行队列,检查是否可再次运行,并根据结果再次运行或等待(来自 unit_notify)。每​​次运行作业时,它都会从运行队列中删除。这基本上就是为什么 systemctl start 会一直等待的原因(如果您不使用 --no-block)。

这是对一些活动部件的概述。关于作业,有三件事要记住:它们有类型(启动、停止、重新启动、重新加载等)、结果(超时、完成、取消、依赖、跳过等)和模式(替换、隔离、刷新等)。模式适用于整个事务(请求的作业及其要求和传播依赖的作业以一致的方式一起应用),有关于它们各自做什么的文档。

在您的特定情况下,似乎当您执行 systemctl stop 时,另一个作业会进入并替换您的停止作业,并且 systemctl 客户端会断开连接,因为它排队的作业被取消了。这可能是由于某种依赖关系或其他原因(例如 ExecStop= 最终调用 systemctl start unit(仅在第一次有效)或类似的东西,或者 Wants/Requires/BindsTo 同一单元的单元启动触发启动作业来替换您触发的停止作业等)。它可能是一个套接字激活的服务,由于连接繁忙而被重新触发,由于套接字单元中的 Triggers= 依赖关系而排队启动作业,取消您的停止作业。它也可能是计时器或其他东西:简而言之,由于其他一些作业进入并替换它,停止作业被替换。

当然,正如您所说,这一切都容易发生竞争,这种情况可能发生,也可能不会发生,所以在您的情况下偶尔会发生这种情况。检查您的设置以避免这些问题是个好主意。

答案2

就我而言

[root@server:~]# systemctl start nginx
Job for nginx.service canceled.

原因是我已经定义了 nginx 来拥有BindsTo=另一个服务,以便它在另一个服务运行时准确运行。

由于一个错误,有一天另一个服务立即开始退出,这导致 systemd 取消了 nginx 的启动作业。

不幸的是,systemd 似乎没有给出进一步的指示原因取消——我觉得如果取消的话会好得多(而且我已经功能请求它)。

相关内容