管理 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 對照
| 用途 | VM | CT |
|---|---|---|
| 前提 | 安裝並啟用 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-write | pct push |
| 取回檔案 | API file-read | pct 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 可以傳檔案,搭配迴圈就能一次處理整個節點的機器。
參考資料
- Proxmox VE 文件:qm(1)
https://pve.proxmox.com/pve-docs/qm.1.html - Proxmox VE 文件:pct(1)
https://pve.proxmox.com/pve-docs/pct.1.html - Proxmox VE Wiki:Qemu-guest-agent
https://pve.proxmox.com/wiki/Qemu-guest-agent - QEMU 文件:QEMU Guest Agent Protocol Reference
https://www.qemu.org/docs/master/interop/qemu-ga-ref.html



