: O. Yuanying

Jetson Orin Nano を Kubernetes のノードにした

前回の続き。

containerd の設定

CRI のデフォルトランタイムを以下を参考に nvidia に変更。

$ sudo mkdir -p /etc/containerd
$ sudo curl -o /etc/containerd/config.toml https://raw.githubusercontent.com/NVIDIA/cloud-native-stack/master/playbooks/config.toml
$ sudo systemctl restart containerd

Kernel のリビルド

下記の記事によると、前世代のJetson NanoをKubernetesのノードにするためにはカーネルのリビルドが必要になるらしい。(どうでもいいけどKubernetesに関連する記事を検索すると高確率で同僚の記事がヒットするのが面白い。)

Jetson Orin Nano 上で確認したところ、最低限の設定はされていそう。

$ zcat /proc/config.gz | grep CONFIG_IP_NF_TARGET_REDIRECT
CONFIG_IP_NF_TARGET_REDIRECT=m

$ zcat /proc/config.gz | grep CONFIG_CGROUP_HUGETLB
CONFIG_CGROUP_HUGETLB=y

どちらにしろ、CSIでISCSIを有効化するためには以下を設定しないといけないのでどちらにしろカーネルビルドをやってみることにする。

$ zcat /proc/config.gz | grep ISCSI
# CONFIG_ISCSI_IBFT is not set
# CONFIG_SCSI_ISCSI_ATTRS is not set
# CONFIG_ISCSI_TCP is not set
# CONFIG_ISCSI_BOOT_SYSFS is not set
# CONFIG_SCSI_CXGB3_ISCSI is not set
# CONFIG_SCSI_CXGB4_ISCSI is not set
# CONFIG_SCSI_BNX2_ISCSI is not set
# CONFIG_BE2ISCSI is not set
# CONFIG_SCSI_QLA_ISCSI is not set

コンパイル準備

とりあえず公式ドキュメントでカーネルコンパイルの方法を確認。

以下からカーネルのソースを入手。

$ curl -LO https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v3.1/sources/public_sources.tbz2
$ tar -xjf public_sources.tbz2

カーネルのソースコードを解凍。

$ cd Linux_for_Tegra/source/public
$ tar -xjf kernel_src.tbz2
$ cd kernel/kernel-5.10/

現状の .config を入手して make nconfig で設定。

Jetson で iSCSI デバイスを使うための設定は以下の情報を参考にした。

$ zcat /proc/config.gz > .config.org
$ cp .config.org .config
$ make nconfig

こんな感じ。

$ diff -u .config.org .config
--- .config.org 2023-04-29 13:15:52.741355433 +0900
+++ .config     2023-04-29 15:33:25.786778247 +0900
@@ -2,10 +2,10 @@
 # Automatically generated file; DO NOT EDIT.
 # Linux/arm64 5.10.104 Kernel Configuration
 #
-CONFIG_CC_VERSION_TEXT="aarch64-buildroot-linux-gnu-gcc.br_real (Buildroot 2020.08) 9.3.0"
+CONFIG_CC_VERSION_TEXT="gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0"
 CONFIG_CC_IS_GCC=y
-CONFIG_GCC_VERSION=90300
-CONFIG_LD_VERSION=233010000
+CONFIG_GCC_VERSION=90400
+CONFIG_LD_VERSION=234000000
 CONFIG_CLANG_VERSION=0
 CONFIG_LLD_VERSION=0
 CONFIG_CC_CAN_LINK=y
@@ -445,6 +445,7 @@
 CONFIG_CC_HAS_BRANCH_PROT_PAC_RET=y
 CONFIG_CC_HAS_SIGN_RETURN_ADDRESS=y
 CONFIG_AS_HAS_PAC=y
+CONFIG_AS_HAS_CFI_NEGATE_RA_STATE=y
 # end of ARMv8.3 architectural features

 #
@@ -807,10 +808,6 @@
 # end of GCOV-based kernel profiling

 CONFIG_HAVE_GCC_PLUGINS=y
