Jetson Linux 36.3 (JetPack 6.0) を Turing Pi 2上のNVIDIA Jetson Orin Nanoにインストールした記録

はじめに
Turing Pi 2 というボード上にJetson Orin Nano(4GB)を4枚差してKubernetesクラスターのノードとして利用している。
2024年5月に Jetson Linuxの最新バージョン 36.3 がリリースされたので、OSの入れ替えを行った際のメモを残す。
リリースされたものをそのままインストールするのではなく、以下のようなカスタマイズを行った

ビルド環境作成
カーネルのビルドやJetsonへのOSイメージへの書き込みには別マシンを使う。
今回は以下のようなマシンを利用した。
CPU: Core i3 12100F
MEM: 32GB
OS: Ubuntu 22.04
ビルド用マシンはx86-64、Jetsonはaarch64とCPUアーキテクチャが異なるため、クロスコンパイルを行う。
NVIDIA公式のDeveloper Guideを参照しながら進めていく。
ダウンロードが必要なファイルはリリースページ内の以下2つ。
- Driver Package (BSP)
- Bootlin Toolchain gcc 11.3
# Download
cd Downloads
wget https://developer.nvidia.com/downloads/embedded/l4t/r36_release_v3.0/toolchain/aarch64--glibc--stable-2022.08-1.tar.bz2
wget https://developer.nvidia.com/downloads/embedded/l4t/r36_release_v3.0/release/jetson_linux_r36.3.0_aarch64.tbz2
# Cross Compiler
cd ~/
mkdir l4t-gcc && cd l4t-gcc
tar xvf ../Downloads/aarch64--glibc--stable-2022.08-1.tar.bz2
export CROSS_COMPILE=$HOME/l4t-gcc/aarch64--glibc--stable-2022.08-1/bin/aarch64-buildroot-linux-gnu-
# Unpack Jetson
cd ~/
mkdir jetson && cd jetson
tar xvf ../Downloads/jetson_linux_r36.3.0_aarch64.tbz2
ホームディレクトリ下に、以下のようなディレクトリ構成ができる。
.
├── Downloads
│ ├── aarch64--glibc--stable-2022.08-1.tar.bz2
│ └── jetson_linux_r36.3.0_aarch64.tbz2
├── jetson
│ └── Linux_for_Tegra
│ └── <もろもろ>
└── l4t
└── <クロスコンパイラ>

Root File System の作成
Minimalな環境を作る。
Developer Guideの以下のページを参考にする。
標準で用意されているMinimal構成の設定ファイルだけだと起動時にネットワークに繋がらないため、netplan をインストールしておく。
cd Linux_for_Tegra/tools/samplefs/
sudo apt-get install qemu-user-static
echo netplan.io >> nvubuntu-jammy-minimal-aarch64-packages
sudo ./nv_build_samplefs.sh --abi aarch64 --distro ubuntu --flavor minimal --version jammy
作成したRoot File SystemのTarballを所定のパスへ展開する。
cd ../../
rm rootfs/README.txt
sudo tar xvfj tools/samplefs/sample_fs.tbz2 -C rootfs/
netplan の設定ファイルを追加しておく。以下のようなYAMLファイルを作成する。
network:
version: 2
ethernets:
eth0:
dhcp4: yes
それをコピーする。
sudo cp 01-dhcp.yaml rootfs/etc/netplan/
NVIDIA固有のバイナリパッケージも入れる。ただしGUI環境がないとエラーになるものがあるので、あらかじめパッケージファイルを削除しインストールされないようにしておく。
rm nv_tegra/l4t_deb_packages/nvidia-l4t-*-gui-*.deb
sudo ./apply_binaries.sh

