mcpField
W
windows-vm

windows-vm

github.com/obra云与 DevOps434274 次安装21 周前更新
代码仓库

关于 windows-vm

windows-vm 是通过 Docker 和 KVM 运行的无头 Windows 11 虚拟机管理技能,面向需要 SSH 访问 Windows 环境的开发者。它涵盖虚拟机创建、生命周期管理、连接和屏幕调试。

主要功能

  1. 01Docker 中创建无头 Windows 11 VM
  2. 02通过 SSH 连接虚拟机
  3. 03启动、停止、重启和查看状态
  4. 04截取虚拟机屏幕用于调试
  5. 05缓存 ISO 以便重建

适用场景

  1. 01提供隔离的 Windows 命令行环境
  2. 02通过 SSH 管理 Windows VM
  3. 03调试虚拟机启动画面
  4. 04保留 ISO 以减少重建下载

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/obra/superpowers-lab --skill windows-vm

安装后在 Claude Code 中输入/windows-vm确认已加载。

详细介绍

无头 Windows 11 VM

在 Docker 中通过 dockur/windows 运行使用 KVM 加速的无头 Windows 11 VM。只能通过 SSH 访问 VM — 无需 RDP 或 GUI。

主机前置条件

  • Docker
  • 支持 KVM(必须存在 /dev/kvm — 使用 ls /dev/kvm 检查)
  • sshpass(sudo apt install sshpass)
  • imagemagick(可选,用于截图调试:sudo apt install imagemagick)

配置

  • 容器名称:windows11
  • VM 目录:$HOME/windows-vm/
    • storage/ — VM 磁盘映像(由 dockur 管理,重新创建时会清除)
    • iso/win11x64.iso — 缓存的 Windows ISO(7.3GB,重新创建后仍会保留)
    • oem/install.bat — 安装后脚本(安装 OpenSSH Server)
  • 凭据:用户名 / 密码
  • SSH:localhost:2222(仅绑定到 127.0.0.1)
  • RDP:localhost:3389(仅绑定到 127.0.0.1,备用)
  • Web 控制台:localhost:8006(浏览器中的 VNC,用于调试)
  • 资源:8GB 内存、4 个 CPU 核心、64GB 磁盘

操作

