2026年9月22日火曜日

Sonatype Nexus Repository を DGX Spark に構築する

1. 概要

DGX Spark 上に Sonatype Nexus Repository 3 を Docker で構築し、Maven / npm / Docker のキャッシュ(プロキシ)リポジトリを構築する。

目的

  • Maven Central、npm registry、Docker Hub、APT のプロキシ兼キャッシュを構築する
  • 社内やローカル環境から高速にパッケージを取得できるようにする

前提と確認済み事項

項目
ホスト gx10-6acc.local(DGX Spark)
OS / アーキテクチャ Ubuntu / aarch64 (ARM64)
Docker 29.2.1
Docker Compose v5.0.2
Nexus イメージ sonatype/nexus3:latest
ポート 18081(Nexus UI / API)、15000(Docker registry)
認証 匿名アクセス
データ置き場 当面はシステムディスク上の /mnt/repo/nexus-data、SSD 調達後に /mnt/repo を SSD にマウントし直して移行

docker.io からログアウトしておくこと。そうしないと上手く Nexus を利用してくれないっぽい

全体の流れ

  1. 事前準備(SSH 接続、ディレクトリと設定ファイルの作成)
  2. Nexus のデプロイ
  3. リポジトリ作成(Maven / npm / Docker / APT)
  4. クライアント設定
  5. 動作検証
  6. (後日)SSD 調達後の /mnt/repo 移行

2. 事前準備

2.1 SSH 接続

ssh gx10-6acc.local

2.2 作業ディレクトリの作成

Nexus の設定ファイルを置くディレクトリを作成する。

mkdir -p ~/project/nexus
cd ~/project/nexus

2.3 データディレクトリの作成

Nexus のデータを保存するディレクトリを作成する。

sudo mkdir -p /mnt/repo/nexus-data
sudo chown -R 200:200 /mnt/repo/nexus-data
  • 200:200 は Nexus コンテナ内の実行ユーザー(UID/GID 200)である。
  • Nexus がデータディレクトリに書き込めるよう、所有権を合わせる。
  • SSD 移行について: 現時点では /mnt/repo/nexus-data はシステムディスク上に作成する。
  • SSD 調達後は、このディレクトリを SSD にマウントし直すことで、データをそのまま移行できる(詳細は「7. SSD 調達後の移行」参照)。

3. Nexus のデプロイ

3.1 compose.yaml の作成

~/project/nexus/compose.yaml を作成する。

services:
  nexus:
    image: sonatype/nexus3:latest
    container_name: nexus
    restart: always
    ports:
      - "0.0.0.0:18081:8081"   # Nexus UI / API
      - "0.0.0.0:15000:15000"  # Docker registry
    volumes:
      - /mnt/repo/nexus-data:/nexus-data
    environment:
      - INSTALL4J_ADD_VM_PARAMS=-Xms1g -Xmx2g -XX:MaxDirectMemorySize=1g
  • INSTALL4J_ADD_VM_PARAMS で JVM ヒープを設定する。
  • DGX Spark は残メモリが約 4GB なので、ヒープ 2GB + ダイレクトメモリ 1GB の合計約 3GB から始める。
  • /mnt/repo/nexus-data が Nexus のデータディレクトリである。

3.2 Nexus の起動

cd ~/project/nexus
docker compose up -d

起動状態を確認する。

docker compose ps

3.3 起動完了の待機

Nexus は初回起動に数分かかる。 ログを確認して、起動完了を待つ。

docker compose logs -f

ログに Started Sonatype Nexus OSS が表示されたら起動完了である。

3.4 admin パスワードの取得

初回起動時、admin パスワードは自動生成され、コンテナ内のファイルに保存される。

docker exec nexus cat /nexus-data/admin.password

このパスワードを控えておく。

3.5 初期設定(Web UI)

ブラウザで http://gx10-6acc.local:18081 にアクセスし、以下を設定する。

  1. admin / 上記のパスワードでログイン
  2. 新しい admin パスワードを設定
  3. ウィザードが表示されるので、それに従って進める
    • 匿名アクセスを有効化(「Enable anonymous access」を選択)

※ 匿名アクセス: 認証なしでリポジトリを参照できるようにする。

