【NVIDIA Jetson Tips】JetPack SDKを利用するDockerイメージのクロスコンパイル方法

# はじめに
　NVIDIA Jetsonで、JetPack SDKを利用するDockerイメージをクロスコンパイルする方法を紹介する。


# 動作確認環境

* コンパイル環境
  * Ubuntu 22.04 x86_64
  * Docker 27.3.1
    * buildx: v0.17.1
      * Buildxによるクロスコンパイル環境導入済み
* ターゲット環境
  * NVIDIA JetPack
    * 5.0.2 
    * 5.1.1
    * 5.1.2

# 方法
## 事前準備：QEMUの導入
　コンパイル環境に、Dockerを用いたQEMU環境を構築する。

 ```bash
 docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
 ```

QEMUが有効になったか確認する。
 ```bash
cat /proc/sys/fs/binfmt_misc/qemu-aarch64
```

flagsにFが入っていれば成功。
```bash
enabled
interpreter /usr/bin/qemu-aarch64
flags: OCF
offset 0
magic 7f454c460201010000000000000000000200b7
```

## Dockerイメージの作成

:::note info
ポイント  

* ベースとするDockerイメージは、NVIDIA社から提供されているJetson用のDockerイメージを使う
* 上記イメージには、JetPack SDKのライブラリファイルが含まれないので、Dockerfile内でJetPack SDKのライブラリを参照する必要がある場合は、Dockerfile内でダウンロードする
:::

