Jetson nanoをk8sクラスタ参加させた(kubeadm)
Jetson nanoをkubeadm製のkubernetesクラスタに追加するために、OSのセットアップ、カーネルのビルド・・・など、必要ないろいろを設定しました。
環境
- Jetson nano 2GB Developer Kit
- SDカード:32GB
- 正直64GBあったほうが良い(makeが失敗する可能性あるため)
Jetson nanoのセットアップ
以下のプロジェクトで頒布されているイメージ(jetcard_v0p0p0.zip
)を用います。
こちらはJetson nanoをヘッドレスでインストールできるようにと進められているプロジェクトです。
jetcard_v0p0p0.zip
をmicroSDに書き込み、Jetson nanoをLANケーブルで接続して電源をつければ、avahi-daemon
により、以下でアクセスが可能です。
ssh jetson@jetson.local
パスワードはjetson
になります。http://<IPアドレス>:8888
でJupiter notebookにアクセスもできるようですが、今回はターミナルですべて完結させます。
jetcardスクリプトの削除
容量を占めている、不要な/home/jetson/jetcard
配下を削除します。
メモリーカード容量が大きい場合は削除する必要はありません。
sudo rm -rf /home/jetson/jetcard/
ユーザ登録
鍵でアクセスできるユーザを作成します。ついでにNOPASSWDも設定しておきます。
sudo su -
USERNAME="admin"
useradd -m -U -u 1001 -s /bin/bash -G sudo ${USERNAME}
mkdir /home/${USERNAME}/.ssh
cat <<EOF > /home/${USERNAME}/.ssh/authorized_keys
(登録するPublicキーを記載)
EOF
chown ${USERNAME}. -R /home/${USERNAME}/
echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/099_${USERNAME}-nopasswd
chmod 440 /etc/sudoers.d/099_${USERNAME}-nopasswd
gpasswd -a ${USERNAME} video
CUIモードに切り替え
サーバ用途で使用するため、起動モードを切り替えておきます。
systemctl get-default
systemctl set-default multi-user.target
reboot
登録ユーザでsshできることを確認
作成したユーザでsshできること、ipアドレスが変わっていることを確認します。
ssh <ユーザー名>@jetson.local
ip a
netplanでIPアドレスを固定
netplanが使いやすいので、netplanをインストールしてIPアドレスを固定します。
sudo su -
apt install -y netplan.io
cat <<EOF > /etc/netplan/99-manual.yaml
network:
renderer: NetworkManager
version: 2
ethernets:
eth0:
addresses:
- 192.168.3.25/24
dhcp4: false
dhcp6: false
gateway4: 192.168.3.1
nameservers:
addresses:
- 192.168.3.250
- 8.8.8.8
search:
- neko.lab
EOF
netplan apply
apply後、ネットワークが切断されるので、再度接続し直します。
カーネルのリビルド
jetcard
のイメージはk8sで使用する一部のカーネルパラメータが設定されていないみたいです。
CONFIG_IP_NF_TARGET_REDIRECT: not set
CONFIG_AUFS_FS: not set - Required for aufs.
CONFIG_CGROUP_HUGETLB: not set - Required for hugetlb cgroup.
CGROUPS_HUGETLB: missing
error execution phase preflight: [preflight] Some fatal errors occurred:
[ERROR SystemVerification]: unexpected kernel config: CONFIG_IP_NF_TARGET_REDIRECT
先駆者の方によると、カーネルをビルドするしか無い(曲解)ということです。
上記に倣ってカーネルをリビルドしていきます。sudo su -
wget https://developer.nvidia.com/embedded/dlc/public_sources_Nano
tar xvf public_sources_Nano
cd public_sources
tar xf kernel_src.tbz2
cd kernel/kernel-4.9
zcat /proc/config.gz > .config
設定方法
makeのnconfig機能を使います。
# apt install -y libncurses5-dev #←curses.hが無いとエラーが出た場合
make nconfig
↑を入力すると次のような画面が出てきます。
Calicoで必要なカーネル機能がどこにあるかなどが明確に書かれた資料が見つからなかったため、ラズパイのカーネル設定も参考にしながら、この画面のNetworking support --> Networking options
内の殆どの項目をM(module)またはY(buildin)に設定してみました。
本記事添付にしたかったのですが、diffをとってもかなりの量だったため、.config
のソースをアップロードしておきました。どうぞ使ってください。
上を使う場合は、以下のコマンドでダウンロードできます。
rm .config # zcatですでに.configを生成している場合は削除
wget https://raw.githubusercontent.com/nkte8/labo-arcive.22.Dec/2021-12-18-r01/jetson-kernel-config -O .config
上記はk8s-1.21.4
、CNIはCalico v3.20.1
のIPIP
モードで動作を確認しています。(Calicoの設定等については、同リポジトリのansibleディレクトリ内を参照してください。)
カーネルのコンパイル
カーネルのコンパイルをしていきます。パラメータを結構設定したためか、6時間ぐらいかかりました。
make prepare && make modules_prepare && make -j5 Image && make -j5 modules
ls -h arch/arm64/boot/Image
容量は8GBにも膨れ上がります。swapoffをしたり、/var/swapfileを削除したりなどをしてギリギリ足りました。
root@jetson:~/public_sources/kernel/kernel-4.9# du -sh ./
8.6G ./
ゆくゆくはJetson上ではなく他環境でクロスコンパイルなどを検討したほうが良さそうです。
カーネルの置き換え
Jetsonは/boot/Image
をカーネルとして読み込むようなので、新しく作成したイメージを配置します。
# cp /boot/Image /boot/Image.org # ディスクに余裕があればバックアップしたほうが良い
cp arch/arm64/boot/Image /boot/Image
モジュールをインストールし、再起動します。
make modules_install
reboot
再度ログインしてuname
すると、カーネルの日付がビルドした日付になっています。
Linux jetson 4.9.140 #1 SMP PREEMPT Wed Dec 15 23:49:35 PST 2021 aarch64 aarch64 aarch64 GNU/Linux
以上でカーネルビルドは完了です。
カーネルビルド結果の削除
Jetson nanoの方はかなりディスク容量を消費するので、ディレクトリを削除しておきましょう。
sudo su -
rm -rf ~/public_sources/
rm -f ~/public_sources_Nano
OSのセットアップ
desktop環境を使う気が無いので、ubuntu-server
を入れて不要そうなパッケージについてはアンインストールしてしまいます。
ubuntu-serverの導入
ubuntu-desktop
を削除しない場合は以下の操作は不要です。
apt update
apt install -y ubuntu-minimal ubuntu-server
必須ではないアプリケーションの削除
サーバ用途に不要そうなアプリケーションをアンインストールしておきます。(snapd
はmicrok8s
などを使いたい場合は必要なので残してください。)
apt autoremove --purge -y npm snapd chromium-browser thunderbird \
ubuntu-web-launchers ubuntu-docs ubuntu-report
apt autoremove --purge -y ubuntu-desktop ubuntu-settings \
ubuntu-artwork ubuntu-wallpapers ubuntu-sounds \
ubuntu-system-service x11proto-core-dev
Ubuntu 18.04→20.04 にアップデート
Ubuntuのシステムを20.04にアップデートします。
apt install -y update-manager
apt -y dist-upgrade && apt upgrade -y && apt autoremove -y --purge
reboot
# -f DistUpgradeViewNonInteractive をつけると、ユーザ入力不要になるので楽です。
sudo su -
do-release-upgrade -m server # -f DistUpgradeViewNonInteractive 2>&1 | tee update.log
apt autoremove --purge -y
アップデート後、OSバージョン、およびiptablesバージョンが上がっていることを確認します。
root@jetson:~# cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.3 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.3 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
root@jetson:~# iptables --version
iptables v1.8.4 (legacy)
Dockerのセットアップ
jetsonではdockerを使う場合、nvidiaカスタマイズされたdockerパッケージを利用します。
NVIDIA Container Toolkitの導入
コンテナランタイムにndiviaを使うためのパッケージを導入します。
公式の手順に従っていきます。sudo su -
# apt install -y curl
distribution=$(. /etc/os-release;echo $ID$VERSION_ID) && curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - && curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
以下のようなファイルが作成されました。
deb https://nvidia.github.io/libnvidia-container/stable/ubuntu18.04/$(ARCH) /
#deb https://nvidia.github.io/libnvidia-container/experimental/ubuntu18.04/$(ARCH) /
deb https://nvidia.github.io/nvidia-container-runtime/stable/ubuntu18.04/$(ARCH) /
#deb https://nvidia.github.io/nvidia-container-runtime/experimental/ubuntu18.04/$(ARCH) /
deb https://nvidia.github.io/nvidia-docker/ubuntu18.04/$(ARCH) /
nvidia-docker2を導入します。
apt-get update && apt-get install -y nvidia-docker2
daemon.jsonの編集
次のように設定します。nvidia-docker2
インストールの際にruntimes
については自動で作成されます。
{
"runtimes": {
"nvidia": {
"path": "nvidia-container-runtime",
"runtimeArgs": []
}
},
"default-runtime": "nvidia",
"exec-opts": [
"native.cgroupdriver=systemd"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
設定後、docker-daemonを再起動します
systemctl restart docker
docker infoの確認
設定が変更されたかを確認します。
root@node05:~# docker info
Client:
Context: default
Debug Mode: false
Server:
...(中略)...
Storage Driver: overlay2
...(中略)...
Cgroup Driver: systemd
Cgroup Version: 1
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux nvidia runc
Default Runtime: nvidia
...(中略)...
Architecture: aarch64
CPUs: 4
Total Memory: 1.906GiB
...(中略)...
Insecure Registries:
registry.neko.lab:5005
127.0.0.0/8
Live Restore Enabled: false
Storage Driver: overlay2
、Cgroup Driver: systemd
が設定され、問題なさそうです。
k8sクラスタへjoin
kubeadm join
してみます。よさそうです。
[preflight] Running pre-flight checks
[WARNING SystemVerification]: missing optional cgroups: hugetlb
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
...(中略)...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
クラスタの動作確認
動作の確認をしてみます。
NAME STATUS ROLES AGE VERSION
master01 Ready control-plane,master 62d v1.21.4
master02 Ready control-plane,master 62d v1.21.4
master03 Ready control-plane,master 62d v1.21.4
node01 Ready <none> 62d v1.21.4
node02 Ready <none> 62d v1.21.4
node03 Ready <none> 62d v1.21.4
node05 Ready <none> 61m v1.21.4
kube-system calico-node-vgvbp 1/1 Running 3 51m 192.168.3.25 node05 <none> <none>
kube-system kube-proxy-vbwt6 1/1 Running 4 51m 192.168.3.25 node05 <none> <none>
metallb-system speaker-87h6w 1/1 Running 0 2m39s 192.168.3.25 node05 <none> <none>
試しにコンテナ内から、CNIを通して外部へアクセスできるか確認してみます。以下をデプロイします。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: pod-test
spec:
selector:
matchLabels:
app: pod-test
template:
metadata:
labels:
app: pod-test
spec:
containers:
- name: pod-test
imagePullPolicy: IfNotPresent
image: ubuntu:20.04
command:
- sleep
- infinity
% kubectl get pod
...(中略)...
pod-test-8fk5x 1/1 Running 0 5m15s 10.244.186.211 node03 <none> <none>
pod-test-lghtt 1/1 Running 0 5m57s 10.244.114.2 node05 <none> <none>
pod-test-lkw42 1/1 Running 0 7m17s 10.244.196.175 node01 <none> <none>
pod-test-szs9p 1/1 Running 0 6m36s 10.244.140.72 node02 <none> <none>
% kubectl exec -it pod-test-lghtt -- /bin/bash
root@pod-test-lghtt:/# apt update
Get:1 http://ports.ubuntu.com/ubuntu-ports focal InRelease [265 kB]
...(中略)...
Get:18 http://ports.ubuntu.com/ubuntu-ports focal-security/multiverse arm64 Packages [3242 B]
Fetched 17.1 MB in 6s (2669 kB/s)
Reading package lists... Done
Building dependency tree
Reading state information... Done
1 package can be upgraded. Run 'apt list --upgradable' to see it.
root@pod-test-lghtt:/# echo $?
0
問題なくCNIで外部へ通信できるみたいです。
おわりに
本記事ではJetson nanoをkubeadm構築のクラスタに組み込むところまで実施しました。k8s上での利用(=コンテナ内でのGPU利用)については、また次の機会に実施しようと思います。
また、以前執筆した記事Jetson nanoをサーバとしてセットアップしてみた(JetCard)
については、本記事にマージされたため、下書きに戻させていただきました。
余談ですが、Dockerのセットアップからクラスタへjoinは、最終的にansibleにより自動化して実施しています。タグもうっておいたので、気になる方はぜひどうぞ。
備考
2021-12-17現在、nvidiaのkernelは4.9最新(L4T Driver Package (BSP) Sources)
Discussion
jetcard_v0p0p0.zip
の書き込みが上手く行かないみたいです。jetcard_nano-4gb-jp451.zip
の方は起動を確認しました。ssh jetson@nano-4gb-jp451.local
で接続できます。4.9.140-tegra
→4.9.201-tegra
本記事は書き直すかもしれません。