5
8

More than 3 years have passed since last update.

dockerのscratchイメージでgolangのWebアプリを動かす

Last updated at Posted at 2020-01-12

Best practices for writing Dockerfiles (参考訳v18.09ベース)を参考に、golangで書いたWebアプリを動かそうとしたらハマったので、その記録を残します。

ざっくりいうと

  1. golangで書いたwebアプリ(サンプル)を、dockerのscratchイメージ上で動かそうとしたら起動で失敗
  2. 調べたらダイナミックリンクでビルドされており、scratchイメージではファイルが足りなかったのが原因
  3. スタティックリンクでビルドし直したら解決した

環境

  • go 1.13.5
  • docker 19.03.5

ハマるまでの流れ

"Use multi-stage builds"にマルチステージビルドを使うgolangのサンプルがあります。ビルドするときに使うイメージとリリースするイメージを分けることで、リリースイメージに余計なもの入れなくてすみ、イメージのサイズも小さくできます。

それはよさそうだと言うことで、手元にあったWebアプリをサンプルにして次のようなDockerfileを用意しました。

FROM golang:1.13.5-alpine AS build
WORKDIR /go/src/sample-go-server
COPY ./app /go/src/sample-go-server
RUN go build -o /bin/sample-go-server

FROM scratch
COPY --from=build /bin/sample-go-server /bin/sample-go-server
EXPOSE 8080
ENTRYPOINT ["/bin/sample-go-server"]

sample-go-serverは、hello worldを返す簡単なものです。

package main

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

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		_, _ = fmt.Fprintf(w, "hello world")
	})
	log.Fatal(http.ListenAndServe(":8080", nil))
}

このDockerfileをビルドします。

❯ docker build -t sample-go-server .
❯ docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
sample-go-server         latest              0acf7d1e1ed7        42 minutes ago      7.45MB

runで起動すると思いきや次のようなエラーで失敗します。

❯ docker run -d -p 8080:8080 --name sample sample-go-server
1e097919ec5ac31839228646e2b14bd0434f56d74787d73b6afa172154829250

❯ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

❯ docker logs sample
standard_init_linux.go:211: exec user process caused "no such file or directory"

"no such file or directory"と言われましても…。何が足りないかを言ってくれ〜😫

解決方法

ググったところ以下が見つかりました。ありがとうインターネッツ。

netパッケージを含む場合はダイナミックリンクでビルドされ、それが原因でno such fileになるらしいです。
なので、さっそくこれに該当しているかを確認してみます。
ビルドで使ったalpineイメージにはfileコマンドが入っていないため、まずそれを入れます。パッケージは Alpine Linux packagesで探せます。

/bin # apk update && apk add file
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/community/x86_64/APKINDEX.tar.gz
v3.11.2-25-g58afcd742e [http://dl-cdn.alpinelinux.org/alpine/v3.11/main]
v3.11.2-24-g7cfe3a1534 [http://dl-cdn.alpinelinux.org/alpine/v3.11/community]
OK: 11261 distinct packages available
(1/2) Installing libmagic (5.37-r1)
(2/2) Installing file (5.37-r1)
Executing busybox-1.31.1-r8.trigger
OK: 12 MiB in 17 packages
/bin # which file
/usr/bin/file

fileコマンドで確認してみると、たしかにdinamically linkedとなっていました。

/bin # file sample-go-server 
sample-go-server: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, Go BuildID=5E6Qy3Li7DELFoSZUyv5/TdC1EDiTsXrg0i0ta2Xx/49v4RWXcyEm12mEJPV8f/wYNglNx2pwV9Z_45IRLn, not stripped

参考サイトにあったCGO_ENABLED=0でビルドをし直してみます。

FROM golang:1.13.5-alpine AS build
WORKDIR /go/src/sample-go-server
COPY ./app /go/src/sample-go-server
RUN CGO_ENABLED=0 go build -o /bin/sample-go-server

FROM scratch
COPY --from=build /bin/sample-go-server /bin/sample-go-server
EXPOSE 8080
ENTRYPOINT ["/bin/sample-go-server"]

たしかにstatically linkedに変わりました。

/bin # file sample-go-server 
sample-go-server: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=cD81ASWTt8bngTyIxfpe/TdC1EDiTsXrg0i0ta2Xx/49v4RWXcyEm12mEJPV8f/rmgAW8vnYv0XAugzIkMj, not stripped

docker runでも無事起動できました。

❯ docker run -d -p 8080:8080 --name sample sample-go-server
c27e29aaa697ac7131995472377bb2ff58e6a6ebe6a8174fb8a9de64efc767d5

❯ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
c27e29aaa697        sample-go-server    "/bin/sample-go-serv…"   2 seconds ago       Up 1 second         0.0.0.0:8080->8080/tcp   sample

❯ curl http://localhost:8080
hello world

参考

5
8
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
5
8