[英]How to gracefully stop a Dockerized Python ROS2 node when run with docker-compose up?
我有一個基於 Python 的 ROS2 節點在 Docker 容器內運行,我試圖通過捕獲SIGTERM
/ SIGINT
信號和/或通過捕獲KeyboardInterrupt
異常來處理節點的正常關閉。
問題是當我使用docker-compose
在容器中運行節點時。 當容器被停止/殺死時,我似乎無法捕捉到“時刻”。 我明確添加了STOPSIGNAL
在Dockerfile和stop_signal
在泊塢窗,撰寫文件。
這是節點代碼的示例:
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
當我在容器內手動運行節點(使用python3 mynode.py
或通過/run-node.sh
)或當我執行docker run -it mynode /bin/bash -c "/run-node.sh"
,我得到“停止節點...”消息。 但是當我執行docker-compose up
,當我通過Ctrl +C 或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
atexit
代替signal
docker kill --signal
docker stop
和docker kill --signal
我還在docker 容器中檢查了這個Python,優雅地停止了問題,但那里沒有明確的解決方案,而且我不確定使用 ROS/rclpy 是否會使我的設置不同(另外,我的主機是 Ubuntu 18.04,而那個用戶是在 Windows 上)。
是否可以在我的stop_node
方法中捕獲容器的停止?
當您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,除非它被明確編碼為.
這里要做的最重要的重組是讓你的包裝腳本執行Python 腳本。 這會導致它替換包裝器,因此它成為主進程並接收信號。 如果沒有別的改變最后一行
exec python3 /nodes/mynode.py
可能會有所幫助。
我會在這里更進一步,確保將盡可能多的代碼內置到您的 Docker 映像中,並盡量減少顯式 shell 包裝器的數量。 “做一些初始化,然后exec
一些東西”是一種非常常見的 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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.