容器化Go應用--基礎鏡像的未知時區問題

Go開發的應用程序的一個優點在於,能夠從"零"開始構建應用的Docker鏡像,鏡像中僅須要包含Go應用程序編譯後的二進制文件,不須要額外安裝其餘執行環境。這樣一來Go應用鏡像佔用的空間確實很小(一般是幾MB),並且也會更安全些。經常使用的alpine鏡像(alpine是專門爲容器設計的小型Linux發行版)中存在一個安全漏洞,該漏洞爲大量生產容器留下了空的root用戶密碼,因此若是你的的Go應用程序在沒有alpine(或任何其餘操做系統)的容器中運行,黑客就不能利用操做系統的漏洞去攻擊容器裏的應用。linux

使用Docker的多階段構建,從頭開始構建映像很是簡單,上一期的文章《線上Go項目的Docker鏡像應該怎麼構建?》裏已經對此進行了詳細描述。怎麼從"scratch"基礎鏡像,使用多階段構建制做Go應用程序的鏡像。今天接着上期的話題繼續說一個從零構建的應用鏡像的容器時區設置的問題。golang

若是你的應用程序在初始化函數init裏有設置時區的操做,那麼在啓動應用容器時會遇到下面這個運行時panicdocker

unknown time zone Asia/Shanghai
複製代碼

若是你在應用程序裏不顯示地設置時區,應用容器確實是能正常啓動的,只不過這樣time包裏的函數統一用的是UTC時區,等你發現問題時再在程序裏去顯示設置時區仍然會遇到上面的運行時錯誤。shell

下面咱們來作個試驗,看看上面說的問題的現象。安全

首先寫一個簡單的Go應用程序bash

package main

import (
   "fmt"
   "time"
)

func main() {
   // 輸出當前的時區
   fmt.Print("Local time zone ")
   fmt.Println(time.Now().Zone())
   fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
}
複製代碼

而後寫一個用來構建應用鏡像的Dockerfile,使用的就是以前介紹的多階段構建。app

FROM golang:alpine as build
RUN apk --no-cache add tzdata WORKDIR /app ADD . /app RUN CGO_ENABLED=0 GOOS=linux go build -o myapp 
FROM scratch as final

COPY --from=build /app/myapp . ENV TZ=Asia/Shanghai

CMD ["/myapp"] 複製代碼

Dockerfile裏,咱們用ENV指令設置了TZ這個環境變量。Go運行時會查找TZ這個環境變量來設置本身的時區,上面咱們把TZ設置成了Asia/Shanghai,接下來咱們看看在容器裏應用是否是能如期運行,輸出正確的時區和時間。函數

➜  docker build -t go_timezone .


➜  docker run --rm go_timezone
Local time zone UTC 0
2020-07-17 04:47:37

複製代碼

根據運行結果發現時區的設置並沒生效。ui

Linux系統下Go運行時會從多個來源讀取時區信息,在$GOROOT/src/time/zoneinfo.unix文件裏可以找到Go運行時是從哪些地方讀取時區信息的。spa

// Many systems use /usr/share/zoneinfo, Solaris 2 has
// /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ.
var zoneSources = []string{
   "/usr/share/zoneinfo/",
   "/usr/share/lib/zoneinfo/",
   "/usr/lib/locale/TZ/",
   runtime.GOROOT() + "/lib/time/zoneinfo.zip",
}
複製代碼

因而我就進到剛纔鏡像的容器裏看了看,上面列的幾個目錄都沒有找到。到這裏算是定位到問題了,scratch鏡像裏並不包含這些時區文件。那麼解決辦法就是從build階段的鏡像裏拷貝時區文件到最終的應用鏡像。

FROM golang:alpine as build
RUN apk --no-cache add tzdata
WORKDIR /app
ADD . /app
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp

FROM scratch as final

COPY --from=build /app/myapp .
### 下面這行是新加的
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
ENV TZ=Asia/Shanghai

CMD ["/myapp"]
複製代碼

從新構建鏡像、運行容器後就能發現時區設置已經正常了,Go運行時按照環境變量TZ裏指定的時區打印了當前時間。

➜  docker image rm go_timezone
➜  docker run --rm go_timezone
Local time zone CST 28800
2020-07-17 13:12:18.206 CST

複製代碼

相關文章
相關標籤/搜索