ラベル Ubuntu の投稿を表示しています。 すべての投稿を表示
ラベル Ubuntu の投稿を表示しています。 すべての投稿を表示

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月12日土曜日

DGX Spark をデジタルフォトフレーム化する

DGX Spark(GX10)にデジタルフォトフレーム機能を追加する作業を記録をまとめた。

結果だけ見たい方は mikoto2000/dgx-spark-photoframe: GUI 無しの DGX Spark をデジタルフォトフレーム化するモノ を参照。

目標

まず目標の確認から。

  1. 15 秒ごとに photo ディレクトリ以下の画像をディスプレイに表示したい
  2. 表示はフルスクリーンで
  3. X, Wayland は使いたくない
  4. 電源 ON にしたら自動でフォトフレーム機能を実行してほしい

現状確認

次は現状確認。

現状はこのブログの手順を実行した直後という感じ。

  • https://mikoto2000.blogspot.com/2026/09/dgx-sparkasus-ascent-gx10.html
  • https://mikoto2000.blogspot.com/2026/09/dgx-sparkasus-ascent-gx102-deepseek-v4.html

USB-C モニタが認識されない問題に遭遇

USB-C のモニタを接続したが、認識してくれない。原因を調査していく。

この不具合に当たっている。

対応策は sudo apt purge nvidia-drm-options-modeset0 らしい。試してみる。

モニタの回路から完全に電源を抜かなければならない。

sudo apt purge nvidia-drm-options-modeset0 してから poweroff して、GX10 の電源ケーブルを抜いて 1 分程度待ってから電源をつける。

無事モニタが認識され、ログインプロンプトが表示された。

フォトフレーム機能の実装

なんかファイルを作ってばかりで進捗をツイートしにくい。

デジタルフォトフレーム機能のための Dockerfile と compose.yaml を作っている。

Dockerfile

まず Dockerfile はこれ。シンプルに mpv だけインストールした Ubuntu 環境。

FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
       mpv \
    && rm -rf /var/lib/apt/lists/*

COPY entrypoint.sh /entrypoint.sh

RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh

次は entrypoint.sh。指定された写真フォルダ内の画像を再帰的に探して mpv に渡す。

#!/bin/bash

# コマンド実行時にエラーが発生したら即終了する。
# 未定義変数の参照や、パイプ途中のコマンド失敗もエラーとして扱う。
set -euo pipefail

# /photos 配下から表示対象の画像ファイルを検索し、
# mpv 用のプレイリストを生成する。
#
# 対象形式:
# - JPEG
# - PNG
# - WebP
#
# ファイル名に空白などが含まれていても扱えるよう、
# find では NULL 区切りで取得している。
find /photos \
  -type f \
  \( \
    -iname '*.jpg' -o \
    -iname '*.jpeg' -o \
    -iname '*.png' -o \
    -iname '*.webp' \
  \) \
  -print0 \
  | sort -z \
  | tr '\0' '\n' \
  > /tmp/photos.m3u

# 表示対象の画像が1枚も存在しない場合は、
# mpv を起動せずエラー終了する。
if [ ! -s /tmp/photos.m3u ]; then
  echo "表示可能な画像が /photos に存在しません。" >&2
  exit 1
fi

# mpv を起動し、DRM/KMS を利用して
# デスクトップ環境を介さずモニタへ直接描画する。
#
# DRM_CONNECTOR:
#   使用する DRM connector。
#   例: DP-1
#   未指定の場合は DP-1。
#
# PHOTO_DURATION:
#   1枚の写真を表示する秒数。
#   未指定の場合は15秒。
#
# PHOTO_ROTATE:
#   写真を回転して表示する角度。
#   0 / 90 / 180 / 270 を想定。
#   未指定の場合は0度。
#
# --loop-playlist=inf:
#   最後の写真まで表示したら先頭へ戻り、
#   スライドショーを無限に繰り返す。
#
# exec を使用することで mpv 自体をコンテナの PID 1 とし、
# docker stop などのシグナルを直接受信できるようにする。
exec mpv \
  --vo=drm \
  --drm-device="${DRM_DEVICE:-/dev/dri/card1}" \
  --drm-connector="${DRM_CONNECTOR:-DP-1}" \
  --drm-mode=preferred \
  --video-rotate="${PHOTO_ROTATE:-0}" \
  --profile=sw-fast \
  --fullscreen \
  --no-audio \
  --image-display-duration="${PHOTO_DURATION:-15}" \
  --loop-playlist=inf \
  --playlist=/tmp/photos.m3u

compose.yaml

次は compose.yaml。.env を読み込んでさっきの Dockerfile のイメージを起動する。

services:
  photoframe:
    build:
      context: .

    container_name: dgx-spark-digital-photoframe

    restart: unless-stopped

    devices:
      - "${DRM_DEVICE:-/dev/dri/card1}:${DRM_DEVICE:-/dev/dri/card1}"

    volumes:
      - "${PHOTO_DIR:-./photos}:/photos:ro"

    environment:
      DRM_DEVICE: "${DRM_DEVICE:-/dev/dri/card1}"
      DRM_CONNECTOR: "${DRM_CONNECTOR:-DP-1}"
      PHOTO_DURATION: "${PHOTO_DURATION:-15}"
      PHOTO_ROTATE: "${PHOTO_ROTATE:-0}"

.env(.env.example)

最後に .env(.env.example)。

写真ディレクトリ、出力デバイス、表示秒数、回転角度を指定できる。

# ホスト側の写真ディレクトリ。
# コンテナ内では /photos として読み取り専用でマウントされる。
PHOTO_DIR=~/photos

# 使用する DRM デバイス。
#
# 利用可能な DRM デバイスは、ホスト側で次のコマンドを実行して確認できる。
#
#   ls -l /dev/dri/
#
# 例:
#   card1
#   renderD128
#
# この場合は /dev/dri/card1 を指定する。
DRM_DEVICE=/dev/dri/card1

# 使用する DRM connector。
#
# 接続中の connector は、ホスト側で次のコマンドを実行して確認できる。
#
#   grep -H connected /sys/class/drm/card*-*/status
#
# 例:
#   /sys/class/drm/card0-DP-1/status: connected
#   /sys/class/drm/card0-DP-2/status: disconnected
#
# この場合は DP-1 を指定する。
DRM_CONNECTOR=Unknown-4

# 1枚の写真を表示する時間(秒)。
PHOTO_DURATION=15

# 写真の回転角度。
# 0 / 90 / 180 / 270 を指定する。
# モニタを縦置きする場合は 90 または 270 を指定する。
PHOTO_ROTATE=0

起動

これで .env を書いて docker compose up -d すればモニタに画像が映る。

参考資料

2026年9月9日水曜日

DGX Spark(ASUS ASCENT GX10)2 台連結と DeepSeek V4 Flash Vision Exp セットアップ記録

前回 は 1 台のセットアップを記録した。

今回は 2 台を QSFP ケーブルで連結し、DeepSeek V4 Flash Vision Exp を動かすまでの手順を記録した。

1. QSFP ケーブルでの 2 台接続

まず QSFP ケーブルで 2 台の GX10 を接続する。

QSFP ケーブルを差すときは、有線 LAN ポートに近い方のポートを使う。 そうすると、後で行うインターフェースが、この記事で書かれているものと同じになるはず。

2. インターフェースの特定と固定 IP の割り当て

接続後、ibdev2netdev コマンドでインターフェースを確認する。

ibdev2netdev

以下のような出力になる。

roceP2p1s0f0 port 1 ==> enP2p1s0f0np0 (Up)
roceP2p1s0f1 port 1 ==> enP2p1s0f1np1 (Down)
rocep1s0f0 port 1 ==> enp1s0f0np0 (Up)
rocep1s0f1 port 1 ==> enp1s0f1np1 (Down)

Up となっているインターフェースに、それぞれ固定 IP を割り当てる。

GX10-1 では /etc/netplan/40-cx7.yaml を編集する。