-CONFIG_GCC_PLUGINS=y
-# CONFIG_GCC_PLUGIN_CYC_COMPLEXITY is not set
-# CONFIG_GCC_PLUGIN_LATENT_ENTROPY is not set
-# CONFIG_GCC_PLUGIN_RANDSTRUCT is not set
 # end of General architecture-dependent options

 CONFIG_RT_MUTEXES=y
@@ -1134,6 +1131,7 @@
 #
 CONFIG_NETFILTER_XT_MARK=m
 CONFIG_NETFILTER_XT_CONNMARK=m
+# CONFIG_NETFILTER_XT_SET is not set

 #
 # Xtables targets
@@ -1216,7 +1214,24 @@
 CONFIG_NETFILTER_XT_MATCH_U32=m
 # end of Core Netfilter Configuration

-# CONFIG_IP_SET is not set
+CONFIG_IP_SET=m
+CONFIG_IP_SET_MAX=256
+CONFIG_IP_SET_BITMAP_IP=m
+CONFIG_IP_SET_BITMAP_IPMAC=m
+CONFIG_IP_SET_BITMAP_PORT=m
+CONFIG_IP_SET_HASH_IP=m
+CONFIG_IP_SET_HASH_IPMARK=m
+CONFIG_IP_SET_HASH_IPPORT=m
+CONFIG_IP_SET_HASH_IPPORTIP=m
+CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
+CONFIG_IP_SET_HASH_MAC=m
+CONFIG_IP_SET_HASH_NETPORTNET=m
+CONFIG_IP_SET_HASH_NET=m
+CONFIG_IP_SET_HASH_NETNET=m
+CONFIG_IP_SET_HASH_NETPORT=m
+CONFIG_IP_SET_HASH_NETIFACE=m
+CONFIG_IP_SET_LIST_SET=m
 CONFIG_IP_VS=m
 # CONFIG_IP_VS_IPV6 is not set
 # CONFIG_IP_VS_DEBUG is not set
@@ -1439,6 +1454,7 @@
 # CONFIG_NET_EMATCH_META is not set
 # CONFIG_NET_EMATCH_TEXT is not set
 # CONFIG_NET_EMATCH_CANID is not set
+# CONFIG_NET_EMATCH_IPSET is not set
 # CONFIG_NET_EMATCH_IPT is not set
 CONFIG_NET_CLS_ACT=y
 CONFIG_NET_ACT_POLICE=m
@@ -2166,15 +2182,18 @@
 # CONFIG_CHR_DEV_SG is not set
 # CONFIG_CHR_DEV_SCH is not set
 # CONFIG_SCSI_CONSTANTS is not set
+CONFIG_SCSI_CONSTANTS=y
 # CONFIG_SCSI_LOGGING is not set
+CONFIG_SCSI_LOGGING=y
 # CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_SCAN_ASYNC=y

 #
 # SCSI Transports
 #
 # CONFIG_SCSI_SPI_ATTRS is not set
 # CONFIG_SCSI_FC_ATTRS is not set
-# CONFIG_SCSI_ISCSI_ATTRS is not set
+CONFIG_SCSI_ISCSI_ATTRS=m
 CONFIG_SCSI_SAS_ATTRS=m
 CONFIG_SCSI_SAS_LIBSAS=m
 CONFIG_SCSI_SAS_ATA=y
@@ -2183,12 +2202,12 @@
 # end of SCSI Transports

 CONFIG_SCSI_LOWLEVEL=y
-# CONFIG_ISCSI_TCP is not set
-# CONFIG_ISCSI_BOOT_SYSFS is not set
-# CONFIG_SCSI_CXGB3_ISCSI is not set
-# CONFIG_SCSI_CXGB4_ISCSI is not set
-# CONFIG_SCSI_BNX2_ISCSI is not set
-# CONFIG_BE2ISCSI is not set
+CONFIG_ISCSI_TCP=m
+CONFIG_ISCSI_BOOT_SYSFS=m
+CONFIG_SCSI_CXGB3_ISCSI=m
+CONFIG_SCSI_CXGB4_ISCSI=m
+CONFIG_SCSI_BNX2_ISCSI=m
+CONFIG_BE2ISCSI=m
 # CONFIG_BLK_DEV_3W_XXXX_RAID is not set
 # CONFIG_SCSI_HPSA is not set
 # CONFIG_SCSI_3W_9XXX is not set
