跳到主要內容
節省工具箱Jason Tools
經驗分享

PVE 從節點對 VM 與 CT 下指令

Jason Cheng 發布 瀏覽 1 次
PVE 從節點對 VM 與 CT 下指令
本文目錄
  1. 對 VM 下指令:qm guest
  2. 對 CT 下指令:pct
  3. 批次操作
  4. VM 與 CT 對照
  5. 安全性
  6. 結論
  7. 參考資料

管理 Proxmox VE 時,常常只是想在某台 VM 裡跑個指令:看一下版本、確認服務狀態、查 IP。每次都要開主控台登入,或是先查 IP 再 SSH 進去,台數一多就很浪費時間。

其實在 PVE 節點的 Shell 上,就能直接對 VM 和 CT 下指令,不需要網路連線、不需要 SSH 帳密。VM 是透過 QEMU Guest Agent,CT 則是直接進入容器的命名空間。這篇把兩者的用法整理在一起,包含 CT 推送、取回檔案的方式。

對 VM 下指令:qm guest

前提條件

qm guest 系列指令是透過 QEMU Guest Agent 和 VM 溝通,所以:

  • VM 的「選項 → QEMU Guest Agent」要啟用(啟用後 VM 要完整關機再開機一次才會生效)
  • VM 裡要安裝並啟動 guest agent:
# Debian / Ubuntu
apt install qemu-guest-agent
systemctl enable --now qemu-guest-agent

# RHEL / Rocky / Alma
dnf install qemu-guest-agent
systemctl enable --now qemu-guest-agent

Windows 則是安裝 VirtIO 驅動光碟裡的 guest-agent\qemu-ga-x86_64.msi。

先確認 agent 有沒有回應:

qm guest cmd 102 ping

沒有任何輸出就代表正常;agent 沒裝或沒啟動的話,會回 QEMU guest agent is not running。

qm guest exec:執行指令

qm guest exec 102 -- uname -a

-- 後面就是要在 VM 裡執行的指令與參數。回傳的是 JSON:

{
   "exitcode" : 0,
   "exited" : 1,
   "out-data" : "Linux web01 6.8.0-45-generic #45-Ubuntu SMP ... x86_64 GNU/Linux\n"
}

只要指令輸出的話,用 jq 取出 out-data:

qm guest exec 102 -- uname -a | jq -r '."out-data"'

要注意的地方:

  • 指令不是在 Shell 裡執行的,管線(|)、重新導向(>)、萬用字元(*)都不會作用,要用的話自己包一層 Shell:
qm guest exec 102 -- bash -c "df -h | grep -v tmpfs"
  • 錯誤輸出在 err-data,結束代碼在 exitcode,寫腳本時要一起判斷
  • 預設最多等 30 秒(--timeout),超過就只回傳 PID,指令會繼續在背景執行

長時間執行的指令

不想等的話加 --synchronous 0,直接拿到 PID,之後再查結果:

qm guest exec 102 --synchronous 0 -- bash -c "apt update && apt -y upgrade"
# 回傳 { "pid" : 12345 }

qm guest exec-status 102 12345

exec-status 會回傳 exited 是否已結束,以及輸出內容。

Windows VM

Windows 一樣可以用,透過 cmd 或 powershell 執行:

qm guest exec 105 -- cmd /c "ipconfig /all"
qm guest exec 105 -- powershell -Command "Get-Service | Where-Object Status -eq 'Running'"

Windows 的輸出是 Big5 等本地編碼時,中文可能會變成亂碼,可以在 PowerShell 裡先設定輸出編碼為 UTF-8,或只取需要的英文欄位。

把檔案內容送進 VM

qm guest exec 有個 --pass-stdin 參數,可以把 PVE 節點上的標準輸入轉給 VM 裡的程式,拿來送小檔案很方便(上限 1 MiB):

cat /root/motd.txt | qm guest exec 102 --pass-stdin 1 -- tee /etc/motd

大一點的檔案還是用 scp,或是透過 API 的 agent/file-write。

qm guest cmd:Guest Agent 內建指令

除了執行任意指令,guest agent 還有一組內建的查詢與操作指令,回傳結構化的 JSON,比自己解析指令輸出可靠:

qm guest cmd 102 get-osinfo               # 作業系統名稱與版本
qm guest cmd 102 get-host-name            # 主機名稱
qm guest cmd 102 network-get-interfaces   # 網路介面與 IP
qm guest cmd 102 get-fsinfo               # 檔案系統與使用量
qm guest cmd 102 get-users                # 目前登入的使用者
qm guest cmd 102 get-time                 # VM 內的時間
qm guest cmd 102 info                     # agent 版本與支援的指令

