AnyBot/deploy/mac/gvisor-vm.sh

92 lines
4.7 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# 強隔離 on this Mac: macOS can't run gVisor directly, and OrbStack/Docker Desktop's Linux VM
# can't take it. This makes a small Ubuntu VM (Lima, Apple Virtualization) with its OWN Docker
# and gVisor (runsc): Bot 的電腦 runs there on a virtualized kernel, verified by a sandboxed
# Chromium — the Mac's kernel is a whole VM away.
#
# ./gvisor-vm.sh create + start the VM (idempotent; re-run to fix/update)
# ./gvisor-vm.sh --rm remove the VM (the box's files live in a volume inside it)
#
# Afterwards, in AnyBot 設定 › 電腦 › 在哪裡執行:
# Docker 主機: ssh://lima-anybot-gvisor
# 容器隔離: 強隔離(gVisor)
# and press 更新 on the computer card. (AnyBot's Docker host accepts ssh://, the VNC viewer
# forwards through it.)
set -euo pipefail
VM=anybot-gvisor
CPUS=4
MEM=4 # GiB: the box is capped at 2 GB, Docker and the system take ~0.5 GB.
# The Mac never gets back memory the VM once used, so a bigger VM
# just grows bigger (it reached all of 6 GB).
DISK=60 # GiB
if [ "${1:-}" = "--rm" ]; then
limactl delete --force "$VM" 2>/dev/null || true
echo "已移除 VM ${VM}。"
exit 0
fi
command -v limactl >/dev/null 2>&1 || { echo "先裝 Lima:brew install lima"; exit 1; }
STATUS=$(limactl list "$VM" --format '{{.Status}}' 2>/dev/null || true)
if [ "$STATUS" = "Running" ]; then
echo "VM $VM 已在執行。"
elif [ -n "$STATUS" ]; then
echo "VM $VM 是 ${STATUS},啟動中…"
limactl start --tty=false "$VM"
else
echo "建立 VM ${VM}($CPUS CPU / ${MEM}GB / ${DISK}GB,第一次要下載映像檔,幾分鐘)…"
# template:docker-rootful = Lima's rootful Docker template (runsc wants a plain root daemon;
# the rootless docker template runs the daemon in a user namespace).
limactl start --name="$VM" --tty=false --cpus "$CPUS" --memory "$MEM" --disk "$DISK" template:docker-rootful
fi
echo "VM 裡安裝 gVisor(runsc,systrap 平台:不需要 KVM,ARM VM 可用)…"
limactl shell "$VM" -- bash -ec '
set -e
if ! command -v runsc >/dev/null 2>&1; then
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | sudo tee /etc/apt/sources.list.d/gvisor.list >/dev/null
sudo apt-get update -qq
sudo apt-get install -y -qq runsc
fi
# Register the runtime in the VM'\''s Docker daemon (merge, do not clobber other settings).
# Restart Docker only when that changed: a restart stops Bot 的電腦 running in this VM.
CHANGED=$(sudo python3 - <<EOF
import json, os
p = "/etc/docker/daemon.json"
d = json.load(open(p)) if os.path.exists(p) else {}
if d.get("runtimes", {}).get("runsc", {}).get("path") != "runsc":
d.setdefault("runtimes", {})["runsc"] = {"path": "runsc"}
os.makedirs("/etc/docker", exist_ok=True)
json.dump(d, open(p, "w"), indent=2)
print("changed")
EOF
)
if [ "$CHANGED" = changed ]; then sudo systemctl restart docker; fi
# Services a VM without modems, removable disks or SAN storage never uses (~70 MB).
sudo systemctl disable --now ModemManager.service udisks2.service multipathd.service multipathd.socket >/dev/null 2>&1 || true
# Prove the wall works: a container whose kernel really is gVisor.
docker run --rm --runtime=runsc --entrypoint sh alpine:3.20 -c "grep -q gVisor /proc/version && uname -r" | sed "s/^/runsc 容器核心: /"
'
# Bot 的電腦 lives in this VM: start it with the Mac, or after a restart the computer is gone
# until someone runs this again.
limactl autostart enable "$VM" >/dev/null 2>&1 || limactl start-at-login "$VM" --enabled >/dev/null 2>&1 || echo "(沒能設定開機自動啟動:Mac 重開機後要再跑一次這個腳本)"
# Lima forwards the VM's Docker socket to the Mac: AnyBot's Docker 主機用這個 unix:// 位址,
# 不用 ssh、不用碰 ~/.ssh/config。
SOCK="$HOME/.lima/$VM/sock/docker.sock"
DOCKER_HOST="unix://$SOCK" docker info --format '{{range $k, $v := .Runtimes}}{{$k}} {{end}}' | grep -qw runsc || { echo "從 Mac 連到 VM 的 Docker 看不到 runsc"; exit 1; }
KERNEL=$(DOCKER_HOST="unix://$SOCK" docker run --rm --runtime=runsc --entrypoint cat alpine:3.20 /proc/version | cut -d' ' -f3)
cat <<EOF
都好了(從 Mac 驗證:runsc 容器的核心是 ${KERNEL})。AnyBot 設定 › 電腦 › 在哪裡執行:
Docker 主機: unix://$SOCK
容器隔離: 強隔離(gVisor)
然後在電腦卡片按「建立/更新」。要讓舊 box 的檔案與登入跟著走,
先完全退出 AnyBot、跑同資料夾的 gvisor-migrate.sh,再回來開。
EOF