ASUS Ascent GX10 (NVIDIA GB10) 세팅 가이드

ASUS Ascent GX10(DGX Spark) 한 대를 추론 서버로 세팅하기: 필수 설정, 추천 설정, 트러블슈팅, 바로 돌려볼 레시피

관련 글: GB10 클럭 상한 거는 법 → / LLM 추론에서 가장 뜨거운 것은 GPU가 아니었다 → / DGX Spark 2대로 DeepSeek V4 Flash 돌리기 → / DGX Spark 4대를 스위치로 연결하기 →

In English → / 日本語 →

2026-08-28 업데이트. 네 대째 기계를 세팅하며 확인한 것을 반영했다. 공장 이미지의 깨진 netplan 파일(3장), 펌웨어 캡슐 동시 적용 실패와 재시도(1.3), purge 보호 게이트 수정(1.1), 서버 모드 후에도 남는 dgx-dashboard(1.1), Wi-Fi 종료 시점(1.4), 200G 경로당 상한 실측 갱신(3장).

ASUS Ascent GX10(NVIDIA GB10, DGX Spark 제품군)을 세 대째 들이면서, 공장 상태의 기계를 추론 서버로 만드는 절차를 처음부터 다시 밟았다. 이 글의 대상은 ASUS Ascent GX10이다. NVIDIA DGX Spark나 다른 제조사의 GB10 기기도 비슷할 가능성이 높지만, 펌웨어, 기본 이미지, 패키지 구성은 제품에 따라 다를 수 있으니 그대로 따라 하기 전에 각자 장비에서 확인해야 한다. 앞의 두 대에서 몇 주에 걸쳐 하나씩 알아낸 것들을 이번에는 한 번에 적용했고, 그 과정에서 겪은 문제도 같이 적는다. 이 글은 그 절차를 네 덩어리로 나눈다. 꼭 해야 하는 것, 하면 좋은 것, 겪은 문제, 그리고 세팅이 끝난 한 대에서 바로 돌려볼 수 있는 레시피다. 집 네트워크에만 해당하는 설정(VLAN, 주소, 스위치)은 뺐다.

주의. 이 글에는 펌웨어 업그레이드, 드라이버 교체, 데스크탑 패키지 삭제, 방화벽 설정처럼 되돌리기 어렵거나 원격 접속을 끊을 수 있는 시스템 변경이 들어 있다. 명령을 실행하기 전에 각자의 장비와 버전에서 무엇이 바뀌는지 직접 확인하고, 본인 책임으로 진행하기 바란다. 수치는 우리 장비의 실측이며 보증이 아니다.

차례
  1. 시작하기: 화면 안내대로, 그다음은 SSH로
  2. 필수 설정: 서버 모드, 드라이버 595, PD 펌웨어, 보안 기본값
  3. 추천 설정: GPU 클럭 상한, CPU 성능코어 상한, vLLM 스핀 패치, Tailscale
  4. 트러블슈팅: 겪은 문제, 잘못 알려진 정보, 200G ConnectX-7
  5. 한 대에서 바로 돌려볼 레시피: ollama Qwen3.8-27B, DeepSeek V4 Flash EXL3
  6. 한 화면 체크리스트
  7. 부록: 왜 128GiB가 아니라 121GiB로 보이나

0. 시작하기: 화면 안내대로, 그다음은 SSH로

처음 켜면 나오는 설정 화면(계정, 키보드, 네트워크)은 안내대로 마치면 된다. 특별히 고를 것이 없다. 그다음부터는 모니터와 키보드를 치우고 SSH로 작업하는 쪽이 편하다. 이 글의 나머지 절차도 전부 SSH로 했다. 코딩 에이전트(예: Claude Code)에 SSH 접속 권한을 주고 함께 진행하는 방식을 추천한다. 아래 절차는 대부분 점검과 대조의 반복이라 에이전트가 잘 맞는다.

# DGX Spark 쪽 (처음 한 번, 로컬 터미널에서)
ip -br a                       # 유선 IP 확인
systemctl status ssh           # SSH 서버 동작 확인 (없으면 sudo apt install openssh-server)

# 작업 PC 쪽
ssh-copy-id user@<spark-ip>     # 공개키 복사. 이후 비밀번호 없이 접속
ssh user@<spark-ip>

공장 상태는 이랬다. 드라이버와 펌웨어 버전은 기계마다 다를 수 있으니 먼저 확인한다. 네 대째 기계는 시간대가 처음부터 Asia/Seoul이었고 OTA 메타패키지도 신버전이라, 아래 표와 조금씩 달랐다.

항목공장 상태 (2026-08 입고분)이 글의 목표
OS / 커널Ubuntu 24.04.4 / 6.17.0-1031-nvidia그대로
기본 타깃graphical (GNOME, gdm3, snap 13개, 설정 마법사 앱)multi-user
GPU 드라이버580.173.02595.84
USB-C PD 펌웨어0x10x516
클럭GPU 최대 3003MHz, CPU 성능코어 3.9GHz (제한 없음)(추천) GPU 2000MHz, 성능코어 2.8GHz
SSH / 방화벽비밀번호 로그인 허용 / 방화벽 꺼짐키 전용 / 기본 거부
보안 업데이트자동화 없음, 15건 대기자동 적용, 자동 재부팅 없음

0.1 초기 상태 점검: 포트와 장치가 다 올라왔는지

세팅을 시작하기 전에, 그리고 끝난 뒤 한 번 더, OS에서 보이는 장치 목록을 기대값과 대조한다. 한 줄씩 실행해도 되고 묶어서 스크립트로 돌려도 된다.

