鯨魚背上的大貓咪——在 Linux 電腦上通過 Docker 來運行 Mac OS X

鯨魚,即是 Docker。而 Mac OS X,因其之前使用大型貓咪來命名,故而稱其爲大貓咪。在 Linux 電腦上通過 Docker 來運行 Mac OS X,自然也就是鯨魚背上的大貓咪了。——題記

在處理 D102669 時,遇到了一個僅在 Mac 上纔會觸發的 bug。而手上除了豹豹有一臺公司的 MBP 外,整個家族也就再無觸手可即的 Mac 了。雖說是公司的電腦,但畢竟是豹豹的,所以也就不好意思拿來編譯和 debug。再加上一直以來在 OS X 上都沒有搞掂 debug 相關的事情,於是便萌生了自己來弄一個 Mac 電腦的想法。

作爲一個窮鬼書生,買是買不起的,畢竟家父不是國王。於是,便思考起左道旁門來。比如:進到一家蘋果店裏,把展出的 Mac 電腦連到實驗室的伺服器上開一個反向代理,然後白嫖人家的展示機。不過最後還是鑑於良心上的譴責以及確實登錄會存在問題而放棄了這個方案。(好孩子不可以學我這樣做哦)

因此,看起來比較切實可行的方案就只剩下了黑蘋果和虛擬機。在一次偶然的搜索時,發現了這樣一個項目:Docker-OSX,於是瞬間豁然開朗。這個項目把 QEMU KVM 運行 OS X 虛擬機所必須的文件都封裝到一個 Docker 鏡像中,從而完美地解決了自己配置 OS X 虛擬機搞不掂的問題。使用起來也相當方便且順暢,只需要一條命令把這個 Docker 鏡像運行起來,你便擁有了一個可用的 OS X 環境。

別家的使用教程已經有很多了,這裏就不再贅述,我只給出一些個人的使用經驗。官方教程影片在這裏(https://youtu.be/wLezYl77Ll8),有興趣可自行學習。我這邊使用的是默認版本的鏡像,也就是 Mac OS 10.15 Catalina。啓動之後,默認的鏡像是裸機環境,需要自行安裝系統。安裝大約耗時半小時,安裝完成之後就可以直接使用了。由於沒有 GPU 加速,因此 UI 會比較卡頓。但打開 SSH 登錄之後,直接使用控制檯時並不存在卡頓的問題。因此如果需求是使用 GUI 應用較多的話,還是需要考慮一下配置 GPU 加速的。由於我這邊是跑編譯和 debug,用的工具也是控制檯上運行的 Vim 和 GDB/LLDB,而 GUI 的卡頓對於我的影響也就微乎其微了。

由於編譯需要消耗比較多的記憶體以及 CPU 資源,於是便轉移到了實驗室的伺服器上來運行。這裏便遇到了第一個需要解決的問題:遠端的 QEMU 界面如何在本地顯示。解決的方法就是 X forwarding。首先在 SSH 連接實驗室的伺服器時需要開啓 X forwarding,即 ssh 命令的 -X-Y 參數。同時爲了不卡頓,還需要開啓流量壓縮,即 -C 參數。爲了鑑權方便,我這裏選擇了 -Y 參數。如果是使用其他 SSH 客戶端的話,需要查看一下自己的客戶端如何啓用這兩個選項。

local$ ssh -YC user@hostname

然而,這只是第一步。這只能讓伺服器上運行的 GUI 程式顯示在本地的熒屏上。而對於一個 Docker container 來講,其運行的環境可以認爲完全是另外一臺電腦,因此還需要讓 Docker container 中的 GUI 程式可以利用窩們轉發的 X 數據。需要解決的有兩點:連接和鑑權。這裏採用了直接使用 host 網路來啓動的方式來解決連接問題,即 --net=host。此時,Docker container 中的 DISPLAY 變量不需要額外改動,直接使用外邊的值即可。此外,X server 鑑權所依賴的 $HOME/.Xauthority,也需要給 1000 用戶開放讀取權限並放到 Docker container 中(如果你本人的賬戶 ID 不是 1000 的話)。

remote$ chmod 666 ~/.Xauthority

記憶體和 CPU 用量的設置是通過環境變量來進行的:CPU 是 SMP 以及 CORES,記憶體是 RAM(單位:GiB)。默認參數是 4 核 4 GiB 記憶體。此外,如果想要持久化硬碟中的數據,可以將 Docker image 中的 /home/arch/OSX-KVM/mac_hdd_ng.img 文件拷貝出來,然後在啓動 Docker 的時候放進去即可。

remote$ docker run -it --rm \
          -v /path/to/disk:/tmp/disk \
          --entrypoint=/bin/bash \
          sickcodes/docker-osx:latest
container$ cp /home/arch/OSX-KVM/mac_hdd_ng.img /tmp/disk

啓動 QEMU 時,會將虛擬機 SSH 的 22 端口轉發到 INTERNAL_SSH_PORT(默認爲 10022),以及 VNC 的 5900 端口轉發到 SCREEN_SHARE_PORT(默認爲 5900)。由於前面使用了 host 網路,因此如果這兩個默認佔用的端口如果已被使用,則需要通過這兩個環境變量來重新定義。

remote$ docker run --rm -it \
          --net=host -e DISPLAY=$DISPLAY \
          -v $HOME/.Xauthority:/home/arch/.Xauthority \
          --device /dev/kvm \
          -v /path/to/disk/mac_hdd_ng.img:/home/arch/OSX-KVM/mac_hdd_ng.img \
          -e INTERNAL_SSH_PORT=40022 \
          -e SCREEN_SHARE_PORT=45900 \
          -e SMP=32 -e CORES=32 -e RAM=64 \
          sickcodes/docker-osx:latest

這個時候,如果沒有遇到什麼奇怪的問題的話,應該就可以看到控制檯上刷出一些 QEMU 的啓動 log 然後彈出虛擬機的熒屏窗口了。

至此,你便擁有了一隻鯨魚背上的大貓咪。後面的工作就交給 Homebrew 吧。