快轉到主要內容

Linux用Docker容器跑黑蘋果macOS ~ Docker-OSX安裝教學

Linux系統 虛擬機與容器技術 MacOS Docker QEMU
🗓️ 民國112年 癸卯年
✍ 切換正體/簡體字
目錄

謎之音:既然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說明

  1. 安裝 Docker Engine

  2. 安裝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
  1. 啟用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
  1. 將自己加入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虛擬機
#

  1. 硬碟容量至少需要64GB以上,新增一個目錄
mkdir ~/docker-osx
cd docker-osx
touch output.env
  1. 新增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"
  1. 執行xhost指令,讓任意程式都能顯示在X伺服器上
xhost +
  1. 啟動容器。啟動過程會先產生mac機器序號(serial)
sudo docker compose up
  1. 之後QEMU視窗會跳出來,滑鼠點一下,按Enter進入macOS Base System

  2. 點選Disk Utility

  3. 點選容量最大的硬碟,按Erase開始格式化(不會真的把你的硬碟格式化)

  4. 分區格式選MacOS Extended Journaled(APFS似乎容易出問題)

  5. 格式化完成後關閉視窗,點選Reinstall macOS

  6. 同意後等待安裝完成,大約4小時,中間會自行重開機

  7. 經過漫長的等待後,開機選安裝好系統的硬碟。按照指示設定地區、使用者帳號、登入Apple ID。

  8. 點選macOS左上角,將虛擬機關機

  9. 停止Docker容器(或是在終端機按CTRL+C)

sudo docker compose down
  1. 安裝後將裝好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
  1. 修改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"
  1. 安裝完成。關閉容器指令:cd ~/docker-osx && docker compose down;啟動容器指令:cd ~/docker-osx && docker compose up -d

  2. 要清理用不到的映像檔空間,使用sudo docker image ls查找用不到的映像檔,再用sudo docker image rm <映像檔ID>將其移除。

4. 進階用法
#

4.1. 修改螢幕解析度
#

macOS的解析度是跟序號綁死的,每改一次就要重新產生序號。

如果您不想改解析度,又怕虛擬機螢幕超出視窗,那麼可以點選QEMU左上角選單 → View → Zoom to fit,讓虛擬機畫面自動適應視窗大小。

  1. 停止容器。
cd /home/user/docker-osx
sudo docker compose down
  1. 修改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"
  1. 啟動容器。
sudo docker compose up -d

4.2. SSH連線 & 共享資料夾
#

Docker OSX的SSH通訊埠為50922

  1. 首先在 macOS啟用SSH服務。查找Docker的虛擬機IP
sudo docker ps
sudo docker inspect "容器ID" | jq -r '.[0].NetworkSettings.IPAddress'
  1. Linux就可以登入虛擬機了。安裝 內網穿透軟體即可從外部網路連線。
ssh <macOS使用者名稱>@<容器IP> -p 50922

# 或者用localhost
ssh <macOS使用者名稱>@localhost -p 50922

# 或者外部網路用Linux實體機IP連線到虛擬機
ssh <macOS使用者名稱>@<Linux實體機IP> -p 50922
  1. 如果要共享資料夾,只要在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工作階段,不做任何認證。

  1. 修改docker-compose.yml,在environments最下面加一段
- "EXTRA=-vnc :0"
  1. 啟動容器後,查找容器IP
sudo docker ps
sudo docker inspect "容器ID" | jq -r '.[0].NetworkSettings.IPAddress'
  1. 開啟 Remmina,輸入容器IP:5900遠端連線。

如果需要從外部網路存取容器,請安裝 內網穿透軟體

4.5. headless模式
#

  1. 停止容器。

  2. 修改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. 啟動容器,等待1分鐘開機完成後用SSH連線。
sudo docker compose up -d

5. 建置自訂映像檔
#

Docker OSX有些功能需要自行建置映像檔。

  1. 複製Docker OSX儲存庫
https://github.com/sickcodes/Docker-OSX.git
cd Docker-OSX
  1. 例如,建置一個預設虛擬硬碟128GB,啟用VNC伺服器的Ventura映像檔。當然手動在macOS裡面裝VNC也是可以的。
sudo docker build -t docker-osx --build-arg SHORTNAME=ventura . --build-arg SIZE=128

相關文章

如何在Windows 11安裝WSL2子系統 (Ubuntu)
Linux系統 虛擬機與容器技術 Windows Subsystem for Linux Docker Ubuntu
ReDroid,在Windows電腦架設Android雲手機的方法
Linux系統 虛擬機與容器技術 ReDroid Docker Android Windows Subsystem for Linux Scrcpy
用RDP+ZeroTier存取遠端Linux的Windows虛擬機的桌面
Linux系統 虛擬機與容器技術 Remote Desktop Windows Linux ZeroTier QEMU

留言板

此處提供二種留言板。點選按鈕,選擇您覺得方便的留言板。要討論程式碼請用Giscus,匿名討論請用Disqus。

這是Giscus留言板,需要Github帳號才能留言。支援markdown語法,若要上傳圖片請貼Imgur連結。您的留言會在Github Discussions向所有人公開。

這是Disqus留言板,您可能會看到Disqus強制投放的廣告。有時留言可能會被系統判定需審核,導致延遲顯示,請見諒。