例如只取出 VM 的 IPv4 位址:

qm guest cmd 102 network-get-interfaces | \
  jq -r '.[] | select(.name != "lo") | ."ip-addresses"[]? | select(."ip-address-type" == "ipv4") | ."ip-address"'

完整的指令有:fsfreeze-freeze、fsfreeze-status、fsfreeze-thaw、fstrim、get-fsinfo、get-host-name、get-memory-block-info、get-memory-blocks、get-osinfo、get-time、get-timezone、get-users、get-vcpus、info、network-get-interfaces、ping、shutdown、suspend-disk、suspend-hybrid、suspend-ram。

重設 VM 內的密碼

忘了 VM 裡的帳號密碼時,也可以從 PVE 直接重設:

qm guest passwd 102 root

會要求輸入兩次新密碼。

對 CT 下指令:pct

CT 不需要 guest agent,PVE 節點可以直接進入容器的命名空間執行指令,容器要在執行中。

pct exec:執行指令

pct exec 201 -- uname -a
pct exec 201 -- bash -c "systemctl status nginx --no-pager | head -5"

和 qm guest exec 不同,pct exec 的輸出直接印在畫面上,不是 JSON,結束代碼也會直接傳回,寫腳本更簡單。

注意:pct exec 預設會把 PVE 節點目前的環境變數帶進容器(--keep-env),官方說明這個預設值在 PVE 9 會改為關閉。腳本如果依賴環境變數(例如 PATH、LANG),建議明確加上 --keep-env 1 或在指令裡自己設定。

pct enter:進入容器

要互動操作的話,直接進入容器的 Shell:

pct enter 201

離開時輸入 exit。這是以容器內的 root 身分進入,不需要密碼。

pct push:把檔案推進容器

pct push 201 /root/nginx.conf /etc/nginx/nginx.conf

可以同時指定擁有者與權限:

pct push 201 /root/app.env /opt/app/.env --user app --group app --perms 0600
  • --user / --group:用名稱的話,容器裡要有這個使用者與群組;也可以直接寫數字 ID
  • --perms:預設是八進位,例如 0644、0600

pct pull:從容器取回檔案

pct pull 201 /var/log/nginx/error.log /root/ct201-error.log

一樣可以加 --user、--group、--perms 設定取回後的擁有者與權限。

pct push 和 pull 一次只處理一個檔案,要傳整個目錄可以先在容器裡打包:

pct exec 201 -- tar czf /tmp/nginx-conf.tar.gz -C /etc nginx
pct pull 201 /tmp/nginx-conf.tar.gz /root/ct201-nginx-conf.tar.gz

批次操作

這些指令最好用的地方是批次處理。例如列出節點上所有執行中 VM 的作業系統版本:

for id in $(qm list | awk '$3=="running"{print $1}'); do
    printf '%s\t' "$id"
    qm guest cmd "$id" get-osinfo 2>/dev/null | jq -r '."pretty-name" // "agent 無回應"'
done

所有執行中的 CT 一起更新套件清單:

for id in $(pct list | awk '$2=="running"{print $1}'); do
    echo "== CT $id"
    pct exec "$id" -- bash -c "apt update -qq 2>/dev/null | tail -1"
done

VM 與 CT 對照

用途VMCT
前提安裝並啟用 QEMU Guest Agent容器在執行中
執行指令qm guest exec <id> -- <指令>pct exec <id> -- <指令>
輸出格式JSON(out-data、err-data)直接輸出
互動 Shell無(用主控台)pct enter <id>
推送檔案--pass-stdin(1 MiB 以內)或 API file-writepct push
取回檔案API file-readpct pull
系統資訊qm guest cmd <id> get-osinfo 等pct exec 自己查
重設密碼qm guest passwd <id> <使用者>pct exec 執行 passwd

安全性

能在 PVE 節點上以 root 執行這些指令的人,等於擁有每一台 VM 和 CT 的最高權限,不需要知道任何 VM 裡的密碼。所以:

  • PVE 節點的 root 與 Shell 存取要嚴格控管
  • 透過 API 使用時,給帳號的權限要最小化,VM 的 guest agent 相關權限不要隨便開放
  • VM 裡的 guest agent 執行的指令都有完整權限(Linux 是 root、Windows 是 SYSTEM),有稽核需求的話要另外記錄

結論

qm guest exec 和 pct exec 是我日常維護最常用的指令之一,查版本、看服務、改設定都不用登入。VM 記得裝好 guest agent,CT 則多了 push、pull 可以傳檔案,搭配迴圈就能一次處理整個節點的機器。

參考資料

Jason Cheng

Jason Cheng

企業開源技術顧問,專注架構規劃與實務導入

關於作者 →

延伸閱讀