@@ -2243,6 +2262,8 @@
 # CONFIG_SCSI_PM8001 is not set
 # CONFIG_SCSI_VIRTIO is not set
 # CONFIG_SCSI_DH is not set
+CONFIG_SCSI_DH=y
+CONFIG_SCSI_DH_ALUA=y
 # end of SCSI device support

 CONFIG_HAVE_PATA_PLATFORM=y
@@ -2350,22 +2371,38 @@
 CONFIG_BLK_DEV_DM_BUILTIN=y
 CONFIG_BLK_DEV_DM=y
 # CONFIG_DM_DEBUG is not set
+CONFIG_DM_DEBUG=y
+CONFIG_DM_BIO_PRISON=y
+CONFIG_DM_PERSISTENT_DATA=y
 CONFIG_DM_BUFIO=y
 # CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING is not set
 # CONFIG_DM_UNSTRIPED is not set
 CONFIG_DM_CRYPT=y
 # CONFIG_DM_SNAPSHOT is not set
+CONFIG_DM_SNAPSHOT=y
 # CONFIG_DM_THIN_PROVISIONING is not set
+CONFIG_DM_THIN_PROVISIONING=y
 # CONFIG_DM_CACHE is not set
+CONFIG_DM_CACHE=y
+CONFIG_DM_CACHE_SMQ=y
 # CONFIG_DM_WRITECACHE is not set
 # CONFIG_DM_EBS is not set
 # CONFIG_DM_ERA is not set
+CONFIG_DM_ERA=y
 # CONFIG_DM_CLONE is not set
 # CONFIG_DM_MIRROR is not set
+CONFIG_DM_MIRROR=y
+CONFIG_DM_LOG_USERSPACE=y
 # CONFIG_DM_RAID is not set
+CONFIG_DM_RAID=y
 # CONFIG_DM_ZERO is not set
+CONFIG_DM_ZERO=y
 # CONFIG_DM_MULTIPATH is not set
+CONFIG_DM_MULTIPATH=y
+CONFIG_DM_MULTIPATH_QL=y
+CONFIG_DM_MULTIPATH_ST=y
 # CONFIG_DM_DELAY is not set
+CONFIG_DM_DELAY=y
 # CONFIG_DM_DUST is not set
 # CONFIG_DM_INIT is not set
 CONFIG_DM_UEVENT=y
@@ -2374,7 +2411,10 @@
 CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
 # CONFIG_DM_VERITY_FEC is not set
 # CONFIG_DM_SWITCH is not set
+CONFIG_DM_SWITCH=y
 # CONFIG_DM_LOG_WRITES is not set
+CONFIG_DM_LOG_WRITES=y
+CONFIG_DM_ZONED=y
 # CONFIG_DM_INTEGRITY is not set
 # CONFIG_TARGET_CORE is not set
 # CONFIG_FUSION is not set
@@ -2508,6 +2548,7 @@
 CONFIG_CHELSIO_T3=m
 CONFIG_CHELSIO_T4=m
 CONFIG_CHELSIO_T4VF=m
+CONFIG_CHELSIO_LIB=m
 CONFIG_CHELSIO_INLINE_CRYPTO=y
 CONFIG_NET_VENDOR_CISCO=y
 CONFIG_ENIC=m
@@ -7586,7 +7627,6 @@
 CONFIG_TEGRA_HV_PM_CTL=y
 CONFIG_TEGRA_HV_MANAGER=y
 CONFIG_TEGRA_VIRTUALIZATION=y
-CONFIG_TEGRA_NVLINK=y
 # end of Device Drivers

 #
