作業中のメモ

よく「計算機」を使って作業をする.知らなかったことを中心にまとめるつもり.

Dockerを用いてVSOMEIPを使ってみる

どうも,筆者です.

唐突であるが,vsomeipのサンプルを使って,リクエスト/レスポンスを実現してみようと思う.

目的

ここでは,Docker Desktop for Windowsを用いて,リクエスト側のコンテナとレスポンス側のコンテナを立ち上げ,2つのコンテナを用いて通信が実現できることを確認する.

動作環境

2020/11/8時点で,Docker Desktop for Windowsは,WSL(Windows Subsystem for Linux)の機能を借りることでWindows Homeでも起動できるようになった.今回は,この仕組みを用いて確認を実施する.

対象 バージョン情報
OS Windows 10 Home 64bit 10.0.18363 ビルド 18363
Docker Desktop for Windows 2.5.0.0 (49427)
Windows Subsystem for Linux 2
vsomeip 3.1.16.1

環境構築

WSL2の導入

まず,WSL2が利用できるように準備する.以下のサイトを参考に,WSL2を導入する.実施内容は以下の3つ.

  1. 機能の追加
  2. WSL 2 Linux カーネルの更新
  3. WSL 2 を既定のバージョンとして設定する

dev.classmethod.jp

Docker Desktop for Windowsのインストール

下記のサイトを参考に,インストールを実施する.

dev.classmethod.jp

これで,一通り準備が整った.次は,Dockerfileとdocker-composeを用意し,コンテナの作成に取り掛かる.

設定ファイルの準備とコンテナの作成

ディレクトリ構成

ここでは,以下のようなディレクトリ構成を考える.

./vsomeip_work
├─ docker-compose.yml
├─ Dockerfile
├─ start.sh
│
└─code

Dockerfileの作成

今回は,Ubuntu 20.04の環境をベースにイメージを作成する. また,vsomeipのインストール先は「/usr/local/vsomeip」とする.

以下のようなDockerfileを作成する.(いい感じのシンタックスハイライタが無かったためshで代用)

FROM ubuntu:20.04

LABEL maintainer="user"
LABEL description="build vsomeip"
# avoid interaction mode in tzdata
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Tokyo
ENV VSOMIP_VERSION=3.1.16.1
ENV VSOMEIP_PREFIX=/usr/local/vsomeip

SHELL ["/bin/bash", "-c"]

RUN    apt-get update \
    && apt-get upgrade -y \
    && apt-get install -y tzdata gcc build-essential cmake make wget libtool unzip net-tools \
                          libboost-system1.71-dev libboost-thread1.71-dev libboost-log1.71-dev \
    && cat /usr/share/zoneinfo/${TZ} > /etc/localtime \
    && dpkg-reconfigure tzdata

RUN    wget -O vsomeip.zip https://github.com/GENIVI/vsomeip/archive/${VSOMIP_VERSION}.zip \
    && unzip vsomeip.zip \
    && mkdir -p ${VSOMEIP_PREFIX} \
    && pushd vsomeip-${VSOMIP_VERSION} \
    && mkdir build \
    && cd build \
    && cmake -DENABLE_SIGNAL_HANDLING=1 -DENABLE_MULTIPLE_ROUTING_MANAGERS=1 -DCMAKE_INSTALL_PREFIX:PATH=${VSOMEIP_PREFIX} .. \
    && make \
    && make install \
    && popd \
    && rm -rf vsomeip-${VSOMIP_VERSION} vsomeip.zip

COPY ./start.sh /start.sh
RUN chmod +x /start.sh

CMD ["/start.sh"]

Dockerfile内でコピーしているshell scriptは以下のような内容である. 内容はシンプルで,コンテナ起動後に無限ループで待機するような構成となっている.

#!/bin/bash

trap_TERM() {
    now=$(date "+%Y/%m/%d-%H:%M:%S")
    echo "[${now}]" SIGTERM ACCEPTED
    exit 0
}

trap 'trap_TERM' TERM

while :
do
    sleep 1
done

docker-compose.ymlの作成

ここでは,以下のようなネットワーク構成を考える.

