ZenohをUbuntuのDockerイメージで動かすまで

## はじめに

本記事はZenoh アドベントカレンダー3日目の記事です．

https://qiita.com/advent-calendar/2024/zenoh

本当はZenohで作ったプログラムの評価をする予定でしたが、思っていたよりも環境構築に時間を取られたので、備忘録を兼ねて記事を作成しました。
ハマりポイントについても記載したので、参考にしてもらえると助かります。

## Zenoh とは

> Zenoh は Zero network overhead なプロトコルです．
> Pub/Sub 型の通信に加えて Store/Query なデータ管理と Compute な機能を備えています．
> (Zenohアドベントカレンダーより)

また、以下の記事を毎回参考にさせていただいております。

https://qiita.com/Shintaro_Hosoai/items/0bde489cde43a00d6f96

## モチベーション

公式のDockerイメージだとOSがUbuntuじゃないので、UbuntuのDockerイメージを活用できるDockerfileを記述するのが今回のモチベーションです。また、Dockerfileの記述にすることで他のイメージにもZenohをインストールできるようになります。

## Zenohってaptで入るよね？

以下のZenohのGitHubページに記載の通り、Zenohをaptでインストールすることができます。
しかも、Zenoh本体だけでなくC++版やpico版もインストールできるようです。

https://github.com/eclipse-zenoh/zenoh?tab=readme-ov-file#linux-debian

さて、これをDockerfileの記述に変えると以下のようになります。

```Dockerfile
FROM ubuntu:22.04
RUN echo "deb [trusted=yes] https://download.eclipse.org/zenoh/debian-repo/ /" | tee -a /etc/apt/sources.list.d/zenoh.list > /dev/null && \
  apt update && \
  apt install -y -qq zenoh
```

これを以下のコマンドを使用してビルドを行いました。
```bash
docker build --rm -t zenoh-test-image .
```

上記のDockerfileをビルドすると以下のエラーが発生しました。
```
6.895 The following additional packages will be installed:
6.895   zenoh-plugin-rest zenoh-plugin-storage-manager zenohd
6.917 The following NEW packages will be installed:
6.917   zenoh zenoh-plugin-rest zenoh-plugin-storage-manager zenohd
10.70 0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
10.70 Need to get 8531 kB of archives.
10.70 After this operation, 40.6 MB of additional disk space will be used.
10.73 Selecting previously unselected package zenohd.
(Reading database ... 80348 files and directories currently installed.)
10.84 Preparing to unpack .../zenohd_1.0.3_amd64.deb ...
10.85 Unpacking zenohd (1.0.3) ...
11.08 Selecting previously unselected package zenoh-plugin-storage-manager.
11.08 Preparing to unpack .../zenoh-plugin-storage-manager_1.0.3_amd64.deb ...
11.08 Unpacking zenoh-plugin-storage-manager (1.0.3) ...
11.20 Selecting previously unselected package zenoh-plugin-rest.
11.21 Preparing to unpack .../zenoh-plugin-rest_1.0.3_amd64.deb ...
11.21 Unpacking zenoh-plugin-rest (1.0.3) ...
11.34 Selecting previously unselected package zenoh.
11.35 Preparing to unpack .../archives/zenoh_1.0.3_amd64.deb ...
11.35 Unpacking zenoh (1.0.3) ...
11.41 Setting up zenohd (1.0.3) ...
11.47 System has not been booted with systemd as init system (PID 1). Can't operate.
11.47 Failed to connect to bus: Host is down
11.47 dpkg: error processing package zenohd (--configure):
11.47  installed zenohd package post-installation script subprocess returned error exit status 1
11.47 dpkg: dependency problems prevent configuration of zenoh:
11.47  zenoh depends on zenohd (= 1.0.3); however:
11.47   Package zenohd is not configured yet.
11.47 
11.47 dpkg: error processing package zenoh (--configure):
11.47  dependency problems - leaving unconfigured
11.47 dpkg: dependency problems prevent configuration of zenoh-plugin-rest:
11.47  zenoh-plugin-rest depends on zenohd (= 1.0.3); however:
11.47   Package zenohd is not configured yet.
11.47 
11.47 dpkg: error processing package zenoh-plugin-rest (--configure):
11.47  dependency problems - leaving unconfigured
11.47 dpkg: dependency problems prevent configuration of zenoh-plugin-storage-manager:
11.47  zenoh-plugin-storage-manager depends on zenohd (= 1.0.3); however:
11.47   Package zenohd is not configured yet.
11.47 
11.47 dpkg: error processing package zenoh-plugin-storage-manager (--configure):
11.47  dependency problems - leaving unconfigured
11.48 Errors were encountered while processing:
11.48  zenohd
11.48  zenoh
11.48  zenoh-plugin-rest
11.48  zenoh-plugin-storage-manager
11.49 E: Sub-process /usr/bin/dpkg returned an error code (1)
```
重要なのは以下の一文です。
```
System has not been booted with systemd as init system (PID 1). Can't operate.
```
つまりsystemdがPID 1で動作していない**仮想マシンではZenohをaptでインストールできない**ようです。
~~そのため、マニュアルインストールでDockerfileの記述をする必要があります。~~

[2024/12/4追記] @takasehideki 氏の指摘を元に再度調査した結果、docker-buildxを使用すると良かったようです。

https://stackoverflow.com/questions/48098671/build-with-docker-and-privileged

docker-buildxを使用するほうが手間を少なくできるので、その方法も記載します。

## 解決策 その1. docker-buildxを使用したエラーの回避

### docker-buildx とは

docker-buildxはdocker buildのようなUIでマルチプラットフォームイメージのビルドなどができるように拡張したCLIプライグインです。

https://matsuand.github.io/docs.docker.jp.onthefly/buildx/working-with-buildx/

