Carpe Diem

備忘録。https://github.com/jun06t

Scratch imageでtime: missing Location

概要

以前以下の記事でscratch imageの作成方法を紹介しました。

christina04.hatenablog.com

しかしgolangのコードによっては以下のpanicが起きることがあります。

panic: time: missing Location in call to Time.In

今回はその対応方法です。

環境

  • golang 1.8.3
  • docker 17.06.0-ce

原因

scratch imageにはzoneinfoが無いためです。
通常は/usr/share/zoneinfoにあり、golangのプログラムもそこを参照しようとしますが存在しない場合は

tz, err := time.LoadLocation("Asia/Tokyo")

といったコードのときにエラーが発生します。もしここでエラーハンドルしない場合はpanicになります。

対処方法

サンプルコード

今回以下のようなコードを用意しました。time.LoadLocation("Asia/Tokyo")でエラーハンドルしていないコードです。

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    now := time.Now()
    tz, _ := time.LoadLocation("Asia/Tokyo")
    fmt.Println("Local time: ", now)
    fmt.Println("Tokyo time: ", now.In(tz))

    _, err := http.Get("https://golang.org/")
    if err == nil {
        fmt.Println("GoLang website is UP")
    } else {
        fmt.Printf("GoLang website is DOWN\nErr: %s\n", err.Error())
    }
}

ローカルのMacで実行すると以下のようになります。

$ go run main.go 
Local time:  2017-08-13 19:57:21.032806177 +0900 JST
Tokyo time:  2017-08-13 19:57:21.032806177 +0900 JST
GoLang website is UP

golangのビルド

以前と同様にビルドしておきます。

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o main main.go

バイナリ情報は以下です。scratch imageで実行できるバイナリですね。

$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

zoneinfoの用意

Macや一般的なLinuxディストリビューションには/usr/share/zoneinfoにあるので、そちらを利用します。tar.gzで保持しておきます。tar.gzにする理由はDockerfileのADDで自動で展開してくれるからです。

$ tar -cvzf zoneinfo.tar.gz -C / usr/share/zoneinfo

フォルダ構造

以下のようなファイル配置になります。
ca-certificates.crtは以前と同じように用意しておきます。

.
├── Dockerfile
├── ca-certificates.crt
├── main
├── main.go
└── zoneinfo.tar.gz

Dockerfileの用意

FROM scratch
ADD ca-certificates.crt /etc/ssl/certs/
ADD zoneinfo.tar.gz /
ADD main /
CMD ["/main"]

ポイントはADD zoneinfo.tar.gz /です。先に述べたようにADDtar.gzを展開してくれるので、これによって/usr/share/zoneinfoにzoneinfoが展開されます。

動作検証

dockerをビルドします。

$ docker build -t example-scratch .

実行します。

$ docker run -it example-scratch
Local time:  2017-08-13 10:58:20.286529522 +0000 UTC
Tokyo time:  2017-08-13 19:58:20.286529522 +0900 JST
GoLang website is UP

エラーハンドルしていないですが、panicにならずちゃんとJSTの時間が出力されました。
ローカルのMac上で実行したのと違ってLocal timeはUTCですね。

ファイルサイズはどれくらい増えるのか?

goのバイナリ入れず、ca-certificates.crtzoneinfoだけの場合でどれくらいのimageサイズなるか測ってみました。

zoneinfoなし

$ docker images example-scratch  
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
example-scratch     latest              695004c086e6        3 weeks ago         274kB

zoneinfoあり

$ docker images example-scratch  
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
example-scratch     latest              1adbb2b490f9        About an hour ago   466kB

200kB弱ほど増えました。goバイナリを考えると誤差レベルなので、zoneinfoが有ったほうが扱いやすいimageになりそうです。

ソース