使用 docker-compose up 运行时如何优雅地停止 Dockerized Python ROS2 节点?

2024-04-02

我有一个基于 Python 的 ROS2 节点在 Docker 容器内运行,我试图通过捕获来处理节点的正常关闭SIGTERM/SIGINT信号和/或通过捕捉KeyboardInterrupt例外。

问题是当我使用以下命令在容器中运行节点时docker-compose。我似乎无法捕捉到容器被停止/杀死的“时刻”。我已经明确添加了STOPSIGNAL https://docs.docker.com/engine/reference/builder/#stopsignal在 Dockerfile 和stop_signal https://docs.docker.com/compose/compose-file/#stop_signal在 docker-compose 文件中。

以下是节点代码示例:

import signal
import sys
import rclpy

def stop_node(*args):
    print("Stopping node..")
    rclpy.shutdown()
    return True

def main():
    rclpy.init(args=sys.argv)
    print("Creating node..")
    node = rclpy.create_node("mynode")
    print("Running node..")
    while rclpy.ok():
        rclpy.spin_once(node)

if __name__ == '__main__':
    try:
        signal.signal(signal.SIGINT, stop_node)
        signal.signal(signal.SIGTERM, stop_node)
        main()
    except:
        stop_node()

以下是用于重新创建映像的示例 Dockerfile:

FROM osrf/ros2:nightly

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
RUN apt-get update && \
    apt-get install -y vim
WORKDIR /nodes
COPY mynode.py .
ADD run-node.sh /run-node.sh
RUN chmod +x /run-node.sh
STOPSIGNAL SIGTERM

这是示例 docker-compose.yml:

version: '3'

services:
  mynode:
    container_name: mynode-container
    image: mynode
    entrypoint: /bin/bash -c "/run-node.sh"
    privileged: true
    stdin_open: false
    tty: true
    stop_signal: SIGTERM

这是 run-node.sh 脚本:

source /opt/ros/$ROS_DISTRO/setup.bash
python3 /nodes/mynode.py

When I manually run the node inside the container (using python3 mynode.py or by /run-node.sh) or when I do docker run -it mynode /bin/bash -c "/run-node.sh", I get the "Stopping node.." message. But when I do docker-compose up, I never see that message when I stop the container, by Ctrl+C or by docker-compose down.

$ docker-compose up
Creating network "ros-node_default" with the default driver
Creating mynode-container ... done
Attaching to mynode-container
mynode-container | Creating node..
mynode-container | Running node..

^CGracefully stopping... (press Ctrl+C again to force)
Stopping mynode-container ... done
$

我试过了:

  • 将呼叫移至signal.signal
  • using atexit代替signal
  • using docker stop and docker kill --signal

我也检查过这个docker容器内的Python,优雅地停止 https://stackoverflow.com/q/53852681/2745495问题,但没有明确的解决方案,并且我不确定使用 ROS/rclpy 是否会使我的设置有所不同(另外,我的主机是 Ubuntu 18.04,而该用户使用的是 Windows)。

是否有可能在我的容器中捕捉到容器的停止stop_node method?


当你的docker-compose.yml文件说:

entrypoint: /bin/bash -c "/run-node.sh"

由于这是一个裸字符串,Docker 将其包装在一个/bin/sh -c包装纸。所以你的容器的主要流程是这样的

/bin/sh -c '/bin/bash -c "/run-node.sh"'

反过来,bash 脚本保持运行。它启动一个 Python 脚本,并作为其父脚本保持运行,直到该脚本退出。 (两个级别sh -c包装器可能会也可能不会保持运行。)

这里重要的部分是这个包装外壳,而不是你的脚本,是接收信号的主容器进程,并且(事实证明)除非明确编码为,否则不会收到 SIGTERM https://stackoverflow.com/questions/56288953/what-is-the-easiest-way-to-run-a-single-nodejs-script-using-docker-and-be-able-t/56290341#56290341.

这里要做的最重要的重组是让你的包装脚本exec http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_20Python 脚本。这导致它取代包装器,因此它成为主进程并接收信号。如果没有别的改变最后一行

exec python3 /nodes/mynode.py

可能会有所帮助。


我会在这里更进一步,确保尽可能多的代码内置到您的 Docker 映像中,并尝试最大限度地减少显式 shell 包装器的数量。 “进行一些初始化,然后execSomething” 是一种极其常见的 Docker 模式,您可以编写此脚本并将其设为映像的入口点:

#!/bin/sh
# Do the setup
# ("." is the same as "source", but standard)
. "/opt/ros/$ROS_DISTRO/setup.bash"
# Run the main CMD
exec "$@"

同样,您的主脚本应该以“shebang”行开头,例如

#!/usr/bin/env python3
import ...

您的 Dockerfile 已经包含能够直接运行包装器的设置,您可能需要类似的RUN chmod主脚本行。但随后你可以添加

ENTRYPOINT ["/run-node.sh"]
CMD ["/nodes/my-node.py"]

由于这两个脚本都是可执行的并且具有“shebang”行,因此您可以直接运行它们。使用 JSON 语法可以防止 Docker 添加额外的 shell 包装器。由于您的入口点脚本现在将运行任何命令,因此很容易单独更改它。例如,如果您想要一个已完成环境变量设置的交互式 shell 来尝试调试容器启动,则可以仅覆盖命令部分