create — 首次设置或完整重新创建
  1. 确保目录存在:

    mkdir -p "$HOME/windows-vm/oem" "$HOME/windows-vm/storage" "$HOME/windows-vm/iso"
    
  2. 确保 $HOME/windows-vm/oem/install.bat 存在并包含 OpenSSH 设置:

    @echo off
    echo Installing OpenSSH Server...
    powershell -Command "Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0" 2>nul
    powershell -Command "Get-WindowsCapability -Online -Name OpenSSH.Server* | Add-WindowsCapability -Online" 2>nul
    dism /Online /Add-Capability /CapabilityName:OpenSSH.Server~~~~0.0.1.0 2>nul
    powershell -Command "Start-Service sshd" 2>nul
    powershell -Command "Set-Service -Name sshd -StartupType Automatic"
    powershell -Command "New-ItemProperty -Path 'HKLM:\SOFTWARE\OpenSSH' -Name DefaultShell -Value 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -PropertyType String -Force"
    powershell -Command "New-NetFirewallRule -Name 'OpenSSH-Server' -DisplayName 'OpenSSH Server' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22"
    powershell -Command "Get-Service sshd" 2>nul
    echo Done.
    
  3. 如果重新创建,请移除旧容器和磁盘:

    docker stop windows11 && docker rm windows11
    rm -f "$HOME/windows-vm/storage/data.img"
    
  4. 启动容器。有两种情况:

    如果缓存的 ISO 存在($HOME/windows-vm/iso/win11x64.iso):

    docker run -d \
      --name windows11 \
      -p 127.0.0.1:3389:3389 \
      -p 127.0.0.1:2222:22 \
      -p 127.0.0.1:8006:8006 \
      -e RAM_SIZE="8G" \
      -e CPU_CORES="4" \
      -e DISK_SIZE="64G" \
      -e USERNAME="user" \
      -e PASSWORD="password" \
      --cap-add NET_ADMIN \
      --device /dev/kvm \
      -v "$HOME/windows-vm/storage:/storage" \
      -v "$HOME/windows-vm/oem:/oem" \
      -v "$HOME/windows-vm/iso/win11x64.iso:/boot.iso" \
      dockurr/windows
    

    首次创建(没有缓存的 ISO) — 省略 /boot.iso 挂载并添加 VERSION:

    docker run -d \
      --name windows11 \
      -p 127.0.0.1:3389:3389 \
      -p 127.0.0.1:2222:22 \
      -p 127.0.0.1:8006:8006 \
      -e RAM_SIZE="8G" \
      -e CPU_CORES="4" \
      -e DISK_SIZE="64G" \
      -e VERSION="win11" \
      -e USERNAME="user" \
      -e PASSWORD="password" \
      --cap-add NET_ADMIN \
      --device /dev/kvm \
      -v "$HOME/windows-vm/storage:/storage" \
      -v "$HOME/windows-vm/oem:/oem" \
      dockurr/windows
    

    ISO 下载完成且 Windows 启动后,必须在停止容器之前立即将 ISO 复制出来(dockur 在重新创建时会清除 /storage):

    cp "$HOME/windows-vm/storage/win11x64.iso" "$HOME/windows-vm/iso/win11x64.iso"
    
  5. 等待 Windows 安装和 OpenSSH 设置完成。全新安装需要 20-30 分钟,因为 OEM install.bat 会在 Windows OOBE 结束时运行,并从 Microsoft 下载 OpenSSH,过程较慢。使用以下命令监控:

    docker logs -f windows11
    

    也可以通过 http://localhost:8006 的 Web 控制台查看 VM 屏幕。

    检查 SSH 是否已启动:

    sshpass -p 'password' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -p 2222 user@localhost "whoami"
    
  6. SSH 可用后,通过 stdin 管道传入设置脚本来安装 Node.js 和 Claude Code(避免通过 SSH 使用 PowerShell 时繁琐的转义处理):

    cat << 'PS' | sshpass -p 'password' ssh -o StrictHostKeyChecking=no -p 2222 user@localhost "powershell -ExecutionPolicy Bypass -Command -"
    # Download and install Node.js silently
    Invoke-WebRequest -Uri 'https://nodejs.org/dist/v22.14.0/node-v22.14.0-x64.msi' -OutFile 'C:\Users\user\node-install.msi'
    Start-Process msiexec.exe -ArgumentList '/i C:\Users\user\node-install.msi /qn /norestart' -Wait -Verb RunAs
    Write-Host "Node.js installed"
    
    # Install Claude Code globally
    & 'C:\Program Files\nodejs\npm.cmd' install -g @anthropic-ai/claude-code
    Write-Host "Claude Code installed"
    
    # Add npm global bin to SYSTEM PATH (user PATH is not read by sshd)
    $systemPath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
    $additions = @()
    if ($systemPath -notlike '*AppData*npm*') { $additions += 'C:\Users\user\AppData\Roaming\npm' }
    if ($systemPath -notlike '*Git\cmd*') { $additions += 'C:\Program Files\Git\cmd' }
    if ($additions.Count -gt 0) {
        [Environment]::SetEnvironmentVariable('Path', $systemPath + ';' + ($additions -join ';'), 'Machine')
        Write-Host "Added to system PATH: $($additions -join ', ')"
    }
    
    # Set execution policy machine-wide (required for claude.ps1)
    Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force -ErrorAction SilentlyContinue
    
    # Create system-wide PowerShell profile that rebuilds PATH from registry on login.
    # Without this, interactive SSH sessions don't pick up the full system PATH.
    $profileDir = Split-Path $PROFILE.AllUsersAllHosts
    if (-not (Test-Path $profileDir)) { New-Item -ItemType Directory -Path $profileDir -Force }
    @'
    $machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
    $userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
    $env:Path = "$machinePath;$userPath"
    '@ | Set-Content -Path $PROFILE.AllUsersAllHosts -Force
    Write-Host "PowerShell profile created"
    
    # Restart sshd so it picks up the new PATH
    Restart-Service sshd -Force
    PS
    

    注意:sshd 重启时连接会断开 — 这是正常现象。

  7. 清除过期的主机密钥(新 VM 会有新的主机密钥)并进行验证:

    ssh-keygen -f ~/.ssh/known_hosts -R '[localhost]:2222'
    sshpass -p 'password' ssh -o StrictHostKeyChecking=no -p 2222 user@localhost "claude --version"
    