ネットワーク構成

このネットワーク構成となるように,以下のようなdocker-compose.ymlを作成する.

version: '3.4'

services:
  NodeA:
    build:
      context: .
      dockerfile: Dockerfile
    image: custom_node
    restart: always
    container_name: node_a
    volumes:
      - ./code:/code
    networks:
      node_AB_net:
        ipv4_address: 192.168.100.2

  NodeB:
    build:
      context: .
      dockerfile: Dockerfile
    image: custom_node
    restart: always
    container_name: node_b
    volumes:
      - ./code:/code
    networks:
      node_AB_net:
        ipv4_address: 192.168.100.3
      node_BC_net:
        ipv4_address: 192.168.200.3

  NodeC:
    build:
      context: .
      dockerfile: Dockerfile
    image: custom_node
    restart: always
    container_name: node_c
    volumes:
      - ./code:/code
    networks:
      node_BC_net:
        ipv4_address: 192.168.200.2

networks:
  node_AB_net:
    name: node_AB_net
    ipam:
      config:
        - subnet: 192.168.100.0/24
  node_BC_net:
    name: node_BC_net
    ipam:
      config:
        - subnet: 192.168.200.0/24

イメージの作成

この時点で,コンテナの作成に用いるイメージを作成できる. vsomeip_work直下でpower shellを起動し,以下のコマンドにより,イメージを作成する.

ベースイメージのダウンロードやライブラリのインストールを行うため,イメージ作成にしばらく時間がかかる.

docker-compose build

コンテナの作成

イメージの作成ができたら,コンテナを作成する. コンテナ作成前に,確認に用いるサンプルをcodeディレクトリ以下に格納しておく.格納するファイル本体は,以下のGitHubを参照のこと. * config * examples

github.com

格納後のディレクトリ構成は以下のようになる.

./vsomeip_work
├─ docker-compose.yml
├─ Dockerfile
├─ start.sh
│
└─code
    ├─config
    │
    └─examples

格納後,以下のコマンドを実行し,コンテナを作成する.

docker-compose up -d

下記のコマンドにより,コンテナが起動していることを確認する.

docker ps -a

# 以下出力
# CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
# 477050b48b7d        custom_node         "/start.sh"         4 seconds ago       Up 3 seconds                            node_a
# 41087c9f4381        custom_node         "/start.sh"         4 seconds ago       Up 2 seconds                            node_b
# 1fe66ba8d07e        custom_node         "/start.sh"         4 seconds ago       Up 3 seconds                            node_c

また,以下のコマンドでネットワークの設定も確認しておく.

docker network ls

# 以下出力
# NETWORK ID          NAME                DRIVER              SCOPE
# 34a65fc51f6e        bridge              bridge              local
# a14d7fe77f49        host                host                local
# 436f6828074a        node_AB_net         bridge              local
# 44f087d44fcd        node_BC_net         bridge              local
# d6420bba8ea5        none                null                local

docker network inspect node_AB_net

# 以下出力
#[
#    {
#        "Name": "node_AB_net",
#        "Id": "b304072f2e8a0a95b3be42b0cc4c85681e478b025799d777254141b7e7a22955",
#        "Created": "2020-11-08T10:14:02.637112Z",
#        "Scope": "local",
#        "Driver": "bridge",
#        "EnableIPv6": false,
#        "IPAM": {
#            "Driver": "default",
#            "Options": null,
#            "Config": [
#                {
#                    "Subnet": "192.168.100.0/24"
#                }
#            ]
#        },
#        "Internal": false,
#        "Attachable": true,
#        "Ingress": false,
#        "ConfigFrom": {
#            "Network": ""
#        },
#        "ConfigOnly": false,
#        "Containers": {
#            "83a2415c7b4fbfaf8e5e4fd843b8234b2680e13ca26c274e5d895378f2b48ebc": {
#                "Name": "node_a",
#                "EndpointID": "fc46b62edb430bb844c73875dba7105855b6c5d03cd22602c6703f4ffcf77653",
#                "MacAddress": "02:42:c0:a8:64:02",
#                "IPv4Address": "192.168.100.2/24",
#                "IPv6Address": ""
#            },
#            "b455a5072adb518ec4edd3171fae2362dc5e471330ea8d40efba4500b13d171d": {
#                "Name": "node_b",
#                "EndpointID": "88287a36392668324bcf3524127bd281e9360b76434475c63d3ead0c3e5898e2",
#                "MacAddress": "02:42:c0:a8:64:03",
#                "IPv4Address": "192.168.100.3/24",
#                "IPv6Address": ""
#            }
#        },
#        "Options": {},
#        "Labels": {
#            "com.docker.compose.network": "node_AB_net",
#            "com.docker.compose.project": "someip_work",
#            "com.docker.compose.version": "1.27.4"
#        }
#    }
#]

