LXDコンテナへのGPUパススルー、LXD-UIを使えば簡単ですが、わざわざLXD-UIにアクセスするまでもない場合のためにスクリプトを用意しました。KonomiTV環境を構築する際などに利用できます。

ここで作成するスクリプトのフロー
lspciでGPU(VGA/3D/Displayコントローラ)を検出し、PCIアドレス・ベンダー/デバイスID・関連する/dev/driノードを表示- 複数GPUがあれば番号選択、1つだけなら自動選択
- パススルー確認
[y/N] lxc listでコンテナ一覧を番号付きで表示(状態も表示)- 対象コンテナを番号で選択 → 最終確認
- パススルー方式を選択
gpu (pci)方式(推奨): 選んだGPUをPCIアドレスで限定してパススルー。複数GPUがあるホストで「このGPUだけ」を渡せるgpu (physical)方式:/dev/dri配下を丸ごとパススルー。単一GPU環境向け
- NVIDIA GPU(vendor
10de)の場合はnvidia.runtime=trueの設定も提案 - コンテナ起動中なら再起動の確認
補足
- NVIDIA/AMD/IntelどのGPUでも同じスクリプトで対応できるよう、ベンダー判定で分岐しています
- 実行には
pciutils(lspci)が必要です。なければsudo apt install pciutils - パススルー後の動作確認は
lxc exec <container> -- nvidia-smiやls -la /dev/driで行えます - 既存の
gpu0デバイスがあれば自動でgpu1,gpu2… と番号をずらして追加するので、複数GPUを同じコンテナに追加しても安全です
スクリプト(ホストにインストール)
sudo mkdir -p /opt/lxd-data/konomitv-backup
cd /opt/lxd-data/konomitv-backup
sudo nano lxd-gpu-passthrough.sh
sudo bash lxd-gpu-passthrough.sh
#!/usr/bin/env bash
#
# lxd-gpu-passthrough.sh
#
# ホストのGPUをLXDコンテナにパススルーするスクリプト (Ubuntu 26.04 / LXD想定)
#
# 機能:
# 1. ホストで認識しているGPU一覧を表示
# 2. パススルーするGPUを選択 (複数GPUがある場合)
# 3. パススルー実行の確認
# 4. LXDコンテナ一覧を表示し、番号で対象コンテナを指定
# 5. `lxc config device add` で GPUデバイスをコンテナに追加
#
# 注意:
# - LXDはホストカーネルを共有するため、ドライバ(NVIDIAドライバ等)は
# ホスト側にインストールされていればコンテナ側でも基本的に共有可能。
# ただしコンテナ内にも同バージョンのユーザランド(nvidia-utils等)が
# 必要な場合がある。
# - root権限が必要 (sudo実行を推奨)
#
set -euo pipefail
# ---- 色定義 ----
C_RESET='\033[0m'
C_BOLD='\033[1m'
C_GREEN='\033[1;32m'
C_YELLOW='\033[1;33m'
C_RED='\033[1;31m'
C_CYAN='\033[1;36m'
info() { echo -e "${C_CYAN}[INFO]${C_RESET} $*"; }
warn() { echo -e "${C_YELLOW}[WARN]${C_RESET} $*"; }
err() { echo -e "${C_RED}[ERROR]${C_RESET} $*" >&2; }
ok() { echo -e "${C_GREEN}[OK]${C_RESET} $*"; }
# ---- 事前チェック ----
if [[ $EUID -ne 0 ]]; then
err "このスクリプトはroot権限で実行してください (例: sudo $0)"
exit 1
fi
if ! command -v lxc &>/dev/null; then
err "lxc コマンドが見つかりません。LXDがインストールされているか確認してください。"
exit 1
fi
if ! command -v lspci &>/dev/null; then
err "lspci コマンドが見つかりません。'apt install pciutils' でインストールしてください。"
exit 1
fi
echo -e "${C_BOLD}=== LXD GPUパススルー設定スクリプト ===${C_RESET}"
echo
# ---------------------------------------------------------------------------
# 1. GPU検出
# ---------------------------------------------------------------------------
info "ホストのGPUをスキャン中..."
echo
# VGA / 3D / Display controller を対象に抽出
mapfile -t GPU_LINES < <(lspci -Dnn | grep -Ei 'VGA compatible controller|3D controller|Display controller')
if [[ ${#GPU_LINES[@]} -eq 0 ]]; then
err "GPUが検出されませんでした。"
exit 1
fi
declare -a GPU_PCI_ADDR
declare -a GPU_DESC
declare -a GPU_VENDOR_ID
declare -a GPU_DEVICE_ID
idx=0
for line in "${GPU_LINES[@]}"; do
# 例: 0000:01:00.0 VGA compatible controller [0300]: NVIDIA Corporation ... [10de:2204] (rev a1)
pci_addr=$(echo "$line" | awk '{print $1}')
desc=$(echo "$line" | sed -E 's/^[^ ]+ //')
ids=$(echo "$line" | grep -oP '\[\K[0-9a-f]{4}:[0-9a-f]{4}(?=\])' | tail -1)
vendor_id=$(echo "$ids" | cut -d: -f1)
device_id=$(echo "$ids" | cut -d: -f2)
GPU_PCI_ADDR+=("$pci_addr")
GPU_DESC+=("$desc")
GPU_VENDOR_ID+=("$vendor_id")
GPU_DEVICE_ID+=("$device_id")
echo -e " ${C_BOLD}[$idx]${C_RESET} ${pci_addr} ${desc}"
# 関連するドライバ情報も表示 (lspci -k)
driver_info=$(lspci -ks "${pci_addr#0000:}" 2>/dev/null | grep -E 'Kernel driver in use|Kernel modules' || true)
if [[ -n "$driver_info" ]]; then
echo "$driver_info" | sed 's/^/ /'
fi
# 関連するレンダーノード / カードノードも表示
for drm in /dev/dri/by-path/*"${pci_addr#0000:}"*; do
[[ -e "$drm" ]] && echo " -> $(readlink -f "$drm" 2>/dev/null || echo "$drm")"
done
idx=$((idx+1))
echo
done
# ---------------------------------------------------------------------------
# 2. パススルーするGPUの選択
# ---------------------------------------------------------------------------
SELECTED_GPU_IDX=0
if [[ ${#GPU_PCI_ADDR[@]} -gt 1 ]]; then
while true; do
read -rp "パススルーするGPUの番号を入力してください [0-$((${#GPU_PCI_ADDR[@]}-1))]: " sel
if [[ "$sel" =~ ^[0-9]+$ ]] && (( sel >= 0 && sel < ${#GPU_PCI_ADDR[@]} )); then
SELECTED_GPU_IDX=$sel
break
fi
warn "無効な番号です。再入力してください。"
done
else
info "GPUが1つのみ検出されたため、これを使用します。"
fi
SEL_PCI=${GPU_PCI_ADDR[$SELECTED_GPU_IDX]}
SEL_DESC=${GPU_DESC[$SELECTED_GPU_IDX]}
SEL_VENDOR=${GPU_VENDOR_ID[$SELECTED_GPU_IDX]}
SEL_DEVICE=${GPU_DEVICE_ID[$SELECTED_GPU_IDX]}
# lxc config device add で使うPCIアドレス表記 (0000:01:00.0 -> 0000:01:00.0 のままでOK)
echo
echo -e "${C_BOLD}選択されたGPU:${C_RESET}"
echo " PCIアドレス : $SEL_PCI"
echo " デバイス : $SEL_DESC"
echo " Vendor:Device : ${SEL_VENDOR}:${SEL_DEVICE}"
echo
read -rp "このGPUをLXDコンテナにパススルーしますか? [y/N]: " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
info "キャンセルしました。"
exit 0
fi
# ---------------------------------------------------------------------------
# 3. LXDコンテナ一覧表示・選択
# ---------------------------------------------------------------------------
echo
info "LXDコンテナ一覧を取得中..."
echo
mapfile -t CONTAINERS < <(lxc list -c n --format csv 2>/dev/null)
if [[ ${#CONTAINERS[@]} -eq 0 ]]; then
err "LXDコンテナが見つかりませんでした。"
exit 1
fi
declare -a CT_STATUS
idx=0
printf " %-4s %-25s %-10s\n" "No." "コンテナ名" "状態"
printf " %-4s %-25s %-10s\n" "---" "-------------------------" "----------"
for ct in "${CONTAINERS[@]}"; do
status=$(lxc list "^${ct}\$" -c s --format csv 2>/dev/null)
CT_STATUS+=("$status")
printf " ${C_BOLD}%-4s${C_RESET} %-25s %-10s\n" "[$idx]" "$ct" "$status"
idx=$((idx+1))
done
echo
while true; do
read -rp "パススルー先のコンテナ番号を入力してください [0-$((${#CONTAINERS[@]}-1))]: " ct_sel
if [[ "$ct_sel" =~ ^[0-9]+$ ]] && (( ct_sel >= 0 && ct_sel < ${#CONTAINERS[@]} )); then
break
fi
warn "無効な番号です。再入力してください。"
done
TARGET_CT=${CONTAINERS[$ct_sel]}
TARGET_STATUS=${CT_STATUS[$ct_sel]}
echo
echo -e "${C_BOLD}対象コンテナ:${C_RESET} $TARGET_CT (状態: $TARGET_STATUS)"
echo
read -rp "コンテナ '$TARGET_CT' に GPU ($SEL_DESC) をパススルーします。よろしいですか? [y/N]: " final_confirm
if [[ ! "$final_confirm" =~ ^[Yy]$ ]]; then
info "キャンセルしました。"
exit 0
fi
# ---------------------------------------------------------------------------
# 4. パススルー方式の選択 (PCI方式 / GPUデバイス方式)
# ---------------------------------------------------------------------------
echo
echo -e "${C_BOLD}パススルー方式を選択してください:${C_RESET}"
echo " [0] gpu (pci) デバイス - 指定したGPUのみをPCIアドレスで限定してパススルー (推奨・複数GPU環境向け)"
echo " [1] gpu (physical) デバイス - /dev/dri 以下の全GPUデバイスを丸ごとパススルー (単一GPU環境向け)"
echo
while true; do
read -rp "方式番号を選択 [0/1] (デフォルト: 0): " mode_sel
mode_sel=${mode_sel:-0}
if [[ "$mode_sel" == "0" || "$mode_sel" == "1" ]]; then
break
fi
warn "0 か 1 を入力してください。"
done
DEVICE_NAME="gpu0"
# 既存の同名デバイスがあれば確認
if lxc config device show "$TARGET_CT" 2>/dev/null | grep -q "^${DEVICE_NAME}:"; then
n=1
while lxc config device show "$TARGET_CT" 2>/dev/null | grep -q "^gpu${n}:"; do
n=$((n+1))
done
DEVICE_NAME="gpu${n}"
fi
echo
info "デバイス名 '$DEVICE_NAME' としてコンテナに追加します。"
if [[ "$mode_sel" == "0" ]]; then
# PCIアドレス指定方式 (gputype=physical, pci=<addr>)
# lxc が要求するPCIアドレス形式は "0000:01:00.0" のようなフルアドレス
info "実行コマンド: lxc config device add \"$TARGET_CT\" \"$DEVICE_NAME\" gpu gputype=physical pci=\"$SEL_PCI\""
if lxc config device add "$TARGET_CT" "$DEVICE_NAME" gpu gputype=physical pci="$SEL_PCI"; then
ok "GPU (PCI: $SEL_PCI) をコンテナ '$TARGET_CT' に追加しました (デバイス名: $DEVICE_NAME)"
else
err "デバイス追加に失敗しました。"
exit 1
fi
else
# 物理デバイス丸ごと方式
info "実行コマンド: lxc config device add \"$TARGET_CT\" \"$DEVICE_NAME\" gpu gputype=physical"
if lxc config device add "$TARGET_CT" "$DEVICE_NAME" gpu gputype=physical; then
ok "GPU (全/dev/driデバイス) をコンテナ '$TARGET_CT' に追加しました (デバイス名: $DEVICE_NAME)"
else
err "デバイス追加に失敗しました。"
exit 1
fi
fi
# ---------------------------------------------------------------------------
# 5. NVIDIAドライバの場合の追加情報
# ---------------------------------------------------------------------------
if [[ "$SEL_VENDOR" == "10de" ]]; then
echo
warn "NVIDIA GPUが検出されました。NVIDIAドライバを使用する場合は以下も確認してください:"
echo " - ホストに 'nvidia-container-toolkit' 相当のCDI設定が必要な場合があります"
echo " - LXD 5.x以降では 'nvidia.runtime=true' を設定することでNVIDIAランタイムを"
echo " 自動的にコンテナへ注入できます:"
echo " lxc config set \"$TARGET_CT\" nvidia.runtime=true"
echo " - コンテナ起動後、コンテナ内で 'nvidia-smi' が使えるか確認してください"
echo
read -rp "nvidia.runtime=true をこのコンテナに設定しますか? [y/N]: " nv_confirm
if [[ "$nv_confirm" =~ ^[Yy]$ ]]; then
lxc config set "$TARGET_CT" nvidia.runtime=true
ok "nvidia.runtime=true を設定しました。"
fi
fi
# ---------------------------------------------------------------------------
# 6. コンテナ再起動の確認
# ---------------------------------------------------------------------------
echo
if [[ "$TARGET_STATUS" == "RUNNING" ]]; then
read -rp "設定を反映するためコンテナ '$TARGET_CT' を再起動しますか? [y/N]: " restart_confirm
if [[ "$restart_confirm" =~ ^[Yy]$ ]]; then
info "コンテナを再起動中..."
lxc restart "$TARGET_CT"
ok "再起動しました。"
else
warn "設定を完全に反映するには手動で再起動してください: lxc restart $TARGET_CT"
fi
else
info "コンテナは停止中です。次回起動時に設定が反映されます。"
fi
echo
ok "完了しました。"
echo
echo "確認コマンド:"
echo " lxc config device show $TARGET_CT"
echo " lxc exec $TARGET_CT -- ls -la /dev/dri"
[[ "$SEL_VENDOR" == "10de" ]] && echo " lxc exec $TARGET_CT -- nvidia-smi"
パススルーされているかの確認
コマンドで確認してもよいのですが、「EasyLXD」を導入しておくと簡単です。


