謎之音:既然macOS不能原生跑Docker,要靠Linux虛擬機中介,何不用原生支援Docker的Linux跑macOS虛擬機,反正也不是原生的(← 支離滅裂的發言)
Running macOS VM inside Docker container on Linux OS.
sickcodes開發的「Docker-OSX」,是一個在Linux系統用Docker跑macOS虛擬機的方案,當然是黑蘋果。
macOS虛擬機可以幹嘛呢?雖然圖形效能不彰,但還是可以跑Xcode,跟iOS裝置連線,寫寫iOS程式。
1. 前言#
既然都叫黑蘋果了,自用無妨,公開在商業場合使用可能會有法律問題。
Docker OSX本質上就是在Docker裡面跑QEMU/KVM虛擬機,相較於我之前介紹的直接 用KVM跑macOS,Docker部署更為彈性這樣,要headless執行CI/CD pipeline也是有可能的。我個人認為Docker化更方便管理,寫好docke-compose後虛擬機就可以用 Portainer圖形界面控制了。
Passthrough USB裝置比較麻煩,也無法像真的虛擬機那樣做GPU passthrough。
安裝macOS虛擬機的過程會有點迂迴,本文會盡量講明白,至少安裝後您會得到一個虛擬機。
Docker OSX只要有Docker就可以跑,按照開發者說法Windows WSL也是支援的(需啟用巢狀虛擬化)。然而我不想那麼麻煩,本文以Linux系統+Docker為主,需跑在支援虛擬化x86架構CPU電腦上。
為求穩定,桌面使用X11工作階段,不使用Wayland。
2. 安裝前置依賴套件#
參考 原作者Github說明
安裝Libvirt、KVM、xhost套件
# Arch系
sudo pacman -S qemu libvirt dnsmasq virt-manager bridge-utils flex bison iptables-nft edk2-ovmf xorg-xhost
# Debian系
sudo apt install qemu qemu-kvm libvirt-clients libvirt-daemon-system bridge-utils virt-manager libguestfs-tools x11-xserver-utils
# Fedora系
sudo dnf install libvirt qemu-kvm xorg-x11-server-utils
- 啟用Libvirt服務,載入KVM核心模組
sudo systemctl enable --now libvirtd
sudo systemctl enable --now virtlogd
echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs
sudo modprobe kvm
- 將自己加入KVM群組
sudo usermod -a -G libvirt $USER
sudo usermod -a -G libvirt root
sudo usermod -a -G kvm $USER
sudo usermod -a -G kvm root
sudo systemctl restart virtlogd
3. 安裝macOS虛擬機#
- 硬碟容量至少需要64GB以上,新增一個目錄
mkdir ~/docker-osx
cd docker-osx
touch output.env
- 新增
docker-compose.yml
,填入以下內容。注意我寫的註解,為方便用Portainer部署,儲存資料的路徑都是寫絕對路徑。
version: '3.4'
services:
osx:
container_name: docker-osx
# SSH通訊埠,暴露的是50922
ports:
- 50922:10022
# 使用macOS Ventura映像檔
image: sickcodes/docker-osx:ventura
privileged: true
devices:
- /dev/kvm
environment:
- DISPLAY=${DISPLAY:-:0.0}
# 啟用PulseAudio接收音訊,如果無法連線就註解掉這行
- AUDIO_DRIVER=pa,server=unix:/tmp/pulseaudio.socket
# 分配RAM
- RAM=8
# 分配4個CPU核心
- SMP=4
- CORES=4
# 以下是初次啟動產生序號之用
- GENERATE_UNIQUE=true
- GENERATE_SPECIFIC=true
- DEVICE_MODEL="iMacPro1,1"
- "MASTER_PLIST_URL=https://raw.githubusercontent.com/sickcodes/osx-serial-generator/master/config-custom.plist"
# 建議第一次啟動就指定解析度
- WIDTH=1920
- HEIGHT=1080
# 網路使用橋接模式
network_mode: "bridge"
cap_add:
- ALL
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
- /dev:/dev
- /lib/modules:/lib/modules
# 啟用PulseAudio接收音訊,如果無法連線就註解掉這行
- "/run/user/$(id -u)/pulse/native:/tmp/pulseaudio.socket"
# 儲存產生的序號成output.env,否則每次開機序號都會不一樣,無法正常使用iCloud
- "/home/user/linux-docker-osx/output.env:/env"
- 執行xhost指令,讓任意程式都能顯示在X伺服器上
xhost +
- 啟動容器。啟動過程會先產生mac機器序號(serial)
sudo docker compose up
之後QEMU視窗會跳出來,滑鼠點一下,按Enter進入macOS Base System
點選Disk Utility
點選容量最大的硬碟,按Erase開始格式化(不會真的把你的硬碟格式化)
分區格式選MacOS Extended Journaled(APFS似乎容易出問題)
格式化完成後關閉視窗,點選Reinstall macOS
同意後等待安裝完成,大約4小時,中間會自行重開機
經過漫長的等待後,開機選安裝好系統的硬碟。按照指示設定地區、使用者帳號、登入Apple ID。
點選macOS左上角,將虛擬機關機
停止Docker容器(或是在終端機按CTRL+C)
sudo docker compose down
- 安裝後將裝好macOS的虛擬硬碟複製出來。該映像檔預設大小是整個硬碟,它的容量會慢慢增長。
# 尋找macOS虛擬硬碟位置
sudo find /var/lib/docker -size +10G | grep mac_hdd_ng.img | head -n 1
# 範例輸出:/var/lib/docker/overlay2/334db858/diff/home/arch/OSX-KVM/mac_hdd_ng.img
# 將其複製到欲放置的目錄
sudo cp /var/lib/docker/overlay2/334db858/diff/home/arch/OSX-KVM/mac_hdd_ng.img ~/linux-docker-osx/hdd
- 修改
docker-compose.yml
,改用docker-osx-naked
映像檔,即可從任意虛擬硬碟啟動macOS。
version: '3.4'
services:
osx:
container_name: docker-osx-ventura
# SSH通訊埠,暴露的是50922
ports:
- 50922:10022
# 改用docker-osx:naked映像檔
image: sickcodes/docker-osx:naked
privileged: true
devices:
- /dev/kvm
environment:
- DISPLAY=${DISPLAY:-:0.0}
# 啟用PulseAudio接收音訊,如果無法連線就註解掉這行
- AUDIO_DRIVER=pa,server=unix:/tmp/pulseaudio.socket
- RAM=8
- SMP=4
- CORES=4
# 跳過開機硬碟選取畫面
- NOPICKER=true
network_mode: "bridge"
cap_add:
- ALL
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
- /dev:/dev
- /lib/modules:/lib/modules
# 啟用PulseAudio接收音訊,如果無法連線就註解掉這行
- "/run/user/$(id -u)/pulse/native:/tmp/pulseaudio.socket"
# 虛擬硬碟所在目錄
- "/home/user/linux-docker-osx/hdd/mac_hdd_ng.img:/image"
# 沿用第一次開機產生的序號
- "/home/user/linux-docker-osx/output.env:/env"
安裝完成。關閉容器指令:
cd ~/docker-osx && docker compose down
;啟動容器指令:cd ~/docker-osx && docker compose up -d
要清理用不到的映像檔空間,使用
sudo docker image ls
查找用不到的映像檔,再用sudo docker image rm <映像檔ID>
將其移除。
4. 進階用法#
4.1. 修改螢幕解析度#
macOS的解析度是跟序號綁死的,每改一次就要重新產生序號。
如果您不想改解析度,又怕虛擬機螢幕超出視窗,那麼可以點選QEMU左上角選單 → View → Zoom to fit,讓虛擬機畫面自動適應視窗大小。
- 停止容器。
cd /home/user/docker-osx
sudo docker compose down
- 修改
docker-compose.yml
,加入指定解析度。
version: '3.4'
services:
osx:
container_name: docker-osx-ventura
ports:
- 50922:10022
image: sickcodes/docker-osx:naked
privileged: true
devices:
- /dev/kvm
environment:
- DISPLAY=${DISPLAY:-:0.0}
- AUDIO_DRIVER=pa,server=unix:/tmp/pulseaudio.socket
- NOPICKER=true
- RAM=8
- SMP=4
- CORES=4
# 重新產生序號
- GENERATE_UNIQUE=true
# 要變更的解析度。有效數字:800x600、1280x768、1600x900、1920x1080、2560x1600
- WIDTH=1280
- HEIGHT=768
network_mode: "bridge"
cap_add:
- ALL
volumes:
- "/home/user/linux-docker-osx/hdd/mac_hdd_ng.img:/image"
- /tmp/.X11-unix:/tmp/.X11-unix
- /dev:/dev
- /lib/modules:/lib/modules
- "/run/user/$(id -u)/pulse/native:/tmp/pulseaudio.socket"
- "/home/user/linux-docker-osx/output.env:/env"
- 啟動容器。
sudo docker compose up -d
4.2. SSH連線 & 共享資料夾#
Docker OSX的SSH通訊埠為50922
- 首先在 macOS啟用SSH服務。查找Docker的虛擬機IP
sudo docker ps
sudo docker inspect "容器ID" | jq -r '.[0].NetworkSettings.IPAddress'
- Linux就可以登入虛擬機了。安裝 內網穿透軟體即可從外部網路連線。
ssh <macOS使用者名稱>@<容器IP> -p 50922
# 或者用localhost
ssh <macOS使用者名稱>@localhost -p 50922
# 或者外部網路用Linux實體機IP連線到虛擬機
ssh <macOS使用者名稱>@<Linux實體機IP> -p 50922
- 如果要共享資料夾,只要在Linux系統,用SSHFS掛載macOS的目錄即可:
mkdir ~/dokcer-osx/macos_mnt
sshfs <macOS使用者名稱>@<容器IP>:/ -p 50922 ~/dokcer-osx/macos_mnt
4.3. 將iPhone連接到macOS虛擬機#
使用usbflux方案,轉發usbmuxd的網路界面。
參考 usbflux使用教學
4.4. VNC遠端連線#
此處使用QEMU內建的功能,將螢幕輸出重新導向至VNC工作階段,不做任何認證。
- 修改
docker-compose.yml
,在environments
最下面加一段
- "EXTRA=-vnc :0"
- 啟動容器後,查找容器IP
sudo docker ps
sudo docker inspect "容器ID" | jq -r '.[0].NetworkSettings.IPAddress'
- 開啟
Remmina,輸入
容器IP:5900
遠端連線。
如果需要從外部網路存取容器,請安裝 內網穿透軟體。
4.5. headless模式#
停止容器。
修改
docker-compose.yml
,去掉DISPLAY
version: '3.4'
services:
osx:
container_name: docker-osx-ventura
ports:
- 50922:10022
image: sickcodes/docker-osx:naked
privileged: true
devices:
- /dev/kvm
environment:
- NOPICKER=true
- RAM=8
- SMP=4
- CORES=4
network_mode: "bridge"
cap_add:
- ALL
volumes:
- "/home/user/linux-docker-osx/hdd/mac_hdd_ng.img:/image"
- /dev:/dev
- /lib/modules:/lib/modules
- "/run/user/$(id -u)/pulse/native:/tmp/pulseaudio.socket"
- "/home/user/linux-docker-osx/output.env:/env"
- 啟動容器,等待1分鐘開機完成後用SSH連線。
sudo docker compose up -d
5. 建置自訂映像檔#
Docker OSX有些功能需要自行建置映像檔。
- 複製Docker OSX儲存庫
https://github.com/sickcodes/Docker-OSX.git
cd Docker-OSX
- 例如,建置一個預設虛擬硬碟128GB,啟用VNC伺服器的Ventura映像檔。當然手動在macOS裡面裝VNC也是可以的。
sudo docker build -t docker-osx --build-arg SHORTNAME=ventura . --build-arg SIZE=128