プログラムのコンパイルと実行

プログラムのコンパイル

cmakeが利用できるはずであるが,途中でエラーが発生したため,手動でコンパイルを実施する.

# コンテナ内に入る(ここではNodeA)
docker exec -it node_a bash

# =======================
# 以下,NodeAコンテナ内での作業
# =======================
# /code/examplesに移動
cd /code/examples
# build用ディレクトリの作成
mkdir build
# build用ディレクトリ内に移動
cd build
# コンパイル
g++ -o request-sample  ../request-sample.cpp  -I/usr/local/vsomeip/include -L/usr/local/vsomeip/lib -lvsomeip3 -lpthread
g++ -o response-sample ../response-sample.cpp -I/usr/local/vsomeip/include -L/usr/local/vsomeip/lib -lvsomeip3 -lpthread

実行

ここでは,NodeAをリクエスト(client),NodeBをレスポンス(service)として動作確認を行う.

jsonファイルの準備

code/examples/build以下に「sample-vsomeip-tcp-client.json」と「sample-vsomeip-tcp-client.json」をそれぞれ作成する. 「unicast」のIPアドレスをコンテナのIPアドレスに合わせて変更している.

sample-vsomeip-tcp-client.json

{
    "unicast" : "192.168.100.3",
    "logging" :
    {
        "level" : "debug",
        "console" : "true",
        "file" : { "enable" : "false", "path" : "/tmp/vsomeip.log" },
        "dlt" : "false"
    },
    "applications" :
    [
        {
            "name" : "service-sample",
            "id" : "0x1277"
        }
    ],
    "services" :
    [
        {
            "service" : "0x1234",
            "instance" : "0x5678",
            "reliable" : { "port" : "30509", "enable-magic-cookies" : "false" },
            "events" :
            [
                {
                    "event" : "0x8777",
                    "is_field" : "false",
                    "is_reliable" : "true",
                    "update-cycle" : "2000"
                },
                {
                    "event" : "0x8778",
                    "is_field" : "true",
                    "is_reliable" : "true",
                    "update-cycle" : "0"
                },
                {
                    "event" : "0x8779",
                    "is_field" : "false",
                    "is_reliable" : "true"
                }
            ],
            "eventgroups" :
            [
                {
                    "eventgroup" : "0x4455",
                    "events" : [ "0x8777", "0x8778" ]
                },
                {
                    "eventgroup" : "0x4465",
                    "events" : [ "0x8778", "0x8779" ]
                },
                {
                    "eventgroup" : "0x4555",
                    "events" : [ "0x8777", "0x8779" ]
                }
            ]
        },
        {
            "service" : "0x1235",
            "instance" : "0x5678",
            "unreliable" : "30509",
            "multicast" :
            {
                "address" : "224.225.226.234",
                "port" : "32344"
            }
        }
    ],
    "routing" : "service-sample",
    "service-discovery" :
    {
        "enable" : "true",
        "multicast" : "224.244.224.245",
        "port" : "30490",
        "protocol" : "udp",
        "initial_delay_min" : "10",
        "initial_delay_max" : "100",
        "repetitions_base_delay" : "200",
        "repetitions_max" : "3",
        "ttl" : "3",
        "cyclic_offer_delay" : "2000",
        "request_response_delay" : "1500"
    }
}

sample-vsomeip-tcp-client.json