カーネルのビルド
ソースコードを取得する。
cd source
./source_sync.sh -k -t jetson_36.3
初期のConfigファイルを生成する。
sudo apt install flex bison libssl-dev
make ARCH=arm64 -C kernel/kernel-jammy-src LOCALVERSION=-tegra defconfig
make ARCH=arm64 -C kernel/kernel-jammy-src LOCALVERSION=-tegra prepare
ZFSのモジュールを合わせてビルドしたいので、ソースコードを取得しカーネルのソースツリーに加える。
git clone https://github.com/openzfs/zfs.git
cd zfs
git checkout refs/tags/zfs-2.2.4
./autogen.sh
ARCH=arm64 ./configure --build=x86_64-linux-gnu --host=aarch64-linux-gnu --target=aarch64-linux-gnu --enable-linux-builtin=yes --with-linux=../kernel/kernel-jammy-src --with-linux-obj=../kernel/kernel-jammy-src
./copy-builtin ../kernel/kernel-jammy-src
cd ../
カーネルのConfigに追記を行う。
vi kernel/kernel-jammy-src/.config
ファイル末尾に以下を追記する。
Ciliumの必須カーネルオプションは公式ドキュメントに記載があるので、こちらを参考に足りないものを足していった。
# for Cilium
CONFIG_NET_CLS_BPF=y
CONFIG_NETFILTER_XT_SET=m
CONFIG_IP_SET=m
CONFIG_IP_SET_HASH_IP=m
CONFIG_IP_NF_RAW=m
CONFIG_INET6_ESP=m
CONFIG_INET6_IPCOMP=m
CONFIG_INET6_TUNNEL=m
CONFIG_INET6_XFRM_TUNNEL=m
CONFIG_INET_ESP=m
CONFIG_XFRM=y
CONFIG_XFRM_ALGO=y
CONFIG_XFRM_USER=y
CONFIG_NET_CLS_CGROUP=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=m
CONFIG_NETFILTER_XT_TARGET_CT=m
CONFIG_NETFILTER_XT_MATCH_MARK=m
CONFIG_NETFILTER_XT_MATCH_SOCKET=m
# for PT3
CONFIG_DVB_DEMUX_SECTION_LOSS_LOG=y
CONFIG_MEDIA_PCI_SUPPORT=y
CONFIG_DVB_PT3=m
CONFIG_MEDIA_TUNER_MXL301RF=m
CONFIG_MEDIA_TUNER_QM1D1C0042=m
# for ZFS
CONFIG_ZFS=y
設定が終わったらビルドを行う。
カーネルのビルド時は KERNEL_DEF_CONFIG=olddefconfig
を付けないと、追記したConfigが消失してデフォルト値に戻ってしまうので注意。
make KERNEL_DEF_CONFIG=olddefconfig -C kernel
export KERNEL_HEADERS=$PWD/kernel/kernel-jammy-src
make modules
make dtbs
ビルドが完了したら、前項で作成したRoot File System上にインストールする。
export INSTALL_MOD_PATH=$PWD/../rootfs
sudo -E make install -C kernel
cp kernel/kernel-jammy-src/arch/arm64/boot/Image ../kernel/Image
sudo -E make modules_install
cd ../
sudo ./tools/l4t_update_initrd.sh
cd source
cp nvidia-oot/device-tree/platform/generic-dts/dtbs/* ../kernel/dtb/

Jetsonに書き込む
Root File Systemにカーネルをインストールしたら、あとはJetsonに書き込むのみ。
まずは事前の準備作業。
SSHのホスト鍵を消しておかないと、すべて同一のホスト鍵を持ったサーバー群が出来上がってしまう。消しておくと初回のデーモン起動時に自動で再生成されるため、各マシンでユニークなホスト鍵を持つことができる。
Turing Pi 2を利用している場合はデバイスのEEPROMサイズを0にしないといけないので、そちらも修正している。
cd ../
# 書き込みに必要なパッケージをビルドマシンにインストール
sudo ./tools/l4t_flash_prerequisites.sh
sudo rm rootfs/etc/ssh/ssh_host_*
# for Turing Pi 2
sed -i 's/cvb_eeprom_read_size = <0x100>/cvb_eeprom_read_size = <0x0>/g' bootloader/generic/BCT/tegra234-mb2-bct-misc-p3767-0000.dts
準備ができたらビルドマシンとTuring Pi 2 をUSB A-Aケーブルで接続し、BMCから対象のボード番号に
対しFlash modeで接続を行ったのちボードの電源を入れる。
そして以下コマンドで書き込みを行う。
sudo ./tools/l4t_create_default_user.sh -u <ユーザー名> -p <パスワード> -n <ホスト名> --accept-license
sudo ./tools/kernel_flash/l4t_initrd_flash.sh --external-device nvme0n1p1 \
-c tools/kernel_flash/flash_l4t_t234_nvme.xml -p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
--showlogs --network usb0 jetson-orin-nano-devkit internal
Turing Pi 2 のドキュメントではNode1ではOSの書き込みができないような記述がある。
たしかに失敗するが、何度かチャレンジすればそのうち成功するので、わざわざ筐体を開けてスロット交換する必要はなさそう。BMC v2.0.5環境で確認。
2024/05/09追記
Jetson AGX Orin Developer Kit (NVMe)のFlashは以下コマンドで行う
sudo ./tools/kernel_flash/l4t_initrd_flash.sh --external-device nvme0n1p1 \
-c tools/kernel_flash/flash_l4t_t234_nvme.xml \
--showlogs --network usb0 jetson-agx-orin-devkit external

OS起動後の作業
ここからは書き込み完了後のJetson上で行う作業
# カーネルを上書きしないように、関連パッケージのバージョンを固定
sudo apt-mark hold nvidia-l4t-display-kernel nvidia-l4t-kernel nvidia-l4t-kernel-dtbs \
nvidia-l4t-kernel-headers nvidia-l4t-kernel-oot-headers nvidia-l4t-kernel-oot-modules
# JetPackとjetson-statsのインストール
sudo apt update && sudo apt install -y nvidia-jetpack
sudo apt install -y python3-pip
sudo pip3 install -U jetson-stats
# Kubernetesノードとして使うため、SWAPを無効化
sudo systemctl disable nvzramconfig
# ZFSを利用するノードのみ、関連パッケージをインストール
sudo apt install zfsutils-linux
# 接続済みディスクのzpoolをインポートする
sudo zpool import
sudo zpool import -f <zpool名>
Kubernetesに必要なモジュールとカーネルパラメータの設定
sudo vi /etc/modules-load.d/containerd.conf
overlay
br_netfilter
vxlan
sudo vi /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
containerd のインストール
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update && sudo apt install -y containerd.io
containerd の設定。systemd cgroupドライバーとNVIDIA Container Runtime を使用するように変更する。
containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
sudo nvidia-ctk runtime configure --runtime=containerd --set-as-default
sudo systemctl restart containerd
Kubernetesのインストール。今回は1.29系を使用した。
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
ここまで終わったらOSを再起動して、モジュールやカーネルパラメータの設定を反映させる。
再起動完了後、以下のコマンドでコントロールプレーンに接続する。
sudo kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>
token と hash はコントロールプレーンにログインして以下コマンドで取得する
# 作成済みのtokenがあれば以下コマンドで表示される
kubeadm token list
# 上記が空の場合は以下コマンドでtokenを作成する
kubeadm token create
# hash は以下コマンドで表示可能
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'

NVIDIA device plugin
kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.5/nvidia-device-plugin.yml
0.15.0もリリースされてるけど、エラーで動かなかった。
エラー内容の深掘り調査はしてないけど、0.14.5に下げたら動いたのでいったんこれで。