4. リポジトリの作成

4.1 基本情報の定義

これから Web API でリポジトリを作成するため、以下の情報を定義しておく。

NEXUS_URL="http://gx10-6acc.local:18081"
NEXUS_USER="admin"
 NEXUS_PASS='<YOUR_ADMIN_PASSWORD>'
BLOB_STORE="default"
  • <YOUR_ADMIN_PASSWORD> は 3.5 で設定した admin パスワードに置き換える。

4.2 Maven プロキシリポジトリ

Web API で maven2 (proxy) 形式のリポジトリを作成する。

項目
Name maven-central
Remote storage https://repo1.maven.org/maven2/
Blob store default
その他 既定値のまま

プロキシリポジトリは、リモート(Maven Central)から取得したパッケージをローカルにキャッシュする。

実行コマンド

curl -u ${NEXUS_USER}:${NEXUS_PASS} -X POST \
  ${NEXUS_URL}/service/rest/v1/repositories/maven/proxy \
  -H "Content-Type: application/json" \
  -d '{
    "name": "maven-central",
    "online": true,
    "storage": { "blobStoreName": "default", "strictContentTypeValidation": true },
    "proxy": { "remoteUrl": "https://repo1.maven.org/maven2/", "contentMaxAge": 1440, "metadataMaxAge": 1440 },
    "negativeCache": { "enabled": true, "timeToLive": 1440 },
    "httpClient": { "blocked": false, "autoBlock": true },
    "maven": { "versionPolicy": "RELEASE", "layoutPolicy": "PERMISSIVE" }
  }'

4.3 npm プロキシリポジトリ

Web API で npm (proxy) 形式のリポジトリを作成する。

項目
Name npmjs
Remote storage https://registry.npmjs.org/
Blob store default
その他 既定値のまま

実行コマンド

curl -u ${NEXUS_USER}:${NEXUS_PASS} -X POST \
  ${NEXUS_URL}/service/rest/v1/repositories/npm/proxy \
  -H "Content-Type: application/json" \
  -d '{
    "name": "npmjs",
    "online": true,
    "storage": { "blobStoreName": "default", "strictContentTypeValidation": true },
    "proxy": { "remoteUrl": "https://registry.npmjs.org/", "contentMaxAge": 1440, "metadataMaxAge": 1440 },
    "negativeCache": { "enabled": true, "timeToLive": 1440 },
    "httpClient": { "blocked": false, "autoBlock": true }
  }'

4.4 Docker プロキシリポジトリ

Web API で docker (proxy) 形式のリポジトリを作成する。

項目
Name docker-hub
Remote storage https://registry-1.docker.io/
HTTP port 15000
HTTPS port (空のまま)
Blob store default
  • Docker プロキシは HTTP ポート 15000 で公開する。
  • ローカル(HTTP)で使うため、HTTPS ポートは設定しない。

実行コマンド

リポジトリ作成

curl -u ${NEXUS_USER}:${NEXUS_PASS} -X POST \
  ${NEXUS_URL}/service/rest/v1/repositories/docker/proxy \
  -H "Content-Type: application/json" \
  -d '{
    "name": "docker-hub",
    "online": true,
    "storage": { "blobStoreName": "default", "strictContentTypeValidation": true },
    "proxy": { "remoteUrl": "https://registry-1.docker.io/", "contentMaxAge": 1440, "metadataMaxAge": 1440 },
    "negativeCache": { "enabled": true, "timeToLive": 1440 },
    "httpClient": { "blocked": false, "autoBlock": true },
    "docker": { "v1Enabled": false, "forceBasicAuth": false, "httpPort": 15000 },
    "dockerProxy": { "indexType": "HUB", "cacheForeignLayers": false }
  }'
  • forceBasicAuth: 匿名アクセスを有効にする場合は false に設定する

匿名アクセス用 Realm を追加

curl -sS -u "${NEXUS_USER}:${NEXUS_PASS}" \
  "${NEXUS_URL}/service/rest/v1/security/realms/active" \
  | jq 'if index("DockerToken") then . else . + ["DockerToken"] end' \
  | curl -sS -u "${NEXUS_USER}:${NEXUS_PASS}" \
      -X PUT \
      -H 'Content-Type: application/json' \
      --data-binary @- \
      "${NEXUS_URL}/service/rest/v1/security/realms/active"