network:
  version: 2
  ethernets:
    enp1s0f1np1:
      addresses:
        - 192.168.100.10/24
      dhcp4: no
    enP2p1s0f1np1:
      addresses:
        - 192.168.101.10/24
      dhcp4: no

GX10-2 では同じファイルに以下の内容を書く。

network:
  version: 2
  ethernets:
    enp1s0f1np1:
      addresses:
        - 192.168.100.11/24
      dhcp4: no
    enP2p1s0f1np1:
      addresses:
        - 192.168.101.11/24
      dhcp4: no

両方で設定を適用する。

sudo chmod 600 /etc/netplan/40-cx7.yaml
sudo netplan apply

ip addr show で、設定が正しく適用されていることを確認する。

3. パスワードレス SSH の設定

ノード間でパスワードレス SSH を行うための設定をする。

両方の GX10 でキーペアを作成する。

ssh-keygen -t ed25519

お互いに公開鍵を送信し合う。

GX10-1 から GX10-2 へ公開鍵を送信する。

ssh-copy-id 192.168.100.11

GX10-2 から GX10-1 へ公開鍵を送信する。

ssh-copy-id 192.168.100.10

ノード間通信を確実に QSFP ケーブルで行うため、hosts ファイルにエントリを追加する。

192.168.100.10 gx10-1
192.168.100.11 gx10-2

ここまでで連携の土台が完成する。

4. DeepSeek V4 Flash レシピの適用

次に、DeepSeek-v4-Flash-DSpark-2x-DGX-Spark のレシピを適用する。

GX10-1 で作業を開始する。

mkdir -p ~/project
cd project
git clone https://github.com/MiaAI-Lab/DeepSeek-v4-Flash-DSpark-2x-DGX-Spark.git

env ファイルを用意する。

cd DeepSeek-v4-Flash-DSpark-2x-DGX-Spark
cp .env.dspark.example .env.dspark
vim .env.dspark

以下を指定する。

WORKER_HOST=192.168.100.11
MASTER_ADDR=192.168.100.10

VLLM_HOST_IP=192.168.100.10
WORKER_VLLM_HOST_IP=192.168.100.11

GX10-2 のユーザーを docker グループに入れ忘れていたのが発覚したのでここで入れる。

sudo usermod -aG docker <ユーザー名>

両方の GX10 で Docker イメージを pull する。

docker pull ghcr.io/anemll/dspark-vllm-gx10:0.1.1

次にモデルをダウンロードする。GX10-1 で以下を実行する。

./prepare-dspark-model-cache.sh --official

しばらく待つと、GX10-1 と GX10-2 の両方にモデルがダウンロードされる。

5. サーバー起動と動作確認

これで準備が整ったので、サーバーを起動する。

./start-deepseek-v4-flash-dspark.sh

boot-shape-warmup: 47/47 requests ok in 77s のような文字列が表示され、コマンド実行が終われば起動完了である。

docker ps でコンテナが動いていることを確認する。

最後に、curl コマンドでチャットのエンドポイントにリクエストを送り、動作確認する。

ポート 8888 で待ち受けているので、以下のコマンドでリクエストを投げる。

curl http://localhost:8888/v1/chat/completions \
    -H 'Content-Type: application/json' \
    -d '{"model":"deepseek-v4-flash-vision-exp","messages":[{"role":"user","content":"Design a small rate"}]}'

応答の JSON が返却されれば OK.

あとは OpenAI 互換の好きなクライアントで接続すれば良い。

以上。

参考資料

2026年9月8日火曜日

DGX Spark(ASUS ASCENT GX10)開封とセットアップ作業記録

DGX Spark は、日本では ASUS ASCENT GX10 として入手できる。 今回はその開封からセットアップまでを、トラブルも含めて記録した。

1. 開封と初回セットアップ

まずはコンセントにつないで電源を入れる。

