Go time.LoadLocation 在Docker 遇到的坑

最近用Go在撰寫LineBot時遇到時區的問題,花了快兩周才解掉,希望後面的人不要踩到同樣的陷阱(看完本篇立馬多賺了兩周生命)。情況是這樣:
程式使用cron定時執行程式去抓取遠端資料後,存在資料庫,在開發區直接執行一切都很順利,但上到Docker後,一直不會跑。
起初以為是程式寫錯,到處放炸彈,但在開發環境就是正常,後來想是不是正式環境影響? 於是檢查了Dockfile,也沒有寫錯,程式也可以正常編譯。只好觀察Docker運行的log

docker logs -f -t --tail 20 containerName

發現運行時間到了,就會出現下列錯誤訊息:

open /usr/local/go/lib/time/zoneinfo.zip: no such file or directory

查了一下這個檔案,發現是 Go time.LoadLocation 使用,因為我在程式中用了時區轉換:time.LoadLocation(“Asia/Taip”),而正式運行的Docker image 因為要採最小化,所以採用:

FROM alpine

推測是container中沒有go環境(只有編譯好的go程式),所以造成錯誤。找了一下主機內發現zoneinfo.zip 位於:/usr/local/go/lib/time/目錄下,於是在Docker file 增加:

FROM alpine
....略

COPY /usr/local/go/lib/time/zoneinfo.zip /usr/local/go/lib/time/zoneinfo.zip

這樣總會跑了吧!於是重新編譯啟動,時間到了還是沒跑,觀察Docker log發現還是一樣的錯誤訊息,試了幾次都一樣,也確認/usr/local/go/lib/time/目錄下有zoneinfo.zip這個檔案,百思不得其解。之後,只好先把 zoneinfo.zip複製到Go開發環境目錄下,更改dockfile內容:

FROM alpine
....略

COPY ./zoneinfo.zip /usr/local/go/lib/time/zoneinfo.zip

再次執行,竟然就可以了……雖然還沒搞懂為何會這樣,但花了快兩周時間找到原因及解法,代價可不小呀。

結論

Docker不是萬靈丹,還是要小心與開發環境的不同,以免少了某些需要的東西,而造成程式無法運行。

參考資料