{
    "unicast" : "192.168.100.2",
    "netmask" : "255.255.255.0",
    "logging" :
    {
        "level" : "info",
        "console" : "true",
        "file" : { "enable" : "true", "path" : "/var/log/vsomeip.log" },
        "dlt" : "true"
    },
    "applications" :
    [
        {
            "name" : "client-sample",
            "id" : "0x1343"
        },
        {
            "name" : "second-client-sample",
            "id" : "0x1344"
        },
        {
            "name" : "third-client-sample",
            "id" : "0x1345"
        },
        {
            "name" : "fourth-client-sample",
            "id" : "0x1346"
        }
    ],
    "clients" :
    [
        {
            "service" : "0x1234",
            "instance" : "0x5678",
            "reliable" : [ "41234" ]
        }
    ],
    "routing" : "client-sample",
    "service-discovery" :
    {
        "enable" : "true",
        "multicast" : "224.244.224.245",
        "port" : "30490",
        "protocol" : "udp",
        "initial_delay_min" : "10",
        "initial_delay_max" : "100",
        "repetitions_base_delay" : "200",
        "repetitions_max" : "3",
        "ttl" : "3",
        "cyclic_offer_delay" : "2000",
        "request_response_delay" : "1500"
    }
}

リクエスト側

新たに端末を起動し,以下の手順でNodeA内に入り,プログラムを実行する.

# NodeAに入る
 docker exec -it node_a bash
# 移動
cd /code/examples/build
# プログラムを実行
LD_LIBRARY_PATH=/usr/local/vsomeip/lib VSOMEIP_CONFIGURATION=sample-vsomeip-tcp-client.json VSOMEIP_APPLICATION_NAME=client-sample ./request-sample

レスポンス側

新たに端末を起動し,以下の手順でNodeB内に入り,プログラムを実行する.

# NodeBに入る
 docker exec -it node_b bash
# 移動
cd /code/examples/build
# プログラムを実行
LD_LIBRARY_PATH=/usr/local/vsomeip/lib VSOMEIP_CONFIGURATION=sample-vsomeip-tcp-service.json VSOMEIP_APPLICATION_NAME=service-sample ./response-sample

実行結果

リクエスト側