GX10 はヘッドレスセットアップ(モニタやキーボードを接続せずに設定する方式)に対応しているので、モニタやキーボードは不要。 タブレットから接続して進める。

  1. タブレットで GX10-xxxx というネットワークに接続
    • 正確な SSID とパスワードは説明書に記載されている
  2. 同じくタブレットで http://gx10-xxxx.local に接続
    • この URL も説明書に記載されている
  3. ブラウザで GX10 のウェルカムページが開くので Get Started を押す
  4. 言語とタイムゾーンを設定して Continue
  5. 利用規約を読んで 承認
  6. ユーザー名とパスワードを設定して 続行
  7. 情報収集に協力するかどうかの選択
  8. GX10 が使用する Wi-Fi ネットワークを設定
  9. Wi-Fi につながると更新が始まる。ファームウェア更新も含まれるため、完了まで時間がかかる
  10. 更新が終わったらセットアップ完了。開始する を押す

これでセットアップは完了。あとは GX10 の IP アドレスを確認して SSH 接続するだけ。 IP アドレスは、GX10 に接続できる端末から ip addr を実行するか、ルーターの管理画面で確認できる。

2. カーネルパニック発生

セットアップが終わったと思ったら、カーネルパニック(OS が致命的なエラーで停止する状態)が発生した。

SSH が繋がらないため、電源長押しで強制終了し、再起動したのが原因だと思われる。 多分まだ裏でアップデート処理が走っていた…。

復旧するため、NVIDIA 公式の System Recovery ガイド を見ながら進める。リカバリイメージは NVIDIA のダウンロードページ から入手できる。

  1. リカバリイメージをダウンロードして展開
  2. リカバリ USB 作成スクリプト(CreateUSBKey.cmd)を実行し、リカバリ USB として使う USB デバイスを選択
    • 選択を誤るとデータが失われるため注意が必要だ
  3. リカバリ USB を DGX Spark に接続し、起動直後から DEL か INS を連打して UEFI 画面を開く
    • UEFI 画面を開くのは DEL キーだったようだ
  4. UEFI で Boot > DGX OS Recovery > Boot to this USB drive と選択
  5. Quit without saving? と聞かれるので Yes を選択
  6. しばらく待つとリカバリが終わるので再起動

リカバリ後、ホスト名が spark-xxx に変わっていた。

3. 再セットアップとサーバー化

再起動したら、スレッドの最初からもう一度セットアップをやり直す。

リカバリ後の OS では、ホスト名で SSH 接続できる。

SSH 接続後、中身は Ubuntu ベースなのでいつものアップグレードを実行。

sudo apt update
sudo apt upgrade

サーバーとして使うので、固定 IP を設定する。

sudo vim /etc/netplan/90-xxx.yaml

GUI はいらないので multi-user モードに変更。

sudo systemctl set-default multi-user.target

パスワード無しで SSH ログインできるように公開鍵を配置。

mkdir .ssh
chmod 700 .ssh
cd .ssh
vim authorized_keys
chmod 600 authorized_keys

GPU が熱暴走しないように、動作周波数を少し下げる。

sudo nvidia-smi -lgc 0,2200

ここまでやったら一度リブート。

sudo reboot

4. GPU を認識しなくなった

リブートしたら、GPU を認識しなくなった。

原因は、upgrade ではなく full-upgrade を実行しなかったことだ。 full-upgrade は依存関係の変更も反映するため、upgrade ではカーネル更新が GPU ドライバと整合しなかった。

sudo apt update
sudo apt full-upgrade

5. 工場出荷状態に戻して再インストール

ここで「GX10 用の初期化ユーティリティがある」ことを知った。

npaka さんの note Asus Ascent GX10 を工場出荷状態に戻す を参考に、 工場出荷状態に戻してやり直すことにした。

なお、セクション2のリカバリ(リカバリイメージとリカバリ USB 作成スクリプトを使う方法)とは別に、 ここでは ASUS の初期化ユーティリティで工場出荷状態に戻す。

  1. ASUS のサポートページ からリカバリイメージをダウンロード
  2. ダウンロードしたリカバリイメージは iso なので、Rufus で USB デバイスに書き込む
  3. USB を GX10 につないで USB から起動
  4. インストーラーの指示に従ってインストール
  5. インストールの進捗は表示されないため、完了まで待つ
  6. インストール完了、再起動後はまた最初と同じ初期セットアップ