@@ -7925,10 +7965,6 @@
 # Memory initialization
 #
 CONFIG_INIT_STACK_NONE=y
-# CONFIG_GCC_PLUGIN_STRUCTLEAK_USER is not set
-# CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF is not set
-# CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL is not set
-# CONFIG_GCC_PLUGIN_STACKLEAK is not set
 # CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set
 # CONFIG_INIT_ON_FREE_DEFAULT_ON is not set
 # end of Memory initialization

コンパイル

準備ができたのでコンパイル。数時間かかるそうなので放置。

$ make prepare
$ make modules_prepare
$ make -j5 Image
$ make -j5 modules

カーネルのインストール

インストールして再起動。

$ sudo cp /boot/Image /boot/Image.org
$ sudo cp arch/arm64/boot/Image /boot/Image
$ sudo make modules_install
$ sudo reboot

カーネルがついさっきビルドしたものに変わっていることを確認。

$ uname -a
Linux uribo 5.10.104 #2 SMP PREEMPT Sat Apr 29 14:42:12 JST 2023 aarch64 aarch64 aarch64 GNU/Linux

Kubernetes へ Join

自分は kubeadm を使ってないので適時 kubelet をインストールし systemd などの設定を手作業でやったが、普通は kubeadm join で大丈夫と思われる。

✦ ➜ k get node uribo -o wide
NAME    STATUS   ROLES    AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION   CONTAINER-RUNTIME
uribo   Ready    <none>   63s   v1.24.6   192.168.1.152   <none>        Ubuntu 20.04.5 LTS   5.10.104         containerd://1.6.12

無事、Kubernetesにノードとして追加された。

ちなみに、メモリも少ないので kubelet の設定で Swap を有効化しておいた。

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
...
(snip)
...
failSwapOn: false
featureGates:
  NodeSwap: true

nvidia device plugin のインストール

下記の記事に従って、device plugin をインストールする。

containerdはすでに設定しており、nvidia-container-toolkit もインストール済みのため、/etc/docker/daemon.json を少し編集して、

{
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}

マニフェストをapplyするのみ。

$ kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.0/nvidia-device-plugin.yml

動作確認

devicequery を実行。

✦ ➜ cat <<EOF | k apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: devicequery
spec:
  restartPolicy: Never
  containers:
  - name: nvidia
    image: xift/jetson_devicequery:r32.5.0
    command: [ "./deviceQuery" ]
    resources:
      limits:
        nvidia.com/gpu: 1
  nodeSelector:
    kubernetes.io/hostname: "uribo"
  tolerations:
  - effect: NoSchedule
    key: nvidia.com/gpu
    operator: Exists
EOF
✦ ➜ k get pod devicequery
NAME          READY   STATUS      RESTARTS   AGE
devicequery   0/1     Completed   0          11s

✦ ➜ k logs devicequery
./deviceQuery Starting...

 CUDA Device Query (Runtime API) version (CUDART static linking)

Detected 1 CUDA Capable device(s)

Device 0: "Orin"
  CUDA Driver Version / Runtime Version          11.4 / 10.2
  CUDA Capability Major/Minor version number:    8.7
  Total amount of global memory:                 6480 MBytes (6794899456 bytes)
