14
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Kubernetes CNI (flannelなど) を使ってROSロボットを動かそう

Posted at

はじめに

ROSロボットをKubernetesクラスタ上で動かす際に、hostNetwork: trueで動かす事例を非常に多く見かけます。非常に多くと言うか99.9999%それしか見ない気がします。

私達が公開しているOSSであるRDBOXのチュートリアルでもその方式を採用しています。
各種デバックツール(rviz、rqtなど)を使う際に、Kubernetes上で動いていることを気にしなくても良い点は大きなメリットです。

ですが、公式ドキュメントでは

hostPortの理由と同じくして、hostNetworkの使用はできるだけ避けてください。

とあります。なるほど。

確かにCNI(Container Network Interface)を使うことで、hostNetworkを使うこと以上のメリットがあることが分かっています。セキュリティ上のメリット、アクセスコントロールなどなど。詳しくはCluster Networking | Kubernetesを御覧ください。
多くの人が採用していないように、CNIを使うことは多少面倒です。しかし、両論併記するのが正しいと感じ、本投稿ではCNIを使ってROSロボットを動かしてみたいと思います。
なお、CNIとしてはflannelを利用しました。

TL;DR

サンプルコードはこちらにあります。rdbox-intec/study-cni

roscoreとChatterのいつものROSのサンプルです。
以下に示す感じののDeploymentを構成します。注目は、hostNetwork: trueを一切指定していないところです。
rdbox/study-cni:v1.0はtalker及び、listenerをroslaunchで起動できるようにしたコンテナです。
技術的な各POINTは、コメントにある通りです。詳細については後で説明します。

実行結果

$ git clone https://github.com/rdbox-intec/study-cni.git
$ cd study-cni
$ kubectl apply -f manifest
deployment.apps/simulation-listener created
deployment.apps/simulation-roscore created
deployment.apps/simulation-talker created
service/simulation-roscore created

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
simulation-listener-6976c99bd4-84729   1/1     Running   0          25m
simulation-roscore-5fcf5c9654-7wdfh    1/1     Running   0          25m
simulation-talker-6cffc7c7d4-k4t4s     1/1     Running   0          25m

$ kubectl logs simulation-listener-6976c99bd4-84729 -f
[INFO] [1594001173.025856]: /listenerI heard hello world 1594001173.024013
[INFO] [1594001173.125795]: /listenerI heard hello world 1594001173.124052
[INFO] [1594001173.226228]: /listenerI heard hello world 1594001173.2242084
[INFO] [1594001173.326462]: /listenerI heard hello world 1594001173.3246925
[INFO] [1594001173.425796]: /listenerI heard hello world 1594001173.4240358
[INFO] [1594001173.526164]: /listenerI heard hello world 1594001173.524595
[INFO] [1594001173.626199]: /listenerI heard hello world 1594001173.6245856
[INFO] [1594001173.726744]: /listenerI heard hello world 1594001173.724751

roscore

apiVersion: apps/v1
kind: Deployment
metadata:
  name: simulation-roscore
  labels:
    app: simulation-roscore
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simulation-roscore
  template:
    metadata:
      labels:
        app: simulation-roscore
    spec:
      containers:
      - name: simulation-roscore
        image: ros:noetic-ros-core-focal
        tty: true
        args:
        - roscore
        env:
          - name: ROS_IP                                     # POINT1
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        ports:                                               # POINT2
          - containerPort: 11311
            protocol: TCP

---
                                                             # POINT3
apiVersion: v1
kind: Service
metadata:
  name: simulation-roscore
  labels:
    app: simulation-roscore
spec:
  ports:
  - port: 11311
    protocol: TCP
  selector:
    app: simulation-roscore

Application

talker

apiVersion: apps/v1
kind: Deployment
metadata:
  name: simulation-talker
  labels:
    app: simulation-talker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simulation-talker
  template:
    metadata:
      labels:
        app: simulation-talker
    spec:
      containers:
        - name: simulation-talker
          image: rdbox/study-cni:v1.0
          tty: true
          args:                                             # POINT4
          - roslaunch
          - --screen
          - --wait
          - talker
          - talk.launch
          env:
            - name: ROS_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: ROS_MASTER_URI                          # POINT5
              value: "http://simulation-roscore:11311"

