簡體   English   中英

使用 docker-compose up 運行時,如何優雅地停止 Dockerized Python ROS2 節點?

[英]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 stopdocker 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM