Carpe Diem

備忘録

Go + Alpine でtzdataはapk addしなくていい

背景

Alpine Linuxはデフォルトではtzdataが含まれていないため、以下のような

package main

import (
        "log"
        "time"
)

func main() {
        loc, err := time.LoadLocation("Asia/Tokyo")
        if err != nil {
                log.Fatal("%w", err)
        }
        log.Printf("%v", loc)
}

time.LoadLocation()が含まれるコードだと

unknown time zone Asia/Tokyo

といったエラーが発生します。

なのでこれまではワークアラウンドとして

FROM golang:1.20-alpine as builder

WORKDIR /go/src/

COPY go.* main.go .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main main.go

FROM alpine:3.18

RUN apk add --no-cache tzdata
COPY --from=builder /go/src/main .

CMD ["./main"]

のようにtzdataをapk addしていましたが、Go 1.15からはそれをしなくて済むというお話です。

環境

  • Go v1.20.4
  • Alpine v3.18.0

time/tzdata

Go 1.15からtime/tzdataがimportしたりbuild時に埋め込めるようになっています。

New embedded tzdata package

埋め込むことでバイナリサイズが800 KBほど増えますが、

  • ベースのコンテナが変わった
  • Bazel使ってるのでパッケージ追加がし辛い

といったこともあるので、tzdataについてはこちらに寄せることをオススメします。

導入方法

方法は2つあります。

a) time/tzdataをブランクインポート

time/tzdataを次のようにインポートするとtzdataを埋め込んでくれます。

package main

import (
    "log"
    "time"
    _ "time/tzdata"  // here
)

func main() {
    loc, err := time.LoadLocation("Asia/Tokyo")
    if err != nil {
        log.Fatal("%w", err)
    }
    log.Printf("%v", loc)
}

b) go build時にtagを指定

build時に-tags timetzdataを設定するとtzdataを埋め込んでくれます。

$ go build -tags timetzdata -o main main.go

動作確認

Dockerfileからapk add tzdataを抜いて

FROM golang:1.20-alpine as builder

WORKDIR /go/src/

COPY go.* main.go .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main main.go

FROM alpine:3.18

COPY --from=builder /go/src/main .

CMD ["./main"]

docker buildします。

$ docker build -t time-zone .

期待通りに動きます。

$ docker run time-zone
2023/06/07 05:49:32 Asia/Tokyo

まとめ

Go 1.15リリース時に一度話題に挙がっていましたが、検索してみても未だにapk addのやり方を紹介するケースが検索上位にあがるので紹介してみました。