匿名ユーザーアクセスの有効化

匿名アクセスは 3.5 の初期設定ウィザードで既に有効化済みである。

ここでの API 呼び出しは、その設定を確認・確実にするためのもので、既に有効なら何も変わらない(冪等)。

curl -sS -u "${NEXUS_USER}:${NEXUS_PASS}" \
  -X PUT \
  -H 'Content-Type: application/json' \
  "${NEXUS_URL}/service/rest/v1/security/anonymous" \
  -d '{
    "enabled": true,
    "userId": "anonymous",
    "realmName": "NexusAuthorizingRealm"
  }'

4.5 APT プロキシリポジトリ

Ubuntu archive

curl -u ${NEXUS_USER}:${NEXUS_PASS} \
  -X POST \
  "${NEXUS_URL}/service/rest/v1/repositories/apt/proxy" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "ubuntu-archive",
    "online": true,
    "storage": {
      "blobStoreName": "'"${BLOB_STORE}"'",
      "strictContentTypeValidation": true
    },
    "proxy": {
      "remoteUrl": "http://archive.ubuntu.com/ubuntu/",
      "contentMaxAge": 1440,
      "metadataMaxAge": 60
    },
    "negativeCache": {
      "enabled": true,
      "timeToLive": 1440
    },
    "httpClient": {
      "blocked": false,
      "autoBlock": true
    },
    "apt": {
      "distribution": "",
      "flat": false,
      "enforceDistribution": false
    }
  }'

Ubuntu security

curl -u ${NEXUS_USER}:${NEXUS_PASS} \
  -X POST \
  "${NEXUS_URL}/service/rest/v1/repositories/apt/proxy" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "ubuntu-security",
    "online": true,

    "storage": {
      "blobStoreName": "'"${BLOB_STORE}"'",
      "strictContentTypeValidation": true
    },
    "proxy": {
      "remoteUrl": "http://security.ubuntu.com/ubuntu/",
      "contentMaxAge": 1440,
      "metadataMaxAge": 60
    },
    "negativeCache": {
      "enabled": true,
      "timeToLive": 1440
    },
    "httpClient": {
      "blocked": false,
      "autoBlock": true
    },
    "apt": {
      "distribution": "",
      "flat": false,
      "enforceDistribution": false
    }
  }'

4.6 作成後の確認

作成したリポジトリが一覧に表示されていることを確認する。

  • maven-central
  • npmjs
  • docker-hub
  • ubuntu-archive
  • ubuntu-security

リポジトリ一覧の確認

# 全リポジトリの一覧(名前・形式・タイプ・URL)
curl -u ${NEXUS_USER}:${NEXUS_PASS} \
  ${NEXUS_URL}/service/rest/v1/repositories

特定リポジトリの詳細確認

# Maven
curl -u ${NEXUS_USER}:${NEXUS_PASS} \
  ${NEXUS_URL}/service/rest/v1/repositories/maven/proxy/maven-central

# npm
curl -u ${NEXUS_USER}:${NEXUS_PASS} \
  ${NEXUS_URL}/service/rest/v1/repositories/npm/proxy/npmjs

# Docker
curl -u ${NEXUS_USER}:${NEXUS_PASS} \
  ${NEXUS_URL}/service/rest/v1/repositories/docker/proxy/docker-hub

# APT
curl -u ${NEXUS_USER}:${NEXUS_PASS} \
  ${NEXUS_URL}/service/rest/v1/repositories/apt/proxy/ubuntu-archive

# APT security
curl -u ${NEXUS_USER}:${NEXUS_PASS} \
  ${NEXUS_URL}/service/rest/v1/repositories/apt/proxy/ubuntu-security

キャッシュ状況の確認

取得後に、Nexus の blob store にキャッシュが溜まっているかを確認できる。

# 各リポジトリのキャッシュ状況(asset 数など)
curl -u ${NEXUS_USER}:${NEXUS_PASS} \
  ${NEXUS_URL}/service/rest/v1/repositories/maven/proxy/maven-central

5. クライアント設定

