最近因為一個蠻莫名的問題,花了點時間在研究依靠 tail 來把 log 輸出到 docker log driver,然後發現其實這個作法算是一種 anti-pattern。目前認知到的問題有兩個:
1. tail 可能無法如預期地運作
在 stackoverflow 上,有好幾篇問題都做了類似的事情:
FROM debian:latest RUN touch /var/log/mylog.log CMD ["tail", "-F", "/var/log/mylog.log"]
在上面的 Dockerfile 中,先 touch 產生一個 log 檔,接著就一直 tail –F 去看那個 log 檔,感覺上好像很普通。但實際執行起來,會發現 docker logs 裡面什麼也沒有,tail 指令好像根本沒有正常在工作。而這個問題的根本原因是在於 docker 中間有 image layer,每一行 RUN 指令都會覆蓋上一層 image layer,因此實際上 touch 指令和 tail 指令在 docker 中是存取到不同的 inode。建議的作法應該要改為:
CMD ["sh", "-c", "touch /var/log/mylog.log && tail -f /var/log/mylog.log"]
也就是說,產生檔案以及監看檔案應該要在同一個 CMD 裡進行,這樣才能避免被 image layer 隔開而看不到預期的結果。
2. 若 container 的 main process 並非是主要的運行指令,container 可能無法對某些錯誤做正確的反應
這個問題主要是考量到能不能善用 container 本身的特性。在透過 Dockerfile 執行程序時,container 對程序的期待是如果程序執行完了,container 就會被認為不再需要了,於是 container 就會自動關閉。這個特性也可以用在如果程序執行有問題導致非預期地中斷,container 也一樣會自動關閉。此時如果外頭有 load balancer 的話,load balancer 就可以自動做一些反應,例如因為 container 數量減少了,因此自動做 scale-out 重新生出一個 container。但如果 container 的 main process 並非是在執行主要程序,而是在執行 tail 的話,那有可能會遭遇到主要程序出問題結束了,但 tail 沒有問題,這時就可能存在失敗的服務一直繼續留在那邊無法正確被關閉。當然這個動作也可以透過例如另外做一套 health check 的流程來處理,不過如果能夠很簡單地就利用 container 的天性來達成這個目的,也是可以考慮利用一下~。