26
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GoとDockerでscratchを使うときに気をつけること

Last updated at Posted at 2019-05-19

TL;DR

はじめに

 DockerはDockerfileのFROMscratchを指定できます。scratchはその名の通り何も入っていないためこれをベースにイメージを作れば非常に軽量になります。基本的にマルチステージビルドを利用してビルドします。

 Goはlibcに非依存のため静的リンクをするとバイナリ一つを持ち歩くだけで済みます。ですがいくつか気をつけないと困ることがあるためそれをまとめます。

cgo

 唐突ですが、先ほどGoはlibcに非依存だと言いましたがあれは嘘です。Go1.4からnetパッケージなどでおそらく速度向上のためデフォルトではlibcに依存しています。そのため、利用可能な環境ではcgoを利用してdynamic linkを行なっているようです。

root@d466ec4c1889:/go/src# cat main.go 
package main

import "net/http"

func main() {
        http.ListenAndServe(":9090", nil)
}
root@d466ec4c1889:/go/src# go build main.go 
root@d466ec4c1889:/go/src# ldd main
        linux-vdso.so.1 (0x00007ffca11f5000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f5c65afe000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5c6575f000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f5c65d1b000)
root@d466ec4c1889:/go/src# 

 しかしこれではscratchベースのイメージに持ってきた際に動かなくなり非常に不便です。解決策としてcgoを切ります。go build等を利用してビルドすると思いますが、その際に

CGO_ENABLED=0

の環境変数を渡すとcgoが無効化されてビルドされます。
Dockerのマルチステージビルドを利用してビルドした場合は

ARG CGO_ENABLED=0

go buildの前に書いておけば大丈夫です。

ルートCA証明書

 HTTPSでサーバに対して通信する際、サーバ側の証明書が正当なものか検量するために各システムにはルートCA証明書が保存されています。しかし、scratchにはそれがありません。安全にHTTPS通信を行うためにはこれが必要です。
 Goがどこに保存されているルートCA証明書を参照するかはcrypto/x509/root_linux.goに保存されています。Linuxに関してはこれです。
 このファイルを別のイメージから持って来る必要があります。

COPY --from=golang:1.12 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 

DockerのCOPY --fromはステージだけでなくイメージも利用できます。ステージではなくイメージを利用しているのはBuildkit等を利用した際並列化がしやすそうであるからで、golang イメージを利用しているのはマルチステージでビルドするためのイメージとして利用している可能性が高いからです。デフォルトで/etc/ssl/certsを持っていれば他のイメージでも大丈夫です。

タイムゾーン

 Goがデフォルトのタイムゾーンを決定する仕組みについてはGolangのローカルのタイムゾーンが決まる仕組みと指定方法という記事がよく纏まっていてわかりやすかったです。

 簡単に説明するとUNIX環境においては、TZ環境変数で指定がなければ/etc/localtimeを読みに行き、これが失敗すればUTC。UTC以外を取得した場合はtime/zoneinfo_unix.gozoneSourcesのリスト内のデータを利用してゾーンネームとオフセットの変換を行なっているようです。(Asia/Tokyo→+09:00など)

 Dockerにおいては/etc/localtimeはなくともTZ環境変数を指定すれば問題ありませんがzoneSourcesがないとデータベースを使うときなどに面倒なことになりがちです。

これも先ほどと同じように

COPY --from=golang:1.12 /usr/share/zoneinfo /usr/share/zoneinfo

でコピーできます。また、別の解決策としてファイルをコピーせずにtime.Localに直接コードから指定することもできます

Go 1.15の場合[2020/08/12追記]

日本時間2020/08/12でGo 1.15がリリースされました。Go 1.15ではtime/tzdata パッケージが追加されました。Go 1.15以降では

import _ "time/tzdata"

のようにインポートするとシステム上に存在しなくてもtzdataパッケージを参照してくれます。

参照: https://golang.org/doc/go1.15#time/tzdata

alpineの場合

scratchほどでないもののalpineはかなり軽量なイメージです。同じくデフォルトだと存在しませんが、パッケージマネージャがあるので

apk add --update --no-cache ca-certificates tzdata

このように追加できます。

まとめ

scratchでイメージを作る際に私が苦労した点と解決策をまとめました。
他にも何かあれば追記していこうと思います。scratchとGoで快適なDockerライフを!

26
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?