lspci | grep -E 'ConnectX|NVIDIA|Non-Volatile|Realtek|MEDIATEK'   # PCI 장치
ip -br link                                                        # 네트워크 인터페이스
ethtool enP7s7 | grep -E 'Speed|Link detected'                     # 10G RJ45
for d in /sys/class/net/en*np*; do echo $(basename $d) $(cat $d/operstate) $(cat $d/speed); done   # 200G
ls /sys/bus/usb/devices | grep -c '^usb'; lsusb                    # USB 루트허브 수와 장치
lsblk -d -o NAME,SIZE,MODEL | grep nvme                            # NVMe
nproc; free -g | grep Mem; nvidia-smi -L                           # CPU, 메모리, GPU
nvidia-smi --query-gpu=driver_version,clocks.sm --format=csv       # 드라이버, 클럭
for z in /sys/class/thermal/thermal_zone*; do printf '%s=%s°C ' $(cat $z/device/path | sed 's/.*\.//') $(( $(cat $z/temp)/1000 )); done; echo   # 열 존 7개
fwupdmgr get-devices | grep 'Current version'                      # EC, SoC FW, PD, NVMe 펌웨어
sudo dmesg --level=err,crit | grep -v -i tpm | tail                # 커널 에러
systemctl list-units --state=failed                                # 실패한 유닛
항목GX10에서 기대하는 값 (네 대 모두 동일했다)
PCI 장치NVIDIA VGA(GB10) 1, NVMe(Phison) 1, Realtek 8127 10G 1, MediaTek 7925 Wi-Fi 1, ConnectX-7 4개(케이블이 꽂혀 있을 때만)
네트워크 인터페이스enP7s7(10G RJ45) UP 10000Mb/s, wlP9s9(Wi-Fi), 200G는 enp1s0f0np0, enp1s0f1np1, enP2p1s0f0np0, enP2p1s0f1np1 4개(물리 포트는 2개, 각 포트가 PCIe 함수 2개로 보인다). 케이블 꽂힌 포트는 up 200000
USB루트허브 12개(USB 2.0 6 + 3.0 6), 내장 장치로 Wi-Fi/BT 콤보 1개
저장장치NVMe 1개(출하 구성에 따라 1TB 또는 4TB)
CPU / 메모리 / GPU20코어 / MemTotal 121GiB / GPU 0: NVIDIA GB10
열 존7개: TSOC, TS0E, TS0P, TS1E, TS1P, TGPU, TUNC. 유휴 42~50°C
펌웨어EC, SoC FW(UEFI), USB-C PD, NVMe 버전이 나열된다. PD가 0x1이면 1.3으로
커널 에러 / 실패 유닛nvidia-fs: warning: error retrieving numa node는 무해. 그 외 에러 0, 실패 유닛 0

물리적으로도 확인하자. OS에 장치가 보이는 것과 포트가 실제로 동작하는 것은 다르다. 특히 200G QSFP 두 포트는 케이블이 없으면 PCI 장치 자체가 안 보이기 때문에, DAC 케이블을 실제로 꽂아 두 케이지 모두 링크가 뜨는지(operstate up, speed 200000) 봐야 한다. USB-C 포트는 각각 장치를 꽂아 lsusb에 잡히는지, RJ45는 링크 LED와 ethtool의 10000Mb/s, HDMI는 모니터로 한 번씩. 기계를 설치 장소에 고정하기 전, 교환이 가능한 기간 안에 해 두는 편이 좋다. 우리는 세 번째 기계의 200G 포트를 케이블 없이 들였다가 "PCI에 안 보인다"를 한참 의심했다(3장).

1. 필수 설정

1.1 서버 모드 전환: 플랫폼 패키지를 지키면서

공장 이미지는 GNOME 데스크탑이다. 추론 서버로 쓸 거라면 걷어내는 편이 좋다. GB10은 CPU와 GPU가 128GiB 메모리 풀 하나를 나눠 쓰는데, 데스크탑이 떠 있으면 그 풀에서 수 GiB를 차지한다. 단일 노드 레시피의 README는 데스크탑이 올라간 호스트의 기동 시 가용 메모리를 약 114.5GiB로 적고 있는데, 서버 모드로 바꾼 이 노드는 같은 레시피 기동 시 117.4GiB, 유휴 상태에서는 119.0GiB였다. 같은 기계에서 전후를 잰 것은 아니라 엄밀한 비교는 아니지만, 3~4.5GiB 차이는 큰 모델을 올릴 때 그대로 KV 캐시 여유가 된다.

그림 1. 가용 메모리(MemTotal 121.6GiB 기준). 데스크탑 호스트(레시피 README, 기동 시) ~114.5GiB, 이 노드 서버 모드는 기동 시 117.4GiB, 유휴 119.0GiB.

128GiB 기계에서 MemTotal이 121GiB로 보이는 이유와 그 내역는 부록에서 다룬다.

문제는 데스크탑을 지우는 명령 자체가 아니라 그 뒤의 apt autoremove --purge다. 두 번째 기계를 세팅할 때 이 명령이 NVIDIA 플랫폼 패키지 14개를 통째로 걷어간 적이 있다. 이 패키지들은 dgx-spark-ota-update-meta라는 메타패키지의 의존 패키지로만 잡혀 있어서, 메타패키지가 snapd와 함께 빠지는 순간 "아무도 필요로 하지 않는 패키지"가 된다. 부팅 파라미터를 넣어 주는 스니펫 두 개(nvidia-spark-initcall-bl, nvidia-spark-grub-pci)가 같이 사라졌고, 재설치와 update-grub, 재부팅으로 되돌렸다.

이번에는 세 단계로 막았다. 지우기 전에 플랫폼 패키지를 수동 설치로 표시하고, 지우는 명령은 먼저 시뮬레이션으로 돌려 제거 목록에 드라이버, CUDA, 플랫폼 패키지가 섞이면 멈추게 하고, 끝난 뒤 부팅 파라미터를 대조한다.

# 이하 1, 2장 명령은 root 셸(sudo -i) 기준

# 1) 보호: 메타패키지의 의존 패키지(snap 관련 제외) + 설치된 NVIDIA/DGX/docker/CUDA 패키지를 수동 설치로 표시
apt-cache depends dgx-spark-ota-update-meta | awk '/Depends:/{print $2}' | grep -v snaps | xargs apt-mark manual
apt-mark manual $(dpkg -l | awk '/^ii/{print $2}' | grep -E '^(nvidia-|dgx-|docker|containerd|cuda|lldpd|mlnx)')
# (기준 기계가 있으면 그쪽 `apt-mark showmanual` 목록과 대조해 빠진 것을 더한다)

# 2) 전환 + 시뮬레이션 게이트 (보호 대상이 제거 목록에 섞이면 멈춘다)
systemctl set-default multi-user.target
CAND="ubuntu-desktop ubuntu-desktop-minimal gdm3 gnome-shell xorg xwayland gnome-control-center \
      libreoffice-core network-manager-gnome snapd nvidia-desktop-default-snaps nvidia-system-station \
      dgxstation-desktop dgx-oobe-desktop dgx-spark-ota-update-meta ubuntu-server-minimal"
PURGE=""; for p in $CAND; do dpkg -s "$p" >/dev/null 2>&1 && PURGE="$PURGE $p"; done   # 설치된 것만
apt-get -s purge $PURGE | awk '/^Remv/{print $2}' | grep -E '^(nvidia-driver|linux-modules-nvidia|cuda|docker|nvidia-spark|network-manager$|netplan|openssh-server$|ufw$)' \
  && { echo "보호 대상 포함, 중단"; exit 1; }