MapSMtoCores for SM 8.7 is undefined.  Default to use 64 Cores/SM
MapSMtoCores for SM 8.7 is undefined.  Default to use 64 Cores/SM
  ( 8) Multiprocessors, ( 64) CUDA Cores/MP:     512 CUDA Cores
  GPU Max Clock rate:                            624 MHz (0.62 GHz)
  Memory Clock rate:                             624 Mhz
  Memory Bus Width:                              64-bit
  L2 Cache Size:                                 2097152 bytes
  Maximum Texture Dimension Size (x,y,z)         1D=(131072), 2D=(131072, 65536), 3D=(16384, 16384, 16384)
  Maximum Layered 1D Texture Size, (num) layers  1D=(32768), 2048 layers
  Maximum Layered 2D Texture Size, (num) layers  2D=(32768, 32768), 2048 layers
  Total amount of constant memory:               65536 bytes
  Total amount of shared memory per block:       49152 bytes
  Total number of registers available per block: 65536
  Warp size:                                     32
  Maximum number of threads per multiprocessor:  1536
  Maximum number of threads per block:           1024
  Max dimension size of a thread block (x,y,z): (1024, 1024, 64)
  Max dimension size of a grid size    (x,y,z): (2147483647, 65535, 65535)
  Maximum memory pitch:                          2147483647 bytes
  Texture alignment:                             512 bytes
  Concurrent copy and kernel execution:          Yes with 2 copy engine(s)
  Run time limit on kernels:                     No
  Integrated GPU sharing Host Memory:            Yes
  Support host page-locked memory mapping:       Yes
  Alignment requirement for Surfaces:            Yes
  Device has ECC support:                        Disabled
  Device supports Unified Addressing (UVA):      Yes
  Device supports Compute Preemption:            Yes
  Supports Cooperative Kernel Launch:            Yes
  Supports MultiDevice Co-op Kernel Launch:      Yes
  Device PCI Domain ID / Bus ID / location ID:   0 / 0 / 0
  Compute Mode:
     < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) >

deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 11.4, CUDA Runtime Version = 10.2, NumDevs = 1
Result = PASS

どうやら動いてるらしいが、色々クセがあってなんか使いづらい。。

ホストの CUDA 関連ライブラリがPodにインストールされていない

nvidia-container-runtimeは /etc/nvidia-container-runtime/host-files-for-container.d/ にある csv を参照して、ホストのライブラリその他をコンテナにマウントしてくれるらしい。

しかし、JetPack 5.1からはなぜか l4t.csv のみが存在し、今まであったはずの cuda.csvcudnn.csv が見当たらない。

$ ls -la /etc/nvidia-container-runtime/host-files-for-container.d/
total 24
drwxr-xr-x 2 root root  4096 Mar 20 12:01 .
drwxr-xr-x 3 root root  4096 Apr 29 18:15 ..
-rw-r--r-- 1 root root 16370 Mar 20 00:14 l4t.csv

下記のフォーラムを見ると、コンテナ内に自分でインストールする必要があるらしい。

ただ、コンテナごとにインストールするのが面倒だったので、結局 hostPath でマウントすることにした。

      volumes:
      - name: cuda
        hostPath:
          path: /usr/local/cuda-11.4
      - name: cudnn
        hostPath:
          path: /usr/lib/aarch64-linux-gnu/libcudnn.so.8

コンテナのユーザがrootじゃないとGPUを利用できない

コンテナ内でPyTorchをインストールして一般ユーザでGPUが利用できるかチェックしたところ、以下のエラー。

➜ python3 -c "import torch; print(torch.cuda.is_available())"
NvRmMemInitNvmap failed with Permission denied
549: Memory Manager Not supported


****NvRmMemInit failed**** error type: 196626


*** NvRmMemInit failed NvRmMemConstructor
/home/yuanying/src/github.com/yuanying/uribo/.venv/lib/python3.8/site-packages/torch/cuda/__init__.py:107: UserWarning: CUDA initialization: Unexpected error from cudaGetDeviceCount(). Did you run some cuda functions before calling NumCudaDevices() that might have already set an error? Error 801: operation not supported (Triggered internally at /opt/pytorch/pytorch/c10/cuda/CUDAFunctions.cpp:109.)
  return torch._C._cuda_getDeviceCount() > 0
False

Permission denied とは何のPermissionかわからなかったが、/dev/nvhost* を確認したところ、一般ユーザがGPUを使うためにはvideo グループに属していないといけないようだった。

ということで、Dockerfile で作成した一般ユーザを video グループに追加したらうまく動くようになった。

RUN useradd -G video -g 50 -m -s /bin/bash  -u 501 "$USER"

ただ、gpu-operatorで作成した環境だと、/dev/nvhost* は誰でも読み書きできるようになっていたため、色々謎がある。