跳到主要内容

!已弃用!为容器或 Pod 生成 systemd 单元文件

概述

podman generate systemd [options] container|pod

描述

已弃用

官方已经弃用该命令

注意:podman generate systemd 命令已弃用。我们推荐使用 Quadlet 文件来在 systemd 下运行 Podman 容器或 Pod。目前没有计划移除这个命令,但我们将只修复紧急的 bug,不会添加新功能。

podman generate systemd 命令用于生成一个 systemd 单元文件,该文件可用于控制容器或 Pod。默认情况下,该命令将单元文件的内容打印到标准输出。

为 Pod 生成单元文件要求该 Pod 是使用基础容器(infra container)创建的(使用 --infra=true 选项)。基础容器在整个 Pod 的生命周期内运行,因此它是 systemd 管理 Pod 主单元生命周期所必需的。

  • 注意:当在远程客户端(包括 Mac 和 Windows(不包括 WSL2)机器)上使用此命令时,请将生成的单元文件放置在远程系统上。此外,请确保 XDG_RUNTIME_DIR 环境变量已设置。如果未设置,请通过 export XDG_RUNTIME_DIR=/run/user/$(id -u) 命令进行设置。

  • 注意:生成的 podman run 命令包含一个 --sdnotify 选项,其值取自容器。如果容器没有显式设置该值或设置为 ignore,则使用 conmon 值。覆盖默认值 container 的原因是,几乎没有容器工作负载会发送通知消息。如果容器的值设置为 container 且容器不发送通知消息,则 systemd 会等待一个永远不会到来的就绪消息。这可能是用户无意中使用了默认值,因此使用覆盖的默认值。

选项

此命令的选项可能包括控制单元文件行为的参数,例如指定输出文件的名称、设置单元文件的属性等。然而,具体的选项列表可能随着 Podman 版本的更新而有所变化。因此,建议查阅 Podman 的官方文档或运行 podman generate systemd --help 命令来获取最新的选项列表和描述。

Kubernetes 集成

通过 podman-kube@.service systemd 模板,可以在 systemd 中执行 Kubernetes YAML 文件。模板的参数是 YAML 文件的路径。假设在用户主目录中存在一个 workload.yaml 文件,可以按照以下方式执行:

escaped=$(systemd-escape ~/workload.yaml)
systemctl --user start podman-kube@$escaped.service
systemctl --user is-active podman-kube@$escaped.service
active

*选项

--after=dependency_name

添加 systemd 单元文件的 After= 选项,用于定义该服务与依赖列表之间的启动顺序。这个选项可以指定多次。