6. 最終セットアップ

工場出荷状態に戻したため、初期セットアップからやり直す必要がある。 初期セットアップが終わったら、セクション3と同じ手順(SSH 接続、アップグレード、固定 IP、multi-user モード、公開鍵配置、動作周波数の調整、リブート)を再実行する。

7. 初期設定の完了と次の手順

これで初期設定は完了。

今回はここまで、次は GX10 同士を接続し、以下のレシピを適用する。

参考資料

2026年8月13日木曜日

WSL2 上で llama.cpp をビルドする

前回 は、 DGX Spark 向けにビルドしましたが、今回は WSL2 上でビルドする手順。

今回は初めからデーモン化する前提で進めていく。

前提

  • Windows: Windows 11 Pro 25H2 ビルド 26200.8524
  • グラフィックボード: NVIDIA GeForce RTX 4070 Ti SUPER (16 GB)
  • WSL2: Ubuntu 24.04 LTS

前提パッケージのインストール

llama.cpp 版をビルドするために必要なパッケージをインストールする。

sudo apt update
sudo apt install -y git build-essential cmake libssl-dev nvidia-cuda-toolkit

llama.cpp 実行ユーザーの追加

llama というシステムユーザーを追加する。

sudo useradd \
  --system \
  --home-dir /opt/llama \
  --shell /usr/sbin/nologin \
  llama

llama.cpp 用のディレクトリ作成

sudo mkdir /opt/llama
sudo chown llama:llama /opt/llama

llama.cpp ソースコードのクローン

いつものように git clone する。

cd /opt/llama
sudo -u llama git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp

llama.cpp ビルド

NVIDIA のグラボなので、それ用の make コマンドを叩く。

sudo -u llama cmake -B build-cuda \
      -DGGML_CUDA=ON \
      -DCMAKE_BUILD_TYPE=Release \
      -DLLAMA_CURL=ON \
      -DLLAMA_OPENSSL=ON
sudo -u llama cmake --build build-cuda -j"$(nproc)" --target llama-server

動作確認

とりあえず普通に動くかを確認。

sudo -u llama /opt/llama/llama.cpp/build-cuda/bin/llama-server -hf unsloth/DeepSeek-V4-Flash-0731-GGUF:Q8_0

OK.

systemd 用のファイル作成

cat << 'EOF' | sudo tee /etc/systemd/system/llama-server.service
[Unit]
Description=llama.cpp OpenAI-compatible inference server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=llama
Group=llama
WorkingDirectory=/opt/llama/llama.cpp/build-cuda/bin

ExecStart=/opt/llama/llama.cpp/build-cuda/bin/llama-server \
  -hf unsloth/Qwen3.6-35B-A3B-GGUF:UD-IQ3_XXS \
  --gpu-layers all \
  --no-mmap \
  --host 0.0.0.0 \
  --port 11434

Restart=on-failure
RestartSec=5

TimeoutStartSec=0
TimeoutStopSec=120
KillSignal=SIGINT

StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

設定反映

sudo systemctl daemon-reload
sudo systemctl enable llama-server
sudo systemctl start llama-server

以上。

2026年8月6日木曜日

DGX Spark 1 台で DeepSeek V4 Flash 0731 を動かす(llama.cpp 版)

前回 は DwarfStar で DeepSeek V4 Flash 0731 を動かしたが、今回は llama.cpp 版で動かしてみる。

サーバーの安定性が DwarfStar よりも高いらしいので、llama.cpp 。

前提

  • マシン: ASUS Ascent GX10 GX10-GG0006BN 90MS0371-M00060 (1TB)
  • Ollama, DwarfStar のデーモンを止めた後から

前提パッケージのインストール

llama.cpp 版をビルドするために必要なパッケージをインストールする。

sudo apt update
sudo apt install -y git build-essential cmake libssl-dev

llama.cpp ソースコードのクローン