作成したリポジトリを各クライアントから参照するための設定である。

5.1 Maven

~/.m2/settings.xml にミラー設定を追加する。

<settings>
  <mirrors>
    <mirror>
      <id>nexus</id>
      <mirrorOf>*</mirrorOf>
      <url>http://gx10-6acc.local:18081/repository/maven-central/</url>
    </mirror>
  </mirrors>
</settings>

mirrorOf* にすると、すべての Maven リポジトリを Nexus 経由にする。

5.2 npm

npm のレジストリを Nexus に向ける。

npm config set registry http://gx10-6acc.local:18081/repository/npmjs/

確認:

npm config get registry

5.3 Docker

Docker は HTTP(非 TLS)のレジストリを使うため、daemon 側で insecure registry を許可する必要がある。

/etc/docker/daemon.json に以下を追加する。

{
  "registry-mirrors": ["http://gx10-6acc.local:15000"],
  "insecure-registries": ["gx10-6acc.local:15000"]
}
  • registry-mirrors は Docker Hub へのアクセスを Nexus 経由にするための設定である。
  • insecure-registries は HTTP(非 TLS)でのアクセスを許可するための設定である。

Docker を再起動する。

sudo systemctl restart docker

5.4 APT

既存の sources をバックアップ。

sudo cp \
  /etc/apt/sources.list.d/ubuntu.sources \
  /etc/apt/sources.list.d/ubuntu.sources.bak

Nexus 用に sources を更新。

sudo tee /etc/apt/sources.list.d/ubuntu.sources >/dev/null <<'EOF'
Types: deb
URIs: http://gx10-6acc.local:18081/repository/ubuntu-archive/
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

Types: deb
URIs: http://gx10-6acc.local:18081/repository/ubuntu-security/
Suites: noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
EOF

6. 動作検証

各リポジトリが正しくキャッシュできるか確認する。

6.1 Maven

mvn dependency:get -Dartifact=junit:junit:4.13.2

Nexus の Web UI で maven-central リポジトリに junit がキャッシュされていることを確認する。

6.2 npm

npm view lodash version

Nexus の Web UI で npmjs リポジトリに lodash がキャッシュされていることを確認する。

6.3 Docker

5.3 で設定した registry-mirrors により、Docker Hub への pull は Nexus 経由になる。そのため、通常の docker pull alpine で検証できる。

docker pull alpine

Nexus の Web UI で docker-hub リポジトリに alpine がキャッシュされていることを確認する。

registry-mirrors 経由では、docker pull alpine は自動的に Docker Hub の library/alpine として解決される。library/ プレフィックスを明示する必要はない。

6.4 APT

apt update で Nexus 経由でのパッケージリスト取得を確認する。

sudo apt update

これで一通りのキャシュ検証完了。以上。

2026年9月14日月曜日

michaelfeil/infinity を DGX Spark 向け ARM64 CUDA イメージとしてビルドする

michaelfeil/infinity の ARM64 CUDA イメージのビルド、動作確認を行ったのでその作業を記録する。

なお、この作業記録は Codex が自身の作業を記録したものである。

なお成果物は mikoto2000/infinity at for_dgx_spark に置いた。

目的

DGX Spark 上で、 Infinity を使いたかったので、 ARM 用の Docker イメージを作成したい。

実行環境

  • 作業日: 2026-09-14
  • リポジトリ: Windows F:\project\infinity / WSL /mnt/f/project/infinity
  • WSL2: Ubuntu-24.04、ユーザー mikoto、ホスト linux/amd64
  • ホスト Docker Engine: 29.6.1
  • DinD 内 Docker Engine: 29.8.0
  • 専用 DinD コンテナ: infinity-spark-dind
  • Docker データボリューム: infinity-spark-dind-data
  • ソースは DinD の /workspace に読み取り専用でマウントする。
  • PowerShell のサンドボックスから通常権限では WSL の列挙が Wsl/EnumerateDistros/Service/E_ACCESSDENIED になったが、昇格実行で利用可能と確認した。 前の回答の「Docker がない」は Windows の PATH の確認に限られていた。

参照した仕様

DinD の準備 (WSL2 のシェルで実行)

