<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns="http://purl.org/rss/1.0/"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:admin="http://webns.net/mvcb/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">
  <channel rdf:about="https://www.fraction.jp/log/">
    <title>Computer (Program/Mac/PC) -- 煩悩分画</title>
    <link>https://www.fraction.jp/log/</link>
    <description>世の中に寝るより楽はなかりけり&lt;br /&gt;浮世の馬鹿は起きて働く</description>
    
    <dc:creator>O. Yuanying</dc:creator>
	<dc:date>2025-05-29T09:06:24+00:00</dc:date>
	<admin:generatorAgent rdf:resource="https://github.com/yuanyig/komo"/>
    <items>
      <rdf:Seq>
        <rdf:li rdf:resource="https://www.fraction.jp/log/archives/2025/05/29/evo-x2-gfx1151" />
        <rdf:li rdf:resource="https://www.fraction.jp/log/archives/2025/01/14/ubuntu-on-raspberry-pi5" />
        <rdf:li rdf:resource="https://www.fraction.jp/log/archives/2023/05/02/jetson-pytorch" />
        <rdf:li rdf:resource="https://www.fraction.jp/log/archives/2023/04/29/jetson-k8s" />
        <rdf:li rdf:resource="https://www.fraction.jp/log/archives/2023/04/27/jetson-orin-nano" />
        <rdf:li rdf:resource="https://www.fraction.jp/log/archives/2023/03/23/new-pc" />
        <rdf:li rdf:resource="https://www.fraction.jp/log/archives/2022/09/29/kubevirt-noip-after-restart" />
        <rdf:li rdf:resource="https://www.fraction.jp/log/archives/2022/09/26/macvlan" />
        <rdf:li rdf:resource="https://www.fraction.jp/log/archives/2022/09/18/kubevirt" />
        <rdf:li rdf:resource="https://www.fraction.jp/log/archives/2022/08/07/m2-macbook-air" />
      </rdf:Seq>
    </items>
  </channel>
  <item rdf:about="https://www.fraction.jp/log/archives/2025/05/29/evo-x2-gfx1151">
    <title>Ryzen AI MAX+395（on Ubuntu）で PyTorch をビルド</title>
    <link>https://www.fraction.jp/log/archives/2025/05/29/evo-x2-gfx1151</link>
    <description>GMKtec の EVO-X2 を買った。Windows はここ最近使ったことなかったので、速攻で Ubuntu をインストールしたのだが、結構茨の道だった。そもそも手順が悪いのか、ROCmがまだ Ryzen AI Max+ 395 (gfx1151) に対応してないからなのか、pytorch が動かない。すなわち芋蔓式に、Stable Diffusionも動かなければFramepackも動かないし、llama.cpp も Vulkan で動かさざるを得ない。ということで Ryzen AI Max...</description>
    <content:encoded><![CDATA[
        <p>GMKtec の EVO-X2 を買った。</p>

<p class='image'>
<img  src='/log/archives/2025/05/29/IMG_0960.jpg'
      width='800'
      height='600'
      alt='GMKtec Evo-X2'  />
</p>


<p>Windows はここ最近使ったことなかったので、速攻で Ubuntu をインストールしたのだが、結構茨の道だった。そもそも手順が悪いのか、ROCmがまだ Ryzen AI Max+ 395 (gfx1151) に対応してないからなのか、pytorch が動かない。</p>

<p>すなわち芋蔓式に、Stable Diffusionも動かなければFramepackも動かないし、llama.cpp も Vulkan で動かさざるを得ない。</p>

<p>ということで Ryzen AI Max+ 395 で動く pytorch は自分でビルドすれば手に入るということを知ったので自分でビルドした。</p>

<p>ちなみに、単にコンテナで動かしたいだけなら以下のコンテナイメージをベースにすれば良さそう。</p>

<ul>
<li><a href="https://github.com/ROCm/TheRock/pkgs/container/therock_pytorch_dev_ubuntu_24_04_gfx1151">therock_pytorch_dev_ubuntu_24_04_gfx1151</a></li>
</ul>


<p>こんな感じで動作確認できる。</p>

<pre><code class="bash">$ sudo docker run -it --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
--device=/dev/kfd --device=/dev/dri --group-add video \
--ipc=host ghcr.io/rocm/therock_pytorch_dev_ubuntu_24_04_gfx1151:nightly \
python -c "import torch; a=torch.randn(3).to('cuda'); print(a)"
tensor([0.0159, 0.7455, 1.3787], device='cuda:0')
</code></pre>

<p>ビルドするなら、現時点で自分が試したのは以下の二つ。</p>

<ol>
<li><a href="https://github.com/ROCm/TheRock"><code>ROCm/TheRock</code></a></li>
<li><a href="https://github.com/scottt/rocm-TheRock"><code>scottt/rocm-TheRock</code></a></li>
</ol>


<h2>環境</h2>

<pre><code class="Dockerfile">FROM ubuntu:24.04
</code></pre>

<p>自分はメインの作業をコンテナ上でやっているので上記となる。ちなみにホストのOSも Ubuntu 24.04である。</p>

<h2><code>ROCm/TheRock</code></h2>

<p>実際のところ、<code>ghcr.io/rocm/therock_pytorch_dev_ubuntu_24_04_gfx1151:nightly</code> に gfx1151 用の pytorch がインストールされているので再ビルドすることもなくそれを持ってこれればいいのだけど、インストール済みの python パッケージを再パッケージ化する方法がわからなかったので、ビルドした。</p>

<p>方法は docker がインストールされていれば単純で、READMEに書いてある通りに、</p>

<pre><code class="bash"># Install Ubuntu dependencies
sudo apt install gfortran git git-lfs ninja-build cmake g++ pkg-config xxd patchelf automake python3-venv python3-dev libegl1-mesa-dev

# Clone the repository
git clone https://github.com/ROCm/TheRock.git
cd TheRock

# Init python virtual environment and install python dependencies
python -m venv .venv &amp;&amp; source .venv/bin/activate
pip install -r requirements.txt

# Download submodules and apply patches
python ./build_tools/fetch_sources.py
</code></pre>

<p>そして、おもむろに <code>docker build</code></p>

<pre><code class="bash">sudo docker build --build-arg AMDGPU_TARGETS=gfx1151 \
     --file dockerfiles/pytorch-dev/pytorch_dev_ubuntu_24.04.Dockerfile \
     --target pytorch_build --tag ghcr.io/rocm/therock_pytorch_dev_ubuntu_24_04_gfx1151:pytorch_build \
     .
</code></pre>

<p>PyTorch のコンテナイメージをビルドする <code>Dockerfile</code> が <code>dockerfiles/pytorch-dev/pytorch_dev_ubuntu_24.04.Dockerfile</code> にあるのだけど、ビルドが終了するとせっかく生成した <code>torch-*.whl</code> が削除されてしまうので、途中の <code>pytorch_build</code> をターゲットにしてビルドする。</p>

<p>出来上がった <code>ghcr.io/rocm/therock_pytorch_dev_ubuntu_24_04_gfx1151:pytorch_build</code> には</p>

<ul>
<li><code>/opt/rocm</code>: gfx1151 用の ROCm</li>
<li><code>/therock/pytorch/dist</code>: pytorch の wheel が含まれたディレクトリ</li>
<li><code>/therock/pytorch_vision/dist</code>: pytorch_vision の wheel が含まれたディレクトリ</li>
<li><code>/thero-k/pytorch_audio/dist</code>: pytorch_audio の wheel が含まれたディレクトリ</li>
</ul>


<p>があるので、よしなにコピーしてきて使う。(<code>docker run</code> した後に <code>docker cp</code> して持ってくるなど。)</p>

<p>ちなみに、性能はそれほど良くない。</p>

<h2><code>scottt/rocm-TheRock</code></h2>

<p>実際、上記を試してて、Stable Diffusionが遅いなーと思っていたところ、TheRock をメンテしてるっぽい scottt さん<code>scottt/rocm-TheRock</code> を知った。</p>

<ul>
<li><a href="https://github.com/ROCm/TheRock/discussions/244">gfx1151: I have pytorch, pytorch-vision, and hipBLASLt working well enough to run GPT2</a></li>
</ul>


<p>gfx1151 で GPT2 を十分に動かすことができる <code>pytorch</code>, <code>pytorch-vision</code>, <code>hipBLASLt</code> を持ってるらしい。マジか。</p>

<p>コメントにはご丁寧にもビルド方法が書いてある。</p>

<pre><code class="bash">git clone git@github.com:scottt/rocm-TheRock.git
cd rocm-TheRock
git switch gfx1151
podman build -t rocm-dev-f41 -f ./dockerfiles/pytorch-dev/rocm_fedora.Dockerfile .
podman build -t pytorch-dev-f41 -f dockerfiles/pytorch-dev/pytorch_dev_fedora.Dockerfile .
podman build -t pytorch-vision-dev-f41 -f dockerfiles/pytorch-dev/pytorch_vision_dev_fedora.Dockerfile .
</code></pre>

<p>最終的にできた <code>pytorch-vision-dev-f41</code> に、<code>hipBLASLt</code> が有効化された <code>ROCm</code> と <code>torch</code>, <code>torch_vision</code> が含まれているのだが、出来上がったパッケージが python3.13 用だったりビルドがうまく走らなかったりしたのでちょっと修正した。</p>

<pre><code class="diff">diff --git a/dockerfiles/pytorch-dev/pytorch_dev_fedora.Dockerfile b/dockerfiles/pytorch-dev/pytorch_dev_fedora.Dockerfile
index 462af8c..24f3705 100644
--- a/dockerfiles/pytorch-dev/pytorch_dev_fedora.Dockerfile
+++ b/dockerfiles/pytorch-dev/pytorch_dev_fedora.Dockerfile
@@ -15,6 +15,10 @@ RUN --mount=type=cache,id=pytorch-f${FEDORA_VER},target=/therock \
                --no-patch \
                --no-hipify

+RUN --mount=type=cache,id=f${FEDORA_VER},target=/var/cache/dnf  \
+       dnf5 install -y python3.12 python3.12-devel &amp;&amp; \
+    alternatives --install /usr/bin/python python /usr/bin/python3.12 2
+
 # pytorch-prep
 # for `git am`
 RUN git config --global user.email "you@example.com" &amp;&amp; \
@@ -31,7 +35,8 @@ RUN --mount=type=cache,id=pytorch-f${FEDORA_VER},target=/therock \
 # pytorch-build
 RUN --mount=type=cache,id=pytorch-f${FEDORA_VER},target=/therock \
        cd /therock/pytorch &amp;&amp; \
-       uv pip install --system -r requirements.txt
+       uv pip install --system -r requirements.txt &amp;&amp; \
+       uv pip install --system cmake==3.25.2

 ENV CMAKE_PREFIX_PATH=/opt/rocm
 ENV USE_KINETO=OFF
@@ -50,8 +55,12 @@ RUN --mount=type=cache,id=pytorch-f${FEDORA_VER},target=/therock \
 # Development image
 FROM rocm-dev-f${FEDORA_VER} AS pytorch-dev-f${FEDORA_VER}
 COPY --from=build /opt/torch-*.whl /opt
-RUN uv pip install --system /opt/*.whl
+RUN \
+    dnf5 install -y python3.12 python3.12-devel &amp;&amp; \
+    alternatives --install /usr/bin/python python /usr/bin/python3.12 2 &amp;&amp; \
+    python -m ensurepip
+RUN python -m pip install /opt/*.whl

 # the setuptools from rocm-dev-f${FEDORA_VER} could be too new
 # and cause C++ extensions of pytorch, like pytorch-vision to fail to build
-RUN uv pip install --system 'setuptools&gt;=62.3.0,&lt;75.9'
+RUN python -m pip install 'setuptools&gt;=62.3.0,&lt;75.9'
</code></pre>

<p>できたがったコンテナイメージから、<code>/opt/rocm</code> と <code>/opt/*.whl</code> をコピーして利用する。</p>

<p>ちなみに <code>docker build</code> で動かなかったので <code>podman</code> を Ubuntu にインストールする必要があったりする。</p>

<h2>終わりに</h2>

<p><code>scottt/rocm-TheRock</code> でビルドしたパッケージはそのままだと Ubuntu で動かないので <code>LD_LIBRARY_PATH</code> に <code>/opt/rocm/lib/llvm/lib</code> を加える必要があったり、その他必要パッケージがあったりするのでよしなにインストールしてください。</p>

    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:creator>O. Yuanying</dc:creator>
    <dc:date>2025-05-29T17:12:00+09:00</dc:date>
  </item>
  <item rdf:about="https://www.fraction.jp/log/archives/2025/01/14/ubuntu-on-raspberry-pi5">
    <title>Ubuntu で Raspberry Pi 5 の NVMe ブート</title>
    <link>https://www.fraction.jp/log/archives/2025/01/14/ubuntu-on-raspberry-pi5</link>
    <description>TL;DRMon 23 Sep 13:02:56 UTC 2024 のファームウェアだとデフォルトで NVMe に対応してるので何もせず NVMe に好きな OS を書き込めば勝手に起動する。Ubuntu Server 24.04.01 だと ssh がデフォルトでパスワードログインがオフになっているため、ヘッドレスでインストールしたかったら user-data を変更する必要がある。承前Raspberry Pi 5 を衝動買いしてしまった。自宅で飼ってる Kubernetes クラスターのコント...</description>
    <content:encoded><![CDATA[
        <h2>TL;DR</h2>

<ul>
<li><code>Mon 23 Sep 13:02:56 UTC 2024</code> のファームウェアだとデフォルトで NVMe に対応してるので何もせず NVMe に好きな OS を書き込めば勝手に起動する。</li>
<li><a href="https://ubuntu.com/download/raspberry-pi">Ubuntu Server 24.04.01</a> だと ssh がデフォルトでパスワードログインがオフになっているため、ヘッドレスでインストールしたかったら <code>user-data</code> を変更する必要がある。</li>
</ul>


<h2>承前</h2>

<p>Raspberry Pi 5 を衝動買いしてしまった。自宅で飼ってる Kubernetes クラスターのコントロールプレーンノードに Raspberry Pi 4 を使ってるのだけれども、<code>etcdHighCommitDurations</code> のアラートがよく出るようになってしまったのでノードをアップグレードしたかったのもある。</p>

<p>ということで、何はともあれ Raspberry Pi 5 に Ubuntu サーバーのインストールだ。</p>

<p class='image'>
<img  src='/log/archives/2025/01/14/IMG_0473.jpg'
      width='800'
      height='600'
      alt=''  />
</p>


<p>今回は PoE + NVMe を追加する HAT を買ったので、だいぶコンパクトになった。(2.5inch SSDはデカかった。)</p>

<h2>ラズパイの状況確認</h2>

<p>まずは SD カードにラズパイOSをインストールして NVMe に SSD を繋げた状態で状況を確認してみる。</p>

<p><code>lsblk</code> を実行してみたところ、最初から NVMe の SSD が見えてる。</p>

<pre><code class="bash">yuanying@raspberrypi:~$ lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
mmcblk0     179:0    0  57.6G  0 disk
├─mmcblk0p1 179:1    0   512M  0 part /boot/firmware
└─mmcblk0p2 179:2    0  57.1G  0 part /
nvme0n1     259:0    0 119.2G  0 disk
</code></pre>

<p>ファームウェアを確認してみたところ、どうやら最新っぽい。多分、つい最近公式で <a href="https://www.raspberrypi.com/news/raspberry-pi-ssds-and-ssd-kits/">Raspberry Pi SSDs and SSD Kits</a> が売り出されたのでそのタイミングで有効化されたのでは無いだろうか。</p>

<pre><code class="bash">yuanying@raspberrypi:~$ sudo rpi-eeprom-update
BOOTLOADER: up to date
   CURRENT: Mon 23 Sep 13:02:56 UTC 2024 (1727096576)
    LATEST: Mon 23 Sep 13:02:56 UTC 2024 (1727096576)
   RELEASE: default (/lib/firmware/raspberrypi/bootloader-2712/default)
            Use raspi-config to change the release.
</code></pre>

<p>ということで、eeprom も特に編集を行わず、Ubuntu を NVMe にインストールすることにする。</p>

<h2>Ubuntu のインストール</h2>

<h3>Ubuntu のダウンロード</h3>

<p>まずは、UbuntuのOSイメージをダウンロードしてくる。現時点で LTS の最新をダウンロード。</p>

<pre><code class="bash">curl -LO https://cdimage.ubuntu.com/releases/24.04.1/release/ubuntu-24.04.1-preinstalled-server-arm64+raspi.img.xz
</code></pre>

<h3>ダウンロードしたイメージをカスタマイズ</h3>

<p>ラズパイにはキーボードやディスプレイを繋げずSSHからヘッドレスでインストールを完結したかったので、OSイメージをカスタマイズする。
ダウンロードしてきたOSイメージの最初のパーティションに <code>user-data</code> があるので、それを変更してやれば良い。</p>

<p>ので、まずはイメージを解凍。</p>

<pre><code class="bash">xzcat &lt; ubuntu-24.04.1-preinstalled-server-arm64+raspi.img.xz \
      &gt; ubuntu-24.04.1-preinstalled-server-arm64+raspi.img
</code></pre>

<p><code>raspi.img</code> の一つ目のパーティションをマウントする。手順は以下。</p>

<ol>
<li><code>raspi.img</code> を loopback device として認識させる。</li>
</ol>


<pre><code class="bash">yuanying@raspberrypi:~ $ sudo losetup -f --show ubuntu-24.04.1-preinstalled-server-arm64+raspi.img
/dev/loop0
</code></pre>

<ol>
<li>パーティションを認識させる。</li>
</ol>


<pre><code class="bash">yuanying@raspberrypi:~ $ sudo partprobe /dev/loop0

yuanying@raspberrypi:~ $ lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
loop0         7:0    0   3.4G  0 loop
|-loop0p1   259:3    0   512M  0 part
`-loop0p2   259:4    0   2.9G  0 part
mmcblk0     179:0    0  57.6G  0 disk
|-mmcblk0p1 179:1    0   512M  0 part /boot/firmware
`-mmcblk0p2 179:2    0  57.1G  0 part /
nvme0n1     259:0    0 119.2G  0 disk
</code></pre>

<ol>
<li><code>loop0p1</code> をマウントする。</li>
</ol>


<pre><code class="bash">yuanying@raspberrypi:~ $ sudo mount -t vfat /dev/loop0p1 /mnt
</code></pre>

<ol>
<li><code>/mnt/usr-data</code> を編集する。</li>
</ol>


<pre><code class="bash">yuanying@raspberrypi:~ $ sudo vi /mnt/user-data
</code></pre>

<p>自分は以下のエントリを追加した。</p>

<pre><code class="yaml">users:
- name: yuanying
  primary_group: staff
  groups: users, adm
  sudo: ["ALL=(ALL) NOPASSWD:ALL"]
  shell: /bin/bash
  ssh_authorized_keys:
  - ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvZ4sFxzmLCYdEm...
</code></pre>

<ol>
<li>後処理</li>
</ol>


<pre><code class="bash">sudo umount /mnt
sudo losetup -D
</code></pre>

<p>アンマウントしてloopback deviceの関連付けを削除したら終了。</p>

<h3>カスタマイズしたイメージをインストール</h3>

<p>あとは NVMe に Ubuntu を書き込んで終了。</p>

<pre><code class="bash">sudo dd if=ubuntu-24.04.1-preinstalled-server-arm64+raspi.img of=/dev/nvme0n1 bs=4M status=progress
</code></pre>

<p>SDカードを外して再起動したところ、無事起動した。</p>

    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:creator>O. Yuanying</dc:creator>
    <dc:date>2025-01-14T18:30:00+09:00</dc:date>
  </item>
  <item rdf:about="https://www.fraction.jp/log/archives/2023/05/02/jetson-pytorch">
    <title>Jetson Orin Nano に PyTorchとtorchvisionをインストール</title>
    <link>https://www.fraction.jp/log/archives/2023/05/02/jetson-pytorch</link>
    <description>JetPack 5.10 がインストールされたJetson上のコンテナにPyTorchとtorchvisionをインストールしたい。そのままインストールができなかったため以下を参考にする。Machine Learning Containers for Jetson and JetPackPyTorch のインストールこれはドキュメントを参考に。Installing PyTorch for Jetson Platform$ export TORCH_INSTALL=https://developer...</description>
    <content:encoded><![CDATA[
        <p>JetPack 5.10 がインストールされたJetson上のコンテナにPyTorchとtorchvisionをインストールしたい。そのままインストールができなかったため以下を参考にする。</p>

<ul>
<li><a href="https://github.com/dusty-nv/jetson-containers/blob/master/Dockerfile.pytorch">Machine Learning Containers for Jetson and JetPack</a></li>
</ul>


<h2>PyTorch のインストール</h2>

<p>これはドキュメントを参考に。</p>

<ul>
<li><a href="https://docs.nvidia.com/deeplearning/frameworks/install-pytorch-jetson-platform/index.html">Installing PyTorch for Jetson Platform</a></li>
</ul>


<pre><code>$ export TORCH_INSTALL=https://developer.download.nvidia.cn/compute/redist/jp/v51/pytorch/torch-2.0.0a0+8aa34602.nv23.03-cp38-cp38-linux_aarch64.whl
$ python3 -m pip install --no-cache $TORCH_INSTALL
</code></pre>

<h2>torchvisionのインストール</h2>

<p><code>
</code></p>

    ]]></content:encoded>
    <dc:subject>Program/Python</dc:subject>
    <dc:subject>Linux</dc:subject>
    <dc:creator>O. Yuanying</dc:creator>
    <dc:date>2023-05-02T19:01:00+09:00</dc:date>
  </item>
  <item rdf:about="https://www.fraction.jp/log/archives/2023/04/29/jetson-k8s">
    <title>Jetson Orin Nano を Kubernetes のノードにした</title>
    <link>https://www.fraction.jp/log/archives/2023/04/29/jetson-k8s</link>
    <description>前回の続き。containerd の設定CRI のデフォルトランタイムを以下を参考に nvidia に変更。NVIDIA Cloud Native Stack - v8.1 Install Guide for Jetson AGX Xavier or Jetson Xavier NX DevKit or Jetson Orin$ sudo mkdir -p /etc/containerd$ sudo curl -o /etc/containerd/config.toml https://raw.g...</description>
    <content:encoded><![CDATA[
        <p><a href="/log/archives/2023/04/27/jetson-orin-nano">前回</a>の続き。</p>

<h2>containerd の設定</h2>

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

<ul>
<li><a href="https://github.com/NVIDIA/cloud-native-stack/blob/master/install-guides/Jetson_Xavier_v9.0.md#Installing-Containerd">NVIDIA Cloud Native Stack - v8.1 Install Guide for Jetson AGX Xavier or Jetson Xavier NX DevKit or Jetson Orin</a></li>
</ul>


<pre><code>$ 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
</code></pre>

<h2>Kernel のリビルド</h2>

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

<ul>
<li><a href="https://zenn.dev/nkte8/articles/2021-12-06-r01">Jetson nanoをk8sクラスタ参加させた(kubeadm)</a></li>
<li><a href="https://qiita.com/ysakashita/items/566e082a5d060eef5046">NVIDIA Jetson Nano in Kubernetesの検証</a></li>
</ul>


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

<pre><code>$ 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
</code></pre>

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

<ul>
<li><a href="https://forums.developer.nvidia.com/t/jetson-nano-and-iscsi-module-support/199993/3">Jetson nano and iscsi module support</a></li>
</ul>


<pre><code>$ 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
</code></pre>

<h3>コンパイル準備</h3>

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

<ul>
<li><a href="https://docs.nvidia.com/jetson/archives/r35.3.1/DeveloperGuide/text/SD/Kernel/KernelCustomization.html">Manually Downloading and Expanding Kernel Sources</a></li>
</ul>


<p>以下からカーネルのソースを入手。</p>

<ul>
<li><a href="https://developer.nvidia.com/embedded/jetson-linux-r3531">Jetson Linux 35.3.1</a></li>
</ul>


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

<p>カーネルのソースコードを解凍。</p>

<pre><code>$ cd Linux_for_Tegra/source/public
$ tar -xjf kernel_src.tbz2
$ cd kernel/kernel-5.10/
</code></pre>

<p>現状の <code>.config</code> を入手して <code>make nconfig</code> で設定。</p>

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

<ul>
<li><a href="https://www.netapp.com/media/21140-tr-4875-design.pdf">Configuring NVIDIA Jetson Nano with NetApp E-Series Storage Systems</a></li>
</ul>


<pre><code>$ zcat /proc/config.gz &gt; .config.org
$ cp .config.org .config
$ make nconfig
</code></pre>

<p>こんな感じ。</p>

<pre><code class="diff">$ 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
</code></pre>

<h3>コンパイル</h3>

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

<pre><code>$ make prepare
$ make modules_prepare
$ make -j5 Image
$ make -j5 modules
</code></pre>

<h3>カーネルのインストール</h3>

<p>インストールして再起動。</p>

<pre><code>$ sudo cp /boot/Image /boot/Image.org
$ sudo cp arch/arm64/boot/Image /boot/Image
$ sudo make modules_install
$ sudo reboot
</code></pre>

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

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

<h2>Kubernetes へ Join</h2>

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

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

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

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

<pre><code class="yaml">apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
...
(snip)
...
failSwapOn: false
featureGates:
  NodeSwap: true
</code></pre>

<h3>nvidia device plugin のインストール</h3>

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

<ul>
<li><a href="https://medium.com/techbeatly/running-kubernetes-on-gpu-nodes-9ddd97dc4793">Running Kubernetes on GPU Nodes</a></li>
</ul>


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

<pre><code class="json">{
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}
</code></pre>

<p>マニフェストをapplyするのみ。</p>

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

<h3>動作確認</h3>

<p>devicequery を実行。</p>

<pre><code>✦ ➜ cat &lt;&lt;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:
     &lt; Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) &gt;

deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 11.4, CUDA Runtime Version = 10.2, NumDevs = 1
Result = PASS
</code></pre>

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

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

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

<ul>
<li><a href="https://github.com/dusty-nv/jetson-containers/issues/194">OSError: libcurand.so.10: cannot open shared object file: No such file or directory</a></li>
</ul>


<p>しかし、JetPack 5.1からはなぜか <code>l4t.csv</code> のみが存在し、今まであったはずの <code>cuda.csv</code> や <code>cudnn.csv</code> が見当たらない。</p>

<pre><code>$ 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
</code></pre>

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

<ul>
<li><a href="https://forums.developer.nvidia.com/t/missing-cuda-csv-cudnn-csv-tensorrt-csv-in-etc-nvidia-container-runtime-host-files-for-container-d/240831/8">Missing cuda.csv, cudnn.csv, tensorrt.csv in /etc/nvidia-container-runtime/host-files-for-container.d/</a></li>
</ul>


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

<pre><code class="yaml">      volumes:
      - name: cuda
        hostPath:
          path: /usr/local/cuda-11.4
      - name: cudnn
        hostPath:
          path: /usr/lib/aarch64-linux-gnu/libcudnn.so.8
</code></pre>

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

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

<pre><code>➜ 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() &gt; 0
False
</code></pre>

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

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

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

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

    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:creator>O. Yuanying</dc:creator>
    <dc:date>2023-04-29T11:25:00+09:00</dc:date>
  </item>
  <item rdf:about="https://www.fraction.jp/log/archives/2023/04/27/jetson-orin-nano">
    <title>Jetson Orin Nano のセットアップ</title>
    <link>https://www.fraction.jp/log/archives/2023/04/27/jetson-orin-nano</link>
    <description>Jetson Orin Nano を買ったのでセットアップしました。インストールしたのは JetPack v5.1.1。まあ、普通にSDカードからOSを起動してもいいのですが、せっかくNVMeがついてるので、NVMe接続のSSDにOSをインストールすることにしました。また、後々K8sのノードとして利用する予定なので不要なGUI周りのパッケージをアンインストールしました。NVMeからのOS起動普通にSDカードにOSをインストールして起動したのちに、ターミナルからJetPackをダウンロードして NV...</description>
    <content:encoded><![CDATA[
        <p>Jetson Orin Nano を買ったのでセットアップしました。</p>

<p class='image'>
<img  src='/log/archives/2023/04/27/IMG_0860.jpg'
      width='720'
      height='540'
      alt=''  />
</p>


<p>インストールしたのは JetPack v5.1.1。</p>

<p>まあ、普通にSDカードからOSを起動してもいいのですが、せっかくNVMeがついてるので、NVMe接続のSSDにOSをインストールすることにしました。また、後々K8sのノードとして利用する予定なので不要なGUI周りのパッケージをアンインストールしました。</p>

<h2>NVMeからのOS起動</h2>

<p>普通にSDカードにOSをインストールして起動したのちに、ターミナルからJetPackをダウンロードして NVMe のSSDに書き込みます。</p>

<pre><code>$ curl -LO https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v3.1/sd_card_b49/jp511-orin-nano-sd-card-image.zip

$ /usr/bin/unzip -p jp511-orin-nano-sd-card-image.zip | sudo /bin/dd of=/dev/nvme0n1 bs=1M status=progress
[sudo] password for yuanying:
22029074432 bytes (22 GB, 21 GiB) copied, 291 s, 75.7 MB/s
0+337904 records in
0+337904 records out
22144876544 bytes (22 GB, 21 GiB) copied, 293.348 s, 75.5 MB/s
</code></pre>

<p>そのままNVMeに書き込んだOSイメージから起動できれば良いのだが、イメージ中の<code>/boot/extlinux/extlinux.conf</code> を書き換えないと起動しないとのことなので書き換える。</p>

<p>NVMeのSSDをマウントしたのちに、</p>

<pre><code>$ sudo mount /dev/nvme0n1p1 /mnt
</code></pre>

<p><code>/mnt/boot/extlinux/extlinux.conf</code> を以下のように書き換える。(<code>/dev/mmcblk1p1</code> でSDカードが指定してあったところを <code>/dev/nvme0n1p1</code> に変更。)</p>

<pre><code class="diff">--- extlinux.conf.backup        2023-04-27 17:45:49.921921693 +0900
+++ extlinux.conf       2023-04-27 17:46:20.238288895 +0900
@@ -8,7 +8,7 @@
       LINUX /boot/Image
       FDT /boot/dtb/kernel_tegra234-p3767-0003-p3768-0000-a0.dtb
       INITRD /boot/initrd
-      APPEND ${cbootargs} root=/dev/mmcblk1p1 rw rootwait rootfstype=ext4 mminit_loglevel=4 console=ttyTCU0,115200 console=ttyAMA0,115200 firmware_class.path=/etc/firmware fbcon=map:0 net.ifnames=0
+      APPEND ${cbootargs} root=/dev/nvme0n1p1 rw rootwait rootfstype=ext4 mminit_loglevel=4 console=ttyTCU0,115200 console=ttyAMA0,115200 firmware_class.path=/etc/firmware fbcon=map:0 net.ifnames=0

 # When testing a custom kernel, it is recommended that you create a backup of
 # the original kernel and add a new entry to this file so that the device can
</code></pre>

<p>アンマウントしたのちに再起動。</p>

<pre><code>$ sudo umount /mnt
$ sudo reboot
</code></pre>

<h2>GUI のパッケージ削除</h2>

<p>Jetson をディスプレイに繋いで利用するつもりはなかったので、GUIその他を諸々削除します。</p>

<p>まずは、とりあえず以下の記事を参考に <code>multi-user.target</code> をデフォルトに変更。</p>

<ul>
<li><a href="https://www.youtalk.jp/2023/03/28/jetson-orin-nano.html">NVIDIA Jetson Orin Nano開発者キットのセットアップ</a></li>
</ul>


<pre><code>$ sudo systemctl set-default multi-user.target
Removed /etc/systemd/system/default.target.
Created symlink /etc/systemd/system/default.target → /lib/systemd/system/multi-user.target.
</code></pre>

<p>SSHでログインしたところ、まだGNOME関連のプロセスが起動していたので、それらも以下の記事を参考に削除。</p>

<ul>
<li><a href="https://nvidia-ai-iot.github.io/jetson-min-disk/step1.html">Guide to Minimizing Jetson Disk Usage</a></li>
</ul>


<p>上記の記事中の <code>nvubuntu-focal-packages_only-in-desktop</code> はリンクが切れていたので、github にあった多分同じものと思われるリストを利用。</p>

<pre><code>$ sudo apt-get update
$ suro apt-get -y install curl
$ curl -LO https://raw.githubusercontent.com/NVIDIA-AI-IOT/jetson-min-disk/main/assets/nvubuntu-focal-packages_only-in-desktop.txt
$ sudo apt-get purge $(cat nvubuntu-focal-packages_only-in-desktop.txt)
$ sudo apt-get install -y netwprk-kanager netplan.io
$ cat &lt;&lt;EOF | sudo tee /etc/netplan/00-init.yaml
network:
  renderer: NetworkManager
  ethernets:
    eno1:
      dhcp4: true
  version: 2
EOF
$ sudo netplan apply
</code></pre>

<p>削除後に、<code>network-manager</code> を再度インストールしないとネットワークに繋がらなくなって酷い目にあうらしいので注意。自分は<code>netplan</code>で設定した。</p>

<h2>感想</h2>

<p>VanillaなUbuntu Serverをインストールしたいんだけど、なぜかうまくいかない。</p>

    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:creator>O. Yuanying</dc:creator>
    <dc:date>2023-04-27T17:48:00+09:00</dc:date>
  </item>
  <item rdf:about="https://www.fraction.jp/log/archives/2023/03/23/new-pc">
    <title>自宅のKubernetesにGPUノードを追加した</title>
    <link>https://www.fraction.jp/log/archives/2023/03/23/new-pc</link>
    <description>ChatGPT周りが面白かったので、LLM関連を学習したくなり、久しぶりにPCを自作した。最近はNUCやらDeskMiniなどのベアボーン、ラズパイばかり触っていたので自作PC周りはなかなかの浦島太郎状態。ブログのエントリをあさってみると2007年のPC環境で自作PCをデスクトップに使ってるのを発見できた。グラボはATIしか買った記憶はなかったのだが、GeForce 7900 GS とか使ってる。。謎だ。HW自作PCは久しぶりということもあって全く状況が掴めなかったのもあり、一瞬、どこかのPCショ...</description>
    <content:encoded><![CDATA[
        <p>ChatGPT周りが面白かったので、LLM関連を学習したくなり、久しぶりにPCを自作した。
最近はNUCやらDeskMiniなどのベアボーン、ラズパイばかり触っていたので自作PC周りはなかなかの浦島太郎状態。</p>

<p>ブログのエントリをあさってみると<a href="/log/archives/2007/08/1208">2007年のPC環境</a>で自作PCをデスクトップに使ってるのを発見できた。グラボはATIしか買った記憶はなかったのだが、GeForce 7900 GS とか使ってる。。謎だ。</p>

<h2>HW</h2>

<p>自作PCは久しぶりということもあって全く状況が掴めなかったのもあり、一瞬、どこかのPCショップのBTOでも買うかと日和った考えが頭によぎったが、最近、自転車の組み立てで自分で組むのは面倒、という理由でショップに任せたところとても不快なめにあったので、自分でできることは自分でしようという気分になりパーツを買い集めた。</p>

<p class='image'>
<img  src='/log/archives/2023/03/23/IMG_0654.jpg'
      width='800'
      height='600'
      alt=''  />
</p>


<ul>
<li>CPU: Intel  Core i5 13400 BOX

<ul>
<li>32,640円</li>
</ul>
</li>
<li>MB: ASUS  TUF GAMING B760M-PLUS D4 (B760 1700 MicroATX)

<ul>
<li>26,446円</li>
</ul>
</li>
<li>Memory: Crucial CT32G4DFD832A DDR4-3200 32GB x2

<ul>
<li>21,300円 x 2</li>
</ul>
</li>
<li>SSD: Crucial  P5 Plus CT1000P5PSSD8JP (M.2 2280 1TB)

<ul>
<li>12,660円</li>
</ul>
</li>
<li>GPU: NVRTXA4000 [NVIDIA RTX A4000 16GB GDDR6]

<ul>
<li>135,800 円</li>
</ul>
</li>
</ul>


<p>電源とケースは使い回し。自作PCは久しぶりなはずだが、電源とケースが余っているのが謎だ。</p>

<h2>SW</h2>

<h3>OSインストール</h3>

<ul>
<li>OS: Ubuntu 22.04.03</li>
<li>Container Runtime: containerd</li>
<li>Kubernetes: v1.24.6</li>
</ul>


<p>OS は Ubuntu 22.04 を選んだが、そのままインストールすると途中で処理が止まりインストールできず。少し焦った。
最新のカーネルのバックポートを利用してインストールする必要があったようだ。 メモ: <code>Boot and Install with the HWE kernel</code> 。</p>

<p>OSをインストールしたらそのまま containerd/kubelet をインストールしてクラスタにジョイン。</p>

<h3>GPU のドライバのインストール</h3>

<p>Kubernetesクラスタの一員になったのでドライバのインストールも GPU Operator に任せる。</p>

<ul>
<li><a href="https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/getting-started.html#common-deployment-scenarios">NVIDIA GPU OPERATOR: Getting Started</a></li>
</ul>


<p>ノードの名前は <code>poissonnerie</code>。ドライバやFeature DiscoveryはGPUノードにだけいればいいので、いい感じにラベルとtaintをつける。</p>

<pre><code>k label node poissonnerie unstable.cloud/nvidia-gpu=yes
k taint node poissonnerie nvidia.com/gpu=:NoSchedule
</code></pre>

<p>インストールはhelmから直接せずに一旦templateでファイルに落としてからkustomizeをかける。</p>

<pre><code>helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

# renovate: datasource=helm depName=gpu-operator-chart packageName=gpu-operator registryUrl=https://helm.ngc.nvidia.com/nvidia
CHART_VER=22.9.2
CHART=nvidia/gpu-operator
VALUES=src/values.yaml
MANIFEST=src.yaml
NAMESPACE=gpu-operator
NAME=gpu-operator

helm template --include-crds --version v${CHART_VER} --values ${VALUES} --namespace ${NAMESPACE} ${NAME} ${CHART} &gt; ${MANIFEST}
</code></pre>

<p>kustomizeからいきなりhelmを使えるのだが、一旦helmの出力するマニフェストを確認したいので毎回templateで出力してる。</p>

<pre><code>resources:
- src.yaml
patchesStrategicMerge:
- |-
  apiVersion: apps/v1
  kind: DaemonSet
  metadata:
    name:  gpu-operator-node-feature-discovery-worker
  spec:
    template:
      spec:
        nodeSelector:
          unstable.cloud/nvidia-gpu: "yes"
</code></pre>

<p>こんな感じでyamlを作った後に、<code>kubectl apply -k .</code> でインストール。</p>

<pre><code>✦ ➜ k get pod
NAME                                                          READY   STATUS      RESTARTS      AGE
gpu-feature-discovery-q57nz                                   1/1     Running     0             10m
gpu-operator-5df795584-2mknv                                  1/1     Running     2 (40m ago)   46m
gpu-operator-node-feature-discovery-master-84c7c7c6cf-h7lpf   1/1     Running     0             46m
gpu-operator-node-feature-discovery-worker-tdhs2              1/1     Running     0             11m
nvidia-container-toolkit-daemonset-j6xbw                      1/1     Running     0             10m
nvidia-cuda-validator-j9qwp                                   0/1     Completed   0             8m2s
nvidia-dcgm-exporter-sbw6w                                    1/1     Running     0             10m
nvidia-device-plugin-daemonset-ddjj9                          1/1     Running     0             10m
nvidia-device-plugin-validator-2p4hf                          0/1     Completed   0             6m20s
nvidia-driver-daemonset-klfrn                                 1/1     Running     0             10m
nvidia-operator-validator-f8jr5                               1/1     Running     0             10m
</code></pre>

<p>いい感じにCPUノードだけで動いてる。</p>

<pre><code>✦ ➜ k get node poissonnerie -o json | jq '.status.allocatable'
{
  "cpu": "16",
  "ephemeral-storage": "884016022498",
  "hugepages-1Gi": "0",
  "hugepages-2Mi": "0",
  "memory": "65483284Ki",
  "nvidia.com/gpu": "1",
  "pods": "110"
}
</code></pre>

<p><code>"nvidia.com/gpu": "1"</code> がついた。</p>

<pre><code>yuanying@poissonnerie:~$ sudo cat /etc/containerd/config.toml| grep nvidia
      default_runtime_name = "nvidia"
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
            BinaryName = "/usr/local/nvidia/toolkit/nvidia-container-runtime"
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-experimental]
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-experimental.options]
            BinaryName = "/usr/local/nvidia/toolkit/nvidia-container-runtime-experimental"
</code></pre>

<p>GPU Operator は <code>/etc/containerd/config.toml</code> もいじってくるらしい。変に自分で編集しないように気をつけたい。</p>

<pre><code>cat &lt;&lt;EOF | k apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: cuda-vectoradd
spec:
  restartPolicy: OnFailure
  containers:
  - name: cuda-vectoradd
    image: "nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda11.7.1-ubuntu20.04"
    resources:
      limits:
        nvidia.com/gpu: 1
  tolerations:
  - effect: NoSchedule
    key: nvidia.com/gpu
    operator: Exists
EOF
</code></pre>

<p>Getting Started 通りにサンプルアプリケーションをインストール。</p>

<pre><code>✦ ➜ k get pod
NAME             READY   STATUS      RESTARTS   AGE
cuda-vectoradd   0/1     Completed   0          9s

✦ ➜ k logs cuda-vectoradd
[Vector addition of 50000 elements]
Copy input data from the host memory to the CUDA device
CUDA kernel launch with 196 blocks of 256 threads
Copy output data from the CUDA device to the host memory
Test PASSED
Done
</code></pre>

<p>ちゃんと動いた。</p>

<h3>Wake on LAN の有効化</h3>

<p>GPUは電気を食うので使わないときはシャットダウンしておきたい。ということで同時に Wake on LAN も以下を参考に有効化した。</p>

<ul>
<li><a href="https://chatnoirlibre.com/ubuntu-22-04-wake-on-lan/">Ubuntu 22.04　Wake on LanでリモートのUbuntuデスクトップパソコンを起動する</a></li>
<li><a href="https://kapibara-sos.net/archives/949">LinuxでWake on LAN設定</a></li>
<li><a href="https://www.asus.com/jp/support/FAQ/1045950/">[マザーボード] BIOSでWOL (Wake On Lan) 機能を設定する方法</a></li>
</ul>


<h2>その他</h2>

<p>Kubernetes上で Stable Diffusion も動いた。</p>

<p class='image'>
<img  src='/log/archives/2023/03/23/Screenshot-sd.png'
      width='1000'
      height='743'
      alt=''  />
</p>


<p>満足したので機械学習の学習をする。</p>

    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:creator>O. Yuanying</dc:creator>
    <dc:date>2023-03-23T15:32:00+09:00</dc:date>
  </item>
  <item rdf:about="https://www.fraction.jp/log/archives/2022/09/29/kubevirt-noip-after-restart">
    <title>kubevirtのVMをリスタートしたらIPアドレスが消えた</title>
    <link>https://www.fraction.jp/log/archives/2022/09/29/kubevirt-noip-after-restart</link>
    <description>症状UbuntuのVMをkubectl virt restart した後に ssh しようとしたところ、No route to host となって繋がらなくなった。$ k virt restart node-01VM node-01 was scheduled to restart$ ssh ubuntu@node-01.staging.vms.svc.fraction.clusterssh: connect to host node-01.staging.vms.svc.fraction.clu...</description>
    <content:encoded><![CDATA[
        <h2>症状</h2>

<p>UbuntuのVMを<code>kubectl virt restart</code> した後に ssh しようとしたところ、<code>No route to host</code> となって繋がらなくなった。</p>

<pre><code>$ k virt restart node-01
VM node-01 was scheduled to restart

$ ssh ubuntu@node-01.staging.vms.svc.fraction.cluster
ssh: connect to host node-01.staging.vms.svc.fraction.cluster port 22: No route to host
</code></pre>

<h3>再起動後の Mac Address が変わっていた</h3>

<p><code>kubectl virt console</code> で調べてみたところ、Mac Addressが再起動後に変わっていた。</p>

<pre><code class="diff">➜ diff -u before.txt after.txt
--- before.txt  2022-09-29 00:12:28.024068282 +0000
+++ after.txt   2022-09-29 00:13:00.940507202 +0000
@@ -5,9 +5,5 @@
        valid_lft forever preferred_lft forever
     inet6 ::1/128 scope host
        valid_lft forever preferred_lft forever
-2: enp1s0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000
-    link/ether b2:81:6c:00:81:56 brd ff:ff:ff:ff:ff:ff
-    inet 10.244.8.23/24 metric 100 brd 10.244.8.255 scope global dynamic enp1s0
-       valid_lft 86313396sec preferred_lft 86313396sec
-    inet6 fe80::b081:6cff:fe00:8156/64 scope link
-       valid_lft forever preferred_lft forever
+2: enp1s0: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN group default qlen 1000
+    link/ether 62:1c:50:f6:fe:e3 brd ff:ff:ff:ff:ff:ff
</code></pre>

<p>netplan の設定ファイルを見るに、初回起動時の Mac Address を指定してネットワークの設定を行なっていた。</p>

<pre><code class="yaml">ubuntu@node-01:~$ cat /etc/netplan/50-cloud-init.yaml
# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    ethernets:
        enp1s0:
            dhcp4: true
            match:
                macaddress: f2:a5:6b:0c:3b:9f
            set-name: enp1s0
    version: 2
</code></pre>

<h2>解決方法</h2>

<p><code>networkData</code> を指定して、システムデフォルトの設定を上書きする。</p>

<pre><code class="yaml">      - cloudInitNoCloud:
          networkData: |
            version: 2
            ethernets:
              enp1s0:
                dhcp4: true
          userData: |-
            #cloud-config
            password: password
        name: cloudinitdisk
</code></pre>

<p>以上。</p>

<ul>
<li>https://github.com/kubevirt/kubevirt/issues/1646</li>
</ul>


    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>O. Yuanying</dc:creator>
    <dc:date>2022-09-29T09:04:00+09:00</dc:date>
  </item>
  <item rdf:about="https://www.fraction.jp/log/archives/2022/09/26/macvlan">
    <title>KubernetesのPodに二枚目のNICでmacvlanを試す</title>
    <link>https://www.fraction.jp/log/archives/2022/09/26/macvlan</link>
    <description>kubevirt の環境を作って いくつか気になる点があるのだけれども、その一つがネットワークである。VMをPod Networkに繋ぐことができるのは良いのだが、今までvirshを使って手作業でVMを利用していたときは bridge を作ってホストのネットワークに参加させていたので、できれば同じようにkubevirtのVMもホストのネットワークに繋げたい。幸いなことに、kubevirt は multus に対応している。そこで二枚目のNICをVMに刺して、それをノードのネットワークに参加させれば...</description>
    <content:encoded><![CDATA[
        <p><a href="/log/archives/2022/09/18/kubevirt">kubevirt の環境を作って</a> いくつか気になる点があるのだけれども、
その一つがネットワークである。</p>

<p>VMをPod Networkに繋ぐことができるのは良いのだが、
今までvirshを使って手作業でVMを利用していたときは bridge を作ってホストのネットワークに参加させていたので、
できれば同じようにkubevirtのVMもホストのネットワークに繋げたい。</p>

<p>幸いなことに、kubevirt は <a href="https://github.com/k8snetworkplumbingwg/multus-cni">multus</a> に対応している。</p>

<p>そこで二枚目のNICをVMに刺して、それをノードのネットワークに参加させれば良いのではないかと思い付いた。</p>

<h2>macvlan</h2>

<p>VLAN対応のスイッチを買って自宅のクラスターのノードを参加させるか、などいくつか考えたのだが、残念なことにNUCにNICは二つない。
色々思案した後に <code>macvlan</code> がちょうどいいんじゃないかと思い付いた。</p>

<p>CNI の <a href="https://www.cni.dev/plugins/current/main/macvlan/">macvlan プラグイン</a> は Pod を直接ホストのネットワークに参加させるにはちょうど良いプラグインに見えるが、
<code>macvlan</code> はその、「ホストとの通信ができなくなる」という Pod network として利用するには致命的な制限があるためあまり利用することはないのだが、
今回の要件は Pod network とは別に二枚目のNICをPodに刺す、というユースケースなので、その制限も問題にならない。</p>

<p>と、いうことで以下のような構成を組む。</p>

<p class='image'>
<img  src='/log/archives/2022/09/26/network.png'
      width='720'
      height='405'
      alt=''  />
</p>


<h2>CNI</h2>

<h3>Pod の起動</h3>

<p>試す、と言ってもいきなり multus をインストールして multus 越しにNICを刺すのも面倒だったので、直接手作業で NIC を Pod に刺して試すことにした。</p>

<ul>
<li>https://qiita.com/yuanying/items/68b2a32b4d217e679955</li>
</ul>


<p>まずは適当にPodを作る。</p>

<pre><code class="yaml">$ cat &lt;&lt;EOF | k apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: default
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80
  nodeSelector:
    hostname: pablo
EOF
</code></pre>

<p>Pod の状態を確認。指定したノードに起動している。</p>

<pre><code>➜ k get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          4s    10.244.4.99   pablo   &lt;none&gt;           &lt;none&gt;
</code></pre>

<h3>ノードでPodの確認</h3>

<p>ノードにsshし、<code>crictl</code> を使ってPodを確認する。</p>

<pre><code>$ sudo su -
# crictl pods --name nginx --namespace default
POD ID              CREATED             STATE               NAME                NAMESPACE           ATTEMPT             RUNTIME
bbdc2c603ffb9       2 minutes ago       Ready               nginx               default             0                   (default)
</code></pre>

<p>このPodが利用しているNework Namespaceは以下のコマンドで確認できる。</p>

<pre><code># crictl inspectp $(crictl pods --name ${POD_NAME} --namespace ${POD_NAMESPACE} -q) | gojq '.info.runtimeSpec.linux.namespaces.[]|select(.type=="network")|.path' -r
/var/run/netns/cni-5bcada6f-4722-477c-0e55-b7339fb28fc6
</code></pre>

<p>上記のワンライナーは以下のようなことをやっている。</p>

<ol>
<li><code>crictl pods --name ${POD_NAME} --namespace ${POD_NAMESPACE} -q</code>

<ul>
<li>Pod の ID を取得。</li>
</ul>
</li>
<li><code>crictl inspectp</code>

<ul>
<li>Pod の詳細情報を json で取得。</li>
</ul>
</li>
<li><code>gojq '.info.runtimeSpec.linux.namespaces.[]|select(.type=="network")|.path'</code>

<ul>
<li>Pod の詳細情報から Network namespace のパスを取得。</li>
</ul>
</li>
</ol>


<p>上記の Network namespace に入って状態を確認してみる。</p>

<pre><code># ip netns exec cni-5bcada6f-4722-477c-0e55-b7339fb28fc6 bash
# ip addr 
1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0@if248: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP group default
    link/ether 16:26:ab:97:65:e1 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.4.99/24 brd 10.244.4.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fde7:4ad6:425d:8::248/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::1426:abff:fe97:65e1/64 scope link
       valid_lft forever preferred_lft forever

# exit
</code></pre>

<p>ローカルインターフェースと、デフォルトのPod networkのNICが刺さっていることが確認できる。</p>

<h3>CNI macvlan</h3>

<p>ということで、上記の network namespace に CNI の <code>macvlan</code> プラグインで二枚目のNICを刺してみる。</p>

<p>IPAM に dhcp プラグインを利用するので事前に CNI の <code>dhcp</code> プラグインを <code>daemon</code> モードで起動しておく。</p>

<pre><code># /opt/cni/bin/dhcp daemon
</code></pre>

<p>まずはCNI実行に必要な環境変数をセットアップ。</p>

<pre><code>POD_NAME=nginx
POD_NAMESPACE=default

export CNI_PATH=/opt/cni/bin
export PATH=$CNI_PATH:$PATH

# NIC を追加するので ADD
export CNI_COMMAND=ADD

# 上記のワンライナーで Network namespace のパスを取得
export CNI_NETNS=$(crictl inspectp $(crictl pods --name ${POD_NAME} --namespace ${POD_NAMESPACE} -q) | gojq '.info.runtimeSpec.linux.namespaces.[]|select(.type=="network")|.path' -r)

# Container ID は Network namespace の名前を利用することが多そう
export CNI_CONTAINERID=$(echo ${CNI_NETNS} | cut -f 5 -d "/")

# すでに一枚刺さってるので二枚目なので `eth1` を指定
export CNI_IFNAME=eth1
</code></pre>

<p>そして <code>macvlan</code> プラグインをキック！</p>

<p>master に指定しているNICはホストのネットワークにつなげたいNICを指定すること。</p>

<pre><code>/opt/cni/bin/macvlan &lt;&lt;EOF
{
    "name": "mynet",
    "type": "macvlan",
    "master": "eno1",
    "ipam": {
        "type": "dhcp"
    }
}
EOF
</code></pre>

<p>うまくいくと以下のような出力を得ることができる。</p>

<pre><code>{
    "cniVersion": "0.1.0",
    "ip4": {
        "ip": "192.168.2.11/16",
        "gateway": "192.168.0.1",
        "routes": [
            {
                "dst": "0.0.0.0/0",
                "gw": "192.168.0.1"
            }
        ]
    },
    "dns": {}
}
</code></pre>

<p>もう一回、先ほどのNetwork namespaceに入って、NICが刺さっているか確認する。</p>

<pre><code># ip netns exec $CNI_CONTAINERID ip addr
1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0@if248: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP group default
    link/ether 16:26:ab:97:65:e1 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.4.99/24 brd 10.244.4.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fde7:4ad6:425d:8::248/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::1426:abff:fe97:65e1/64 scope link
       valid_lft forever preferred_lft forever
3: eth1@if2: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP group default
    link/ether 1e:52:59:8f:14:7f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.2.11/16 brd 192.168.255.255 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/64 scope global dynamic mngtmpaddr
       valid_lft 86308sec preferred_lft 14308sec
    inet6 fe80::1c52:59ff:fe8f:147f/64 scope link
       valid_lft forever preferred_lft forever
</code></pre>

<p>DHCPで取得したアドレスのついた二枚目のNICが刺さってることが確認できますね！（ついでにグローバルのIPv6アドレスもついてきた。</p>

<p>別のノードからpingも通る。素晴らしい。</p>

<pre><code>➜ ping 192.168.2.11
PING 192.168.2.11 (192.168.2.11) 56(84) bytes of data.
64 bytes from 192.168.2.11: icmp_seq=1 ttl=63 time=0.604 ms
64 bytes from 192.168.2.11: icmp_seq=2 ttl=63 time=0.290 ms
64 bytes from 192.168.2.11: icmp_seq=3 ttl=63 time=0.487 ms
</code></pre>

<h2>注意点</h2>

<p><code>macvlan</code> の注意点として、ホスト <-> コンテナ間 の通信ができない、というものがあるが、
その制限により、使用しているデフォルトに使用している CNI プラグインによっては同一ノード上の Pod 間通信ができないので注意が必要。</p>

<p>つまり同一ホスト上の以下のような通信ができない場合がある。</p>

<ul>
<li><code>x</code>: Pod A (default interface) -> Pod B (macvlan interface)</li>
<li><code>x</code>: Pod A (macvlan interface) -> Pod B (default interface)</li>
</ul>


<p>ただ、<code>Service</code> 越しの通信はできるので、Pod間通信をしなければならない特別な事情がない限りは問題なさそう。</p>

<h2>まとめ</h2>

<p>自宅クラスタなのでノードにNICが1枚しか刺さってないので、Pod networkの二つ目のネットワークどうしようかと悩んでいたが、
なんのことはない、単にノードのネットワークにつなげたいだけなら二つ目のNICで <code>macvlan</code> を使えば良いことがわかった。</p>

<h2>追記 2022-09-29</h2>

<p>ドキュメントを読んでいたら、二つ目のNICとしてmacvlanは使えない模様。</p>

<ul>
<li><a href="https://kubevirt.io/user-guide/virtual_machines/interfaces_and_networks/#invalid-cnis-for-secondary-networks">kubevirt: Interfaces and Networks - Invalid CNIs for secondary networks</a></li>
</ul>


    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>O. Yuanying</dc:creator>
    <dc:date>2022-09-26T15:34:00+09:00</dc:date>
  </item>
  <item rdf:about="https://www.fraction.jp/log/archives/2022/09/18/kubevirt">
    <title>kubevirt を試す</title>
    <link>https://www.fraction.jp/log/archives/2022/09/18/kubevirt</link>
    <description>インストールkubevirt 本体と cdi (containerized data importer) をインストール。KUBEVIRT_VER := v0.57.1curl -Lo kubevirt/operator.yaml https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VER}/kubevirt-operator.yamlcurl -Lo kubevirt/cr.yaml https://github.c...</description>
    <content:encoded><![CDATA[
        <h2>インストール</h2>

<p>kubevirt 本体と cdi (containerized data importer) をインストール。</p>

<pre><code>KUBEVIRT_VER := v0.57.1
curl -Lo kubevirt/operator.yaml https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VER}/kubevirt-operator.yaml
curl -Lo kubevirt/cr.yaml https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VER}/kubevirt-cr.yaml

CDI_VER := v1.55.0
curl -Lo cdi/operator.yaml https://github.com/kubevirt/containerized-data-importer/releases/download/${CDI_VER}/cdi-operator.yaml
curl -Lo cdi/cr.yaml https://github.com/kubevirt/containerized-data-importer/releases/download/${CDI_VER}/cdi-cr.yaml

kubectl apply -f kubevirt
kubectl apply -f cdi
</code></pre>

<p><code>kubevirt-cr.yaml</code> と <code>cdi-cr.yaml</code> はそれぞれ <code>kubevirt</code> と <code>cdi</code> の実体？というかそれぞれの設定ファイル？っぽいものらしく、これらもデプロイしないと以降のステップがうまく動かなかった。</p>

<h3>その他必要なツールのインストール</h3>

<h4>virtctl</h4>

<p>kubevirt をいい感じに操作するためのCLIツール。krew でインストールする。</p>

<pre><code>kubectl krew install virt
</code></pre>

<h2>VM起動</h2>

<p>起動するVMのディスクをどう用意するのかいくつか方法があるようだ。3通りほど試してみる。</p>

<ul>
<li>VM起動 (1)

<ul>
<li>VMイメージを手元に置き、VM起動のたびVMイメージをアップロードする方法。</li>
</ul>
</li>
<li>VM起動 (2)

<ul>
<li>VMイメージを適当なWebサーバに置き、それを参照する方法。</li>
</ul>
</li>
<li>VM起動 (3)

<ul>
<li>VMイメージをPVCに置き、VM起動のたびPVC上のディスクデータをクローンして利用する方法。</li>
</ul>
</li>
</ul>


<h3>VM起動 (1)</h3>

<p>VMイメージを手元に置いて起動のたび VM イメージをアップロードして起動する方法。</p>

<h4>VMイメージの用意</h4>

<p>なんとなくUbuntuを使いたかったので、Exampleなどにあるfedoraではなく、適当なUbuntu 22.04のイメージをダウンロード。
ここで <code>jammy-server-cloudimg-amd64-disk-kvm.img</code> とかを選択すると起動しなかったりするので注意。</p>

<pre><code>curl -LO https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
</code></pre>

<p><code>kubectl virt image-upload</code> コマンドを使って <code>DataVolumes</code> 形式でイメージをアップロード。
<code>PVC</code> として扱うよりいろいろ便利らしいが、まだ調べてないのでよくわからない。</p>

<pre><code>kubectl virt -n staging image-upload dv jammy-01 \
    --size=10Gi \
    --uploadproxy-url=https://cdi-uploadproxy.cdi.svc.fraction.cluster \
    --insecure \
    --block-volume \
    --storage-class bronze \
    --access-mode ReadWriteOnce \
    --image-path=jammy-server-cloudimg-amd64.img
</code></pre>

<p>最終的にPVCにVMイメージを保存するので、デフォルトのStorageClassとかを指定してない場合は指定が必要。</p>

<p><code>--uploadproxy-url</code> は、<code>cdi</code> をインストールしたときにデプロイされる VM イメージをアップロードするための proxy のアドレス。
コマンドを実行するワークスペースからアクセス可能なURLでなければならないのだけど、うちの環境は <code>fraction.cluster</code> と言うドメインを使うことで <code>Service</code> に直接アクセス可能なためこんな感じのURLに。</p>

<p>VMを起動するディスクを作成する方法はいくつかあるらしいが、手元からVMイメージをアップロードするこの方法が自宅クラスタでは簡便。</p>

<h4>VM作成</h4>

<p>以下のようにマニフェストを適用すると、VMが起動する。</p>

<pre><code>cat &lt;&lt;EOF | k apply -f -
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  generation: 1
  labels:
    kubevirt.io/os: linux
  name: ubuntu-01
spec:
  running: true
  template:
    metadata:
      labels:
        kubevirt.io/domain: ubuntu-01
    spec:
      domain:
        cpu:
          cores: 2
        devices:
          disks:
          - disk:
              bus: virtio
            name: disk0
          - cdrom:
              bus: sata
              readonly: true
            name: cloudinitdisk
        machine:
          type: q35
        resources:
          requests:
            memory: 1024M
      volumes:
      - dataVolume:
          name: jammy-01
        name: disk0
      - cloudInitNoCloud:
          userData: |
            #cloud-config
            hostname: ubuntu-01
            ssh_pwauth: True
            disable_root: false
            ssh_authorized_keys:
            - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCS7YoDiqntTeWKAKCyfa8yylcMc3W7PN7wYEC1mkHEzDMVQzKSObVGiiIDfYfd50o37pptQ0PjAo2LaXTJLQseFfrwEZW8Jifv/8qDu75ef/jau9q3po7ReQebhQTNtgT2SQ1PTa5s0yEcbP1M1Eyp+lqj+TnYuLuqq5AG9UBizPCbO+dnvW8gRvZ0TuX+f2+DzbYkRnL/SHvQ9hxkO4X7mJslw6DIWQJHM4xFZBN6y+7nntpYwxy1S7kfx38S7I9OLVWP+4a39R6x5QVULLM/n1a+aR4R3KhiA/XVoJheTyJY/fe5qVJuFYcwJIxo4Vj51tyiQ8T8PPePgqIYATI5
        name: cloudinitdisk
EOF
</code></pre>

<h3>VM起動 (2)</h3>

<p>先述の <a href="https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"><code>jammy-server-cloudimg-amd64.img</code></a> を適当なWebサーバに置く。</p>

<p>今回は https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img をそのまま指定する。</p>

<h4>VM作成</h4>

<p>以下のようにマニフェストを適用すると、VMが起動する。</p>

<pre><code>cat &lt;&lt;EOF | k apply -f -
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  generation: 1
  labels:
    kubevirt.io/os: linux
  name: ubuntu-02
spec:
  dataVolumeTemplates:
  - metadata:
      name: jammy-02
    spec:
      pvc:
        accessModes:
        - ReadWriteOnce
        volumeMode: Block
        resources:
          requests:
            storage: 10Gi
        storageClassName: bronze
      source:
        http:
          url: https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
  running: true
  template:
    metadata:
      labels:
        kubevirt.io/domain: ubuntu-02
    spec:
      domain:
        cpu:
          cores: 2
        devices:
          disks:
          - disk:
              bus: virtio
            name: disk0
          - cdrom:
              bus: sata
              readonly: true
            name: cloudinitdisk
        machine:
          type: q35
        resources:
          requests:
            memory: 1024M
      volumes:
      - dataVolume:
          name: jammy-02
        name: disk0
      - cloudInitNoCloud:
          userData: |
            #cloud-config
            hostname: ubuntu-02
            ssh_pwauth: True
            disable_root: false
            ssh_authorized_keys:
            - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCS7YoDiqntTeWKAKCyfa8yylcMc3W7PN7wYEC1mkHEzDMVQzKSObVGiiIDfYfd50o37pptQ0PjAo2LaXTJLQseFfrwEZW8Jifv/8qDu75ef/jau9q3po7ReQebhQTNtgT2SQ1PTa5s0yEcbP1M1Eyp+lqj+TnYuLuqq5AG9UBizPCbO+dnvW8gRvZ0TuX+f2+DzbYkRnL/SHvQ9hxkO4X7mJslw6DIWQJHM4xFZBN6y+7nntpYwxy1S7kfx38S7I9OLVWP+4a39R6x5QVULLM/n1a+aR4R3KhiA/XVoJheTyJY/fe5qVJuFYcwJIxo4Vj51tyiQ8T8PPePgqIYATI5
        name: cloudinitdisk
EOF
</code></pre>

<h3>VM起動 (3)</h3>

<p>VMイメージをPVCに置き、VM起動のたびPVC上のディスクデータをクローンして利用する方法。</p>

<h4>VMイメージの用意</h4>

<p>まず、コピー元となる PVC を作成する。
DataVolumeを作れば勝手に作られる。この際、<code>accessMode</code> を <code>ReadWriteMany</code> にしておくとクローンする際に便利そうと思いきや、
このソースとなるPVCはVMイメージそのままじゃないとダメっぽそう。</p>

<pre><code>kubectl virt -n staging image-upload dv jammy-03 \
    --size=3Gi \
    --uploadproxy-url=https://cdi-uploadproxy.cdi.svc.fraction.cluster \
    --insecure \
    --block-volume \
    --storage-class bronze \
    --access-mode ReadWriteOnce \
    --image-path=jammy-server-cloudimg-amd64.img
</code></pre>

<h4>VM作成</h4>

<p>先ほど作ったPVCをソースとして <code>DataVolume</code> を作成するように指定して VM を作成する。</p>

<pre><code>cat &lt;&lt;EOF | k apply -f -
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  generation: 1
  labels:
    kubevirt.io/os: linux
  name: ubuntu-03
spec:
  dataVolumeTemplates:
  - metadata:
      name: jammy-03-clone
    spec:
      pvc:
        accessModes:
        - ReadWriteOnce
        volumeMode: Block
        resources:
          requests:
            storage: 10Gi
        storageClassName: bronze
      source:
        pvc:
          namespace: staging
          name: jammy-03
  running: true
  template:
    metadata:
      labels:
        kubevirt.io/domain: ubuntu-03
    spec:
      domain:
        cpu:
          cores: 2
        devices:
          disks:
          - disk:
              bus: virtio
            name: disk0
          - cdrom:
              bus: sata
              readonly: true
            name: cloudinitdisk
        machine:
          type: q35
        resources:
          requests:
            memory: 1024M
      volumes:
      - dataVolume:
          name: jammy-03-clone
        name: disk0
      - cloudInitNoCloud:
          userData: |
            #cloud-config
            hostname: ubuntu-03
            ssh_pwauth: True
            disable_root: false
            ssh_authorized_keys:
            - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCS7YoDiqntTeWKAKCyfa8yylcMc3W7PN7wYEC1mkHEzDMVQzKSObVGiiIDfYfd50o37pptQ0PjAo2LaXTJLQseFfrwEZW8Jifv/8qDu75ef/jau9q3po7ReQebhQTNtgT2SQ1PTa5s0yEcbP1M1Eyp+lqj+TnYuLuqq5AG9UBizPCbO+dnvW8gRvZ0TuX+f2+DzbYkRnL/SHvQ9hxkO4X7mJslw6DIWQJHM4xFZBN6y+7nntpYwxy1S7kfx38S7I9OLVWP+4a39R6x5QVULLM/n1a+aR4R3KhiA/XVoJheTyJY/fe5qVJuFYcwJIxo4Vj51tyiQ8T8PPePgqIYATI5
        name: cloudinitdisk
EOF
</code></pre>

<p>上記のマニフェストをデプロイすると、まずプロビジョニングのフェーズが始まり、PVCの内容をCDIにアップロードするPodが起動する。</p>

<pre><code>➜ k get pod
NAME                                              READY   STATUS    RESTARTS   AGE
1ad4d7aa-055a-4aa3-9664-7ae90a6462ef-source-pod   1/1     Running   0          101s
cdi-upload-jammy-03-clone                         1/1     Running   0          2m6s
</code></pre>

<p>クローン元のPVCとクローン先のPVCを両方マウントしたPodが作られるのかなと思いきや、クローン元のPVCをマウントしたPodが作られ、内部で cdi のアップロードURLにイメージをhttpでアップロードしていた。
<code>VM起動 (2)</code> の方法よりも起動速くなるかと期待したがそうでもなさそう。</p>

<p>自前でVMイメージを保持したWebサーバを建てる必要がないくらいがメリットかな。</p>

<h2>デバッグ</h2>

<p>結構試行錯誤しないと動かない。最低でも <code>VirtualMachineInstance (VMI)</code> が作成されていれば、コンソールを確認することで処理が最低でもどこまで進んでることが確認できる。</p>

<pre><code>kubectl virt console ubuntu-01
</code></pre>

<p>ちなみに、ssh 接続は以下のコマンドでできたが、自分の環境だと <code>Pod</code> の IP Address に直接アクセスできるので、もしかしたら普通の環境では簡単に ssh できないかもしれない。</p>

<pre><code>kubectl virt ssh ubuntu@ubuntu-01 -i ~/.ssh/id_rsa
</code></pre>

<h2>感想</h2>

<p>起動自体は早いのだが、VMイメージを用意するのが意外と時間がかかるのでそこをどうにかしたい。</p>

<h2>追記 2022-09-29</h2>

<p>結局上記の方法を使うのをやめて、OSイメージが入ったPVCを用意した後は、それをクローンして使うことにしました。</p>

<p>こんな感じ。</p>

<pre><code>---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: node-root-disk
spec:
  storageClassName: "bronze"
  dataSource:
    name: jammy-server
    kind: PersistentVolumeClaim
  accessModes:
  - ReadWriteOnce
  volumeMode: Block
  resources:
    requests:
      storage: 10Gi
</code></pre>

    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>O. Yuanying</dc:creator>
    <dc:date>2022-09-18T14:50:00+09:00</dc:date>
  </item>
  <item rdf:about="https://www.fraction.jp/log/archives/2022/08/07/m2-macbook-air">
    <title>M2 MacBook Air を買った</title>
    <link>https://www.fraction.jp/log/archives/2022/08/07/m2-macbook-air</link>
    <description>前回買ったMacbookから五年ぶりのMacBookは、噂のAppleシリコン搭載 M2 MacBoook となった。とりあえず簡単にセットアップ。会社のPCもMacなので初回セットアップは大体最低限はすぐに完了する。System PreferenceSharingComputer Name を変更TracpadTap to Click をチェックAccessibilityPointer ControlTrackpad OptionsEnable dragging with drag lockDe...</description>
    <content:encoded><![CDATA[
        

<p class='image'>
<img  src='/log/archives/2022/08/07/IMG_0237.jpg'
      width='720' 
      height='540' 
      alt=''  />
</p>


<p><a href="/log/archives/2017/09/03/macbook">前回買ったMacbook</a>から五年ぶりのMacBookは、噂のAppleシリコン搭載 M2 MacBoook となった。</p>

<p>とりあえず簡単にセットアップ。会社のPCもMacなので初回セットアップは大体最低限はすぐに完了する。</p>

<h2>System Preference</h2>

<ul>
<li>Sharing

<ul>
<li>Computer Name を変更</li>
</ul>
</li>
<li>Tracpad

<ul>
<li>Tap to Click をチェック</li>
</ul>
</li>
<li>Accessibility

<ul>
<li>Pointer Control

<ul>
<li>Trackpad Options

<ul>
<li>Enable dragging with drag lock</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Desktop &amp; Screen Saver

<ul>
<li>壁紙を変更</li>
<li>https://twitter.com/tksn4tt/media</li>
</ul>
</li>
<li>Display

<ul>
<li>Resolution: Scaled

<ul>
<li>More Space</li>
</ul>
</li>
</ul>
</li>
<li>Keyboard

<ul>
<li>Key Repeat: Fast</li>
<li>Delay Until Repeat: Short</li>
</ul>
</li>
</ul>


<h2>Homebrew</h2>

<ul>
<li>https://brew.sh</li>
</ul>


<pre><code>/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' &gt;&gt; /Users/yuanying/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
brew install --cask iterm2
brew install --cask slack
brew install --cask visual-studio-code
brew install tmux
brew install nvim
brew install asdf
brew install jq
brew install ghq
echo 'export GHQ_ROOT=~/src' &gt;&gt; ~/.zprofile
</code></pre>

<h2>dotfiles</h2>

<pre><code>ghq get yuanying/dotfiles
cd ~/src/github.com/yuanying/dotfiles
bash bin/mac/setup-packages.sh
bash bin/setup-spaceship-prompt.sh
bash bin/setup.sh
</code></pre>

<p>以上で最低限の開発環境は構築できたが、大体開発はリモートのLinuxでやってるので mosh と nerd font さえインストールされていれば良い気もする。</p>

<h2>追加</h2>

<h3>開発環境</h3>

<h4>Ruby</h4>

<ul>
<li>https://github.com/asdf-vm/asdf-ruby</li>
<li>https://github.com/rbenv/ruby-build/wiki#suggested-build-environment</li>
</ul>


<pre><code class="bash">brew install openssl@1.1 readline libyaml
echo 'export RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl@1.1)"' &gt;&gt; ~/.zshrc.local
asdf plugin add ruby https://github.com/asdf-vm/asdf-ruby.git
asdf install ruby latest
asdf global ruby latest
</code></pre>

<h4>Java</h4>

<pre><code>asdf plugin-add java https://github.com/halcyon/asdf-java.git
asdf install java adoptopenjdk-11.0.16+8
asdf global java adoptopenjdk-11.0.16+8
echo '. ~/.asdf/plugins/java/set-java-home.zsh' &gt;&gt; ~/.zshrc.local
</code></pre>

    ]]></content:encoded>
    <dc:subject>Apple</dc:subject>
    <dc:creator>O. Yuanying</dc:creator>
    <dc:date>2022-08-07T19:17:00+09:00</dc:date>
  </item>
</rdf:RDF>