いつものように git clone する。

cd ~/project
git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp

llama.cpp ビルド

DGX Spark なので、それ用の make コマンドを叩く。

cmake -B build-cuda -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=121 \
      -DCMAKE_BUILD_TYPE=Release -DLLAMA_CURL=ON -DLLAMA_OPENSSL=ON
cmake --build build-cuda -j"$(nproc)" --target llama-server llama-cli llama-bench

ほんとうは llama-server だけで良いのだけど、参考にしたサイト通りに全部ビルドしちゃった。

モデルのダウンロード

前回 のモデルを使うのでダウンロードは省略。

サーバー起動

OpenAI API 互換サーバーを起動する。

./build-cuda/bin/llama-server --host 0.0.0.0 --port 11434 --no-mmap -m ~/project/ds4/gguf/DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix-0731.gguf

結構長い時間待つ感じ。成句すると以下のような出力がされる。

0.00.300.436 I srv    load_model: loading model '/home/mikoto/project/ds4/gguf/DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix-0731.gguf'
8.00.852.448 I srv    load_model: initializing, n_slots = 4, n_ctx_slot = 1048576, kv_unified = 'true'
8.00.861.742 I srv          init: chat template supports preserving reasoning, consider enabling it via --reasoning-preserve
8.00.861.881 I srv  llama_server: model loaded
8.00.861.884 I srv  llama_server: listening on http://0.0.0.0:11434

クライアント起動

OpenAI API 互換クライアントで接続確認。 OK.

> C:\Java\jdk-25\bin\java.exe -jar ..\project\rei\target\rei-0.0.1-SNAPSHOT.jar
WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by org.sqlite.SQLiteJDBCLoader in an unnamed module (jar:nested:/F:/project/rei/target/rei-0.0.1-SNAPSHOT.jar/!BOOT-INF/lib/sqlite-jdbc-3.51.3.0.jar!/)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled

AI Shell
通常入力は chat として扱います。/exit で終了します。
複数行入力: 複数行ペースト対応。行末に \\ を付けるか、Ctrl+J でも改行できます。
/paste で確実な複数行入力モード(終了は単独行の . )
22:15 deepseek-v4-flash> こんにちは!!!!!

┌ User
こんにちは!!!!!
└

=== thinking ===
The user greeted me with "こんにちは!!!" (Hello!!!!!). This is a simple greeting. I should respond naturally in Japanese. Looking at the memory, there was a similar greeting earlier and the assistant responded with "こんにちは!? 何かお手伝いできることはありますか?"

I'll respond similarly. No tools needed.
=== answer(23.7 s) ===
こんにちは!? 今日もお手伝いできることがあれば、何でも言ってくださいね!

models には対応していなさそう。

llama-server の daemon 化

実行ユーザーの追加

llama というシステムユーザーを追加する。

sudo useradd \
  --system \
  --home-dir /opt/llama \
  --shell /usr/sbin/nologin \
  llama

llama-server の配置

llama 用のディレクトリを作成し、ビルドした llama-server を配置する。

sudo cp -a ~/project/llama.cpp/build-cuda /opt/llama
sudo chown -R llama:llama /opt/llama

systemd ユニットを作成

作成していく。

cat << 'EOF' | sudo tee /etc/systemd/system/llama-server.service
[Unit]
Description=llama.cpp OpenAI-compatible inference server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=llama
Group=llama
WorkingDirectory=/opt/llama/bin

ExecStart=/opt/llama/bin/llama-server \
  -m /opt/ds4/ds4flash.gguf \
  --gpu-layers all \
  --no-mmap \
  --host 0.0.0.0 \
  --port 11434

Restart=on-failure
RestartSec=5

TimeoutStartSec=0
TimeoutStopSec=120
KillSignal=SIGINT

StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

設定反映

sudo systemctl daemon-reload
sudo systemctl enable llama-server
sudo systemctl start llama-server

以上。

参考資料

変更履歴

日付 変更内容
2026/8/5 新規作成
2026/8/6 HTTPS 用フラグを追加