cd /mnt/f/project/infinity
docker run -d --name infinity-spark-dind --privileged \
  --mount type=volume,source=infinity-spark-dind-data,target=/var/lib/docker \
  --mount type=bind,source=/mnt/f/project/infinity,target=/workspace,readonly \
  --workdir /workspace \
  docker:29-dind dockerd --host=unix:///var/run/docker.sock
docker exec infinity-spark-dind docker info
docker exec infinity-spark-dind docker run --privileged --rm tonistiigi/binfmt --install arm64
docker exec infinity-spark-dind docker buildx create \
  --name infinity-spark-builder --driver docker-container --use --bootstrap
docker exec infinity-spark-dind docker pull --platform linux/arm64 nvcr.io/nvidia/pytorch:25.11-py3

進行状況

  • DinD の作成と ARM64 エミュレーション確認: 成功。
  • default ビルダー (docker driver / BuildKit v0.33.0) も linux/arm64 に対応していた。 以降は取得済みイメージを共有できる --builder default を使用する。
  • Poetry エクスポートと infinity_emb-0.0.77-py3-none-any.whl 作成: 成功。
  • CPU 用の既存テスト4件、Spark 依存関係変換2件、環境分離2件: WSL2 上で合計8件成功。
  • ARM64 依存インストール: 成功 (309.9 秒)。
  • Infinity 本体 0.0.77 のインストール: 成功。
  • 環境分離後の python -m pip check: No broken requirements found. (終了コード0)。
  • ARM64 スモークテスト: 成功 (初回の読み込みを含め 824.1 秒)。 torch の CPU 演算、torchvision の NMS、小規模 BERT forward、Infinity サーバー構築を確認。
  • infinity_emb v2 --help: 成功 (終了コード0)。
  • ARM64 production イメージ: ビルド・DinD 内への保存に成功 (終了コード0)

追加した構成

  • libs/infinity_emb/Dockerfile.spark: Spark 専用。既存の Jinja 生成対象とは別に直接編集する。
  • libs/infinity_emb/docker_spark_prepare.py: GPU 関連パッケージを NVIDIA ベースのバージョンで制約し、その他は既存 Poetry ロックを使用する。
  • libs/infinity_emb/docker_spark_isolate.py: インストール後、アプリ依存と NVIDIA の torch/torchvision/Triton のみを仮想環境から参照できるようにする。
  • libs/infinity_emb/tests/docker/smoke_spark.py: GPU 不要の import、CPU テンソル演算、小規模 BERT forward、サーバー構築確認。
  • libs/infinity_emb/tests/docker/test_spark_prepare.py: NVIDIA パッケージの保持と不適切なベースイメージの拒否を検証。

依存エクスポート、ARM64 wheel のダウンロード、純 Python wheel 作成は BUILDPLATFORM (amd64) で実行し、production の依存インストールと 動作確認は ARM64/QEMU で実行する。 ダウンロードは Python 3.12 / CPython / ARM64 の manylinux タグを明示する。 GPUtil のみソース配布なので、ネイティブ側で py3-none-any wheel を作る。 wheelhouse はビルド時だけマウントし、最終イメージには含めない。 Python 仮想環境 /opt/infinity はインストール時のみ NVIDIA ベースの system site-packages を参照する。 venv --without-pip --system-site-packagespython -m pip を組み合わせ、 ベース付属の pip 25.3 を利用する。通常の venv は QEMU 上の ensurepip に 3分以上かかったため、初回ビルドを中断してこの方式に変更した。 変更後の仮想環境作成は 3.4 秒で完了した。初回ログは spark-build-initial.log。 次の試行では QEMU 内のオンライン pip 解決・インストールに時間がかかり、 1,328 秒時点でもインストール途中だったため中断した (spark-build-online-install.log)。 ARM64 wheel をネイティブ側で事前取得する処理は 23.2 秒で成功した。 ARM64 側では --no-index --find-links /wheelhouse --no-compile を使用する。 バイトコードの事前生成を省くだけで、ARM64 のネイティブ拡張インストールと実行確認は省略しない。 インストール後、必要なベース由来の Python パッケージと dist-info を仮想環境へ シンボリックリンクし、include-system-site-packages = false にする。 これにより NVIDIA の CUDA バイナリを再利用しつつ、NGC の学習・開発用ツールが アプリの依存解決に混ざることを防ぐ。ベースの実ファイルは削除・変更しない。 追加の x86_64 用 Flash Attention wheel はインストールしない。 既定エンジンは torch、BetterTransformer は無効とする。