listener

apiVersion: apps/v1
kind: Deployment
metadata:
  name: simulation-listener
  labels:
    app: simulation-listener
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simulation-listener
  template:
    metadata:
      labels:
        app: simulation-listener
    spec:
      containers:
        - name: simulation-listener
          image: rdbox/study-cni:v1.0
          tty: true
          args:
          - roslaunch
          - --screen
          - --wait
          - listener
          - listen.launch
          env:
            - name: ROS_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: ROS_MASTER_URI
              value: "http://simulation-roscore:11311"

Dockerfile

FROM ros:noetic-ros-core-focal

LABEL maintainer="INTEC Inc<info-rdbox@intec.co.jp>"

ENV ROS_DISTRO=noetic

COPY ./ros_entrypoint.sh /ros_entrypoint.sh

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
                                    build-essential

RUN /bin/bash -c "source /opt/ros/noetic/setup.bash && \
                mkdir -p /catkin_ws/src && \
                cd /catkin_ws/src && \
                catkin_init_workspace && \
                cd /catkin_ws/ && \
                catkin_make && \
                source /catkin_ws/devel/setup.bash && \
                chmod +x /ros_entrypoint.sh"

COPY ./talker /catkin_ws/src/talker
COPY ./listener /catkin_ws/src/listener

RUN /bin/bash -c "source /opt/ros/noetic/setup.bash && \
                source /catkin_ws/devel/setup.bash && \
                cd /catkin_ws/ && \
                catkin_make && \
                apt autoclean -y && \
                apt autoremove -y && \
                rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*"

ENTRYPOINT ["/ros_entrypoint.sh"]

技術解説

全般

皆様おなじみの通り、ROSのNode間通信はどのポートを使うか分からないので全部開いておく必要があります。ROS Securityそこで、今回採る方式では、ROS Masterの通信(Port:11311、TCP)のみをKubernetesのServiceとして公開し、残りのPod間(ROSでいうところのNode間)通信にはflannelのVXLANをそのまま利用します。

POINT1

env:
  - name: ROS_IP
      valueFrom:
      fieldRef:
          fieldPath: status.podIP

全てのmanifestに登場しますが、Pod間で通信するために、ROSで使用するデフォルトIPをPodIPに切り替えておく必要があります。これによってCNIでアサインされたIPアドレスがROS_IPとして使用できます。fieldRefによってPod固有のプロパティを参照することが出来ます。詳しくはExpose Pod Information to Containers Through Environment Variables | Kubernetesを確認下さい。

POINT2

ROS Masterの通信をサービスとして公開するために公開用のポートを指定します。

POINT3

POINT2で公開設定したポートを、Kubernetesのサービスとして公開します。selectorによってROS MasterのDeploymentファイルで指定したPodが対象のサービスとなっていることを明示しています。

POINT4

launchファイルをwaitオプション付きで起動します。kubectlでApplyされるmanifestファイルは起動順序を考慮しないため、特定のroslaunchファイルを指定して動かす場合には必ずwaitオプションを付与することをおすすめします。
よって、KubernetesでROSを使う場合、各ROSプロセスはrosrunではなくroslaunchを使って起動するように心がける必要があります。

POINT5

POINT2、POINT3で指定した通り、ROS Masterがサービスとして公開されます。クラスタ内部では、simulation-roscoreというURIでこの公開されているサービスにアクセスすることができるようになっています。

おわりに

こんな感じで、ROSをよりKubernetesの流儀に則った形で利用することが出来ました。flannelだけでなくcalico、canalなどより高機能なCNIを使うことでより高度な制御も実現可能です。
また、各デバッグツールはコンテナに閉じ込めてしまえば問題なくデバックもできます。例えば、Tiryohさんが公開されているTiryoh/docker_ros-desktop-vnc: A Docker image to provide HTML5 VNC interface to access Ubuntu LXDE + ROSを組み合わせることで、今までと大きく違いのない形での開発も可能です。是非Kubernetesを有効活用した充実したROSライフをお送り下さい。

次回予告

ROS2版の解説も作ります。

宣伝

Kubernetes上でのROS活用について、我々のOSSではより深く・簡単に理解することが出来ます。是非、ご活用下さい。
rdbox-intec/rdbox: GitHub

14
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?