docker run --rm -it mynode sh
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 docker-compose up 运行时如何优雅地停止 Dockerized Python ROS2 节点? 的相关文章

  • 以编程方式结束/退出粘合作业

    我正在使用 Glue 书签来处理数据 我的工作是每天安排的 但也可以 手动 启动 由于我使用书签 有时胶水作业可以在没有新数据要处理的情况下启动 然后读取的数据帧为空 在这种情况下 我想好好地结束我的工作 因为它没有什么关系 我试过 if
  • python:numpy 运行脚本两次

    当我将 numpy 导入到 python 脚本中时 该脚本会执行两次 有人可以告诉我如何阻止这种情况 因为我的脚本中的所有内容都需要两倍的时间 这是一个例子 usr bin python2 from numpy import print t
  • 在 Python 中倾斜数组

    我有一个 2D 数组 我将使用它保存为灰度图像scipy misc toimage 在此之前 我想将图像倾斜给定角度 像这样进行插值scipy ndimage interpolation rotate 上图只是为了说明倾斜过程 我知道我必须
  • Python 中 eval("input()") 和 eval(input()) 之间的区别

    我正在尝试以下功能 x eval input 输入为 123 x 的类型也是int 它工作正常 In 22 x eval input enter enter 123 In 24 print type x
  • 如何在 Django 中像应用程序一样从配置中注册 Flask 蓝图?

    如何从我的配置中注册 Flask 蓝图 就像 Django 中的应用程序一样 我想在配置文件中定义蓝图 它将自动注册 config py BLUEPRINTS news files 实际上我一直在一个暂定名为的项目中勾勒出类似的东西臀部口袋
  • pandas groupby 并转换为 json 列表

    我有一个如下所示的 pandas 数据框 idx f1 f2 f3 1 a a b 2 b a c 3 a b c 87 e e e 我需要将其他列转换为基于索引列的字典列表 所以 最终结果应该是 idx features 1 f1 a f
  • Dockerize 一个网络核心 Web api

    我正在尝试对 aspnetcore webapi 进行 dockerize 我按照这里的教程进行操作 https docs docker com engine examples dotnetcore https docs docker co
  • AppEngine 警告 - OpenBLAS 警告 - 无法确定该系统上的 L2 缓存大小

    我尝试在 GC AppEngine 上部署应用程序 部署过程中没有错误 但应用程序无法运行 仅显示加载页面 日志中唯一一个奇怪的原始日志 OpenBLAS WARNING could not determine the L2 cache s
  • 如何在 Python 中连接两个列表?

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 如何在 Python 中连接两个列表 Example listone 1 2 3 lis
  • 获取列的 [0, x] 元素的最小值

    我需要计算一列 其中值是对其他列进行矢量化运算的结果 df new col df col1 min 0 df col2 然而 事实证明我不能像上面的语法一样使用 min 那么 获得 pandas 列的零和给定值之间的最小值的正确方法是什么
  • 使用 Celery 通过 Gevent 进行实时、同步的外部 API 查询

    我正在开发一个 Web 应用程序 该应用程序将接收用户的请求 并且必须调用许多外部 API 来编写对该请求的答案 这可以直接从主 Web 线程使用 gevent 之类的东西来扇出请求来完成 或者 我在想 我可以将传入的请求放入队列中 并使用
  • 使用 Python 获取 Youtube 数据

    我正在尝试学习如何分析网络上可用的社交媒体数据 我从 Youtube 开始 from apiclient errors import HttpError from outh2client tools import argparser fro
  • 如何动态选择要在flask中使用的模板目录?

    默认情况下 Flask 使用存储在 template 目录中的模板文件 flaskapp application py templates hello html 有没有办法根据登录的用户动态选择模板目录 这就是我想要的目录结构 flaska
  • 如何将 Django 数据库中的模板标签解释/渲染为 HTML

    我正在尝试添加带有来自 Django 管理站点的图像的帖子 但安全 自动转义关闭过滤器无法解释 Django 的模板标签 My input and page look like 复制图像地址 给出http 127 0 0 1 8000 7B
  • 如何从 PyObject 获取指向字符串的 char*

    我怎样才能得到一个char from a PyObject它指向一个字符串 例如 这是 python 脚本 Test Connect 272 22 20 65 1234 这是 C 代码 static PyObject Connect PyO
  • 内置模块位于哪里?

    我尝试查找列出的所有目录sys path但我找不到任何builtins py文件 那么它在哪里呢 从字面上看 该模块内置于 python 解释器中 gt gt gt import builtins gt gt gt builtins
  • 如何将动态数据传递给装饰器

    我正在尝试编写一个基本的 CRUD 控制器类来执行以下操作 下列的 class BaseCrudController model field validation template dir expose self template dir
  • `numpy.diff` 和 `scipy.fftpack.diff` 在微分时给出不同的结果

    我正在尝试计算一些数据的导数 并且正在尝试比较有限差分的输出和谱方法的输出 但结果却截然不同 我无法弄清楚到底为什么 考虑下面的示例代码 import numpy as np from scipy import fftpack as sp
  • 使用 Visual Studio Tools for Docker 部署和调试远程 Linux Docker 容器

    我试图弄清楚如何使用部署到远程容器适用于 Docker 的 Visual Studio 工具 并调试我的 ASP NET Core 应用程序 实际上 我正在以下场景中工作 我的开发机器是 Hyper V 虚拟机 Docker is inst
  • 收到 Python 错误“来自:无法读取 /var/mail/Bio”

    我正在运行一个 bio python 脚本 这会导致以下错误 from can t read var mail Bio 由于我的脚本与邮件没有任何关系 我不明白为什么我的脚本在 var mail 中查找 这里似乎有什么问题 我怀疑这会有帮助

随机推荐