ビルドコマンド (WSL2)

set -o pipefail
docker exec infinity-spark-dind docker buildx build \
  --builder default --platform linux/arm64 --target production \
  --progress plain -f libs/infinity_emb/Dockerfile.spark \
  -t infinity:spark-arm64 --load libs/infinity_emb \
  2>&1 | tee /mnt/f/project/infinity/spark-build.log

ログは spark-build.logspark-requirements-build.log に保存する (*.log は Git 管理対象外)。 イメージは DinD 内 にロードされる。ホストの docker images には現れない。

取得した NVIDIA ベースのマニフェスト:

  • マルチアーキテクチャ: sha256:417cbf33f87b5378849df37983552cd1f8bc8b62fe1ceabe004de816a55dff21
  • ARM64: sha256:4a85d8cf6fb3a943280960b8948cf4e9b6eca77b4414c68c9b2c7bb863f79b70

厳密に同じベースを使用する場合は、ビルドに以下を追加する。

--build-arg SPARK_BASE_IMAGE=nvcr.io/nvidia/pytorch:25.11-py3@sha256:4a85d8cf6fb3a943280960b8948cf4e9b6eca77b4414c68c9b2c7bb863f79b70

ビルド後の確認コマンド (WSL2)

docker exec infinity-spark-dind docker image inspect infinity:spark-arm64 \
  --format '{{.Os}}/{{.Architecture}} {{.Id}}'
docker exec infinity-spark-dind docker run --rm --platform linux/arm64 \
  --entrypoint python infinity:spark-arm64 /app/smoke_spark.py
docker exec infinity-spark-dind docker run --rm --platform linux/arm64 \
  infinity:spark-arm64 v2 --help

DGX Spark への転送・GPU 確認の手順

WSL2 側でイメージをエクスポートし、gzip 圧縮した tar を Spark に転送する。 大きいイメージなので、出力先の空き容量を確保する。

set -o pipefail
docker exec infinity-spark-dind docker save infinity:spark-arm64 | gzip -1 > infinity.tar.gz

Spark 上で実行する。

docker load -i infinity.tar.gz
docker run --rm --gpus all --entrypoint python infinity:spark-arm64 \
  -c 'import torch; print(torch.__version__, torch.version.cuda); print(torch.cuda.get_device_name(0)); x = torch.ones(4, device="cuda"); print(x + 1)'
docker run --rm --gpus all -p 7997:7997 \
  -v infinity-spark-models:/app/.cache/huggingface \
  infinity:spark-arm64 v2 --model-id BAAI/bge-small-en-v1.5 --engine torch --device cuda --port 7997

最後のコマンドはモデルをダウンロードして API を起動する。 GPU 演算・実モデル推論・性能・Flash Attention・TensorRT・CTranslate2 の GPU 実行は今回の確認範囲に含まれない。

ベースイメージの依存チェック

docker exec infinity-spark-dind docker run --rm --platform linux/arm64 \
  --entrypoint python nvcr.io/nvidia/pytorch:25.11-py3 -m pip check

Infinity 追加前から以下の1件が存在した (終了コード1)。

nvidia-resiliency-ext 0.4.1+cuda13 requires pynvml, which is not installed.

ログ: spark-base-pip-check.log

さらに pip inspect のメタデータ (spark-base-inspect.log) とアプリのロックを比較し、 以下の衝突を確認した。これらの開発用パッケージをアプリ環境から分離する理由となった。

ベース内のパッケージ 要求 アプリのロック
tyro typing-extensions >= 4.13 4.12.2
typeguard typing-extensions >= 4.14 4.12.2
torchtitan datasets >= 2.21 2.14.4
lightning-thunder networkx >= 3.3 3.2.1
lightning-thunder dill >= 0.3.8 0.3.7

