来年1月からアサイン予定のプロジェクトにて、dockerを用いた開発を行なうので、ここ最近勉強したdockerに関する知識をまとめたいと思います。
課題
この記事ではgo言語を用いて簡単なサーバーアプリケーションを作成し、dockerで動かします。後半はさらにmulti-stage-bulidという機能を用いてより軽量なdocker imageを作成するまでを目標にします。
dockerのインストールは各自でやっておいてねw
サーバーアプリケーション
ここでは静的なHTMLを返してくれるアプリケーションを作成します。
/
にアクセスすると、Hello!!を返してくれるものとし、のちほどherokuにデプロイする事を想定してポートは実行時に指定できるようにしましょう。(デフォルトでは:8080ポートを使用します。)
$GOPATH/src
以下にパッケージ名docker_demo
とし以下のコードを配置してください。
package main
import (
"flag"
"fmt"
"log"
"net/http"
)
func main() {
port := flag.String("port", "8080", "port number")
flag.Parse()
http.HandleFunc("/", indexHandler)
fmt.Println("server start!")
if err := http.ListenAndServe(fmt.Sprintf(":%s", *port), nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello!!</h1>")
}
コンパイル&実行
$ go install
$ $GOPATH/bin/docker_demo -9000 #9000ポートを使用
server start!
$ curl http://localhost:9000
<h1>Hello!!</h1>
goで本格的なwebアプリケーションを書いてみたいと思った方はこちらが大変参考になります。
dockerを用いて起動する
下準備これくらいにしてdockerコンテナ内で先ほど作成したアプリケーションを起動してみましょう。
docker imageの作成
Dockerfileを下記のように定義してbulidします。
FROM golang:1.13-alpine
WORKDIR /go/src/docker_demo
COPY . .
CMD go run .
$ docker image build -t docker_demo:latest .
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
docker_demo latest 126666514643 1 minutes ago 359MB
コンテナ内でアプリケーションを立ち上げる
それでは今回のメインディッシュdocker container内で先ほど作成したサーバーアプリを動かしてみましょう。
ポートフォワードを使用する事で、ホスト側のポートとコンテナ側のポートを繋ぐ事ができます。今回動かすプログラムはデフォルトで8080ポートを使用します。ホスト側9000ポートと接続してみましょう。
# -d バックグラウンドで起動、-p ポートフォワードを使用(ホスト:コンテナ)
$ docker container run -d -p 9000:8080 docker_demo
バックグラウンドでコンテナが起動しました。
http requestを送って確認してみましょう。
$ curl http://localhost:9000
<h1>Hello!!</h1>
より軽量なdocker imageを作成する
先ほど作成したdocker imageは問題なくコンテナを作成し、動かす事ができました。
しかしこのimageにはプログラムを実行するのに余分なものが含まれています。
goはコンパイラ言語なので実行にはコンパイルした実行ファイルと、最低限のosの機能さえあれば十分です。
したがってこの余分なものを削ぎ落とす事でイメージのサイズを小さくする事ができます。
multi-stage-build
$ docker image ls
で先ほどビルドしたイメージのサイズを確認してみると359MBもあります... 余計なお肉は削ぎ落としてスリムな体型にしてあげたいものです。
こんな時に便利なmulti-stage-buildという機能が存在します。
その名の通りイメージのbuildを複数段階に分けてbuildできる機能ですが、これを使用する事でアプリケーションをビルドする環境と、実行環境を分離できるというわけです。
では早速Dockerfileを下記のように変更します。
FROM golang:1.13-alpine AS build
WORKDIR /go/src/docker_demo
COPY . .
RUN go build
RUN GOOS=linux GOARCH=amd64 go install
FROM alpine AS docker_demo
RUN apk add ca-certificates
COPY --from=build /go/bin/docker_demo /bin/docker_demo
CMD ./bin/docker_demo -port=9000
ビルド環境イメージにASを用いてエイリアスを貼ることで、実行環境イメージをビルドする際に参照できるようになっています。
実行環境のベースイメージには必要最低限の機能をもつAlpine Linuxを使用し、ビルド環境で出来上がった実行ファイルのみを渡してあげる事で、最終的なイメージを最小にする事ができます。
docker imageをビルドしてサイズを確認してみましょう。
$ docker image build -t docker_demo
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
docker_demo latest 947f14bfd6ad 1 minutes ago 15.2MB
なんという事でしょう!15MBまで小さくする事ができました!
まとめ
今回はgoアプリケーションをコンテナで動かしてみるという簡単な内容でした。multi-stage-buildを使用してビルドと実行の環境を分けるという手法は、docker初心者の僕としては感動ものです。
今回作成したアプリケーションをherokuにデプロイする工程も記事にする予定なので、参考にしていただけると幸いです。