91 lines
4.6 KiB
Bash
Executable File
91 lines
4.6 KiB
Bash
Executable File
#!/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 容器核心: /"
|
||
'
|
||
|
||
# Not started with the Mac: AnyBot starts this VM itself when it is the brain and wants its
|
||
# computer, and leaves it off while it only shows another brain (the VM's memory is the Mac's).
|
||
|
||
# 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
|