有时,当使用systemctl stop test-server
它停止 systemd 服务时会失败,并指出作业已取消:
Unable to stop service test-server: Job for test-server.service canceled.
Run Code Online (Sandbox Code Playgroud)
什么可能导致停止服务被取消?
注意:停止实际上是从 Ansible playbook 启动的,但看不出这有什么关系。
systemd 中的每个单元内部都有一个作业槽,并且一次只能为该单元安装一个作业。作业通常封装单元的状态更改请求,但其效果因单元类型而异。在服务中,它们可能会发起状态更改请求,但即使您取消已安装的作业(或取消并替换为另一种作业类型,这将使另一个作业类型保持等待状态直到该操作完成,该操作也可能会运行,因为unit_start/stop 函数内部也可以决定某个作业何时可以运行)。
例如,如果您有一个需要很长时间的停止操作,那么在停止作业运行时调用 start 将使用默认作业模式(替换)取消已安装/正在运行的停止作业,并在单元中安装启动作业职位空缺。由于 unit_stop 之前已经启动了到停用的转换(以及映射到服务内部子状态的任何内容 - stop、stop-sigterm、stop-sigkill、stop-final、stop-final-sigterm、stop-final-sigkill),unit_start 现在将返回 -EAGAIN,这会导致 systemd 将启动作业置于 JOB_WAITING 状态,并在下一次状态更改时将其添加到运行队列中,检查是否可以再次运行,并根据结果再次运行或放入等待状态(从unit_notify)。每次运行作业时,它都会从运行队列中删除。
这是对这里的一些活动部分的概述。关于作业需要记住三件事:它们有类型(启动、停止、重启、重新加载等)、结果(超时、完成、取消、依赖、跳过等)和模式(替换、隔离、冲洗等)。模式适用于整个事务(请求的工作及其要求和传播相关的工作以一致的方式一起应用),有关于它们每个人做什么的文档。
在您的特定情况下,似乎当您执行 systemctl stop 时,另一个作业进入并替换您的停止作业,并且 systemctl 客户端在它排队的作业被取消时断开连接。这可能是由于某些依赖性或其他原因(例如 ExecStop= 最终调用 systemctl start unit (仅在第一次有效)或类似的东西,或者想要/需要/绑定到同一单元的单元启动触发启动取代您触发的停止作业等的作业)。它可能是一个被套接字激活的服务,并且由于连接繁忙,被重新触发,由于套接字单元中的 Triggers= 依赖项而将启动作业排入队列,取消您的停止作业。它也可能是一个计时器或其他东西:简而言之,由于一些其他作业进入并替换它,停止作业正在被替换。
当然,正如您所指出的,这很容易发生竞争,可能会发生也可能不会发生,因此在您的情况下偶尔会发生。最好检查您的设置以避免这些问题。