start — 启动已停止的 VM
docker start windows11
stop — 停止 VM
docker stop windows11
restart — 重启 VM
docker restart windows11
status — 检查 VM 状态
docker ps -f name=windows11 --format "table {{.Status}}\t{{.Ports}}"
docker logs windows11 2>&1 | tail -5
ssh — 连接到 VM
ssh -p 2222 user@localhost
screenshot — 查看 VM 屏幕内容(用于调试)
docker exec windows11 bash -c "echo 'screendump /tmp/screen.ppm' | nc -w 2 localhost 7100" > /dev/null 2>&1
sleep 1
docker cp windows11:/tmp/screen.ppm /tmp/screen.ppm
convert /tmp/screen.ppm /tmp/screen.png

重要说明

  • ISO 缓存:/storage 卷由 dockur 管理,重新创建时会被清除。请将 ISO 单独存放在 $HOME/windows-vm/iso/,并挂载为 /boot.iso,以跳过 7.3GB 的下载。
  • --cap-add NET_ADMIN 是端口转发正常工作的必需项。没有它,QEMU 会回退到用户模式网络,且端口转发会静默失效。
  • --device /dev/kvm 是硬件加速的必需项。
  • 启动时间:全新安装需要 20-30 分钟(安装 Windows 并从 Microsoft 下载 OpenSSH)。使用现有 data.img 后续启动很快(约 2 分钟)。
  • 端口仅绑定到 127.0.0.1 — 不会暴露到网络。
  • 挂载 /boot.iso 时,不要使用 -e VERSION="win11" — 版本会根据 ISO 自动检测。

安装后的注意事项

  • 未预装 Node.js — Claude Code 安装脚本(irm https://claude.ai/install.ps1 | iex)会报告成功,但没有 Node.js,claude 将无法运行。请先通过 MSI 安装 Node.js。
  • npm 全局二进制目录不在 PATH 中 — Node 的 MSI 会将 C:\Program Files\nodejs 添加到 PATH,但不会添加 C:\Users\user\AppData\Roaming\npm(npm install -g 会将二进制文件放在此处)。必须将其添加到系统 PATH(而不是用户 PATH),因为 OpenSSH 的 sshd 只读取系统 PATH。更改系统 PATH 后,请重启 sshd。
  • PowerShell 执行策略 — 默认策略为 Restricted,会阻止运行 claude.ps1。必须在 LocalMachine 范围(而不是 CurrentUser)将策略设为 RemoteSigned,这样才能在 SSH 会话中生效。
  • 转义问题 — 通过 SSH 运行带有嵌套引号的 PowerShell 命令并不可靠。请使用 powershell -ExecutionPolicy Bypass -Command -,通过 stdin 传递脚本。
  • 交互式 SSH 会话无法获取完整 PATH — Windows OpenSSH sshd 无法正确地将系统 PATH 传递到交互式 PowerShell 会话。解决方法:创建系统范围的 PowerShell 配置文件($PROFILE.AllUsersAllHosts),在每次登录时从注册表重建 $env:Path。
  • winget 可能无法使用 — Microsoft Store 证书在 VM 中可能会失效。请改用直接下载 MSI/安装程序。
  • 主机密钥变更 — 每次重新创建 VM,都会生成新的 SSH 主机密钥。运行 ssh-keygen -R '[localhost]:2222' 以清除旧密钥。

评论

支持 Markdown