Gin*_*pin 4 python python-3.x docker docker-compose ros2
我有一个基于 Python 的 ROS2 节点在 Docker 容器内运行,我试图通过捕获SIGTERM/SIGINT信号和/或捕获KeyboardInterrupt异常来处理节点的正常关闭。
问题是当我使用docker-compose. 我似乎无法捕捉到容器被停止/杀死的“时刻”。我已STOPSIGNAL 在 Dockerfile 和stop_signaldocker-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()
Run Code Online (Sandbox Code Playgroud)
以下是用于重新创建映像的示例 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
Run Code Online (Sandbox Code Playgroud)
这是示例 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 Code Online (Sandbox Code Playgroud)
这是 run-node.sh 脚本:
source /opt/ros/$ROS_DISTRO/setup.bash
python3 /nodes/mynode.py
Run Code Online (Sandbox Code Playgroud)
当我手动运行容器内的节点(使用python3 mynode.py或 通过/run-node.sh)或执行时docker run -it mynode /bin/bash -c "/run-node.sh",我收到“正在停止节点..”消息。但是当我这样做时,当我通过+C 或 bydocker-compose up停止容器时,我永远不会看到该消息。Ctrldocker-compose down
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()
Run Code Online (Sandbox Code Playgroud)
我试过了:
signal.signal atexit而不是signaldocker stop和docker kill --signal 我还检查了docker 容器内的 Python,优雅地停止问题,但没有明确的解决方案,并且我不确定使用 ROS/rclpy 是否会使我的设置有所不同(另外,我的主机是 Ubuntu 18.04,而该用户是在 Windows 上)。
是否可以用我的方法捕获容器的停止stop_node?
当你的docker-compose.yml文件说:
entrypoint: /bin/bash -c "/run-node.sh"
Run Code Online (Sandbox Code Playgroud)
由于这是一个裸字符串,Docker 将其包装在一个/bin/sh -c包装器中。所以你的容器的主要流程是这样的
entrypoint: /bin/bash -c "/run-node.sh"
Run Code Online (Sandbox Code Playgroud)
反过来,bash 脚本保持运行。它启动一个 Python 脚本,并作为其父脚本保持运行,直到该脚本退出。(两个级别的sh -c包装器可能会也可能不会保持运行。)
这里重要的部分是这个包装器 shell,而不是您的脚本,是接收信号的主容器进程,并且(事实证明)不会接收 SIGTERM,除非它显式编码为.
这里要做的最重要的重组是让包装器脚本执行Python 脚本。这导致它取代包装器,因此它成为主进程并接收信号。如果没有别的改变最后一行
/bin/sh -c '/bin/bash -c "/run-node.sh"'
Run Code Online (Sandbox Code Playgroud)
可能会有所帮助。
我会在这里更进一步,确保尽可能多的代码内置到您的 Docker 映像中,并尝试最大限度地减少显式 shell 包装器的数量。“做一些初始化,然后做exec一些事情”是一种非常常见的 Docker 模式,您可以编写此脚本并将其设为图像的入口点:
exec python3 /nodes/mynode.py
Run Code Online (Sandbox Code Playgroud)
同样,您的主脚本应该以“shebang”行开头,例如
#!/bin/sh
# Do the setup
# ("." is the same as "source", but standard)
. "/opt/ros/$ROS_DISTRO/setup.bash"
# Run the main CMD
exec "$@"
Run Code Online (Sandbox Code Playgroud)
您的 Dockerfile 已包含能够直接运行包装器的设置,您可能需要RUN chmod主脚本类似的行。但随后你可以添加
#!/usr/bin/env python3
import ...
Run Code Online (Sandbox Code Playgroud)
由于这两个脚本都是可执行的并且具有“shebang”行,因此您可以直接运行它们。使用 JSON 语法可以防止 Docker 添加额外的 shell 包装器。由于您的入口点脚本现在将运行任何命令,因此很容易单独更改它。例如,如果您想要一个已完成环境变量设置的交互式 shell 来尝试调试容器启动,则可以仅覆盖命令部分
ENTRYPOINT ["/run-node.sh"]
CMD ["/nodes/my-node.py"]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3276 次 |
| 最近记录: |