以 Docker for Mac 搭建開發環境

以 Docker for Mac 搭建開發環境

以前 Mac 上的 Docker

之前 Docker 就可以在 MAC 上面使用,但是實在太麻煩了,需要安裝 Boot2Docker, VirtualBox, Docker-Toolbox,實際上的體驗也遠不如 Linux 版本的。

如:每次重開機都需要執行以下命令,啟動也很花時間,在開發過程中,IP 需要透過 docker-machine 查詢,使用 Docker 開發失去了很多彈性:

docker-machine start default
eval $(docker-machine env default)

Vagrant 的虛擬網路

也因為如此,後來我改用了 Vagrant 作為開發環境。Vagrant 中的 private_network 可以讓 VM 中的網路環境與外部隔離,再也不需要預先決定好端口的映射關係,也不需要擔心端口被佔用的問題。

直到幾天前,遇到了一個問題:某部分的 API 在 VM 中雖然能正確取得回傳值,但總是會收到這個錯誤導致程式扔出異常而終止,即使把域名換成 IP 也是一樣的結果,在 Host 上執行同樣的命令沒有這個問題,測試過在 Guest VM 以及 Host 的出口 IP 都是相同的,無從得知對方的防火牆規則是什麼,因此無法進一步排查問題:

curl: (56) Recv failure: Connection reset by peer 

回到 Docker 的懷抱

經過來回比對之後,幾乎可以明確定位是 VirtualBox 的問題,嘗試升級到最新版,還是沒有辦法解決,沒有很具體的重現步驟可以回報給社群解決,在這個問題消除之前只好暫時棄用 Vagrant 回歸 Docker 的懷抱。

在我電腦上的 Docker 還是舊版的,底層也是透過 VirtualBox 實作,問題依舊存在。這時想起了今年初發佈的 Docker for Mac Beta ,那時候都還是邀請制,一般人沒有辦法下載,這幾天上去看已經是 Stable 可以下載了。不過事情也沒有就這樣一帆風順...在 Container 中需要起 MySQL Server,啟動的時候總是報錯,稍微查了一下,似乎是 CentOS 的大坑,按照網路上的建議換上了 centos:latest 仍然有相同的問題:

Failed to get D-Bus connection: No connection to service manager

最後只好退回到 centos:6.8 的版本才解決了這個問題。

Native 的體驗

雖然號稱是 Native,實際上 Docker Engine 仍是運行在 xhyve VM 中的 Alpine 之上

不過在使用上確實帶來了 Native 的體驗:

  1. 直接使用 Host IP,不需要再透過 docker-machine 查詢虛擬機中的 IP 來訪問容器中的機器
  2. 開機的時候自動啟動,不需要自行修改 .zshrc。啟動速度也比原先要快許多
  3. 選單列中即可修改記憶體、CPU 核心數套用後快速重新啟動
  4. 安裝與其他的應用程式沒有差異,不需要額外設定也沒有相依套件需要安裝

美中不足的缺點

在短暫的使用過程中最惱人的就是磁碟空間無法動態伸縮這件事了,因為原本在 Virtualbox 中的 images/containers 就佔用了不少空間,遷移到 Docker for Mac 我只 pull 一個 image 就爆炸了,查了一下才知道因為底層依賴 QEMU,這部分目前還沒辦法跟 Docker.app 結合得很好,也就是說你在容器中 rm -rf /tmp 或是刪除任何檔案,你的磁碟空間一樣只增不降,嘗試過 qemu-img convert,只能改 Virtual Disk Size。無奈之下還是忍痛重置 Docker for MAC,重置後有 60GB 的空間可以使用,應該可以撐到這個問題被解決吧(苦笑)?

docker rm $(docker ps -a -q) 
docker rmi $(docker images -q) 
docker volume rm $(docker volume ls |awk '{print $2}') rm -rf ~/Library/Containers/com.docker.docker/Data/*

利用 Docker 作為開發環境常用的小技巧

保存/載入 Docker 狀態有以下四個指令,是大家比較容易搞混的:

  • docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
    • Create a new image from a container's changes
  • docker save [OPTIONS] IMAGE [IMAGE...]
    • Save one or more images to a tar archive (streamed to STDOUT by default)
  • docker export [OPTIONS] CONTAINER
    • Export a container's filesystem as a tar archive
  • docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
    • Import the contents from a tarball to create a filesystem image

export/import 會將 images 合併成單一的 images。在一般情況,使用 commit/save 可以保留較高的彈性。

有興趣深入研究的可以參考這篇文章的介紹:
比較 save, export 對於映象檔操作差異

建立 Container 快照 (Snapshot)

我們在除錯的過程中可能會需要保存先前的狀態為新的 Image,在 Docker 我們可以利用 commit 指令來完成:

docker commit CONTAINER_HASH

儲存了快照後,停用運行中的容器,並 docker run 指定你要執行的快照。

docker run -it --name=foo CREATED_IMAGE_HASH bash

每層 image 都是增量式地向上疊加,所以在開發的過程中,大可盡量地 docker commit 保存環境的狀態。