apt-get purge -y $PURGE
apt-get -s autoremove --purge | awk '/^Remv/{print $2}' | grep -E '^(nvidia-|dgx-|lldpd|docker|cuda|mlnx)' \
  && { echo "보호 대상 포함, 중단"; exit 1; }
apt-get autoremove --purge -y
rm -rf /snap /var/snap /var/lib/snapd /home/*/snap

# 3) 대조 (재부팅 뒤에도 한 번 더)
update-grub
grep -o 'initcall_blacklist=[^ ]*\|pci=pcie_bus_safe\|iommu.passthrough=[0-9]' /boot/grub/grub.cfg | sort -u

게이트는 Remv 줄 전체가 아니라 패키지 이름 필드만 뽑아 판정해야 한다. 줄에 버전 문자열이 붙어서(Remv network-manager [1.46.0-1]) 이름 끝을 고정한 패턴은 조용히 안 맞고, 앵커를 빼면 정상 제거 대상인 network-manager-gnome까지 잡는 오탐이 난다. 네 대째 기계에서 줄 전체를 보던 게이트가 실제로는 무력했던 것을 발견하고 위처럼 고쳤다.

OTA 메타패키지는 되살리지 않는다. 되살리면 nvidia-desktop-default-snaps를 통해 snapd가 돌아와 서버 모드가 깨진다. 메타패키지가 하는 일은 "다음 릴리스에서 NVIDIA가 추가하는 패키지를 끌어오는 것"이라, 가끔 Depends 목록과 설치 상태를 대조해 빠진 것만 개별 설치하면 된다. 개별 플랫폼 패키지는 apt upgrade로 정상 갱신된다.

하나 더. 서버 모드로 바꿔도 dgx-dashboarddgx-dashboard-admin은 남는다. 데스크탑용 웹 관리 UI(포트 11000)와 그 뒤의 root D-Bus 백엔드인데, SSH로만 관리하는 서버에서는 쓸 일이 없고 admin 쪽 상주 메모리가 시간이 지나며 수백 MB까지 자란다(우리 노드 실측 270~360MB). systemctl disable --now dgx-dashboard dgx-dashboard-admin으로 꺼 둔다.

1.2 드라이버 580 → 595

바꾸는 이유는 속도가 아니라 결함이다. 우리 2노드 실험에서 580.173.02는 KV 캐시를 디스크로 내리는 경로에서 GPU 오류(Xid 13)가 재현됐고, 595.84로 올리자 같은 코드에서 사라졌다. 새 기계도 처음부터 595로 맞춘다.

apt-get -s install nvidia-driver-595-open linux-modules-nvidia-595-open-nvidia-hwe-24.04 | grep -E '^(Inst|Remv)'
# 제거(Remv) 줄이 전부 580 계열인지 확인한다. 아니면 멈춘다
apt-get -s install nvidia-driver-595-open linux-modules-nvidia-595-open-nvidia-hwe-24.04 \
  | grep ^Remv | grep -v 580 && { echo "580 외 제거 항목, 중단"; exit 1; }
apt-get install -y nvidia-driver-595-open linux-modules-nvidia-595-open-nvidia-hwe-24.04

linux-modules-nvidia-595-open-nvidia-hwe-24.04는 지금 실행 중인 커널에 맞는 미리 빌드된 모듈 패키지를 끌어온다(여기선 …-6.17.0-1031-nvidia). DKMS 빌드가 필요 없고, 기계마다 커널이 달라도 각자 맞는 것을 받는다. 설치 직후에는 재부팅 전까지 nvidia-smi가 "Driver/library version mismatch"를 내는데, 이는 정상이다. 되돌리려면 apt-cache policy nvidia-driver-580-open으로 버전을 확인한 뒤 apt-get install nvidia-driver-580-open=<버전> linux-modules-nvidia-580-open-nvidia-hwe-24.04.

1.3 USB-C PD 펌웨어

GB10 제품군 전반에서 보고되는 현상이 있다(사례: GX10 사용자 보고, PGX 사용자 보고, 정리는 별도 글의 부록). 최대 부하인데도 클럭이 400~950MHz에 머물러 성능이 1/3로 떨어지는데, 스로틀 플래그는 전부 Not Active다. USB-C PD 컨트롤러 펌웨어가 전원 협상을 잘못해 SoC가 스스로 클럭을 낮춰 잠그는 경우로 알려져 있고, 재부팅으로는 안 풀리며 전원 케이블을 뽑고 방전해야 풀린다. 예방은 PD 펌웨어를 최신으로 올리는 것이다. 이 기계의 PD 펌웨어는 0x1이었고, LVFS에 0x516(긴급도 High)이 올라와 있었다(출처: fwupdmgr get-updates 출력, LVFS 펌웨어 ID com.asus.gx10dgx.usbpd.firmware, LVFS 페이지).

fwupdmgr refresh --force
fwupdmgr get-updates                  # "GX10 USB-C PD FW Controller Update" 0x516
fwupdmgr update -y --no-reboot-check  # 캡슐이 /boot/efi/EFI/ubuntu/fw/ 에 준비됨
# 재부팅 (캡슐 1개면 약 2분) → fwupdmgr get-devices 에서 0x00000516 확인

드라이버 교체, 서버 모드 전환, 펌웨어 적용을 한 번의 재부팅으로 처리했다. 같은 경로로 UEFI("GX10 SoC FW")와 EC 펌웨어도 내려온다.

캡슐이 여러 개 대기 중이면 둘을 조심한다. 네 대째 기계는 EC, SoC FW, PD 세 캡슐이 한 재부팅에 묶이며 SSH 복귀까지 약 9분이 걸렸고, 그중 PD만 실패했다(expected 0x516 and got 0x1). fwupdmgr refresh --force 후 다시 update하면 실패한 캡슐이 재스테이징되고, PD 하나만 적용하는 재부팅에서는 성공했다. 결과 판정은 fwupdmgr get-devices --json이 정확하다. 텍스트 출력은 디바이스 이름이 전부 "UEFI Device Firmware"로 같아 구분이 어렵고, JSON의 UpdateState(2=성공, 3=실패, 4=재부팅 대기)를 보면 된다.

1.4 보안과 업데이트 기본값

항목설정메모
sshdPasswordAuthentication no, KbdInteractiveAuthentication no, PermitRootLogin prohibit-password~/.ssh/authorized_keys에 키가 들어간 것을 확인한 뒤 sshd -t && systemctl reload ssh
ufwincoming deny / outgoing allow / routed deny. SSH는 관리망에서만, 서비스 포트는 필요할 때 한 줄씩SSH로 작업 중이라면 ufw allow from <관리망/24> to any port 22 proto tcp먼저 넣고 ufw enable, 기존 세션은 열어 둔 채 새 터미널로 접속을 확인한다. 추론 API 포트(8000, 8888 등)를 LAN에 열 때도 명시적으로 추가한다
unattended-upgrades패키지 목록과 업그레이드 자동, Automatic-Reboot "false", Remove-Unused-Kernel-Packages "true"추론 중 재부팅되면 안 되므로 자동 재부팅은 끈다
시간대 / Wi-Fitimedatectl set-timezone / nmcli radio wifi off공장 상태는 UTC(기계에 따라 다르다). Wi-Fi는 데스크탑 purge와 재부팅, 유선 검증이 끝날 때까지 두 번째 경로로 살려 두고 마지막에 끈다
persistenced / journaldenabled / persistent공장 기본값 그대로

2. 추천 설정

여기부터는 안 해도 동작한다. 하지만 별도 냉각 팬 없이 GB10을 오래 돌릴 거라면 전력과 발열을 한 번은 생각해 보는 게 좋고, 아래 수치가 판단 재료다.

2.1 GPU 클럭 상한: 전력 −40~50%, LLM 디코드 −5%, 영상 생성 +7~17%

GB10에는 와트 단위 전력 한도(nvidia-smi -pl)가 없다. 유일한 조절 수단은 클럭 상한(-lgc)이다. 1400MHz부터 무제한까지 200MHz 간격으로 훑어본 결과가 아래 두 그래프다. 디코드는 메모리 대역폭이 병목이라 클럭을 내려도 토큰 속도가 거의 줄지 않는 반면, 전력과 온도는 클럭을 따라 가파르게 오른다.

그림 2. LLM 디코드(DeepSeek V4 Flash Q2, llama.cpp, 256토큰 3회 중앙값), 상한별 tok/s(실선)와 부하 전력(점선). 2000MHz는 16.7 tok/s에 22W, 무제한은 17.5 tok/s에 45W. 2600 이상은 SM 실측 클럭이 ~2495MHz에 머물러 결과가 같다.

그림 3. 영상 생성(MiniMax-H3, 20 step), 상한별 총 시간(실선)과 부하 전력(점선). 2000MHz는 103s, 50W, 66°C. 2200은 97s, 65W, 73°C. 2400 이상은 약 96s, 85W, 80°C 이상. GPU 연산이 대부분인 작업은 시간 이득이 2200~2400에서 끝난다.

상한용도LLM 디코드영상 생성
2200속도 우선17.1 tok/s, 28W, 57°C97s, 65W, 73°C
2000균형 (추천값)16.7 tok/s, 22W, 55°C103s, 50W, 66°C
1800한여름, 환기 나쁜 날16.2 tok/s, 19W, 53°C107s, 41W, 61°C
1400~1600장시간, 저열15.0~15.7 tok/s, 15~17W, 49~51°C113~125s, 28~33W
# /etc/systemd/system/gpu-clock-cap.service
# 작성 후: systemctl daemon-reload && systemctl enable --now gpu-clock-cap   (끄려면 systemctl stop)
[Unit]
Description=GPU clock cap 300-2000MHz
After=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/nvidia-smi -lgc 300,2000
ExecStop=/usr/bin/nvidia-smi -rgc
[Install]
WantedBy=multi-user.target

2000을 넣으면 실제로는 내부 스텝에 맞춰 약 1989MHz가 된다. 한 가지만 기억하면 된다. 이 상한은 벤치마크 조건이다. 성능 수치를 적을 때 클럭 조건을 함께 적고, 부하 중 nvidia-smi --query-gpu=clocks.sm으로 실제 클럭을 확인한다. 아래 레시피의 숫자는 전부 이 상한이 걸린 상태의 값이다.

2.2 CPU 성능코어 상한: 왜 CPU를 제한하는가

CPU 상한은 GPU와 이유가 다르다. 출발점은 "LLM 추론 중인데 왜 CPU가 뜨거운가"였다. GB10 두 대를 연결해 vLLM(TP=2)으로 디코드를 돌렸더니 GPU는 70°C 안팎인데 성능코어 3~4개가 100%에 고정된 채 SoC 온도를 96°C까지 올렸다. 원인은 vLLM의 프로세스 간 메시지 큐가 새 메시지를 기다리는 동안 스핀하는 기본값이었다(아래 2.3). 그 수정은 소프트웨어 쪽 해법이고, 엔진이나 이미지가 바뀌면 다시 살아날 수 있다. 그래서 호스트 쪽 안전망으로 성능코어 클럭 상한을 같이 건다. 한 대에서 TP=1로 돌리는 구성에는 2.3의 스핀 자체가 없으므로, 이 상한은 다른 엔진이나 구성이 CPU를 과하게 돌릴 때를 위한 보험이고 선택 사항이다.

GB10의 20코어는 성능코어 10개(최대 3.9GHz)와 효율코어 10개(2.8GHz)다. 성능코어를 효율코어와 같은 2.8GHz로 제한했을 때 온도 변화를 존별로 보면, 성능코어에서 멀어질수록 하락 폭이 줄어드는 모양이 나와 열원이 성능코어임이 드러난다.

그림 4. 성능코어 3.9 → 2.8GHz 상한 적용 시 부하 중 온도 변화(°C). 성능코어 −14, 언코어/메모리 −3.7, GPU −1.7, NVMe 0. SoC 종합은 −9. 단일 스트림 속도는 변화가 없었고, 동시 8 스트림에서 −5%.

# /etc/systemd/system/cpu-clock-cap.service: 성능코어(최대 3.9GHz)만 2.8GHz로
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/bash -c 'for p in /sys/devices/system/cpu/cpufreq/policy*; do \
  m=$(cat $p/cpuinfo_max_freq); \
  if [ "$m" -gt 2808000 ]; then echo 2808000 > $p/scaling_max_freq; fi; done'
ExecStop=/usr/bin/bash -c 'for p in /sys/devices/system/cpu/cpufreq/policy*; do \
  cat $p/cpuinfo_max_freq > $p/scaling_max_freq; done'
[Install]
WantedBy=multi-user.target

/sys/devices/system/cpu/cpufreq/boost는 이 플랫폼에서 기본값이 0이라 따로 손댈 게 없다. 정리하면 GPU 상한은 전력과 발열의 상한이고, CPU 상한은 소프트웨어가 CPU를 과하게 돌리기 시작했을 때의 안전망이다. 둘 다 숫자만이 아니라 거는 이유까지 알아야 나중에 제때 풀 수 있다.

2.3 vLLM 스핀 패치: 두 대 이상 연결할 때

적용 범위부터: 이 스핀은 vLLM의 멀티프로세스 실행기(mp executor)에서만 일어난다. 한 대에서 TP=1로 돌리면 해당하지 않는다(워커가 같은 프로세스 안에 있어 큐 자체가 없다). 두 대 이상을 연결하거나 --distributed-executor-backend=mp를 명시했을 때의 이야기다.

vLLM의 shm_broadcast.py에는 메시지 큐를 기다릴 때 1초(busy_loop_s=1) 동안 스핀한 뒤에야 잠드는 기본값이 있다. 디코드 스텝 간격은 1초보다 훨씬 짧으니 사실상 항상 스핀인 셈이다. 코어가 수십 개인 일반 서버에서는 3~4코어 낭비가 눈에 안 띄지만, CPU와 GPU가 한 패키지에 들어 있는 GB10에서는 그 열이 곧 SoC 온도가 된다. py-spy로 스택을 찍어 보고 기본값을 2ms로 바꾸자 vLLM 프로세스 CPU 합이 333%에서 89%로 줄고, SoC 온도는 11°C 내려갔다. 추적 과정은 원 기사에, 통제된 재현은 후속 실험에 있다.

그림 5. 스핀 기본값(1초) 대 2ms 패치. 왼쪽: vLLM 프로세스 CPU 사용량 합(%). 오른쪽: 부하 중 SoC 온도(°C, 외부 팬 없는 노드). 재현 실험에서는 기본값이 부하 90초 만에 85°C, 2분 18초에 90°C, 피크 95°C까지 갔고 패치는 피크 85°C였다.

# 컨테이너/가상환경 안, vLLM 기동 전 (경로는 설치 위치에 맞게)
sed -i 's/busy_loop_s: float = [0-9.]*/busy_loop_s: float = 0.002/' \
  $(python -c 'import vllm,os;print(os.path.dirname(vllm.__file__))')/distributed/device_communicators/shm_broadcast.py
grep -n 'busy_loop_s' $(python -c 'import vllm,os;print(os.path.dirname(vllm.__file__))')/distributed/device_communicators/shm_broadcast.py
# 바뀐 줄이 보여야 한다. 패턴이 없으면 sed는 조용히 아무것도 안 한다.
# 빌드에 따라 환경 변수(VLLM_MQ_SPIN_SECONDS 등)로 노출된 경우는 그쪽을 쓴다.

솔직하게 덧붙일 것이 있다. 나중에 한 재검증에서 단일 스트림 디코드가 기본값 40.9 대 패치 38.7 tok/s로 약 2~6% 느렸다(반복 분산 안쪽이지만 방향은 일관. 동시 4 스트림에서는 차이 없음, CPU 감소는 그대로 재현). 그래서 우리는 이 패치를 "발열이 문제일 때 켜는 스위치"로 둔다. 외부 냉각이 없고 여름이면 켜고, 단일 스트림 속도가 우선이면 끄는 식이다. 2.2의 CPU 상한은 그 선택과 무관하게 걸어 둔다.

2.4 Tailscale: 집 밖에서도 SSH

추론 서버는 한 번 세팅하면 붙잡고 있을 일이 적고, 대부분 다른 곳에서 SSH로 들어온다. 공유기 포트 포워딩 대신 Tailscale을 올려 두면 어디서든 같은 이름으로 붙는다. 이 글의 뒷부분 작업도 집 밖에서 tailnet으로 했다.

curl -fsSL https://tailscale.com/install.sh | sh
tailscale up                      # 출력된 URL로 로그인 (관리 콘솔에서 auth key를 만들어 --authkey 로 넘겨도 된다)
tailscale ip -4                   # 이 기계의 tailnet 주소
ufw allow in on tailscale0        # 방화벽을 켰다면 tailnet 인터페이스는 통째로 허용

MagicDNS를 켜 두면 ssh <호스트명>만으로 붙는다. 추론 API도 같은 경로로 열 수 있으니 LAN 밖에서 쓸 서비스는 ufw에 LAN 규칙을 추가하는 대신 tailnet으로만 열어 두는 쪽이 단순하다.

3. 트러블슈팅: 겪은 문제와 잘못 알려진 정보

증상원인 / 조치
재부팅 후 docker.service 실패: error creating buildkit instance: invalid database공장 이미지에 들어 있던 buildkit 로컬 DB(2025-09 생성)가 손상돼 있었다. 최근 들인 두 대에서도 똑같이 재현된 이미지 공통 결함이다. 빌드 캐시일 뿐이라 systemctl stop docker.socket docker; mv /var/lib/docker/buildkit /var/lib/docker/buildkit.bak; systemctl start docker. 이미지나 컨테이너와는 무관하다. 이어서 nvidia-ctk runtime configure --runtime=docker로 nvidia 런타임을 등록해 둔다.
드라이버 설치 직후 nvidia-smi: Driver/library version mismatch커널 모듈은 구버전이 로드된 채 라이브러리만 새것. 재부팅하면 해소. 재부팅 전에 GPU를 쓰는 작업을 돌리지 말 것.
595에서 nvidia-smi --query-gpu=clocks.applications.graphics, display_mode가 문자열을 돌려준다"Requested functionality has been deprecated". 점검 스크립트가 숫자 비교를 하면 깨진다. clocks.sm을 보거나 문자열을 예외 처리한다.
부하 중 GPU 클럭이 400~950MHz에 머물고 전력 ~10W, 스로틀 플래그는 전부 Not ActivePD 펌웨어 저클럭 고착(1.3, 사례 1, 2). 재부팅이나 nvidia-smi -rgc로는 안 풀리고, 종료 → 전원 케이블 분리 → 전원 버튼으로 방전 → 1분 뒤 재연결. 의도적으로 건 상한(약 1989MHz)과 구분할 것.
apt autoremove 뒤 부팅 파라미터가 사라졌다메타패키지의 의존 패키지였던 플랫폼 패키지가 함께 제거됨(1.1). 큰 purge 뒤에는 /proc/cmdline/etc/default/grub.d/를 기준 기계와 대조한다.
netplan generateInvalid YAML: aliases are not supported로 실패하고 어떤 netplan 설정도 안 먹는다공장 이미지에 내용 전체가 널 바이트인 깨진 netplan 파일이 남아 있는 경우가 있었다(네 대째 기계, 715바이트 전부 0x00). 파일 하나가 깨지면 netplan이 전체를 포기하므로 증상이 엉뚱한 곳에서 난다. /etc/netplan/*file로 훑어 깨진 파일을 치우고 netplan generate를 다시 돌린다.
정적 IP를 넣어도 재부팅하면 DHCP로 돌아온다이 이미지는 netplan이 네트워크 정본이다. NM 프로파일이 /run에만 생기는(휘발성) 기계가 있어 nmcli con mod로 넣은 설정이 재부팅에서 사라질 수 있다. 영속 설정은 /etc/netplan/에 쓴다. /etc/NetworkManager/system-connections/가 비어 있는 것은 고장이 아니라 netplan 렌더러의 정상 동작이다.
200G ConnectX-7이 lspci에 없다케이블이 꽂혀 있지 않으면 NIC이 노출되지 않는다(핫플러그 패키지가 관리). 드라이버 문제가 아니다.
재부팅했더니 /tmp에 두었던 파일이 사라졌다Ubuntu는 부팅 시 /tmp를 비운다. 재부팅 전에 스테이징 파일은 홈으로.
원격 스크립트 종료를 pgrep -f 스크립트명으로 기다렸는데 영원히 "실행 중"기다리는 쪽 ssh 명령줄에 같은 문자열이 있어 자기 자신을 잡는다. pgrep -f "[d]ownload.sh"처럼 괄호 패턴을 쓴다. pkill -f도 같은 이유로 자기 자신을 죽인다.
모델 서버가 메모리를 90% 넘게 차지하면 유저스페이스 OOM 데몬이 먼저 죽일 수 있다earlyoom 같은 데몬이 켜져 있는지 확인한다(systemctl is-active earlyoom). 큰 모델을 의도적으로 꽉 채워 올리는 레시피 중에는 earlyoom을 끄라고 안내하는 것도 있다.
잘못 알려진 정보: "UEFI에서 Display를 끄면 메모리가 는다"

단일 노드 레시피 쪽에서 "데스크탑/디스플레이가 UMA 7GiB를 차지하니 UEFI의 Display 항목을 끄라"는 조언이 돈다(7GiB 출처: MiaAI 레시피 README). 확인 결과 GX10 펌웨어(GX10DGX.0105)에 Display, iGPU, iGPU Memory Carveout 같은 항목이 있긴 하지만 전부 숨김(SuppressIf) 처리라 설정 화면에 나오지 않고, NVIDIA 공식 UEFI 가이드에도 없다. 펌웨어가 실제로 떼는 것은 부록에서 보듯 약 3.9GiB(carveout 3.0GiB 포함)로 고정돼 있고, 문제의 7GiB는 GNOME/Xorg 같은 유저스페이스의 GPU 할당이라 서버 모드로 바꾸면 돌아온다. 펌웨어에서 할 일은 없고, 숨김 변수를 직접 고치는 것은 검증되지 않았으니 권하지 않는다.

200G ConnectX-7: 안 보이거나, 링크는 뜨는데 안 되거나, 느리거나

두 대 이상을 묶을 때 쓰는 200G 포트는 네 단계에서 사람을 헷갈리게 했다. 각 단계의 증상과 해결을 순서대로 적는다. 전부 우리 기계에서 실제로 겪은 것이다.

  1. 케이블이 없으면 장치가 없다. lspci에 ConnectX-7이 한 줄도 안 나온다. 드라이버(mlx5_core)는 로드돼 있다. 핫플러그 패키지가 관리하는 정상 동작이라, 케이블을 꽂으면 나타난다.
  2. 케이블을 꽂으면 포트 하나가 인터페이스 두 개로 보인다. enp1s0f0np0enP2p1s0f0np0처럼 이름이 다른 두 인터페이스가 같은 물리 포트다(PCIe 루트 두 개에 각각 노출되는 socket-direct 구성. NVIDIA 문서도 "QSFP 포트 하나가 리눅스 인터페이스 두 개로 보인다"고 적는다: ConnectX-7 Networking, 구조 해설은 ServeTheHome). cat /sys/class/net/*/phys_switch_id로 보면 네 인터페이스가 같은 값이고 phys_port_name만 p0/p1 둘이다. 본딩하지 말고 두 인터페이스에 각각 다른 서브넷의 IP를 준다(NVIDIA의 Connect Two Sparks 플레이북도 같은 방식). 그래야 NCCL이 두 경로를 나눠 써서 대역폭이 합산된다. 링크와 RoCE 상태가 ACTIVE인데 IP만 없어서 "안 올라온다"로 보이는 단계가 이것이다.
    nmcli con add type ethernet con-name ic200g   ifname enp1s0f0np0   ipv4.method manual ipv4.addresses <rail-A>/24 ipv4.never-default yes 802-3-ethernet.mtu 9000
    nmcli con add type ethernet con-name ic200g-b ifname enP2p1s0f0np0 ipv4.method manual ipv4.addresses <rail-B>/24 ipv4.never-default yes 802-3-ethernet.mtu 9000
    # 상대 기계도 같은 두 서브넷으로. 방화벽은 포트 이름 네 개를 전부 허용해 둔다(케이블을 다른 케이지로 옮겨도 깨지지 않게)
    for i in enp1s0f0np0 enp1s0f1np1 enP2p1s0f0np0 enP2p1s0f1np1; do ufw allow in on $i; done
  3. 모든 지표가 정상인데 처리량만 1/8이다. 링크 200000Mb/s, RoCE 4X×50G, PCIe Gen5 x4, 에러 카운터 0, 점보프레임 통과인데 ib_write_bwiperf3도 13Gb/s 언저리에 멈춘다(정상은 경로당 98~110, 합산 약 196Gb/s. 경로당 상한은 케이블이 아니라 PCIe Gen5 x4 쪽이고, 이후 스위치 구성에서 ib_send_bw로 재면 경로당 109Gb/s로 균일했다. NVIDIA 플레이북의 벤치마크 가이드는 ib_write_bw 약 197Gb/s를 기대치로 적고, 같은 13Gb/s 고착은 NVIDIA 포럼에도 보고돼 있다). 같은 장치에 프로세스 두 개를 돌려 6.9+6.9로 나눠 가지면 장치 상한에 걸린 것이고, 한 기계 안에서 두 인터페이스끼리 쏴도 13이면 케이블과 상대 기계는 혐의를 벗는다. 재부팅으로는 안 풀린다. 두 기계를 종료하고 전원 케이블을 뽑은 뒤 전원 버튼을 눌러 방전하고 1분 뒤 다시 연결하면 풀렸다(13.3 → 108Gb/s, 두 경로 합 196Gb/s). 이후 정규 재부팅을 여러 번 해도 유지됐다. PD 펌웨어 저클럭 고착과 같은 계열의 전원 쪽 현상으로 보인다.
  4. 케이블을 다른 케이지로 옮기면 설정이 깨진다. NetworkManager가 만든 프로파일에 포트 MAC이 박혀 있어 permanent MAC address doesn't match로 활성화가 실패한다. nmcli con mod ic200g 802-3-ethernet.mac-address "" connection.interface-name <새 인터페이스>로 MAC 잠금을 비우고 이름으로 다시 묶는다. 방화벽 규칙도 포트 이름 기준이라 위처럼 네 개를 다 열어 둔다.

판정할 때 두 가지를 기억하자. ufw는 ICMP를 기본 허용하므로 ping은 되는데 TCP가 안 되는 상태가 생긴다(ib_write_bw가 "Couldn't connect"인데 ping은 멀쩡). 그리고 GB10에서는 ib_write_bw가 낮게 나와도 NCCL은 정상인 사례가 보고돼 있어(NVIDIA 포럼), 최종 판정은 NCCL(nccl-tests)로 한다. NCCL 로그의 GPU_DIRECT_RDMA_SUPPORTED=False는 GB10에서 정상이다.

여기까지는 두 대를 케이블로 직결하는 이야기다. 노드가 셋 이상이면 포트가 두 개뿐이라 풀메시 직결이 성립하지 않아 스위치가 필요해진다. 그쪽 구성은 DGX Spark 4대를 스위치로 연결하기에 따로 적었다.

4. 한 대에서 바로 돌려볼 레시피: 직접 검증한 것만

세팅이 끝난 한 대에서 그대로 따라 할 수 있는 두 가지다. 둘 다 위의 GPU 상한(1989MHz)이 걸린 상태에서 쟀다.

4.1 ollama + Qwen3.8-27B (GGUF Q4_K_M): 10분 안에 첫 토큰

가장 빠른 동작 확인. 공식 설치 스크립트로 ollama를 올리고, Hugging Face의 ggml-org/Qwen3.8-27B-GGUF에서 Qwen3.8-27B-Q4_K_M.gguf(19GB)를 받아 Modelfile로 등록한다. 한 가지 함정이 있다. 이 환경에서는 ollama의 기본 컨텍스트가 262144로 잡혀(ollama ps로 확인) 작은 모델도 KV 캐시로 수십 GB를 차지할 수 있다. Modelfile에 num_ctx를 명시한다.

curl -fsSL https://ollama.com/install.sh | sh
pip install -U huggingface_hub          # hf CLI
hf download ggml-org/Qwen3.8-27B-GGUF Qwen3.8-27B-Q4_K_M.gguf --local-dir ~/models/qwen3.8-27b
cat > Modelfile <<EOF
FROM /home/$USER/models/qwen3.8-27b/Qwen3.8-27B-Q4_K_M.gguf
PARAMETER num_ctx 32768
EOF
ollama create qwen3.8-27b-q4km -f Modelfile     # 약 1분 (blob 복사)
ollama run qwen3.8-27b-q4km --verbose "한 문장으로 자기소개해 줘"
측정메모
적재100% GPU, 20GBollama ps. 첫 로드 43초(콜드 페이지캐시), 이후 상주
디코드11.5~11.8 tok/s27B dense Q4 19GB는 GB10 메모리 대역폭(273GB/s) 기준 상한이 약 14 tok/s라 정상 범위
프롬프트 평가290 tok/s첫 요청은 워밍업으로 36초 걸렸고 두 번째부터 0.3초
생성 중 GPU95%, 1989MHz, 25W, 57°CGPU 상한 적용 상태

4.2 DeepSeek V4 Flash를 한 대에서: MiaAI 레시피 (EXL3 3.0bpw, REAP K216)

원본 FP8(166.9GB)은 한 대에 안 들어간다. MiaAI-Lab/DeepSeek-v4-Flash-One-DGX-Spark0xSero/deepseek-v4-flash-0731-spark를 SparkInfer 커널과 DSpark 스펙 디코딩으로 서빙한다. 이 체크포인트는 전문가 256개 중 216개를 남기고(REAP) EXL3 3.0bpw로 양자화한 106.9GB 빌드다. 따라서 "원본 DeepSeek V4 Flash"가 아니라 프루닝+3bpw 판이라는 점은 알고 써야 한다.

# 전제: docker + compose v2, nvidia 런타임 등록(nvidia-ctk runtime configure --runtime=docker), 사용자가 docker 그룹
git clone https://github.com/MiaAI-Lab/DeepSeek-v4-Flash-One-DGX-Spark mia-one && cd mia-one
./download.sh            # 이미지 pull → HF 107GB → TP4→TP1 재결합 → 체크섬. 디스크 ~200GB, 회선 따라 30분~수 시간
systemctl stop earlyoom ollama   # 레시피 요구(서버가 메모리 94%를 의도적으로 차지한다) + 4.1의 ollama도 내린다. free -h 에서 가용 ≥ 114.3 GiB 확인
./start.sh --no-wait     # 기본값: MAX_MODEL_LEN 384000, MAX_NUM_SEQS 1, KV_RECORD=stock432, DSpark 스펙 디코딩
curl -s -o /dev/null -w '%{http_code}\n' localhost:8888/health   # 200 이면 준비. 이 기계에서 552초 걸렸다

서버 모드 덕을 여기서 봤다. 기동 시 가용 메모리가 117.4GiB(레시피 README의 호스트는 114.5)라 KV 풀이 510,778 토큰으로 확보됐다. README 최고치 439,622보다 16% 크다.

그림 6. 프롬프트 길이별 프리필 속도(tok/s, 프리픽스 캐시 없이). 32k~100k에서 ~1,000 tok/s, 330k에서 843 tok/s. 같은 길이의 니들 테스트(숨겨 둔 암호 문구 찾아내기)는 64k, 200k, 330k 전부 정확히 맞혔고 선점(preemption)은 0이었다.

측정README 주장
기동 (/health 200)552s-
디코드 단일 스트림 (thinking off)36~37 (512 tok ×3), 38~44 (인수 테스트 코드 생성)44~47 (330k 컨텍스트, structured)
프리필 32k / 100k1,091 / 1,024 tok/s~1,024
니들 64k / 200k / 330k전부 정확320k/370k 정확
레시피 자체 인수 테스트(acceptance_c1.py)전부 pass게이트 ≥35 tok/s

