Docker 不是萬靈丹,小心謹慎才是

TL;DR 原文結論大致正確,但理由有的實在太瞎。任何工具都必須以正確方法使用在適當情境才能解決問題。

看到朋友轉貼一篇 Docker in Production: A History of Failure 和感想,點進去看了一下,覺得這間公司真是勇氣可嘉。

不熟的東西不要上 production

從原文看來,該公司的技術人員一開始對 Docker 的理解十分淺薄,而且對 production 必要的「穩定」並沒有足夠的認知。

Bug 不只是一個字,它將伴你一生

幾乎所有的程式都會有 bug,它可能存在鮮為人知的陰暗角落,隨時要張牙舞爪擇人而噬。最理想的「穩定」當然是毫無 bug,平平順順一路到底,但顯然不是個值得期待的目標。退而求其次,「穩定」的底線就是 保證在一段時間內會有結果,不論是順利成功、bug 修正或是回復都行。

換句話說,不論任何工具或技術,團隊必須對其運作和原理有充份的掌握。否則發生意外事故時,連判斷是否能修正/是否能在時限內修正都做不到,如何能讓產品穩定?

對問題的警覺性

我本身也是 1.6 才開始用 docker,剛好跟該公司一樣。當時官方文件就有特別註明 如果需要存資料,最好存在 Docker 外面再用 volume 掛進來。就算不提這種 RTFM 的問題,還有一個更大的警訊:不便性。

正常的工具都是為了解決特定的問題,不會是萬靈丹。但發現 某個原以為會很方便的情況下,用了工具反而造成困擾,合理的作法應該是 重新檢視設定、文件甚至是 source code,看看有沒有遺漏或是錯誤,而不會是 認定這個工具有問題,sucks 吧。

原文抱怨 Can’t clean old images 的部份正是一記警鐘,告訴你該回頭看看是 Docker 這把電動起子不適合釘釘子,還是自己要轉螺絲但是沒按電動起子的開關?

對 Docker 認識不足

其實原文的結論,除了抱怨 Docker 不做回溯相容之外,熟悉 Docker 運作的人應該都會會心一笑。

舊版 kernel

Docker 使用了極多 4.x kernel 的新技術,雖然較新的 3.x kernel 也能跑,但穩定性實在是不要抱什麼不切實際的幻想。

你想把 Windows 8/10 灌在十年前的機器上,除了慢一點、麻煩一點,可能還是會跑。但是幻想它會像今年剛出的預裝 Windows 電腦一樣穩?我看還是洗洗睡比較快。

Docker 只適合儲存 stateless 的東西,比如環境和程式

網友指正 發現是我誤解了原文的意思,特此修正。

========== 以下為我的原文 =============
原文抱怨的 Can’t clean old images,其實只要仔細審視 Docker 的設計,就能看出它 不適合儲存 state:Docker 最適合的工作是保持環境一致性,而非傳輸資料 (原文的結論也提到了這點)。

有了正確的理解,你就會發現 docker commit 這個功能只是開發期間方便紀錄和 debug 用,上 production 肯定是不會用到的。Docker image 用 layer 的方式儲存,並不代表這就是最適合的使用方式。

Docker 很多指令都像 git,但它絕對不是 git。
========== 我的原文結束 =============

========== 以下為新加的文字 =============
原文抱怨的 Can’t clean old images,有提到是因為動輒上 G 的 image 因為快速更新而讓大量空間被廢棄的 layer 佔用。Docker 官網雖然有提到它適合快速部署,但沒提到的是: layer 的切分非常關鍵。舉個自己的例子

我接觸 Docker 沒多久的時候,做過一個 image (based on debian:testing)。因為程式編譯問題,它需要安裝一些 header files,而那會讓最終的 image 不必要的肥大。於是,我寫了類似這樣的 Dockerfile

FROM debian:testing
RUN apt-get install -y lib1 lib2 lib1-dev ib2-dev ; do_compile ; apt-get purge -y lib1-dev lib2-dev

這樣最終 image size 可以從 1.3G 降到 7xxM,但 docker push 兩三次之後我就發現一個問題:第二層 layer 佔了 5xxM,每次 push 都是為了推這 5xxM 出去,但事實上編譯完的程式也才幾十 M 而已。

於是我把 Dockerfile 重新改回

FROM debian:testing
RUN apt-get install -y lib1 lib2 lib1-dev ib2-dev
RUN do_compile

雖然整個 image 高達 1.3G,但 docker pushdocker pull 的進度卻顯著提升。當然,代價是初次佈署的傳輸量也大幅提升。

當然,原文所提的問題依然存在,更不是說我的處理方式最好。這裡只是以實例強調理性和嚴密的分析,不要因為許多文章鼓吹「盡可能縮小 image size」而一昧照做,而是要依本身的條件、需求及使用的工具審慎評估。

: 不把 lib1lib1-dev 分成兩個 RUN 的原因是 Debian testing 的套件版本升級及調整很快,有可能你今天做的 image 用 lib1-0.1,明天已經變成 lib1-0.2 而且還有 breaking changes,拆成兩個 RUN 會導致 lib 與 header file 不一致而造成問題;雖然可以用 --no-cache 解決,但這會讓 build time 大幅拉長,不符合我當時的需求。一般情況下,不要在 docker build 的過程中做編譯,通常會是比較好的做法。
========== 新增文字結束 =============

Docker 不怎麼好,但最可怕的是自己更不好

不論是 Docker 或是任何其他的工具/技術,如果你的業務必須著重「穩定性」,建議你好好考慮以下幾點

  • 只使用團隊確定能掌握的工具,或是至少買 consulting
  • 確定自己有正確使用工具,穩定不是從直覺和想像獲得,而是理性和嚴密的分析
  • 隨時做好最壞準備,不要去想像有什麼東西能一勞永逸解決你的困擾,因為 bug 會伴你一生

Written by 

熱愛思辨,喜歡透過自我問答來找到可能的答案

Related posts

發表迴響