以前、Webブラウザ経由でRDP接続出来る「Apache Guacamole」を紹介しましたが、仮想マシンに限定するならば「WebVirtCloud」 というツールもあります。noVNC経由で画面表示ができ、VNCだけでなくSPICE経由でも表示可能となっています。
ただ、そんなに描画が早い訳では無いので確認程度でしょうか。これならCockpitでも十分な気が。複数のマシンで動いているVMを一括管理するのに適していそうでしょうか。
VMホストに直接インストール
一応、LXDコンテナ内でも動くと思いますが、直接インストールしたほうが楽でしょう。
nano install_webvirtcloud.sh
chmod +x install_webvirtcloud.sh
sudo ./install_webvirtcloud.sh
#!/bin/bash
# ============================================================
# WebVirtCloud インストールスクリプト
# 対象: Ubuntu 24.04 / 26.04 (LXD コンテナ・ホスト両対応)
# 使い方: sudo bash install_webvirtcloud.sh
#
# Python 3.13+ 対応:
# - crypt_r パッケージビルド不可 → ソースを直接パッチ
# - import crypt_r → hashlib/hmac で代替実装を注入
# ============================================================
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
[[ $EUID -ne 0 ]] && error "root または sudo で実行してください"
APP_PATH="/srv/webvirtcloud"
PY_VER=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
info "検出した Python バージョン: ${PY_VER}"
IS_PY313_PLUS=false
python3 -c "import sys; exit(0 if sys.version_info >= (3,13) else 1)" 2>/dev/null && IS_PY313_PLUS=true
# ── 1. パッケージ更新 ─────────────────────────────────────────
info "パッケージを更新中..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get upgrade -y -qq
# ── 2. 依存パッケージインストール ────────────────────────────
info "依存パッケージをインストール中..."
apt-get install -y -qq \
git \
python3 python3-dev python3-pip python3-venv python3-lxml \
python3-guestfs \
libvirt-dev libxml2-dev libxslt1-dev \
zlib1g-dev libffi-dev libssl-dev \
libsasl2-dev libsasl2-modules \
libldap2-dev \
gcc pkg-config \
nginx supervisor \
curl wget
# libxcrypt-dev: 名前がディストロで異なるため試行
for PKG in libxcrypt-dev libcrypt-dev; do
apt-cache show "$PKG" &>/dev/null && \
apt-get install -y -qq "$PKG" && \
info " → ${PKG} インストール済み" && break || true
done
# ── 3. WebVirtCloud クローン ──────────────────────────────────
info "WebVirtCloud をクローン中..."
mkdir -p /srv
if [[ -d "$APP_PATH" ]]; then
warn "$APP_PATH が存在します。バックアップして再クローンします..."
mv "$APP_PATH" "${APP_PATH}.bak.$(date +%Y%m%d%H%M%S)"
fi
git clone --depth 1 https://github.com/retspen/webvirtcloud "$APP_PATH"
cd "$APP_PATH"
# ── 4. Python 3.13+ ソースパッチ ─────────────────────────────
# crypt_r は Python 3.13 でビルド不可かつ廃止。
# ソース内の "import crypt_r" を shim モジュールで置き換える。
if $IS_PY313_PLUS; then
info "Python ${PY_VER}: crypt_r ソースパッチを適用中..."
# --- shim: crypt_r.py を作成 ---
# crypt_r の実際の使われ方 = パスワードハッシュ生成/検証
# hashlib + hmac + secrets で同等機能を提供
cat > "${APP_PATH}/crypt_r.py" << 'SHIM_EOF'
"""
crypt_r compatibility shim for Python 3.13+
crypt/crypt_r が削除されたため hashlib で代替実装
"""
import hashlib, hmac, os, re
SHA512_PREFIX = "$6$"
SHA256_PREFIX = "$5$"
def crypt(word: str, salt: str = None) -> str:
"""SHA-512 ベースのパスワードハッシュ (crypt_r.crypt 互換)"""
if salt is None:
salt = SHA512_PREFIX + _make_salt(16)
# 既存ハッシュ値が渡された場合はそこからソルトを抽出
m = re.match(r'(\$[0-9]\$[^$]+\$?)', salt)
if m:
salt_part = m.group(1)
else:
salt_part = SHA512_PREFIX + salt[:16]
# SHA-512 で反復ハッシュ (簡易実装)
raw = (word + salt_part).encode()
digest = hashlib.sha512(raw).digest()
for _ in range(5000):
digest = hashlib.sha512(digest + raw).digest()
return salt_part + "$" + digest.hex()
def _make_salt(n: int = 16) -> str:
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
return "".join(chars[b % len(chars)] for b in os.urandom(n))
# モジュールレベルで crypt 関数を属性として公開
METHOD_SHA512 = SHA512_PREFIX
METHOD_SHA256 = SHA256_PREFIX
SHIM_EOF
info " → ${APP_PATH}/crypt_r.py (shim) を作成しました"
# --- ソース内の import を grep して確認 ---
info " crypt_r を使用しているファイル:"
grep -rl "crypt_r" "${APP_PATH}" --include="*.py" | grep -v "__pycache__" | grep -v "crypt_r.py" || true
fi
# ── 5. venv 作成 ─────────────────────────────────────────────
info "Python 仮想環境を作成中..."
python3 -m venv --system-site-packages venv
source venv/bin/activate
# ── 6. requirements.txt パッチ ────────────────────────────────
info "requirements.txt をパッチ中..."
if $IS_PY313_PLUS; then
# crypt-r はビルド不可のため除外
sed -i '/crypt[-_]r/Id' conf/requirements.txt
# lxml は apt 版を使用するため pip ビルド除外
sed -i '/^lxml[^-]/Id' conf/requirements.txt
info " → crypt-r, lxml を除外しました"
fi
info "requirements.txt (パッチ後):"
grep -v "^#" conf/requirements.txt | grep -v "^$" || true
# ── 7. pip インストール ───────────────────────────────────────
info "pip を最新化中..."
pip install --upgrade pip -q
info "Python パッケージをインストール中... (数分かかります)"
pip install -r conf/requirements.txt || {
warn "一括インストール失敗。個別にリトライします..."
while IFS= read -r pkg || [[ -n "$pkg" ]]; do
[[ -z "$pkg" || "$pkg" =~ ^# ]] && continue
pip install "$pkg" -q && info " OK: $pkg" || warn " SKIP: $pkg"
done < conf/requirements.txt
}
# ── 8. settings.py & SECRET_KEY ──────────────────────────────
info "settings.py を設定中..."
cp webvirtcloud/settings.py.template webvirtcloud/settings.py
SECRET_KEY=$(python3 -c "
import random, string
chars = string.ascii_letters + string.digits + '!@#\$%^&*(-_=+)'
print(''.join(random.SystemRandom().choice(chars) for _ in range(50)))
")
[[ -z "$SECRET_KEY" ]] && error "SECRET_KEY の生成に失敗しました"
sed -i "s|SECRET_KEY = ''|SECRET_KEY = '${SECRET_KEY}'|" webvirtcloud/settings.py
sed -i 's|SECRET_KEY = ""|SECRET_KEY = "'"${SECRET_KEY}"'"|' webvirtcloud/settings.py
if ! grep -qE "SECRET_KEY = '.{10,}'" webvirtcloud/settings.py; then
python3 -c "
import re, pathlib
p = pathlib.Path('webvirtcloud/settings.py')
c = p.read_text()
c = re.sub(r\"SECRET_KEY\s*=\s*['\\\"].*?['\\\"]\", \"SECRET_KEY = '${SECRET_KEY}'\", c)
p.write_text(c)
print('SECRET_KEY を Python で書き込みました')
"
fi
grep "SECRET_KEY" webvirtcloud/settings.py | head -1
# CSRF_TRUSTED_ORIGINS
HOST_IP=$(hostname -I | awk '{print $1}')
FQDN=$(hostname -f 2>/dev/null || hostname)
TRUSTED="['http://${FQDN}', 'http://${HOST_IP}', 'http://localhost', 'http://127.0.0.1']"
if grep -q "^CSRF_TRUSTED_ORIGINS" webvirtcloud/settings.py; then
sed -i "s|^CSRF_TRUSTED_ORIGINS = .*|CSRF_TRUSTED_ORIGINS = ${TRUSTED}|" webvirtcloud/settings.py
else
echo "CSRF_TRUSTED_ORIGINS = ${TRUSTED}" >> webvirtcloud/settings.py
fi
# ── 9. DB migrate & collectstatic ────────────────────────────
info "データベースを初期化中..."
python3 manage.py migrate --noinput
info "静的ファイルを収集中..."
python3 manage.py collectstatic --noinput
deactivate
# ── 10. パーミッション ────────────────────────────────────────
chown -R www-data:www-data "$APP_PATH"
# ── 11. Nginx 設定 ────────────────────────────────────────────
info "Nginx を設定中..."
cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/webvirtcloud.conf
rm -f /etc/nginx/sites-enabled/default
sed -i "s|server_name .*;|server_name ${FQDN} ${HOST_IP} localhost;|" \
/etc/nginx/conf.d/webvirtcloud.conf
# ── 12. Supervisor 設定 ───────────────────────────────────────
info "Supervisor を設定中..."
cp conf/supervisor/webvirtcloud.conf /etc/supervisor/conf.d/webvirtcloud.conf
SUPERVISOR_CONF="/etc/supervisor/supervisord.conf"
if ! grep -q "\[inet_http_server\]" "$SUPERVISOR_CONF" 2>/dev/null; then
printf '\n[inet_http_server]\nport = 127.0.0.1:9001\n' >> "$SUPERVISOR_CONF"
fi
# ── 13. サービス起動 ──────────────────────────────────────────
info "サービスを起動中..."
if command -v systemctl &>/dev/null && systemctl list-units &>/dev/null 2>&1; then
systemctl enable nginx supervisor 2>/dev/null || true
systemctl restart nginx
systemctl restart supervisor
sleep 2
supervisorctl reread || true
supervisorctl update || true
supervisorctl start webvirtcloud:* 2>/dev/null || true
supervisorctl start novncd 2>/dev/null || true
else
warn "systemd 不可 → 直接起動します"
nginx -t && nginx &
supervisord -c /etc/supervisor/supervisord.conf &
sleep 3
supervisorctl -c /etc/supervisor/supervisord.conf reread || true
supervisorctl -c /etc/supervisor/supervisord.conf update || true
fi
# ── 14. 完了 ──────────────────────────────────────────────────
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} WebVirtCloud インストール完了!${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
echo -e " アクセスURL : ${YELLOW}http://${HOST_IP}/${NC}"
echo -e " ユーザー名 : ${YELLOW}admin${NC}"
echo -e " パスワード : ${YELLOW}admin${NC} (ログイン後すぐ変更してください)"
echo ""
echo -e " noVNC ポート : ${YELLOW}6080${NC}"
echo ""
echo -e " ローカルVM管理の追加設定:"
echo -e " sudo -u www-data ssh-keygen -t rsa -N '' -f /var/lib/www-data/.ssh/id_rsa"
echo -e " cat /var/lib/www-data/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys"
echo -e " WebUI: Computes → Add → SSH → 127.0.0.1"
echo ""
ローカルVMが見えない場合は権限を追加します。
# www-data を libvirt・kvm グループに追加
sudo usermod -aG libvirt www-data
sudo usermod -aG kvm www-data
# supervisor を再起動してグループ変更を反映
sudo systemctl restart supervisor
# 反映確認
groups www-data
# 再テスト(これが通ればOK)
sudo -u www-data virsh -c qemu:///system list
Webから管理
Cockpitの仮想マシンよりは細かな設定ができます。ストレージの拡大などがGUIで手軽に出来るのは便利ですね。


一応、Docker版もあるようです。
アンインストール
# 1. サービス停止
sudo supervisorctl stop all
sudo systemctl stop supervisor nginx
sudo systemctl disable supervisor nginx
# 2. WebVirtCloud 本体削除
sudo rm -rf /srv/webvirtcloud
# 3. Nginx・Supervisor 設定削除
sudo rm -f /etc/nginx/conf.d/webvirtcloud.conf
sudo rm -f /etc/supervisor/conf.d/webvirtcloud.conf
# 4. Nginx デフォルトサイト復元(必要なら)
sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
# 5. インストールしたパッケージを削除(任意)
sudo apt-get remove -y nginx supervisor
sudo apt-get autoremove -y
# 6. Nginx・Supervisor を残す場合はリロードだけ
sudo systemctl restart nginx
sudo systemctl restart supervisor
注意点:
nginxやsupervisorを他の用途でも使っている場合はステップ5はスキップしてください- WebVirtCloudのDBは
/srv/webvirtcloud/db.sqlite3にあるので、VMの設定情報も一緒に消えます(VM自体は消えません) /var/lib/www-data/.ssh/に鍵を作った場合はsudo rm -rf /var/lib/www-dataも追加してください

