Open7

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

ussvgrussvgr

はじめに

Turing Pi 2 というボード上にJetson Orin Nano(4GB)を4枚差してKubernetesクラスターのノードとして利用している。
2024年5月に Jetson Linuxの最新バージョン 36.3 がリリースされたので、OSの入れ替えを行った際のメモを残す。
https://developer.nvidia.com/embedded/jetson-linux-r363

リリースされたものをそのままインストールするのではなく、以下のようなカスタマイズを行った

  • Minimal 環境でのインストール
    • 通常インストールだとGUI環境を含めた形でインストールされるが、ディスプレイを繋ぐ予定はなく、むしろ限られたリソースを節約したい。そのため、GUIを省いたMinimal環境でのインストールを行う
  • カーネルのリビルド
    • ソフトウェアやハードウェアの都合で、標準インストール時に含まれないカーネルモジュールを利用したい
ussvgrussvgr

ビルド環境作成

カーネルのビルドやJetsonへのOSイメージへの書き込みには別マシンを使う。
今回は以下のようなマシンを利用した。
CPU: Core i3 12100F
MEM: 32GB
OS: Ubuntu 22.04

ビルド用マシンはx86-64、Jetsonはaarch64とCPUアーキテクチャが異なるため、クロスコンパイルを行う。
NVIDIA公式のDeveloper Guideを参照しながら進めていく。
https://docs.nvidia.com/jetson/archives/r36.3/DeveloperGuide/AT/JetsonLinuxToolchain.html

ダウンロードが必要なファイルはリリースページ内の以下2つ。
https://developer.nvidia.com/embedded/jetson-linux-r363

  • 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
    └── <クロスコンパイラ>
ussvgrussvgr

Root File System の作成

Minimalな環境を作る。
Developer Guideの以下のページを参考にする。
https://docs.nvidia.com/jetson/archives/r36.3/DeveloperGuide/SD/RootFileSystem.html

標準で用意されている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
ussvgrussvgr

カーネルのビルド

ソースコードを取得する。

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の必須カーネルオプションは公式ドキュメントに記載があるので、こちらを参考に足りないものを足していった。
https://docs.cilium.io/en/stable/operations/system_requirements/#linux-kernel

# 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/
ussvgrussvgr

Jetsonに書き込む

Root File Systemにカーネルをインストールしたら、あとはJetsonに書き込むのみ。
https://docs.nvidia.com/jetson/archives/r36.3/DeveloperGuide/IN/QuickStart.html

まずは事前の準備作業。
SSHのホスト鍵を消しておかないと、すべて同一のホスト鍵を持ったサーバー群が出来上がってしまう。消しておくと初回のデーモン起動時に自動で再生成されるため、各マシンでユニークなホスト鍵を持つことができる。
Turing Pi 2を利用している場合はデバイスのEEPROMサイズを0にしないといけないので、そちらも修正している。
https://docs.turingpi.com/docs/orin-nxnano-flashing-os

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
ussvgrussvgr

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に必要なモジュールとカーネルパラメータの設定
https://v1-29.docs.kubernetes.io/ja/docs/setup/production-environment/container-runtimes/#インストールと設定の必須要件

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 のインストール
https://docs.docker.com/engine/install/ubuntu/

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系を使用した。
https://v1-29.docs.kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/install-kubeadm/

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/^.* //'
ussvgrussvgr

NVIDIA device plugin

https://github.com/NVIDIA/k8s-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に下げたら動いたのでいったんこれで。