　ベースとするJetsonのDockerイメージは、[NGC](https://catalog.ngc.nvidia.com/containers?filters=&orderBy=weightPopularDESC&query=&page=&pageSize=)で公開されている。

|Dockerイメージ名|概要|
|:-:|:-:|
|[nvcr.io/nvidia/l4t-base](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/l4t-base)|Linux for Tegraの一番シンプルなコアとなるイメージ|
|[nvcr.io/nvidia/l4t-jetpack](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/l4t-jetpack)|上記にJetPack SDKが付属したイメージ|

今回は、[JetPack SDK](https://developer.nvidia.com/ja-jp/embedded/jetpack)を利用するので、```nvcr.io/nvidia/l4t-jetpack```をベースのDockerイメージとする。

### JetPack SDKライブラリファイルを配置する

　JetPack SDKのライブラリを使ってクロスコンパイルするには、ライブラリファイルをDockerfile内で配置する必要がある。
 JetPack SDKは、```nvcr.io/nvidia/l4t-jetpack```に導入されるが、これらのSDKに依存したソースのコンパイルを行うには、ライブラリファイル（.so）が必要となる。ライブラリファイルは、Dockerの起動時のruntimeで指定されるので、Dockerイメージ作成時のクロスコンパイル環境では利用できない制約がある。（また、Jetson上でのDocker buildにおいても、runtimeを指定する必要がある。）
　そこで、Dockerfile内で、NVIDIAから提供されているDriver Package(BSP)をダウンロードして展開することで、ライブラリファイルを参照できるようにする。BSPは、JetPackごとに[公式サイト](https://developer.nvidia.com/embedded/jetson-linux-archive)から提供されている。
　ここでは、JetPack 5.1.2の場合の例を示す。JetPack 5.1.2は、Jetson Linux 35.4.1に相当するので、[こちら](https://developer.nvidia.com/embedded/jetson-linux-r3541)の以下のDRIVERS -> Driver Package (BSP) からダウンロードする。また、ベースとなるDockerイメージは、[こちら](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/l4t-jetpack/tags)から```nvcr.io/nvidia/l4t-jetpack:r35.4.1```とする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638977/927073de-a4af-0543-509a-9c186c17c15f.png)

　以下にDockerfileの例を示す。JetPack SDKのライブラリは、```/usr/lib/aarch64-linux-gnu/tegra```に配置する必要があるので、BSPからダウンロードしたライブラリのシンボリックリンク```usr/lib/aarch64-linux-gnu/tegra```に作成する。
 Dockerfile内でのクロスコンパイルの例として、JetsonのMultimedia APIサンプルをビルドする。

```bash
cd <your_workspace>
vi Dockerfile
```

```dockerfile: Dockerfileの例（JetPack 5.1.2の場合）
FROM nvcr.io/nvidia/l4t-jetpack:r35.4.1

# tegraライブラリのシンボリックリンクを作成
# JetPack BSP
# 5.0.2(r35.1.0): https://developer.nvidia.com/embedded/l4t/r35_release_v1.0/release/jetson_linux_r35.1.0_aarch64.tbz2
# 5.1.1(r35.3.1): https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v3.1/release/jetson_linux_r35.3.1_aarch64.tbz2
# 5.1.2(r35.4.1): https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v4.1/release/jetson_linux_r35.4.1_aarch64.tbz2
ARG JETSON_BSP_FILE=https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v4.1/release/jetson_linux_r35.4.1_aarch64.tbz2
RUN echo "Jetson BSP drivers wget URL : " ${JETSON_BSP_FILE} && \
    cd /tmp && \
    wget ${JETSON_BSP_FILE} && \
    tar -jxpf jetson_linux_r35*aarch64.tbz2 && \
    cd Linux_for_Tegra/nv_tegra && \
    tar -jxpf nvidia_drivers.tbz2 && \
    cd /usr/lib/aarch64-linux-gnu && \
    echo "Create jetson lib symbolic links" && \
    ln -s /tmp/Linux_for_Tegra/nv_tegra/usr/lib/aarch64-linux-gnu/tegra tegra

# 以下にJetson SDKを使ったライブラリのビルドなどを記載
# 例として、Multimedia APIサンプルをビルド
# サンプルのビルドに必要な依存をインストール
RUN apt update && apt install -y libdrm-dev libvulkan-dev
RUN cd /usr/src/jetson_multimedia_api/samples/00_video_decode && \
    make

# ライブラリのビルドが完了後、tegraライブラリのシンボリックリンクを削除
RUN echo "Delete jetson dependencies: " "${ARCH}" && \
    unlink /usr/lib/aarch64-linux-gnu/tegra && \
    rm -rf /tmp/Linux_for_Tegra && \
    rm -rf /tmp/jetson_linux_r35*aarch64.tbz2
```

DockerfileからArm64環境のDockerイメージを作成する。Buildxの環境構築などは[こちらの記事](https://qiita.com/Tomato_otamoT/items/e808eb7f959c5942eaf6)を参照。

```bash
docker buildx build --platform linux/arm64 -t <image_name>:latest .
```

Multimedia APIサンプルのビルドが成功すると、Dockerイメージ内に```video_decode```の実行ファイルが生成される。

```bash
make[1]: Entering directory '/usr/src/jetson_multimedia_api/samples/common/classes'
Compiling: NvDrmRenderer.cpp
Compiling: NvLogging.cpp
Compiling: NvEglRenderer.cpp
Compiling: NvBufSurface.cpp
Compiling: NvJpegDecoder.cpp
Compiling: NvVideoEncoder.cpp
Compiling: NvV4l2ElementPlane.cpp
Compiling: NvVulkanRenderer.cpp
Compiling: NvV4l2Element.cpp
make[1]: Leaving directory '/usr/src/jetson_multimedia_api/samples/common/classes'
Linking: video_decode
```

Multimedia APIサンプルのビルドに失敗すると、以下のようにJetPack SDKライブラリが見つからずのリンカエラーとなる。

```bash
make[1]: Entering directory '/usr/src/jetson_multimedia_api/samples/common/classes'
Compiling: NvDrmRenderer.cpp
Compiling: NvLogging.cpp
Compiling: NvEglRenderer.cpp
Compiling: NvBufSurface.cpp
Compiling: NvJpegDecoder.cpp
Compiling: NvVideoEncoder.cpp
Compiling: NvV4l2ElementPlane.cpp
Compiling: NvVulkanRenderer.cpp
Compiling: NvV4l2Element.cpp
make[1]: Leaving directory '/usr/src/jetson_multimedia_api/samples/common/classes'
Linking: video_decode
/usr/bin/ld: cannot find -lnvv4l2
/usr/bin/ld: cannot find -lnvbufsurface
/usr/bin/ld: cannot find -lnvbufsurftransform
/usr/bin/ld: cannot find -lnvjpeg
/usr/bin/ld: cannot find -lnvosd
/usr/bin/ld: cannot find -lcuda
collect2: error: ld returned 1 exit status
make: *** [Makefile:53: video_decode] Error 1
```

上記のエラーとなる場合は、JetPack SDKのライブラリが参照できていないので、BSPパッケージのダウンロードやシンボリックリンクの記述を見直してDockerfileを修正する。

## 動作確認

　作成したDockerイメージを起動して動作確認を行う。
 
```bash
docker run -it --rm <image_name>:latest /bin/bash
```

Dockerコンテナ内のターミナルには入れれば成功。


```bash
cd /usr/src/jetson_multimedia_api/samples/00_video_decode
ls | grep video_decode
```
```bash
video_decode
```

```video_decode```が存在しない場合は、Dockerイメージが上手く作成できていないので再度作成する。

# まとめ
　NVIDIA Jetsonで、JetPack SDKを利用するDockerイメージをクロスコンパイルする方法を紹介した。JetPack SDKを使った自作のライブラリなどをクロスコンパイルする際に有用。
　
# 参考

https://github.com/NVIDIA-AI-IOT/deepstream_python_apps/blob/master/bindings/qemu_docker/ubuntu-cross-aarch64.Dockerfile#L75-L85

https://qiita.com/Tomato_otamoT/items/e808eb7f959c5942eaf6