### docker-buildxを使用する際の変更点

コマンド自体はbuildの前にbuildx、後に--loadを付けるだけです。
```bash
docker buildx build --load --rm -t zenoh-test-image .
```

ただし、buildxにすると以下のエラーが発生する場合があります。
```
W: Failed to fetch https://download.eclipse.org/zenoh/debian-repo/InRelease  Certificate verification failed: The certificate is NOT trusted. The certificate issuer is unknown.  Could not handshake: Error in the certificate verification.
```
これはもともとのイメージの証明書が古いということが原因のようなので、証明書の更新を行うようにDockerfileを書き換えてください。
```Dockerfile
FROM ubuntu:22.04
RUN apt update && apt install -y -qq --reinstall ca-certificates && update-ca-certificates
RUN echo "deb [trusted=yes] https://download.eclipse.org/zenoh/debian-repo/ /" | tee -a /etc/apt/sources.list.d/zenoh.list > /dev/null && \
  apt update && \
  apt install -y -qq zenoh
```

また、以下のエラーが発生する場合もありました。
```
ERROR: failed to solve: ubuntu:22.04: failed to resolve source metadata for docker.io/library/ubuntu:22.04: failed to do request: Head "https://registry-1.docker.io/v2/library/ubuntu/manifests/22.04": dial tcp: lookup registry-1.docker.io on
```
こちらはネットワークの問題のようなので、/etc/resolv.confに以下の２行を追加し
```
nameserver 8.8.8.8
nameserver 8.8.4.4
```
以下のコマンドでDockerに反映させてください。
```bash
sudo systemctl daemon-reload
sudo systemctl restart docker
```

https://forums.docker.com/t/ubuntu-22-04-error-response-from-daemon-get-https-registry-1-docker-io-v2-dial-tcp-lookup-registry-1-docker-io-on-127-0-0-53-read-udp-127-0-0-1-48086-127-0-0-53-read-connection-refused/138376/2

## 解決策 その2. マニュアルインストールでのDockerfileの記述

最初に完成版のDockerfileを示します。
```Dockerfile
FROM ubuntu:22.04

RUN set -x && \
  apt-get update -y -qq && \
  apt-get upgrade -y -qq --no-install-recommends && \
  : "basic dependencies" && \
  apt-get install -y -qq \
    build-essential \
    pkg-config \
    cmake \
    git \
    wget \
    curl \
    tar \
    unzip software-properties-common

RUN cd /tmp && curl https://sh.rustup.rs -sSf > rustup-init.sh && sh rustup-init.sh -y
RUN export PATH=$PATH:/root/.cargo/bin && cd / && \
  git clone https://github.com/eclipse-zenoh/zenoh.git && \
  cd zenoh && \
  rustup update && \
  cargo build --release --all-targets
RUN export PATH=$PATH:/root/.cargo/bin && cd / && \
  git clone https://github.com/eclipse-zenoh/zenoh-pico.git && \
  cd zenoh-pico && \
  make && \
  make install
RUN export PATH=$PATH:/root/.cargo/bin && cd / && \
  git clone https://github.com/eclipse-zenoh/zenoh-c.git && \
  cd zenoh-c && \
  mkdir build && \
  cd build && \
  cmake .. && \
  cmake --build . --config Release && \
  cmake --build . --target install
RUN export PATH=$PATH:/root/.cargo/bin && cd / && \
  git clone https://github.com/eclipse-zenoh/zenoh-cpp.git && \
  cd zenoh-cpp && \
  mkdir build && \
  cd build && \
  cmake .. -DZENOHCXX_ZENOHPICO=ON  -DCMAKE_INSTALL_PREFIX=~/.local && \
  cmake --install .
```

以降にDockerfileを作成する上でハマりかけたポイントを解説します。

### RUSTインストールコマンドがそのまま使えない

ZenohのGitHubページで案内される以下のページでRUSTは次のコマンドで簡単に入ると記載されています。

https://doc.rust-lang.org/cargo/getting-started/installation.html

```bash
curl https://sh.rustup.rs -sSf | sh
```
しかし、このコマンドをそのままDockerfileに記述するとYes/Noの確認があるのでコケます。
そのため、一旦ファイルに出力した上でyオプションを付与して実行する必要がありました。
当該箇所はこのようになっています。
```Dockerfile
RUN cd /tmp && curl https://sh.rustup.rs -sSf > rustup-init.sh && sh rustup-init.sh -y
```

### rustupやcargoコマンドが見つからない

Dockerでのビルド実行時に上記のRUSTのインストールでインストールしたrustupやcargoコマンドが見つからないという問題が発生しました。
具体的な要因はわからなかったですが、応急処置としてrustupやcargoコマンドが格納されたディレクトリをPATH環境変数に加えることで対応しています。

## 動作確認

picoやc、cppの方はインストールしてみたもののどのようにテストするのかわからなかったため、とりあえずZenoh本体が動作することを確認してみます。
動作確認の様子は以下のとおりです。
![zenoh_test.GIF](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3326389/ac3948e9-f93d-de53-f6ff-faa4dfc4c01a.gif)
動画の通り、Pub/Subが動作していることが確認できました。

## おわりに

今回はZenohを使用するためのUbuntuのDockerイメージの作成を行いました。いろいろな試行錯誤で結構時間を取られてしまったので、これからDockerでZenohを試してみたい方は同じ轍を踏まないように参考にしてもらえると幸いです。上記のDockerfileの記述を応用すれば、任意のDebian系LinuxのDockerイメージにZenohをインストールして試すことができるようになるはずなので、ぜひともお手元のDockerfileにZenohを加えてみませんか？
次回はZenohが動作するプログラムを書いて記事を投稿したいと思います。