【问题标题】:systemd container: how can I automatically run a bash script?systemd 容器:如何自动运行 bash 脚本?
【发布时间】:2025-12-05 17:40:01
【问题描述】:

在成功关注这些RHEL systemd container instructions 之后,我现在希望容器在每次启动时运行一个 bash 脚本。不过,我希望它只运行一次。对我来说,这似乎不是一种“服务”。我不想启动容器,然后手动运行 bash 脚本(例如 docker exec -it ...)。我应该制作一个 systemd 单元文件来启动脚本,然后在我的 bash 脚本中禁用该服务吗?这看起来有点像黑客,不是吗?

更新:要回答评论者,bash 脚本需要 systemd。所以首先 systemd 必须启动,然后脚本运行,安装软件,然后发出 systemctl start 命令。是的,我知道你在想什么:使用 RUN 安装软件,然后让 systemd 启动它,但不幸的是,我不能为这个项目这样做。我没有编写的脚本期望 systemd 正在运行。此外,该脚本确实在容器内的命名卷中运行。

【问题讨论】:

  • 每个容器启动一次?
  • 从您对docker exec 的引用来看,这个脚本在容器内吗?
  • 您有什么理由不简单地修改容器以在启动时执行该脚本?

标签: bash docker systemd


【解决方案1】:

您可以为您的 bash 脚本创建一个服务类型为 oneshot 的 systemd 单元文件。 oneshot 类型代表短暂的进程,通常作为一次性任务运行。 systemd 等待进程退出,然后再继续处理其他单元。

例如,

[Unit]
Description=my only-once script

[Service]
Type=oneshot
ExecStart=/bin/bash /path/to/only_once_script.sh
RemainAfterExit=true
StandardOutput=journal

[Install]
WantedBy=multi-user.target

请注意,如果您希望 systemd 在启动操作成功完成后将服务视为活动,则必须将RemainAfterExit 设置为true

看看 Example 2Example 3 在 systemd docs.

【讨论】:

  • 在我的情况下,脚本需要 20 分钟,我不想阻止/阻止其他服务的加载。我可以设置 ExecStart=/bin/bash /some/script.sh & 在后台运行它吗?
  • 不,& 不受任何 ExecStart=ExecStartPre=ExecStartPost=ExecReload=ExecStop=ExecStopPost= 选项的支持,如在命令行下的docs
【解决方案2】:

您可以在同一服务本身中执行此操作。在我的机器上考虑以下流程

$ docker run -d ubuntu:16.04 sleep 200
2e76d13cee285143dc91af0e6ddbd51bd31e92d7baa3ec34638726cdd0abb572

$ docker exec -d 2e76d13cee285143dc91af0e6ddbd51bd31e92d7baa3ec34638726cdd0abb572 sleep 300

$ docker exec  2e76d13cee285143dc91af0e6ddbd51bd31e92d7baa3ec34638726cdd0abb572 ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.2  0.0   4384   756 ?        Ss   12:25   0:00 sleep 200
root         5  0.1  0.0   4384   792 ?        Ss   12:25   0:00 sleep 300
root         9  0.0  0.2  34428  2740 ?        Rs   12:25   0:00 ps aux

如你所见,我在后台执行了一个命令(执行命令的-d/--detach)

所以在您的服务文件中。只需在启动 docker 容器的主单元中定义 ExecStartPost= 并使用 exec-d 标志

【讨论】:

    【解决方案3】:

    如果您只需要“systemd”,因为安装程序运行“systemctl start”命令,那么您可以通过使用systemctl-docker-replacement 包装器来避免模糊。

    我经常使用它来覆盖 /usr/bin/systemctl,在这种情况下,“systemctl start”可以正确执行,而没有任何 systemd 守护程序运行。使用大量安装程序进行测试,以模仿他们期望从“systemctl start”获得的行为。

    【讨论】: