NVIDIA Jetson AGX Thor 從開箱到可交付:效能、環境、網路的完整設定與驗收流程。適用 L4T R39 Rev 2.0(JetPack 7.2)。文件版本 v2.2,2026 年 9 月 2 日,云碩科技股份有限公司。
這份指南寫給第一次接手 Thor 設備的工程師。目標是把一台剛開箱、或剛重新燒錄完 BSP 的 Thor,設定到可以交付的狀態。
每一章都是「為什麼要做、怎麼做、怎麼驗證」三段。所有指令都在 節點A 與 節點B 兩台實機、以及 L3 網管交換機上實際執行並驗證過,不是照抄官方文件。第九章的踩坑紀錄每一條都是真的踩過才寫進來的,建議先掃一遍再動手。
設定順序有相依性,請由第三章往後依序執行。
| 項目 | 數值 |
|---|---|
| 10G 單流吞吐量 | 7.48 Gbits/sec |
| 10G 四流吞吐量 | 8.92 Gbits/sec |
| 32 條並行連線 | 7.30 Gbits/sec |
| 小封包(512 bytes) | 3.41 Gbits/sec |
| 延遲 | 0.69 ms |
| 滿載時 CPU 閒置率 | 92% |
從零到可交付共七個階段。每個階段都要驗證通過再進下一個。
| 階段 | 項目 | 為什麼要做 | 需重開機 |
|---|---|---|---|
| 1 | 主機名與時區 | 多台同名無法辨識;UTC 時間讓 log 判讀困難 | 否 |
| 2 | 電源模式 MAXN | 出廠預設是 120W,GPU 被限制在 1386 MHz | 否 |
| 3 | 鎖定時脈 | MAXN 只放開上限,實際頻率仍會浮動 | 否 |
| 4 | CUDA 與 Docker | 開發與部署的基礎環境 | 否 |
| 5 | 交換機拆分設定 | 10G 埠預設是單一 100G,與 Thor 談不攏 | 否 |
| 6 | Thor 網路設定 | 10G 埠預設不會自己啟用 | 否 |
| 7 | 驗收檢查 | 確認前六項都生效且能跨重開機 | 是 |
Thor 出廠的主機名一律是 ubuntu。同時管理兩台以上時,SSH 進去完全分不出是哪一台,這在遠端操作時很容易改錯機器。
sudo hostnamectl set-hostname 節點A sudo sed -i "s/\bubuntu\b/節點A/g" /etc/hosts
第二行不能省。只改 hostnamectl 而沒有同步 /etc/hosts 的話,127.0.1.1 仍指向舊名稱,部分程式在解析自己的主機名時會逾時。
驗證:
hostnamectl --static grep 127 /etc/hosts
出廠預設是 Etc/UTC。台灣現場使用時,所有 log、journalctl、訓練進度的時間戳都要自己加八小時,很容易看錯。
sudo timedatectl set-timezone Asia/Taipei
驗證應顯示 Asia/Taipei (CST, +0800):
timedatectl | grep -E "Local time|Time zone"
Thor 的 nvpmodel 有四個模式。出廠預設是 ID 1(120W),不是最高效能:
| 模式 ID | 名稱 | GPU 上限 | NVDEC 上限 | CPU 上限 |
|---|---|---|---|---|
| 0 | MAXN | 1575 MHz | 1692 MHz | 2601 MHz |
| 1 | 120W(出廠預設) | 1386 MHz | 1557 MHz | 2601 MHz |
| 2 | 90W | 較低 | 較低 | 較低 |
| 3 | 70W | 最低 | 最低 | 最低 |
CPU 上限在 MAXN 與 120W 之間沒有差別,差的是 GPU 與影像解碼單元,GPU 相差約 12%。跑推論或影像管線時這個差距會直接反映在吞吐量上。
這是最容易做半套的地方。nvpmodel -m 0 只寫入執行期狀態檔 /var/lib/nvpmodel/status,開機時由 nvpmodel.service 讀取。但如果那支檔被清除、或系統做過還原,就會退回設定檔裡的 PM_CONFIG DEFAULT,而出廠值是 1。
sudo nvpmodel -m 0 sudo cp -a /etc/nvpmodel/nvpmodel_p3834_0008.conf /etc/nvpmodel/nvpmodel_p3834_0008.conf.bak sudo sed -i "s/^< PM_CONFIG DEFAULT=1 >/< PM_CONFIG DEFAULT=0 >/" /etc/nvpmodel/nvpmodel_p3834_0008.conf
設定檔名稱依板型而異,Thor Developer Kit 是 nvpmodel_p3834_0008.conf,實際檔名可用 readlink -f /etc/nvpmodel.conf 確認。
nvpmodel -q cat /var/lib/nvpmodel/status grep "^< PM_CONFIG" /etc/nvpmodel/nvpmodel_p3834_0008.conf
三者應分別顯示 MAXN / pmode:0000 / DEFAULT=0。
nvpmodel 決定的是頻率上限。實際運作時 CPU governor 仍是 schedutil,會依負載動態調頻,GPU 也會在閒置時降頻。做效能量測、或需要穩定延遲的即時應用時,這種浮動會造成數據不可重現。
jetson_clocks 的作用是把 CPU、GPU、EMC 的最小頻率拉到等於最大頻率,等於鎖死在上限。
jetson_clocks 必須在 nvpmodel 之後執行。順序顛倒的話會鎖到舊模式的上限,得到一台「顯示 MAXN 但實際跑 120W 頻率」的機器,而且很難察覺。
jetson_clocks 不會自己記得原本的值。要能還原,必須在第一次鎖頻之前先存檔:
sudo jetson_clocks --store /etc/l4t_dfs.conf
這一步只能做一次。已經鎖頻後再執行,存下來的就是鎖頻後的狀態,還原功能等於失效。
jetson_clocks 的效果不會跨重開機,必須做成 systemd 服務。建立 /etc/systemd/system/jetson-clocks.service:
[Unit] Description=Lock Jetson clocks to max frequency (jetson_clocks) After=nvpmodel.service Wants=nvpmodel.service [Service] Type=oneshot RemainAfterExit=yes ExecStartPre=/bin/sleep 20 ExecStart=/usr/bin/jetson_clocks ExecStop=/usr/bin/jetson_clocks --restore /etc/l4t_dfs.conf [Install] WantedBy=multi-user.target
三個欄位的用意:
After=nvpmodel.service 保證排序正確,這是 5.2 節的要求。
ExecStartPre=/bin/sleep 20 是緩衝。nvpmodel 是 oneshot 服務,systemd 認定它完成之後,SoC 端仍需要數秒才會穩定套用新的電源模式。沒有這段等待時,偶發會鎖到過渡狀態的頻率。
RemainAfterExit=yes 讓 oneshot 服務執行完仍保持 active 狀態,這樣 systemctl stop 才會觸發 ExecStop 的還原動作。
啟用:
sudo systemctl daemon-reload sudo systemctl enable jetson-clocks.service sudo jetson_clocks
鎖定成功的判準是 MinFreq 等於 MaxFreq:
sudo jetson_clocks --show | grep -E "^cpu0:|gpu-gpc-0|^EMC" cpu0: MinFreq=2601000 MaxFreq=2601000 gpu-gpc-0 MinFreq=1575000000 MaxFreq=1575000000 EMC MinFreq=4266000000 MaxFreq=4266000000
GPU 若停在 1386000000,代表 nvpmodel 沒生效或順序錯了,回頭檢查第四章。
鎖頻後系統會持續維持高頻,待機功耗與發熱都明顯上升。Thor 的風扇由 nvfancontrol 服務以 close_loop 模式自動調節,預設 cool 曲線,正常散熱環境下不需人工介入。但機殼進出風口必須淨空,且應定期確認溫度:
paste <(cat /sys/class/thermal/thermal_zone*/type) <(cat /sys/class/thermal/thermal_zone*/temp)
數值單位為千分之一度,48250 即 48.25 度。閒置時約 45 至 50 度屬正常。
要暫時解除鎖頻:sudo systemctl stop jetson-clocks.service
這是本次最大的軟體坑。直覺會執行 sudo apt install nvidia-jetpack,但在 R39 Rev 2.0 上會直接失敗:
The following packages have unmet dependencies: nvidia-l4t-kernel-openrm : Depends: nvidia-l4t-init-openrm but it is not going to be installed nvidia-l4t-kernel-partitions : Depends: nvidia-l4t-bootloader-utils but it is not going to be installed
原因是 nvidia-jetpack 這個 meta 套件會連 kernel 與 bootloader 套件一起拉,與已經燒錄在裝置上的 BSP 版本衝突。
正確做法是只裝需要的 CUDA 工具鏈:
sudo apt update sudo apt install -y cuda-toolkit-13-2
BSP 本身已內含 GPU 驅動與 L4T 執行期(約 59 個 nvidia 套件),缺的只有開發工具鏈。
nvcc 預設不在 PATH 中,建立 /etc/profile.d/cuda.sh:
export PATH=/usr/local/cuda/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
Ubuntu 內建的 docker.io 版本較舊,且與 NVIDIA Container Toolkit 的搭配較不穩定。使用 Docker 官方 apt 來源:
sudo apt install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=arm64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable" | sudo tee /etc/apt/sources.list.d/docker.list sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
注意 arch=arm64。Thor 是 aarch64 架構,寫成 amd64 會找不到套件。
沒有這一步,容器內看不到 GPU。
sudo apt install -y nvidia-container-toolkit sudo nvidia-ctk runtime configure --runtime=docker sudo systemctl restart docker sudo usermod -aG docker $USER
加入 docker 群組需要重新登入才會生效。
docker info | grep -iE "Runtimes|Default Runtime"
Runtimes 那一行必須看到 nvidia,否則容器拿不到 GPU。
Thor 的 QSFP 連接器不是一個 40G 或 100G 埠,而是四條各自獨立的 10GbE。這寫死在 device tree 裡:
ethernet@a808a10000 if-name=mgbe0_0 instance=0 uphy-gbe-mode=1 fixed-link speed=10000 ethernet@a808b10000 if-name=mgbe1_0 instance=1 uphy-gbe-mode=1 fixed-link speed=10000 ethernet@a808d10000 if-name=mgbe2_0 instance=2 uphy-gbe-mode=1 fixed-link speed=10000 ethernet@a808e10000 if-name=mgbe3_0 instance=3 uphy-gbe-mode=1 fixed-link speed=10000
四個介面的 MAC 位址是連號的(例如 3c:6d:66:e4:3b:d7 到 da),代表它們共用同一顆實體連接器的四條 lane。
Thor 沒有 40G 或 100G 模式可選,也不能靠軟體改。因此對端交換機埠必須拆分成 4×10G,否則一邊講 100G、一邊講 4×10G,模組讀得到但協商不出可用速率,兩端都會顯示 Down。
show interface Ethernet1/49 capabilities
必須看到這兩行:
Speed: 1000,10000,25000,40000,50000,100000 Breakout capable: yes
速率清單裡要有 10000。另外注意 Port Group Members 這一欄,如果同一組內有多個埠,拆分時可能會互相影響。
configure terminal interface breakout module 1 port 49-50 map 10g-4x end copy running-config startup-config
這是全域組態指令,不是進到 interface 底下打的,這點很常搞錯。
套用後埠會變成 Eth1/49/1 到 Eth1/49/4、Eth1/50/1 到 Eth1/50/4,共八個 10G 子介面。原本 Eth1/49、Eth1/50 上的設定會被移除,必須重新套用到子介面上:
configure terminal interface Ethernet1/49/1-4, Ethernet1/50/1-4 switchport no shutdown end copy running-config startup-config
新建的子介面預設是 shutdown 且為 routed 模式,不下這段的話八個埠會停在 disabled。
show interface status | include 1/49|1/50 show running-config | include breakout
八個子介面應全部顯示 connected / full / 10G。
要還原拆分:no interface breakout module 1 port 49-50 map 10g-4x
Thor 端每條 lane 跑 10G NRZ。線材必須能承載四條 NRZ lane。
| 線材 | 可用 | 說明 |
|---|---|---|
| 40G QSFP+ 被動 DAC | 可 | 4 lane × 10G NRZ,規格完全吻合 |
| 100G QSFP28 被動 DAC | 可 | 4 lane × 25G NRZ,降速跑 10G 在電氣上沒問題 |
| 200G QSFP112 DAC | 不可 | 4 lane × 50G PAM4,調變方式不同,無法降到 NRZ |
本次實際使用並驗證可行的是 ONTi 的 ONT-DAC-QS28-1(1 公尺 QSFP28 被動銅纜),交換機讀到的 EEPROM 型號為 QSFP100G-4SFP25G-CU1M。
第三方模組在多數交換機上預設會被擋,必要時開放:
configure terminal service unsupported-transceiver
L3 網管交換機 的 Eth1/1-48 是 10GBASE-T(RJ45 銅線),不是 SFP+。
show module Mod Ports Module-Type Model 1 54 48x10GT + 6x40G/100G Ethernet Module L3 網管交換機
48x10GT 的 T 代表 twisted copper。這表示無法使用「QSFP 轉 4×SFP+ 破線接到前 48 埠」這種常見做法,10G 光纖只能走 Eth1/49-54 那六個 QSFP 埠。採購線材前務必先用 show module 確認介質型別。
| 介面 | 速度 | 角色 | 預設狀態 |
|---|---|---|---|
| enP2p1s0 | 1 GbE | 管理備援 | 已設定,可用 |
| mgbe0_0 至 mgbe3_0 | 10 GbE | 對外主要 | 未啟用 |
四個 10G 埠在 device tree 中被宣告為 fixed-link,這帶來三個必須理解的限制:
第一,沒有 PHY,也沒有 SFP 管理框架。ethtool -m mgbe0_0 會回 Operation not supported,代表核心到光模組之間沒有 I2C 通道。系統看不到模組型號、看不到收光功率,也無法判斷模組是否插著。排查實體問題時軟體端幫不上忙,只能靠交換機端。交換機的 show interface transceiver details 可以補上這塊盲區。
第二,不做自動協商。ethtool 顯示的 Auto-negotiation: on 在 fixed-link 模式下沒有實際意義,因為沒有 PHY 可以協商。
第三,ethtool 顯示的 Speed: 10000Mb/s 是 device tree 寫死的值,不是協商結果。看到 10000 不代表線路已通,要看 Link detected。
接手既有設備時務必先檢查這支檔:
cat /etc/NetworkManager/conf.d/99-unmanage-mgbe.conf
本次在 節點A 上發現此檔存在,內容將三個 10G 埠標為 NetworkManager 不管理:
[keyfile] unmanaged-devices=interface-name:mgbe0_0;interface-name:mgbe2_0;interface-name:mgbe3_0
被列入的介面開機後連 admin up 都不會做,nmcli 顯示 unmanaged,且任何連線設定都無法套用,錯誤訊息也完全不會提到這支檔。要使用該埠必須先將它移出清單或整支刪除。
10G 埠未接上線材時 NetworkManager 會判定為 unavailable,不會套用 IP 設定。建立 /etc/NetworkManager/conf.d/10-mgbe-ignore-carrier.conf:
[device-mgbe] match-device=interface-name:mgbe* ignore-carrier=yes
這樣 IP 會先掛上,線路一通即可使用,不必再登入機器操作。
10G 承載對外主要位址,1G 降為管理備援。兩者都在同一個 VLAN 1 與同一個 192.0.2.0/24。
| 介面 | IP | 閘道 | route-metric | |
|---|---|---|---|---|
| 對外主要 | mgbe0_0(10G) | 192.0.2.11 | 192.0.2.254 | 100 |
| 管理備援 | enP2p1s0(1G) | 192.0.2.21 | 192.0.2.254 | 700 |
兩張網卡都設預設路由,靠 metric 決定優先序。 正常情況全部走 10G;10G 斷線時該路由自動消失,1G 的 metric 700 立刻接手,機器不會失聯也不需人工介入。此行為已實測驗證。
要留意的取捨是:10G 斷線時 192.0.2.11 這個位址會整個消失,因為它只存在於 10G 網卡上。機器本身靠 1G 保持連線,但任何以該位址定址的服務會中斷,需改連管理位址。
sudo nmcli con add type ethernet ifname mgbe0_0 con-name mgbe0_0-10g \ ipv4.method manual \ ipv4.addresses 192.0.2.11/24 \ ipv4.gateway 192.0.2.254 \ ipv4.dns 8.8.8.8 \ ipv4.route-metric 100 \ ipv4.dad-timeout 0 \ ipv6.method disabled \ connection.autoconnect yes sudo nmcli con mod "Wired connection 1" \ ipv4.addresses 192.0.2.21/24 \ ipv4.gateway 192.0.2.254 \ ipv4.route-metric 700
ipv6.method disabled 是刻意的。資料傳輸埠只走 IPv4,關閉 IPv6 可避免介面掛上不必要的 link-local 位址,也減少介面 up 時多餘的鄰居探詢封包干擾排查判讀。
必須先讓 1G 放掉對外位址,10G 才拿得到。 順序顛倒會失敗:
Error: Connection activation failed: IP configuration could not be reserved
原因是 NetworkManager 的位址衝突偵測(ACD)會發現該位址仍在同一台機器的另一張網卡上。正確順序是:
nmcli con up "Wired connection 1" 讓 1G 換到管理位址,釋放對外位址nmcli con up mgbe0_0-10g 讓 10G 接手另外在 10G 的 profile 上設 ipv4.dad-timeout 0 關閉 ACD 探測,可避免殘留的 ARP 快取造成誤判。
強烈建議把整套動作寫成腳本,用 setsid nohup 脫離 SSH 執行,並內建回滾:套用後自動 ping 閘道驗證,若干秒內驗不過就自動還原。搬移 IP 的瞬間必然斷線,沒有回滾機制的話一旦出錯就要進機房接螢幕。本次第一輪就是靠這個機制自動還原,全程沒有失聯。
這一節很重要,因為預設的測試方式會給出假的成功結果。
當 10G 埠與 1G 埠在同一網段時,即使 10G 完全沒有連線,從外部 ping 10G 的 IP 仍然會有回應,因為 Linux 預設會用任何一張可用介面回應本機所有 IP。
| 測試方式 | 是否可信 | 說明 |
|---|---|---|
ping <10G的IP> | 不可信 | 由 1G 埠代為回應 |
ping -I <10G的IP> <目標> | 不可信 | 綁的是 IP,核心仍會改走 1G |
ping -I mgbe0_0 <目標> | 可信 | 綁實體介面,強制由 10G 送出 |
檢查 rx_packets 增量 | 最可信 | 硬體計數器不會說謊 |
判斷連線狀態的唯一可靠依據:
cat /sys/class/net/mgbe0_0/carrier ethtool -S mgbe0_0 | grep link_connect_count
carrier 回 1 代表實體層已連線。link_connect_count 是硬體計數器,只要曾經 link up 過一次就會累加,數值為 0 表示從未連線成功。
同網段測試的陷阱:兩台機器互相 ping 時,回程封包會依 route metric 選擇介面,可能從 1G 回來,而 ping -I 綁定的 socket 不會接受從其他介面進來的封包,導致明明線路正常卻顯示 100% 逾時。做效能或連通性驗證時,建議在 10G 上另外加一個獨立網段(例如 198.51.100.0/24)避開這個干擾。
10G 無法連線時,依此順序排查:
第一步,確認 NetworkManager 沒有停用該介面(8.3 節)。
第二步,在交換機上執行 show interface status,區分兩種 Reason:XCVR not inserted 是沒插線材,Link not connected 是插了、讀得到、但協商不起來。後者代表實體存在無虞,問題在設定或規格。
第三步,確認交換機埠已拆分為 4×10G(第七章)。這是最常見的原因。
第四步,用 show interface EthX/Y transceiver details 確認線材規格,特別是 PAM4 的 200G 線材無法用於 10G。
第五步,將兩台 Thor 以一條線直接對接。連線成功代表 Thor 與線材都正常,問題在交換機端;仍失敗則問題在模組或線材。這個測試能一次把範圍縮小一半,不需要交換機的管理權限。
四個 10G 埠的 MTU 預設是 1466,不是標準的 1500。開機訊息說明了原因:
nvethernet a808a10000.ethernet: Macsec: Reduced MTU: 1466 Max: 9000
MACsec(IEEE 802.1AE 鏈路層加密)在這些埠上是啟用的。它會在每個 frame 插入 SecTAG 與 ICV 完整性檢查碼,驅動為此保留 34 bytes。硬體與 device tree 的 nvidia,max-platform-mtu 都是 9000,但 MACsec 一開就被壓到 1466。
MACsec 保護的是「一條實體線路上兩個相鄰設備之間」的通訊,防的是有人在實體層接分光器或竊聽線路。它是逐跳加密而非端到端,封包進到交換機裡仍是明文,因此與 TLS、VPN 解決的是不同層次的問題。
典型使用場合是線路經過無法控制的實體空間:機房對機房的租用光纖、跨棟跨樓層的園區網路、電信商基地台回傳、金融專線。
Thor 上面有 MACsec 引擎的真正原因是它是車用平台。 車內 ECU 之間走 Automotive Ethernet,攻擊情境是有人拆開門板接進線束、注入假的感測器資料或控制指令,ISO/SAE 21434 這類車輛資安規範會要求車內網路做鏈路層加密。把車用晶片用於機房邊緣運算時,就繼承了這個為汽車設計的預設值。
實測確認兩台機器都沒有任何 MACsec 工作階段:
ip macsec show → 沒有任何 MACsec 介面 NM 連線類型 → 802-3-ethernet(不是 macsec) /etc/wpa_supplicant.conf → 沒有任何 macsec 設定
系統上執行中的 wpa_supplicant 是 Ubuntu 預設實例,沒有帶 MKA 金鑰協商設定。
另外,交換機端也不支援:
show interface Ethernet1/49 capabilities MACSEC capable: no
因此驅動保留了 34 bytes,給一個從未建立過工作階段、且對端也不支援的功能。
實測結果比預期溫和,不必急著處理。
| 情境 | 實際影響 | 嚴重度 |
|---|---|---|
| 容器內 TCP | PMTUD 自動學習,路由快取記錄 mtu 1466,代價是首次一個 RTT(0.6 ms) | 可忽略 |
| LLM 推論、RAG 查詢 | 封包遠小於 1466,完全碰不到上限 | 無影響 |
| 多人連線 | MTU 與連線數無關 | 無影響 |
| 大檔傳輸吞吐量 | 標頭佔比多約 2.3% | 可忽略 |
| UDP 超過 1438 bytes | 靜默丟棄,不會自癒 | 潛在風險 |
| 跨越會過濾 ICMP 的設備 | PMTUD 黑洞,小封包通、大檔卡死 | 潛在風險 |
實測數據:容器(docker0 為 1500)對外送 1500 bytes DF 封包會被丟,但核心回的 ICMP 讓容器立即把 PMTU 學成 1466,路由快取顯示 cache expires 596sec mtu 1466。因為 ICMP 在同一台機器內部產生不會被過濾,此自癒機制可靠。
結論:改動的理由不是效能,而是消除未來的地雷。 非標準 MTU 會在加入 UDP 服務、流量跨越防火牆、或他人接手時炸出極難查的問題。不建議為此單獨安排停機,排到下次因其他原因重開機時順手處理即可。
編輯 /boot/extlinux/extlinux.conf,在 APPEND 行尾加入參數:
sudo cp -a /boot/extlinux/extlinux.conf /boot/extlinux/extlinux.conf.bak sudo sed -i '/^ *APPEND .*root=PARTUUID/ s/$/ nvethernet.macsec_enable=0/' /boot/extlinux/extlinux.conf
重新開機後生效。這只是加一個模組參數而非更換核心,即使參數無效核心也只會忽略,不影響開機。
驗證五項:
cat /proc/cmdline | tr " " "\n" | grep nvethernet # 應顯示 macsec_enable=0 cat /sys/module/nvethernet/parameters/macsec_enable # 應為 N journalctl -k -b | grep -i macsec # Reduced MTU 訊息應消失 cat /sys/class/net/mgbe0_0/mtu # 應為 1500
實測結果:MTU 自動從 1466 變為標準 1500,服務與既有 10G 鏈路完全不受影響。
關閉 MACsec 後 MTU 上限解到 9000。實測可設定範圍:
| 設定值 | 結果 |
|---|---|
| 1500 | 成功 |
| 4000 | 成功 |
| 9000 | 成功 |
| 9216 | 被拒(超過硬體上限) |
Thor 的硬體上限是 9000,不是交換機慣用的 9216。 交換機端設 9216 沒問題(那是上限值),Thor 端只能設 9000。
要啟用 jumbo frame 必須全鏈路一致:Thor 兩端設 9000、交換機設 system jumbomtu 9216 並在八個子介面設 mtu 9216、Docker 設 9000。漏掉任何一環都會出現「小封包通、大檔卡住」的症狀,而且極難診斷。
主機對外介面是 1466 但 docker0 與自訂 bridge 預設是 1500。雖然 TCP 會自行學習,仍建議在 /etc/docker/daemon.json 明確設定:
{ "mtu": 1466 }既有的自訂 bridge 網路需重建才會套用新值。
重新開機後逐項執行。全部通過才算完成設定。
| 編號 | 檢查項目 | 指令 | 預期結果 |
|---|---|---|---|
| 1 | 主機名 | hostnamectl --static | 非 ubuntu,且各台不同 |
| 2 | 時區 | timedatectl | grep "Time zone" | Asia/Taipei (CST, +0800) |
| 3 | 電源模式 | nvpmodel -q | MAXN |
| 4 | 模式持久化 | cat /var/lib/nvpmodel/status | pmode:0000 |
| 5 | 設定檔預設值 | grep "^< PM_CONFIG" /etc/nvpmodel/nvpmodel_p3834_0008.conf | DEFAULT=0 |
| 6 | 鎖頻服務 | systemctl is-enabled jetson-clocks | enabled |
| 7 | CPU 鎖頻 | jetson_clocks --show | grep ^cpu0: | MinFreq 等於 MaxFreq(2601000) |
| 8 | GPU 鎖頻 | jetson_clocks --show | grep gpu-gpc-0 | MinFreq 等於 MaxFreq(1575000000) |
| 9 | 還原檔存在 | ls -l /etc/l4t_dfs.conf | 檔案存在 |
| 10 | CUDA | /usr/local/cuda/bin/nvcc --version | 顯示 13.2 |
| 11 | Docker | docker --version | 29.7.2 或更新 |
| 12 | GPU 容器支援 | docker info | grep Runtimes | Runtimes 含 nvidia |
| 13 | 交換機拆分 | show interface status | include 1/49 | 八個子介面 connected / full / 10G |
| 14 | 10G 實體連線 | cat /sys/class/net/mgbe0_0/carrier | 1 |
| 15 | 10G 曾連線 | ethtool -S mgbe0_0 | grep link_connect_count | 大於 0 |
| 16 | 對外 IP 在 10G 上 | ip -4 -br addr show mgbe0_0 | 顯示對外位址 |
| 17 | 管理 IP 在 1G 上 | ip -4 -br addr show enP2p1s0 | 顯示管理位址 |
| 18 | 路由優先序 | ip route | grep ^default | 兩筆:mgbe0_0 metric 100、enP2p1s0 metric 700 |
| 19 | 對外走 10G | ip route get 8.8.8.8 | dev mgbe0_0 |
| 20 | 故障切換 | 關閉 10G 後測對外連線 | 自動改走 enP2p1s0 |
| 21 | 吞吐量 | iperf3 -c <對端> -P 4 | 約 8.9 Gbits/sec |
| 22 | 溫度 | 見 5.6 節 | 閒置時低於 60 度 |
第 18 項要特別留意。若出現兩筆 metric 相同的預設路由,代表設定有誤,重開機後可能無法預期地選錯介面。
以下每一項都是本次實際遇到並排除的問題。
nvpmodel -m 0 做半套。 只改執行期狀態而未改設定檔預設值,狀態檔一旦遺失就退回 120W,而且外觀上完全看不出來。兩個地方都要改。jetson_clocks 在 nvpmodel 之前執行。 會鎖到舊模式的上限,得到「顯示 MAXN 但實際跑較低頻率」的機器。systemd 服務必須加 After=nvpmodel.service。--store 就鎖頻。 原始設定就此遺失,--restore 還原不回動態調頻,只能重新開機。nvidia-jetpack 整包。 在 R39 Rev 2.0 上必定失敗於 kernel 與 bootloader 套件的相依性衝突。只裝 cuda-toolkit-13-2。arch=arm64。ping 得通就是 10G 通了。 同網段下 1G 埠會代為回應。務必用 ping -I <介面名> 並檢查 rx_packets 增量。ping -I <10G的IP> 逾時就是線路不通。 回程封包依 route metric 走了 1G,而綁定介面的 socket 不接受它,造成線路正常卻顯示 100% 逾時。應改用獨立網段測試,或直接看硬體計數器。99-unmanage-mgbe.conf。 介面被 NetworkManager 排除後,任何設定都不會生效,且錯誤訊息完全不會提到這支檔。接手既有設備時必須先檢查。PCS block lock SUCCESS 代表已收到光訊號。 經對照實驗確認,該訊息來自驅動的 lane bring-up 流程,不能作為實體連線的判斷依據。唯一可信的是 carrier 與 link_connect_count。48x10GT,全部為 RJ45 銅線埠,無法插光纖模組。採購線材前務必用 show module 確認介質。switchport 與 no shutdown。/sys/class/net/<介面>/mtu 確認。ggml-rpc-server 綁在公網位址上。 RPC 協定沒有認證,等於對外開放無認證的遠端執行介面。必須在 10G 上另掛私有位址並只綁該位址。rpc-server。 實際檔名是 ggml-rpc-server,舊版文件與網路文章多寫成前者。--tensor-split 50,50。 兩台可用記憶體不同時會造成單邊 OOM。llama.cpp 依可用記憶體自動配比更可靠,不要手動干預。GGML_RPC:BOOL=OFF,不重新編譯無法使用 RPC。另外 Thor 出廠映像不含 cmake 與編譯工具鏈,白機需先安裝。本章內容已於 節點A 與 節點B 實機驗證完成,實際跑起 151 GB 的 DeepSeek-V4-Flash Q8,數據皆為實測值。
單台 Thor 的可用記憶體上限實測為 125,810 MiB(約 123 GB),由 ggml-rpc-server 啟動時回報。超過這個數字的模型單機放不下,必須跨機。
兩台合計約 245 GB,扣掉作業系統與其他服務,實務上約 200 GB 可用於模型權重。
兩種平行化對網路的要求差了三個數量級:
| 方式 | 每個 token 跨機傳輸量 | 10G 夠不夠 |
|---|---|---|
| 層切分(pipeline parallel) | 只傳一份 hidden state | 綽綽有餘 |
| 張量切分(tensor parallel) | 每層都要 all-reduce 完整張量 | 完全不夠 |
llama.cpp 的 RPC 模式採用的是層切分。以 hidden dimension 8192、fp16 估算,每個 token 跨機一次僅傳輸 16 KB,解碼階段 30 tokens/秒約需 3.8 Mbps,只佔 10G 頻寬的 0.04%。
兩台都必須重新編譯。節點B 原有的建置是 GGML_RPC:BOOL=OFF,不能直接用。
sudo apt install -y cmake build-essential git ccache git clone --depth 1 https://github.com/ggml-org/llama.cpp.git cd llama.cpp export PATH=/usr/local/cuda/bin:$PATH cmake -B build-rpc -DGGML_CUDA=ON -DGGML_RPC=ON \ -DCMAKE_BUILD_TYPE=Release -DCMAKE_CUDA_ARCHITECTURES=110 cmake --build build-rpc --config Release -j 12
CMAKE_CUDA_ARCHITECTURES=110 對應 Thor 的運算能力 11.0,可用 nvidia-smi --query-gpu=compute_cap --format=csv 確認。建議建置到獨立的 build-rpc/ 目錄,不影響既有的 build/。
Thor 出廠映像不含 cmake 與編譯工具鏈,白機需先安裝。
ggml-rpc-server 啟動時會自行警告:
WARNING: Host is != '127.0.0.1' Never expose the RPC server to an open network! This is an experimental feature and is not secure!
RPC 協定沒有任何認證機制,允許遠端配置記憶體與執行運算。若 10G 網段承載的是可路由的公網位址公網位址,直接綁上去等於對外開放一個無認證的遠端執行介面。
正確做法是在 10G 介面上另外掛一組私有位址,RPC 只綁私有位址:
sudo nmcli con mod mgbe0_0-10g +ipv4.addresses 198.51.100.11/24 sudo nmcli con up mgbe0_0-10g
兩台分別使用 198.51.100.11 與 198.51.100.12。這組位址已寫入 NetworkManager 設定,可跨重開機。設定後確認監聽位址:
ss -tlnp | grep 50052 LISTEN 198.51.100.12:50052
必須是私有位址,不可以是 0.0.0.0 或公網位址。
執行檔名是 ggml-rpc-server,不是 rpc-server。
在 worker 端啟動(此例為 節點B):
cd ~/llama.cpp/build-rpc/bin ./ggml-rpc-server -H 198.51.100.12 -p 50052 -c
-c 啟用本地檔案快取,避免每次載入都重新傳輸權重,強烈建議加上。
在主控端執行(此例為 節點A,模型檔放這裡):
cd ~/llama.cpp/build-rpc/bin ./llama-cli -m <模型第一個分片>.gguf \ --rpc 198.51.100.12:50052 \ -ngl 999 --no-mmap -c 4096 -n 64 --single-turn \ -p "你的提示詞"
多分片模型只需指定第一個分片,其餘會自動載入。
只有主控端需要模型檔。 主控端載入 GGUF 之後,透過 RPC 把該由遠端負責的張量推送過去,worker 端不需要有模型檔。
本次實測 151 GB 的模型中約有 67 GB 經 10G 傳到 節點B。以實測 8.9 Gbps 計算約需 60 秒。這是 10G 真正不可取代的地方——同樣的載入若走 1G 需要多花約 9 分鐘。
ggml-rpc-server -c 的本地快取可讓重複載入省去這段傳輸。
llama.cpp 會依各裝置的可用記憶體自動配比,這比手動指定可靠。
本次實測時 節點B 有 14 個容器佔用 34 GB,可用記憶體只有 88 GB,而 節點A 有 118 GB。自動配比的結果是 91 GB / 67 GB,剛好避開 OOM。若照直覺手動設 --tensor-split 50,50(各 75 GB),節點B 會因記憶體不足而失敗。
DeepSeek-V4-Flash-0731 UD-Q8_K_XL(151 GB,5 分片)跨兩台 Thor
| 項目 | 數值 |
|---|---|
| 模型大小 | 151 GB |
| 載入時間 | 約 6 分鐘(含磁碟讀取與 67 GB 網路傳輸) |
| Prompt 處理 | 18.0 tokens/秒 |
| 文字生成 | 10.7 tokens/秒 |
| 節點A 記憶體用量 | 91 GB |
| 節點B 記憶體用量 | 101 GB(含既有容器 34 GB) |
跨機開銷對照(Gemma-4-26B Q4,16 GB,此模型單機跑得動故可對照)
| 項目 | 單機 | 跨機 | 差異 |
|---|---|---|---|
| Prompt 處理 | 153.9 t/s | 86.7 t/s | −44% |
| 文字生成 | 52.5 t/s | 48.5 t/s | −7.6% |
生成速度只掉 7.6%,證實層切分對頻寬需求極低,主要成本是往返延遲(實測 0.69 ms)。Prompt 處理掉 44% 較多,因為預填階段要一次搬移大量 activation,且兩台是輪流工作而非同時運算。
| 模型 | 量化後大小 | 單機(123 GB) | 雙機(約 200 GB) |
|---|---|---|---|
| gpt-oss-120b MXFP4 | 59 GB | 可 | 不需要 |
| Llama-70B Q8 | 70 GB | 可 | 不需要 |
| DeepSeek-V4-Flash Q8 | 151 GB | 不可 | 已實測可行 |
| Llama-405B Q4_K_M | 約 230 GB | 不可 | 超出,需更低量化 |
單機放得下就不要拆。 層切分是兩台輪流工作而非同時運算,本質上是序列化的,一定比單機慢。只有在模型真的塞不進一台時才值得拆。
目前只用了 mgbe0_0 一條 lane。四條可做 LACP 聚合(802.3ad),頻寬上看約 35 Gbps,交換機端八個子介面已具備條件。
但對解碼階段沒有幫助(本來就只用 0.04% 頻寬),只會改善模型載入時的權重傳輸時間(本次約 60 秒,聚合後可降至約 20 秒)與長提示詞預填。以一次性成本換取多一層設定與故障點,除非要做分散式訓練的梯度同步,否則不建議。
thor-init.sh 涵蓋第三章至第六章的所有設定,可重複執行,已完成的項目會自動略過。
sudo bash thor-init.sh 節點A
test10g.sh 用於驗證流量是否真的走 10G,會顯示 carrier、綁定介面的 ping 結果、以及封包計數器增量。
./test10g.sh mgbe0_0 192.0.2.12
判準是 rx 增量必須大於 0,其他都可能是 1G 代為回應。
ipswap.sh 用於把對外位址從 1G 搬到 10G,內建順序控制與自動回滾。搬移 IP 必然會斷線,務必以 setsid nohup 脫離 SSH 執行。
本文所有指令皆於兩台實機與 L3 網管交換機實際執行驗證。技術問題來信 service@cloudinfo.com.tw。