実インストール時には、NGC の httpcore 1.0.9 とアプリ側の h11 0.14.0 の 衝突も検出された。httpcore はアプリの実行依存ではないため、同じ分離処理で解消した。 一時的に system site-packages を共有しているインストール途中のログにはこれらの 依存警告が出るが、最終環境では pip check により不整合がないことを確認した。

GPU スタックを新しい PyPI torch で置き換えたり、アプリのロック全体を更新する処理は行わない。

ARM64 スモークテストの実測結果

architecture: aarch64
torch: 2.10.0a0+b558c986e8.nv25.11
torchvision: 0.25.0a0+7a13ad0f
cuda_build: 13.0
sentence_transformers: 3.3.1
onnxruntime: 1.19.2
gpu_execution_tested: False

torch.backends.cuda.is_built() も真であることを確認した。 GPU を必要とする演算は実行していない。

初回スモークテストは QEMU 上で多数のモジュールを読み込むため約14分かかった。 途中で生成される .pyc とロード済み ARM64 共有ライブラリを確認し、停止ではなく 初期化が進行していることを確認した。 既存コードの torch.backends.*.allow_tf32 に PyTorch 2.10 の非推奨警告が出たが、 演算とスモークテストは成功した。TF32 設定 API の更新は今回の変更に含めない。 スモークテストと別プロセスでの CLI ヘルプ確認を合わせたステージは 1,145.3 秒だった。 この実測は amd64 上の QEMU であり、DGX Spark ネイティブ実行の性能を表すものではない。

完成したイメージ

  • タグ: infinity:spark-arm64
  • 保存先: WSL2 の infinity-spark-dind コンテナ内の Docker
  • OS / Architecture: linux/arm64 (docker image inspect で確認)
  • Docker が報告したイメージ ID: sha256:578dd3cebf7b75a06cf017428ec55ecfdc1534b66dc2d153e66c57819c3cde1c
  • ARM64 イメージマニフェスト: sha256:94b2018bb094a357afe043e0ab415ba7f78b303ebb13d3c2232185af66ea0199
  • docker image inspect の Size: 9725644356 bytes
  • 作成時刻: 2026-09-14T08:42:26.723536977Z (日本時間 17:42)
  • ENTRYPOINT: ["infinity_emb"]
  • インストール一覧: イメージ内 /app/installed-packages.txt。 完成イメージから取り出した一覧を spark-installed-packages.log に保存した。 pip freeze 出力なので、NVIDIA パッケージにはビルド元のローカル wheel URL が含まれる。 再インストール用 requirements としてではなく構成記録として使用する。

ビルド中にスモークテストと CLI の成功を確認したため、完成後に同じ重いテストは 繰り返さず、イメージ属性と保存済みパッケージ一覧を確認した。

DinD コンテナと専用データボリュームは再利用できるよう残している。 WSL2 再起動などで停止した場合は、以下で再開できる。

docker start infinity-spark-dind
docker exec infinity-spark-dind docker image inspect infinity:spark-arm64

Docker Hub / NGC などへのイメージ push は行っていない。Spark への転送結果は末尾に記載。

gzip 圧縮したイメージの保存 (2026-09-14 実施済み)

ユーザーの指定により infinity:spark-arm64docker save で書き出した。 WSL2 の Bash 内で以下を実行し、Windows PowerShell のバイナリパイプを経由せずに保存した。

set -o pipefail
set -o noclobber
docker exec infinity-spark-dind docker save infinity:spark-arm64 \
  | gzip -1 > /mnt/f/project/infinity/infinity.tar.gz.partial
gzip -t /mnt/f/project/infinity/infinity.tar.gz.partial

両コマンドの終了コードが 0 であることを確認後、一時ファイルを同じディレクトリの infinity.tar.gz にリネームした。

  • 保存先: F:\project\infinity\infinity.tar.gz
  • ファイルサイズ: 9679762352 bytes (約 9.68 GB / 9.02 GiB)
  • 検証: gzip -t による圧縮ファイル全体の整合性チェック成功
  • docker save 単体の出力は .tar。今回は gzip 圧縮したため、指定の .tar.gz が正しい。
  • Spark での読み込み: docker load -i infinity.tar.gz

DGX Spark への SCP 転送 (2026-09-14 実施済み)

