久しぶりにdocker buildした
FROM golang:1.11.9-alpine as build
WORKDIR /go/app
RUN apk add --no-cache musl-dev git gcc
COPY . .
RUN go build -o app
FROM alpine:latest
RUN apk add --no-cache musl-dev git gcc tzdata curl
WORKDIR /app
COPY --from=build /go/app/app .
RUN apk --no-cache add tzdata ca-certificates \
&& cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
&& addgroup go \
&& adduser -D -G go go \
&& chown -R go:go /app/app
EXPOSE 8080
CMD ["./app"]
goでmysqlに接続するアプリをdockerで動作させていました。以下のようなデータが混ざったJSONを返すAPI Serverだと思ってください
特に問題も無くコードの変更もしてないのですが、ちょっとした変更がしたくて久しぶりにdocker buildを走らせた結果いきなりUTCに変わりました。
接続情報等の確認
- mysqlへ接続していたので
- https://github.com/go-sql-driver/mysql
- 接続情報になにか変更が入ってしまったのかと思い確認にいきましたが
- ちゃんとAsia/Tokyoになっています
parseTime=True&loc=Asia%2FTokyo
- 段々意味が分からなくなってきたので
- いったんtimezone周りの状況を整理しました
goのlocalどんな感じに決まってるのか
- 文字列が出力されるときに呼び出される
- initLocalという関数の中で決まる
- TZ環境変数
- 無い場合
- /etc/localtimeを参照してこちらの情報を元にtimezoneを決定する
- 有る場合
- TZ環境変数の中身とtime.zoneSourcesにあるディレクトリのリストを順に調査
- 該当した場合はその情報を元にtimezoneを決める
- 無い場合
21 var zoneSources = []string{
22 "/usr/share/zoneinfo/",
23 "/usr/share/lib/zoneinfo/",
24 "/usr/lib/locale/TZ/",
25 runtime.GOROOT() + "/lib/time/zoneinfo.zip",
26 }
- なのでTZ環境変数をしていなくても
- tzdataをinstallさえして
- /etc/localtimeへリンクをはるなりしておけば期待するtimezoneになります
mysql driverの場合
- mysql driverでlocを設定した場合
- どうなるのか。
- dsn.goで
- time.LoadLocationへlocに設定した値が渡されて
- configに設定されます
450 // Time Location
451 case "loc":
452 if value, err = url.QueryUnescape(value); err != nil {
453 return
454 }
455 cfg.Loc, err = time.LoadLocation(value)
456 if err != nil {
457 return
458 }
- ZONEINFO環境変数などが設定されていない場合に関しては
- locに設定した値を元に
- loadLocationが呼び出される
656 if z, err := loadLocation(name, zoneSources); err == nil {
657 return z, nil
658 } else if firstErr == nil {
659 firstErr = err
660 }
661 return nil, firstErr
- この場合もzoneSourcesのどこかに期待するtimezoneの情報があれば
- 正常に動作します
この時点で分かっていること
- mysqlの取得する部分でtimezoneがおかしい問題がおきている
- alpineでtzdataをinstallしていること
- tzdata以外に参照するデータは無いこと
とても怪しいところ
- 勘の良い方なら早々に気づいていたかと思いますが
- 気づかないうちにalpineのバージョンが上がっていたようです
- alpine:3.12.1にその時点で上がっていました
alpine3.12.1でtzdataに何が起きたのか
- tzdataが2020aから2020cへ変更されています
- https://github.com/alpinelinux/aports/compare/v3.12.0...v3.12.1
diff --git a/main/tzdata/APKBUILD b/main/tzdata/APKBUILD
index 9725639106..f70b03cab1 100644
--- a/main/tzdata/APKBUILD
b/main/tzdata/APKBUILD
@@ -2,8 ,8 @@
# Contributor: Natanael Copa <ncopa@alpinelinux.org>
# Maintainer: Natanael Copa <ncopa@alpinelinux.org>
pkgname=tzdata
-pkgver=2020a
-_tzcodever=2020a
pkgver=2020c
_tzcodever=2020c
tzdata2020a-2020cでは何が起きたのか
- こちらのコミットに詳しいですが
- https://github.com/eggert/tz/compare/2020a...2020c
- https://github.com/eggert/tz/commit/6ba6f2117b95eab345a7ed9159cef939e30c4cd3
- tzdataはzicというコンパイラを用いてバイナリデータに変換される
- このzicのオプションにbがあり
- fatからslimへ変更されました
- golang側でもこのissueでも扱われています
- https://github.com/golang/go/issues/42138
どう解決したのか
- その時点でalpineのバージョンを有るところまで戻して固定する
- もしくはTZを指定してtzdataを入れず zoneinfoを自前で入れた場合以下を見ます
- runtime.GOROOT() + "/lib/time/zoneinfo.zip",
- こちらへfat版を貰ってきます
- 後者の方法をとることにしました
goの lib/time/zoneinfo.zipを確認する
- https://github.com/golang/go/commits/master/lib/time/zoneinfo.zip
- こちらを確認すると
- 特定のcommitでslimが入っています
- なので特定のバージョンを落としてきます
export GOROOT=/usr/local/go
export TZ=Asia/Tokyo
curl -Lso/usr/local/go/lib/time/zoneinfo.zip https://github.com/golang/go/blob/5c9a8c0761ae643828a4526db764ac7a50a1a24d/lib/time/zoneinfo.zip?raw=true
確認
- 無事指定のtimezoneになりました
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Printf("%v\n", now)
}
2020-12-17 18:20:51.857490357 +0900 JST m=+0.000876904
まとめ
- 何もしてないのに壊れたになるので
- latestでの運用はやめましょう
- goのversionもあげておかないとdebugger使えなかったりして大変なのであげましょう
追記(重要)
この問題は現在おきていません
- ここを見て貰うと分かりますがfatに戻されています
- https://github.com/alpinelinux/aports/commits/3.12-stable?after=434125893a86436a80f54545797bd57a667a1994+69&branch=3.12-stable
FROM golang:1.11.9-alpine as build
WORKDIR /go/app
RUN apk add --no-cache musl-dev git gcc
COPY . .
RUN go build -o app
FROM alpine:3.12.1
WORKDIR /app
COPY --from=build /go/app/app .
RUN apk --no-cache add tzdata ca-certificates \
&& cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
- こちらで実行するとJSTになります
2020-12-17 18:31:55.460003303 +0900 JST m=+0.003871447