用户定义的依赖会追加到生成的单元文件中,但任何默认需要或已定义的选项(例如 online.target不会被移除或覆盖

--container-prefix=prefix

设置容器 systemd 单元名称的前缀。默认是 container

--env, -e=env

为 systemd 单元文件设置环境变量。

如果指定了一个环境变量但没有值,Podman 会在主机环境中查找该变量的值,并且仅在主机上设置了该变量时才会设置它。作为一种特殊情况,如果指定了一个以 * 结尾的环境变量而没有值,Podman 会在主机环境中搜索以该前缀开头的变量,并将这些变量添加到 systemd 单元文件中。

--files, -f

生成文件而不是打印到标准输出。生成的文件名为 {container,pod}-{ID,name}.service,并放置在当前工作目录中。

注意:在启用了 SELinux 的系统上,生成的文件会继承当前工作目录的上下文。根据 SELinux 的设置,可能需要对生成的文件使用 restoreconchconsemanage 进行更改,以允许 systemd 访问这些文件。或者,在使用 mvcp 命令时,可以使用 -Z 选项。

--format=format

以指定的格式(如 json)打印创建的单元。如果指定了 --files,则打印创建文件的路径而不是单元内容。

--name, -n

在单元文件中使用容器的名称作为启动、停止和描述信息。

--new

此选项生成的单元文件不期望容器和 Pod 已经存在。相反,它们会根据配置文件创建新的容器和 Pod。单元文件是尽最大努力创建的,可能需要进一步编辑;请在使用它们进行生产之前仔细审查生成的文件。

请注意,--new 仅适用于通过 Podman 直接创建的容器和 Pod(即 podman [container] {create,run}podman pod create)。它不适用于通过 REST API 或通过 podman kube play 创建的容器或 Pod。对于 podman kube play,请使用 podman-kube@.service systemd 模板。

--no-header

不生成包含元数据(如 Podman 版本和时间戳)的头部。

--pod-prefix=prefix

设置 Pod 的 systemd 单元名称前缀。默认是 pod

--requires=dependency_name

设置 systemd 单元的 Requires= 选项。与 Wants= 类似,但声明了一个更强的依赖关系。

--restart-policy=policy

设置 systemd 的重启策略。重启策略必须是以下之一:"no"、"on-success"、"on-failure"、"on-abnormal"、"on-watchdog"、"on-abort" 或 "always"。默认策略是 on-failure,除非容器在创建时使用了自定义重启策略。

请注意,在没有使用 --new 选项的情况下为具有自定义重启策略的容器生成单元可能会导致关机时出现问题;systemd 尝试停止单元,而 Podman 尝试重新启动它。建议在创建容器时不使用 --restart,并在生成单元文件时使用 --restart-policy 选项。

--restart-sec=time

设置 systemd 服务的 restartsec 值。它配置了服务(如通过 restart-policy 配置的)在重启之前的休眠时间。时间以秒为单位。

--separator=separator

设置容器/Pod 的名称/ID 和前缀之间的 systemd 单元名称分隔符。默认是 -

--start-timeout=value

使用给定的秒数值覆盖容器的默认启动超时时间。

--stop-timeout=value

使用给定的秒数值覆盖容器的默认停止超时时间。

--template

向 systemd 单元文件中添加模板规范,以从单个单元文件运行多个服务。

请注意,如果未将 --new 设置为 true,则它默认为 true。但是,如果显式将 --new 设置为 false,则命令将失败。

--wants=dependency_name

添加 systemd 单元的 wants (Wants=) 选项,表示此服务依赖于(弱)另一个服务。此选项可以指定多次。此选项不影响服务启动或停止的顺序。

用户定义的依赖项会附加到生成的单元文件中,但任何需要的或默认定义的现有选项(例如 online.target不会被删除或覆盖

示例

为容器生成并打印 systemd 单元文件

为运行 nginx 的容器生成 systemd 单元文件,设置重启策略为 always,并设置 1 秒的超时时间,然后将文件输出到标准输出。请注意,在 Unit 部分中的 RequiresMountsFor 选项确保了在启动服务之前,容器的 GraphRoot 和 RunRoot 存储已经挂载。对于使用像 iSCSI 或其他远程块协议这样的磁盘进行容器存储的系统,这确保了 Podman 在任何必要的存储操作上线之前不会被执行。

podman create --name nginx nginx:latest
podman generate systemd --restart-policy=always -t 1 nginx

输出内容将类似于以下单元文件:

container-de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6.service
autogenerated by Podman 1.8.0
Wed Mar 09 09:46:45 CEST 2020

[Unit]
Description=Podman container-de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/container/storage

[Service]
Restart=always
ExecStart=/usr/bin/podman start de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6
ExecStop=/usr/bin/podman stop -t 1 de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6
KillMode=none
Type=forking
PIDFile=/run/user/1000/overlay-containers/de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6/userdata/conmon.pid

[Install]
WantedBy=default.target

在生成的单元文件中,你可以看到服务的描述、依赖关系、启动和停止命令,以及服务如何安装和集成到 systemd 中。如果你想要将这个单元文件添加到 systemd 中以便自动管理容器,你可以使用 systemctl enable 命令来启用它。

sudo systemctl enable container-de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6.service

然后,systemd 将在系统启动时自动启动这个容器,并在容器退出时根据指定的重启策略重启它。如果容器由于任何原因停止,systemd 也会尝试重新启动它。

使用 --new 标志为容器生成 systemd 单元文件

--new 标志用于生成 systemd 单元文件,这些文件会在服务启动和停止命令(即 ExecStartPreExecStopPost 服务操作)中创建和删除容器。这种单元文件不绑定到特定机器,可以轻松地共享并在其他机器上使用。

sudo podman generate systemd --new --files --name bb310a0780ae

上述命令会生成一个 systemd 单元文件,该文件定义了如何在系统启动时创建容器,以及在服务停止时删除容器。以下是生成的单元文件的一个示例:

# container-busy_moser.service
# autogenerated by Podman 1.8.3
# Fri Apr 3 09:40:47 EDT 2020

[Unit]
Description=Podman container-busy_moser.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/container/storage

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
ExecStartPre=/bin/rm -f %t/%n-pid %t/%n-cid
ExecStart=/usr/local/bin/podman run \
--conmon-pidfile %t/%n-pid \
--cidfile %t/%n-cid \
--cgroups=no-conmon \
-d \
alpine
ExecStop=/usr/local/bin/podman stop \
--ignore \
--cidfile %t/%n-cid -t 10
ExecStopPost=/usr/local/bin/podman rm \
--ignore \
-f \
--cidfile %t/%n-cid
PIDFile=%t/%n-pid
KillMode=none
Type=forking

[Install]
WantedBy=default.target

在这个单元文件中,ExecStartPre 指令在容器启动前执行,确保任何旧的 PID 和容器 ID 文件被删除。ExecStart 指令启动容器,使用 --conmon-pidfile--cidfile 选项分别指定 conmon 进程和容器 ID 文件的路径。ExecStop 指令在停止服务时优雅地停止容器,如果容器无法停止,--ignore 选项允许 podman stop 命令不报错退出。ExecStopPost 指令在容器停止后执行,确保容器被删除。

使用这种方式的单元文件,你可以轻松地在不同的机器上部署和管理容器,因为容器创建和删除的逻辑都包含在 systemd 单元文件中。

要启用并启动这个服务,你可以使用以下命令:

sudo systemctl enable container-busy_moser.service
sudo systemctl start container-busy_moser.service

这将确保在系统启动时自动启动容器,并在停止服务时自动删除容器。

为包含两个简单 Alpine 容器的 Pod 生成 systemd 单元文件

请注意,systemctl 应仅用于 Pod 单元,而不是用于单独启动或停止容器。容器和内部的基础设施容器(infra-container)由 Pod 服务进行管理。

要使用 systemctl statusjournalctl 来检查容器或 Pod 单元文件的状态,请按照以下步骤操作:

首先,创建一个 Pod 并向其中添加两个 Alpine 容器:

podman pod create --name systemd-pod
podman create --pod systemd-pod -d alpine top
podman create --pod systemd-pod -d alpine top

接下来,使用 podman generate systemd 命令生成 systemd 单元文件:

podman generate systemd --files --name systemd-pod

这将生成一个或多个单元文件,包括 Pod 单元和每个容器的单元。这些文件通常会被放置在用户的主目录下,或者你可以指定一个输出目录。

示例输出可能是这样的:

/home/user/pod-systemd-pod.service
/home/user/container-amazing_chandrasekhar.service
/home/user/container-jolly_shtern.service

查看生成的 Pod 单元文件 pod-systemd-pod.service

cat pod-systemd-pod.service

内容应该类似于以下(注意:版本号、时间戳和 UUID 会有所不同):

# pod-systemd-pod.service
# autogenerated by Podman X.Y.Z
# Date and Time

[Unit]
Description=Podman pod-systemd-pod.service
Documentation=man:podman-generate-systemd(1)
Requires=container-amazing_chandrasekhar.service container-jolly_shtern.service
Before=container-amazing_chandrasekhar.service container-jolly_shtern.service
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/var/run/container/storage

[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start <infra-container-UUID>-infra
ExecStop=/usr/bin/podman stop -t 10 <infra-container-UUID>-infra
KillMode=none
Type=forking
PIDFile=/run/user/<UID>/overlay-containers/<infra-container-UUID>/userdata/conmon.pid

[Install]
WantedBy=default.target

<infra-container-UUID> 是自动生成的基础设施容器的 UUID,<UID> 是运行 Podman 的用户的用户 ID。

在上面的单元文件中,RequiresBefore 指令确保容器单元在 Pod 单元之前启动,并且如果容器单元失败,Pod 单元也会失败。ExecStartExecStop 指令分别用于启动和停止 Pod 的基础设施容器。

要启用并启动这个 Pod 服务,你可以使用以下命令:

sudo systemctl enable pod-systemd-pod.service
sudo systemctl start pod-systemd-pod.service

这将会启动 Pod,从而启动其中的所有容器。如果你想要检查 Pod 或容器的状态或日志,你可以使用 systemctl statusjournalctl 命令:

sudo systemctl status pod-systemd-pod.service
sudo journalctl -u pod-systemd-pod.service

对于单个容器,你也可以这样做:

sudo systemctl status container-amazing_chandrasekhar.service
sudo journalctl -u container-amazing_chandrasekhar.service

请注意,如果你的系统没有使用 sudo 来管理 systemctl 命令,那么你可能需要根据你的系统配置调整命令。此外,如果你的用户没有权限直接访问 /var/run/container/storage 或其他必要的目录,你可能需要调整 RequiresMountsFor 选项或使用其他方法来确保正确的权限和访问。

systemd 单元文件的安装

Podman 生成的 systemd 单元文件包含 [Install] 部分,该部分包含单元的安装信息。这部分信息由 systemctl 命令的 enable 和 disable 子命令在安装时使用。

一旦生成了 systemd 单元文件,你需要将其安装到 /etc/systemd/system 目录(如果要以 root 用户身份运行),或者安装到 $HOME/.config/systemd/user 目录(如果要以非 root 用户身份安装)。然后,使用 systemctl enable 命令启用复制过来的单元文件。

注意:将单元文件复制到 /etc/systemd/system 目录并启用它,表示该单元文件将在启动时自动运行。类似地,将单元文件复制到 $HOME/.config/systemd/user 目录并启用它,表示该单元文件将在用户登录时自动运行。

下面是安装过程的示例:

# 生成 systemd 文件
podman pod create --name systemd-pod
podman create --pod systemd-pod alpine top
podman generate systemd --files --name systemd-pod

# 复制所有生成的文件
sudo cp pod-systemd-pod.service container-great_payne.service /etc/systemd/system

# 启用 pod 单元
sudo systemctl enable pod-systemd-pod.service
Created symlink /etc/systemd/system/multi-user.target.wants/pod-systemd-pod.service → /etc/systemd/system/pod-systemd-pod.service.
Created symlink /etc/systemd/system/default.target.wants/pod-systemd-pod.service → /etc/systemd/system/pod-systemd-pod.service.

# 检查 pod 单元是否已启用
sudo systemctl is-enabled pod-systemd-pod.service
enabled

对于放置在 $HOME/.config/systemd/user 目录下的用户服务,要在用户首次登录时运行它们,请使用 --user 标志启用服务:

systemctl --user enable <.service>

systemd 用户实例在用户最后一个会话关闭后会被杀死。要使 systemd 用户实例在启动时启动并在用户注销后继续运行,可以启用 lingering 功能:

loginctl enable-linger <username>

这样,即使用户注销,systemd 用户实例也会保持运行,直到系统关闭。

使用 systemctl 对已安装的生成的单元文件进行操作

使用上面的示例作为参考,为 pod 创建并启用 systemd 单元文件,并使用 systemctl 进行操作。

由于 systemctl 默认使用 root 用户,因此所有使用 systemctl 进行的更改都可以通过在 podman cli 命令后添加 sudo 来查看。要以非 root 用户身份执行 systemctl 操作,请在与 systemctl 交互时使用 --user 标志。

注意:如果先前创建的容器或 pod 使用了共享资源,如端口,请在启动生成的 systemd 单元之前确保先删除它们。

# 以非 root 用户身份启动 pod 服务
systemctl --user start pod-systemd-pod.service

# 检查 pod 状态
podman pod ps
POD ID NAME STATUS CREATED # OF CONTAINERS INFRA ID
0815c7b8e7f5 systemd-pod Running 29 minutes ago 2 6c5d116f4bbe

# 检查 root 用户的容器列表(没有 pod)
sudo podman ps # 0 Number of pods on root.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

# 停止 pod 服务
systemctl stop pod-systemd-pod.service

# 再次检查 pod 状态
podman pod ps
POD ID NAME STATUS CREATED # OF CONTAINERS INFRA ID
272d2813c798 systemd-pod Exited 29 minutes ago 2 6c5d116f4bbe

接下来,创建一个简单的 alpine 容器,并使用 --new 标志生成 systemd 单元文件。启用服务,并使用 systemctl 命令控制操作。

注意:使用 systemctl start 启动容器时,它不会修改已经运行的容器,而是根据相似的配置启动一个“新”的容器。

# 创建一个新的 alpine 容器并生成 systemd 单元文件
podman run --name myalpine -d alpine top
podman generate systemd --new --name myalpine

# 复制并启用 systemd 单元文件
sudo cp container-myalpine.service /etc/systemd/system/
sudo systemctl enable container-myalpine.service

# 启动服务
sudo systemctl start container-myalpine.service

# 检查容器状态
podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
... alpine top ... Up ... ago ... myalpine

# 停止服务
sudo systemctl stop container-myalpine.service

# 移除服务
sudo systemctl disable container-myalpine.service
sudo systemctl daemon-reload # 刷新 systemd 的服务列表
sudo rm /etc/systemd/system/container-myalpine.service

# 如果需要,还可以删除容器
podman rm myalpine

确保在删除服务之前停止它,并在删除单元文件后刷新 systemd 守护进程,以确保下次启动时不会尝试启动已删除的服务。如果需要,还可以删除不再需要的容器。

启用服务

启用服务之前,确保已经创建了容器,并且已经生成了相应的 systemd 单元文件。在这个例子中,我们假设已经创建了一个名为 busy_moser 的容器,并且使用 podman generate systemd 命令生成了 container-busy_moser.service 单元文件。

# 列出所有容器,包括已停止的
sudo podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bb310a0780ae docker.io/library/alpine:latest /bin/sh 2 minutes ago Created busy_moser

# 启用并启动服务
sudo systemctl start container-busy_moser.service

# 再次列出所有容器,这次新容器应该已经启动
sudo podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
772df2f8cf3b docker.io/library/alpine:latest /bin/sh 1 second ago Up 1 second distracted_albattani
bb310a0780ae docker.io/library/alpine:latest /bin/sh 3 minutes ago Created busy_moser

注意,当你使用 systemctl start 命令启动服务时,它会根据 systemd 单元文件中的配置创建一个新的容器实例,而不是重新启动现有的 Created 状态的容器。这就是为什么在 podman ps -a 的输出中,你会看到一个新的容器 distracted_albattani 正在运行,而原始的 busy_moser 容器仍然是 Created 状态。

使用场景

1. 生成容器或 Pod 的 systemd 单元文件

你可以使用 podman generate systemd 命令来生成一个或多个容器或 Pod 的 systemd 单元文件。这些文件随后可以被放置在适当的 systemd 单元目录中,并使用 systemctl 命令进行管理和控制。

2. 自定义和管理容器或 Pod 的生命周期

通过编辑生成的 systemd 单元文件,你可以自定义容器或 Pod 的启动、停止和重启行为。例如,你可以设置依赖关系、定义环境变量、挂载卷等。一旦单元文件被放置在正确的位置并启用,systemd 将负责根据单元文件的配置来管理容器或 Pod 的生命周期。

注意事项

  • 尽管 podman generate systemd 命令已弃用,但在某些情况下,它可能仍然有用或作为过渡解决方案。然而,对于新的部署和长期维护,建议使用推荐的替代方案。
  • 确保生成的 systemd 单元文件与你的系统和环境兼容,并根据需要进行适当的修改和调整。
  • 在使用 systemd 管理容器或 Pod 时,了解 systemd 的工作原理和配置选项是非常重要的。建议查阅 systemd 的官方文档以获取更多详细信息和最佳实践。

总结

尽管 podman generate systemd 命令已弃用,但它仍然可以用于生成用于控制容器或 Pod 的 systemd 单元文件。通过编辑这些文件,你可以自定义和管理容器或 Pod 的生命周期。然而,对于新的部署和长期维护,建议使用推荐的替代方案,如 Quadlet 文件。在使用 systemd 管理容器或 Pod 时,请确保熟悉 systemd 的工作原理和配置选项,并根据需要进行适当的调整和优化。

另请参阅

  • podman(1): Podman 的主命令手册页。
  • podman-container(1): Podman 容器管理的手册页。
  • systemctl(1): 用于控制 systemd 系统和服务管理器的命令行工具。
  • systemd.unit(5): 描述 systemd 单元文件的通用信息。
  • systemd.service(5): 描述 systemd 服务单元文件的特定信息。
  • conmon(8): 容器监控程序,用于与容器运行时交互。
  • podman-systemd.unit(5): Podman 生成的 systemd 单元文件的特定信息。

历史

  • 2020 年 4 月,Sujil Shah(sushah at redhat dot com)更新了详细信息,并添加了使用生成的 .service 文件作为 root 和非 root 用户的用例。
  • 2019 年 8 月,Valentin Rothberg(rothberg at redhat dot com)添加了对 pod 的支持。
  • 2019 年 4 月,最初由 Brent Baude 整理。