ユーザーの指定により Windows OpenSSH の scp で転送した。

scp -o BatchMode=yes -o ConnectTimeout=15 F:/project/infinity/infinity.tar.gz mikoto@gx10-707e.local:~/infinity.tar.gz
ssh -o BatchMode=yes -o ConnectTimeout=15 mikoto@gx10-707e.local 'stat -c "%s %n" ~/infinity.tar.gz'

scp は終了コード 0。転送先 /home/mikoto/infinity.tar.gz のサイズが 9679762352 bytes であり、転送元と一致することを確認した。

(筆者追記)DGX Spark での GPU 実行確認

Codex にイメージファイルの転送までしてもらったので、それ以降の動作確認は自分で行った。

DGX Spark 上で、gzip 圧縮したイメージをロードし、GPU を使用して Infinity サーバーを起動した。

docker load -i ./infinity.tar.gz
docker run -it --rm \
    --gpus all \
    -p 8081:8081 \
    -v models:/app/.cache 
    infinity:spark-arm64 v2 \
        --model-id BAAI/bge-m3 \
        --model-id BAAI/bge-reranker-v2-m3 \
        --engine torch \
        --device cuda \
        --port 8081

curl でモデル一覧取得 API を呼び出し、サーバーが正しく起動していることを確認した。

curl http://localhost:8081/models

以上。

参考資料

2026年9月12日土曜日

自宅ネットワークを構築する

スイッチングハブが届いたので、自宅ネットワークの構築を始めた。

目標の構成

目標の構成は次のとおり。

BUFFALO WSR-1500AX2S-BK の動作確認

押し入れから引っ張り出してきた BUFFALO WSR-1500AX2S-BK の動作確認から始める。

モード切替スイッチをルーターモードにして電源を接続し、リセットボタンを押す。

ルーター本体に書いてある SSID とパスワードで、ルーターの LAN に接続する。

ルーターの初期設定

http://192.168.11.1/login.html にアクセスし、本体に記載のユーザー名とパスワードでログインする。

設定画面 から 詳細設定 を開く。

Internet の設定

まず Internet の設定をする。

今回は宅内 LAN の増設なので、DHCP を有効にするだけでよい。

LAN の設定

次に LAN の設定をする。

デフォルトからの変更点は次のとおりだ。

  • LAN側IPアドレス: 192.168.2.1

ルーターの IP アドレスと LAN のネットワークアドレスが変わったので、一度 PC の Wi-Fi を OFF にして接続し直す。

http://192.168.2.1/login.html にアクセスし、もう一度詳細設定画面を開く。

無線の設定

バンドステアリング Lite を OFF にする。

2.4 GHz 帯を OFF にする。

5 GHz 帯の設定をする。

各マシンのネットワーク設定

母艦 PC はもともと DHCP なので、物理的につなげるだけでよい。

gx10-707e.local の設定

有線の定義を /etc/netplan/50-ether.yaml に書く。

network:
  version: 2
  ethernets:
    enP7s7:
      dhcp4: false
      addresses:
        - 192.168.2.254/24
      routes:
        - to: default
          via: 192.168.2.1
      nameservers:

無線を無効化する。

sudo nmtui を実行し、接続の編集 から対象の SSID を選び、自動的に接続する のチェックを外す。

gx10-6acc.local の設定

有線の定義を /etc/netplan/50-ether.yaml に書く。

network:
  version: 2
  ethernets:
    enP7s7:
      dhcp4: false
      addresses:
        - 192.168.2.253/24
      routes:
        - to: default
          via: 192.168.2.1
      nameservers:

無線を無効化する。

sudo nmtui を実行し、接続の編集 から対象の SSID を選び、自動的に接続する のチェックを外す。

申し送り事項

ネットワーク構成の更新は完了した。

申し送り事項は次のとおりだ。

  1. 母艦 PC のケーブルが 2.5G
    • 調達した 10G ケーブルの長さが足りませんでした…
  2. 無線ガジェットはまだ以前の SSID に接続中
    • 設定が面倒なので必要に応じて徐々に移行
  3. 何か使っているルーターの調子が悪い
    • もしかしたら買い替え必要かも?

「3.」以外は明日以降、順次解消していく。

参考資料