前回は、コンテナの状態と対応するコマンドを確認しました。今回は、Dockerfileを使ってDockerイメージを作成する方法について見ていきます。前回でも少し見てきましたが、DockerではDockerHub等のレジストリで公開されたイメージをそのまま利用するのではなく、そこに手を加えて独自のイメージを作成して利用することが多くあります。今回は、そのようなときに役立つDockerfileについて説明します。

Dockerfileとは

Dockerfileでは、ベースとなるDockerイメージ、必要なコマンドなどをファイルに記載しておくことで、その内容をベースにカスタムイメージを作成することが出来ます。それでは、早速書き方を見ていきましょう。今回は、golangでREST APIサーバを構築する例を示します。

今回利用するgolangのコードは、下記のように環境変数からListenするポートを受け取って、
/にリクエストがあると標準出力へHello Worldを表示します。

package main

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

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World")
}
func main() {
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    http.HandleFunc("/", handler)
    http.ListenAndServe(":" + port, nil)
}

Dockerfileでは、作成したgolangのプログラムを動作させるための最低限の設定を記載していまりす。

# Alpine 3.12ベースのgolang:1.14.6のイメージをベースとして利用します。
FROM golang:1.14.6-alpine3.12

# 8080ポートを公開する
EXPOSE 8080

# ホストのファイルをコンテナにコピーする
COPY ./main.go ./

# ビルドを行い、go-appというバイナリを作成する
RUN go build -o ./go-app ./main.go

# 実行ユーザ
USER nobody
# コンテナ起動時に下記コマンドを実行する
ENTRYPOINT ["./go-app"]

Dockerfileからイメージを作成します。

イメージを作成するには、docker buildコマンドを利用します。-tオプションでイメージの名前、タグを指定することができます。下記例では、イメージ名をgo-app、タグを0.1として実行しています。

 docker build -t go-app:0.1 .

イメージを確認します。

$ docker image ls 
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
go-app              0.1                 271b77ce5aee        9 seconds ago       378MB
golang              1.14.6-alpine3.12   30df784d6206        4 days ago          370MB

作成したイメージをみるとベースにしたイメージも一緒にimageとして見つかります。ローカルリポジトリには、ベースしたイメージも一緒に登録されていることがわかります。それでは、imageを起動します。

docker run -p 8080:8080  go-app:0.1

-p オプションは外部のポートとコンテナ内部のポートを接続するオプションです。コンテナ内部では、8080ポートを公開しているので、コンテナの内部ポートは、8080、外部ポートがかぶっていければ今回はなんでもよいので、8080としました。

別のターミナルから下記コマンドと出力が表示されれば、OKです。

$ curl http://127.0.0.1:8080/ 
Hello World⏎ 

Dockerfileでよく利用する記載項目について

上記例では、golangで記載したAPIサーバをコンテナで動かしてみました。Dockerfileでよく記載として利用する項目についてまとめてみます。

コマンド説明
FROM <image>[:tag]コンテナのベースイメージを記載します。
RUN <command>
RUN ["executable", "param1", "param2"] 
ベースイメージ上で実行するコマンドを記載します。
ADD <src> <dest>srcは、ファイル、ディレクトリ、URLを記載します。
destは、イメージ内のファイルシステム上のパスを記載します。
COPY <src> <dest>
COPY [“<src>”,  “<dest>”]
srcからファイルやディレクトリを新たにコピーして、コンテナ内のファイルシステム上のパス<dest>に追加します。
EXPOSE <port> [<port>…]EXPOSE 命令はコンテナの実行時に、所定ネットワーク上のどのポートをリッスンするかを指定します。コンテナのポートをホストが利用できるようにするには、-pフラグ等を使って公開設定をする必要があります。
ENV <key> <value>
ENV <key>=<value>
環境変数を設定します。key value形式かkey=value形式で指定します。
ENTRYPOINT [“executable”, “param1”, “param2”]コンテナを起動するときに実行する設定を行います。
CMD ["executable","param1","param2"]1つのDockerfile内では、1つのCMD命令しか記載できません。複数のコマンドを記載した場合には、最後のCMD命令のみ処理されます。
USER <user>[:<group>]
USER <UID>[:<GID>]
イメージ実行時のRUN,CMD,ENTRYPOINTを実行する際のユーザを指定します。
WORKDIR /pathWORKDIR 命令はワークディレクトリを設定します。 Dockerfile 内にてその後に続く RUNCMDENTRYPOINTCOPYADD の各命令において利用します。 WORKDIR が存在しないときは生成されます。
Dockerfileコマンドよく使うもの

この表では、よく利用するDockerfileコマンドを抜粋しました。詳細は、公式ドキュメントのDockerfileリファレンス(http://docs.docker.jp/engine/reference/builder.html#cmd-entrypoint)を参照してください。

まとめ

今回は、golangで作成してAPIサーバをDocker上で動かすという想定でDockerfileを作成しイメージの作成、コンテナでの動作確認を行いました。比較的簡単に構築できたのではないでしょうか。Dockerイメージを作成する際には、マルチステージビルドなどもう少し発展的な内容やイメージ作成などの高速化等の方法などもありますが、その内容は、いずれ紹介していくつもりです。お疲れさまでした。次回は、dockerのネットワーク周りについてみていこうと思います。