root@83a2415c7b4f:/code/examples/build# LD_LIBRARY_PATH=/usr/local/vsomeip/lib VSOMEIP_CONFIGURATION=sample-vsomeip-tcp-client.json VSOMEIP_APPLICATION_NAME=client-sample ./request-sample
2020-10-08 19:26:28.407884 [info] Parsed vsomeip configuration in 2ms
2020-10-08 19:26:28.418099 [info] Using configuration file: "sample-vsomeip-tcp-client.json".
2020-10-08 19:26:28.428345 [info] Initializing vsomeip application "client-sample".
2020-10-08 19:26:28.431845 [info] Instantiating routing manager [Host].
2020-10-08 19:26:28.432951 [info] create_local_server Routing endpoint at /tmp/vsomeip-0
2020-10-08 19:26:28.433706 [info] Service Discovery enabled. Trying to load module.
2020-10-08 19:26:28.436601 [info] Service Discovery module loaded.
2020-10-08 19:26:28.437625 [info] Application(client-sample, 1343) is initialized (11, 100).
Client settings [protocol=UDP:quiet=false:cycle=1000]
2020-10-08 19:26:28.438473 [info] Starting vsomeip application "client-sample" (1343) using 2 threads I/O nice 255
2020-10-08 19:26:28.440177 [info] shutdown thread id from application: 1343 (client-sample) is: 7f4c485cf700 TID: 786
2020-10-08 19:26:28.440305 [info] main dispatch thread id from application: 1343 (client-sample) is: 7f4c48dd0700 TID: 785
2020-10-08 19:26:28.444401 [info] Watchdog is disabled!
Service [1234.5678] is NOT available.
Service [1235.5678] is NOT available.
2020-10-08 19:26:28.445496 [info] io thread id from application: 1343 (client-sample) is: 7f4c4963cec0 TID: 783
2020-10-08 19:26:28.445597 [info] REQUEST(1343): [1234.5678:255.4294967295]
2020-10-08 19:26:28.447134 [info] Listening at /tmp/vsomeip-1343
2020-10-08 19:26:28.445702 [info] io thread id from application: 1343 (client-sample) is: 7f4c475cd700 TID: 788
2020-10-08 19:26:28.450898 [info] vSomeIP 3.1.16.1 | (default)
2020-10-08 19:26:28.451717 [info] Network interface "eth0" state changed: up
2020-10-08 19:26:28.453084 [info] Route "default route (0.0.0.0/0) if: eth0 gw: 192.168.100.1" state changed: up
2020-10-08 19:26:28.453754 [info] udp_server_endpoint_impl: SO_RCVBUF is: 212992
2020-10-08 19:26:28.455182 [info] udp_server_endpoint_impl: SO_RCVBUF (Multicast) is: 212992
2020-10-08 19:26:28.456837 [info] SOME/IP routing ready.
Service [1235.5678] is available.
Service [1234.5678] is available.
2020-10-08 19:26:30.398135 [error] Routing info for remote service could not be found! (1343): [1234.5678.0421] 0001
Client/Session [1343/0001] sent a request to Service [1234.5678]
2020-10-08 19:26:36.405809 [warning] Didn't receive a multicast SD message for 2200ms.
2020-10-08 19:26:36.409495 [info] udp_server_endpoint_impl: SO_RCVBUF (Multicast) is: 212992
2020-10-08 19:26:38.453815 [info] vSomeIP 3.1.16.1 | (default)
Service [1234.5678] is NOT available.
Service [1235.5678] is NOT available.
2020-10-08 19:26:42.819227 [warning] Didn't receive a multicast SD message for 2200ms.
2020-10-08 19:26:42.823042 [info] udp_server_endpoint_impl: SO_RCVBUF (Multicast) is: 212992
2020-10-08 19:26:45.827210 [warning] Didn't receive a multicast SD message for 2200ms.
2020-10-08 19:26:45.830797 [info] udp_server_endpoint_impl: SO_RCVBUF (Multicast) is: 212992
2020-10-08 19:26:47.834375 [warning] Didn't receive a multicast SD message for 2200ms.
2020-10-08 19:26:47.838612 [info] udp_server_endpoint_impl: SO_RCVBUF (Multicast) is: 212992
2020-10-08 19:26:48.457299 [info] vSomeIP 3.1.16.1 | (default)
2020-10-08 19:26:49.841712 [warning] Didn't receive a multicast SD message for 2200ms.
2020-10-08 19:26:49.844287 [info] udp_server_endpoint_impl: SO_RCVBUF (Multicast) is: 212992
Service [1235.5678] is available.
Service [1234.5678] is available.
2020-10-08 19:26:50.473382 [error] Routing info for remote service could not be found! (1343): [1234.5678.0421] 0002
Client/Session [1343/0002] sent a request to Service [1234.5678]
^C2020-10-08 19:26:54.118953 [info] RELEASE(1343): [1234.5678]
2020-10-08 19:26:54.125003 [error] Routing info for remote service could not be found! (1343): [1234.5678.0421] 0003
Client/Session [1343/0003] sent a request to Service [1234.5678]
^C2020-10-08 19:26:54.406298 [info] RELEASE(1343): [1234.5678]
terminate called after throwing an instance of 'std::system_error'
  what():  Resource deadlock avoided
Aborted

「Client/Session [1343/0001] sent a request to Service [1234.5678]」とあるから,リクエストを送信できている,と判断する.

レスポンス側

