理系学生日記

おまえはいつまで学生気分なのか

Multi-stage builds で gRPC サーバを立ち上げる

諸々で gRPC サーバを構築したりしているのですが、フロントを開発している人に gRPC サーバ + データベース一式を簡単に立ち上げられるようにしたいという思いがありました。 また、他にもシステム間連携を行う想定もあるので、できれば docker-compose に載せたい。

せっかく golang でアプリケーションを作っているので、Multi-stage build にチャレンジしてみました。

Multi-stage builds とは

Multi-stage builds を説明するためには、Multi-stage builds の前の世界観を説明する必要があります。 たとえば今回のような golang のアプリケーションを Docker で構築する場合、

  1. go の開発環境の入ったコンテナを作り、その中で go build を行う
  2. 同じコンテナで build したアプリを立ち上げる

ということをしていました。

ここで問題となるのは、本来は「アプリケーションを立ち上げる」だけのコンテナの中に、「アプリケーションをビルドする」ためのファイル群が入っていることです。 結果としてコンテナは肥大化し、軽くあるべきのコンテナが重くなり、有史以来人類は必死にコンテナイメージを小さくしようとしてきました。

Multi-stage builds は 1 つの Dockerfile の中で、あるコンテナでアプリケーションをビルドし、別のコンテナでアプリケーションを動かすということを実現します。 以下が Docker の公式 からの引用ですが、 ビルドとアプリケーションの起動が明確に分離されていることが分かります。 ビルドされたアプリケーションは、COPY --from によってアプリ起動用コンテナ (下記の例だと、alpine:lateest) にコピーされ、起動されます。

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

作ってみる

というわけで、簡単につくってみました。

最初のコンテナ builder で、alpiine ベースの golang のコンテナで Protocol Buffer をコンパイルし、gRPC サーバのアプリケーションをビルドします。 それをアプリ起動用のコンテナへコピーし、起動するということで、シンプルですね。

FROM golang:1.11.1-alpine3.8 As builder

WORKDIR /go/src/github.com/kiririmode/grpc-sandbox
COPY . .

RUN apk --no-cache add protobuf make git
RUN go get -u github.com/golang/protobuf/protoc-gen-go
RUN make pb deps
RUN GOOS=linux go build -o grpc-server .

FROM alpine:latest
WORKDIR /app
COPY --from=builder /go/src/github.com/kiririmode/grpc-sandbox/grpc-server ./
EXPOSE 8000
CMD ["/app/grpc-server"]

これを docker build すると、gRPC サーバのコンテナサイズはおよそ 16 MB 足らずになりました。

$ docker system df -v | grep -B5 grpc-server
Images space usage:

REPOSITORY                                 TAG                 IMAGE ID            CREATED ago         SIZE                SHARED SIZE         UNIQUE SiZE         CONTAINERS
grpc-server                                latest              67c54e99d8bc        7 hours ago ago     15.58MB             4.148MB             11.43MB             1n

このような Dockerfile を構成すると、docker-compose の方に組込むことで、ローカル環境で容易にシステム間連携も実現できるようになります。 便利な時代ですね。