디코드만 README의 80~95% 수준이었는데, 우리는 GPU 상한(1989MHz)을 건 상태였고 README 쪽 조건(330k 컨텍스트, structured 출력)과도 달랐다. EXL3 트렐리스 디코드는 연산 비중이 커서 클럭 영향을 받을 것이라는 추정은 있지만, 상한을 푼 A/B는 하지 않았다. 첫 구조화 출력(JSON 스키마) 요청은 23토큰에 28초가 걸리는데, 문법 컴파일이 처음 한 번만 드는 비용이라 두 번째부터는 정상이다. 두 대 이상 있다면 원본 FP8을 TP=2로 올리는 별도 글이 있다.

5. 한 화면 체크리스트

  1. 화면 안내대로 초기 설정 → ip -br a, systemctl status ssh → 작업 PC에서 ssh-copy-id
  2. 초기 상태 점검(0.1): PCI, 네트워크, USB, NVMe, 열 존, 펌웨어, 커널 에러. 200G 포트는 케이블을 꽂아 물리 확인
  3. timedatectl set-timezone, apt-get update
  4. 플랫폼 패키지 apt-mark manual (메타 Depends + 설치된 nvidia-spark-* 등)
  5. ufw, unattended-upgrades, build-essential, python3-dev, jq, htop 등 기본 패키지
  6. unattended 설정(자동 재부팅 off), sshd 키 전용, ufw 기본 거부, docker 그룹
  7. (추천) 클럭 상한 유닛 2개 enable (GPU 300,2000 / CPU 성능코어 2.8GHz), Tailscale
  8. 드라이버 595(시뮬레이션 후), multi-user, purge/autoremove(게이트), grub 대조, PD FW 준비, 보안 업데이트
  9. 임시 파일을 홈으로 옮기고 재부팅 → 검증: 드라이버 버전, 클럭, /proc/cmdline, PD 펌웨어 0x516, failed 유닛 0, docker 정상
  10. 레시피 하나로 동작 확인 (4.1이 가장 빠르다)

부록. 왜 128GiB가 아니라 121GiB로 보이나 (단위 정리, 실측)

이 글의 메모리 숫자는 전부 이진 단위 GiB(230바이트)다. free -g/proc/meminfo가 쓰는 단위이고, 제품 표기 "128GB"도 실제로는 128GiB(십진 137.4GB)다. DMI가 보고하는 메모리 주소 범위 0x80000000~0x207FFFFFFF가 정확히 237바이트 = 128GiB이고, 거기서 아래 순서로 빠진다(모두 이 기계에서 읽은 값).

단계크기내용
물리 LPDDR5X128.00GiBCPU와 GPU가 함께 쓰는 단일 풀 (LPDDR5 8533MT/s, dmidecode)
맵에 없는 구간−0.37GiBUEFI가 커널에 넘긴 메모리 맵(memblock 127.63GiB)에 아예 없는 DRAM 구간. 펌웨어 쪽이 쓰는 것으로 보이지만 용도는 공개돼 있지 않다
맵에 있지만 reserved−3.55GiB디스플레이/iGPU 예약이 여기 들어 있다. NVIDIA 릴리스 노트(2026년 7월)는 "Display Reserved Memory"를 BIOS에서 2GB(기본) 또는 4GB로 고를 수 있다고 적는다(릴리스 노트, 포럼 공지). 우리 ASUS 펌웨어에서는 큰 두 영역이 2616MiB + 460MiB = 3076MiB이고 부팅 프레임버퍼(BOOTFB)가 그 안에 있는데, 3장에서 본 숨김 항목 "iGPU Memory Carveout"의 값 24(×128MiB = 3072MiB)와 맞아떨어진다. 다만 그 항목의 단위는 문서화돼 있지 않고, 최솟값도 24라 더 줄일 수는 없다. 나머지 약 0.55GiB는 UEFI 런타임, ACPI, TPM 등. 남는 System RAM 124.08GiB
커널 자체 고정 할당−2.45GiB4KiB 페이지 32,526,926개의 page 구조체(64B씩 = 1.94GiB) + 커널 이미지, initramfs, percpu 등 0.5GiB. 여기까지 빼면 MemTotal 121.63GiB, free -g의 121. CMA 128MiB는 이 안에 들어 있다
유휴 시스템 사용−2.6GiB서비스, 슬랩(GPU 드라이버 UVM 포함), 페이지캐시 제외. 서버 모드 유휴 MemAvailable 119.0GiB. 데스크탑이 떠 있으면 여기서 몇 GiB가 더 빠진다

"128GB 중 121GB만 보인다"는 고장이 아니라 정상이고, 모델을 올릴 때 기준으로 삼을 수는 MemTotal보다 MemAvailable이다. 100MiB가 아쉬운 크기의 모델을 올린다면 손댈 수 있는 곳은 사실상 하나, page 구조체 1.94GiB를 줄이는 64KiB 페이지 커널이다(NVIDIA가 linux-image-nvidia-64k-hwe-24.04로 제공, 구조체가 약 1/16로 줄어 1.8GiB 남짓 회수. Grace 성능 튜닝 가이드도 큰 메모리 워크로드에 64K를 권한다). 다만 Spark에 설치돼 나오는 DGX OS 커널은 64K가 아닌 -nvidia 커널이고, 595 드라이버에는 4GiB 이상을 한 번에 고정 매핑하는 경로(디스크 KV 오프로딩 같은)에 알려진 결함(open-gpu-kernel-modules #1269)이 있어, 쓰는 워크로드로 먼저 검증하고 4K 부팅 항목을 남겨 둬야 한다. 디스플레이 예약은 BIOS 항목이 있어도 기본값(2GB, 우리 펌웨어에서는 24)이 최솟값이라 더 못 줄이고, CMA(128MiB)는 이동 가능한 페이지로 그대로 쓰이므로 줄여도 이득이 없으며(cma=, crashkernel= 설명은 커널 파라미터 문서), crashkernel은 이미 0이다.

기록: 2026-08-21~22. 장비: ASUS Ascent GX10 (NVIDIA GB10, 128GB). 다른 GB10 제품은 펌웨어와 이미지가 다를 수 있다. 펌웨어 GX10DGX.0105, Ubuntu 24.04.4, 커널 6.17.0-1031-nvidia, 드라이버 595.84. 클럭 상한 스윕과 스핀 실험 수치는 같은 기종의 기존 두 대에서 쟀다(각 링크 참조). 네트워크 구성, 주소, 호스트명 등 환경 의존 항목은 뺐다.