root@b455a5072adb:/code/examples/build# LD_LIBRARY_PATH=/usr/local/vsomeip/lib VSOMEIP_CONFIGURATION=sample-vsomeip-tcp-service.json VSOMEIP_APPLICATION_NAME=service-sample ./response-sample
2020-10-08 19:26:30.740818 [info] Parsed vsomeip configuration in 3ms
2020-10-08 19:26:30.750707 [info] Using configuration file: "sample-vsomeip-tcp-service.json".
2020-10-08 19:26:30.760663 [info] Initializing vsomeip application "service-sample".
2020-10-08 19:26:30.761505 [info] Instantiating routing manager [Host].
2020-10-08 19:26:30.762668 [info] create_local_server Routing endpoint at /tmp/vsomeip-0
2020-10-08 19:26:30.764036 [info] Service Discovery enabled. Trying to load module.
2020-10-08 19:26:30.767126 [info] Service Discovery module loaded.
2020-10-08 19:26:30.767859 [info] Application(service-sample, 1277) is initialized (11, 100).
Static routing OFF
2020-10-08 19:26:30.768479 [info] Starting vsomeip application "service-sample" (1277) using 2 threads I/O nice 255
2020-10-08 19:26:30.770348 [info] main dispatch thread id from application: 1277 (service-sample) is: 7fc1e1ac7700 TID: 788
2020-10-08 19:26:30.772081 [info] Watchdog is disabled!
2020-10-08 19:26:30.776807 [info] io thread id from application: 1277 (service-sample) is: 7fc1e2333ec0 TID: 786
2020-10-08 19:26:30.777148 [info] io thread id from application: 1277 (service-sample) is: 7fc1dbfff700 TID: 791
2020-10-08 19:26:30.778769 [info] vSomeIP 3.1.16.1 | (default)
2020-10-08 19:26:30.779538 [info] Network interface "eth0" state changed: up
Application service-sample is registered.
2020-10-08 19:26:30.770877 [info] shutdown thread id from application: 1277 (service-sample) is: 7fc1e12c6700 TID: 789
2020-10-08 19:26:30.780408 [info] Route "default route (0.0.0.0/0) if: eth0 gw: 192.168.100.1" state changed: up
2020-10-08 19:26:30.781873 [info] OFFER(1277): [1234.5678:0.0] (true)
2020-10-08 19:26:30.783546 [info] udp_server_endpoint_impl: SO_RCVBUF is: 212992
2020-10-08 19:26:30.784057 [debug] Joining to multicast group 224.244.224.245 from 192.168.100.3
2020-10-08 19:26:30.784525 [info] udp_server_endpoint_impl: SO_RCVBUF (Multicast) is: 212992
2020-10-08 19:26:30.784910 [info] SOME/IP routing ready.
2020-10-08 19:26:30.788736 [info] Listening at /tmp/vsomeip-1277
2020-10-08 19:26:30.790500 [info] OFFER(1277): [1235.5678:0.0] (true)
2020-10-08 19:26:30.791146 [info] udp_server_endpoint_impl: SO_RCVBUF is: 212992
2020-10-08 19:26:40.781679 [info] vSomeIP 3.1.16.1 | (default)
2020-10-08 19:26:40.804810 [info] STOP OFFER(1277): [1234.5678:0.0] (true)
2020-10-08 19:26:40.807028 [info] STOP OFFER(1277): [1235.5678:0.0] (true)
2020-10-08 19:26:50.784224 [info] vSomeIP 3.1.16.1 | (default)
2020-10-08 19:26:50.819593 [info] OFFER(1277): [1234.5678:0.0] (true)
2020-10-08 19:26:50.823144 [info] OFFER(1277): [1235.5678:0.0] (true)
2020-10-08 19:26:50.827366 [info] udp_server_endpoint_impl: SO_RCVBUF is: 212992
^C2020-10-08 19:26:55.620901 [info] STOP OFFER(1277): [1234.5678:0.0] (true)
2020-10-08 19:26:55.621967 [info] STOP OFFER(1277): [1235.5678:0.0] (true)
^C2020-10-08 19:26:55.587222 [info] STOP OFFER(1277): [1234.5678:0.0] (true)
2020-10-08 19:26:55.588255 [info] STOP OFFER(1277): [1235.5678:0.0] (true)
terminate called after throwing an instance of 'std::system_error'
  what():  Resource deadlock avoided
Aborted

リクエスト側のプログラムがそれっぽく動作していることは確認できた.一方,レスポンス側のプログラムがレスポンスを返せていない気がする.

この点については,通信シーケンスやjsonのconfig情報を確認した上で再度チェックしたいと思う.(いつになることやら)