👻

How to Write DockerFile

2021/11/27に公開

DockerFile の書き方が毎回わからなくなるので書き方をまとめておく。
公式 reference はこちら[1]

Format

DockerFile は基本的に以下の構文を取る。

# Comment
INSTRUCTION arguments
  • comment について
    # で始まる行は (parser directive でない限り) comment と認識され build 前に取り除かれるため、例えば RUN コマンド中の間の行に comment が存在したとしても問題ない。また、行途中の # は全て argument として認識される。
  • whitespace について
    comment や instruction 前の whitespace は無視されるため DockerFile の実行に影響はないが、可読性などの観点から推奨されない。

INSTRUCTIONS

以下、主要な instruction について説明する。
Instruction は case-insensitive だが、convention として UPPERCASE で記述する。

FROM

全ての DockerFileFROM 句から始まらなければならない。

FROM [--platform=<platform>] <image>[:<tag>/@<digest>] [AS <name>]
  • AS name で build に名前を付与できる (multi-stage build などで利用できる)
  • tag or digest で使用する image を指定できる (default では tag は latest になる)
  • --platformFROM で参照する repository が対応している platform (linux/amd64, linux/arm64 など) を指定できる
multi-stage build について

1 つの DockerFile に複数の FROM 句を書くこともでき、multi-stage build と呼ばれる。
開発環境に必要な package などは実行環境には必要ない場合が多いので、開発環境用の image を構築して必要なもの build した後、新たに軽量な実行用 image を構築することで最終的な image を軽量化するためによく用いられる。

RUN

image の作成で任意のコマンドを実行したいときに用いる。

# shell form
RUN <command>
# exec form
RUN ["executable", "param1", "param2"]

Shell form は SHELL instruction で指定される shell (Linux の default は /bin/sh -c) で実行されるコマンドを記述する。
一方、exec form では shell を起動することなく executable を実行できる。ただし、exec form の入力は JSON として parse されるため、" でくくることや \ を escape するなどの注意が必要である。

SHELL instruction について

以下のようにして shell form で使用する default shell を変更できる。

SHELL ["executable", "parameters"]

CMD / ENTRYPOINT

CMDENTRYPOINT はどちらもコンテナ起動時に一度のみ実行されるコマンドを指定する instruction である (従って最後に記述されたものしか意味がない) 。

CMD

  • CMD ["executable","param1","param2"] (exec form : preferred)
  • CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
  • CMD command param1 param2 (shell form)

ENTRYPOINT

  • ENTRYPOINT ["executable", "param1", "param2"] (exec form)
  • ENTRYPOINT command param1 param2 (shell form)

CMDENTRYPOINT の違いは CMD は ENTRYPOINT で指定されたコマンドの default 引数を設定できるという点である。
ただし、その際はどちらも exec form でなければならず、shell form で ENTRYPOINT を記述した場合、全ての CMD instruction は無視される。
詳しくは Understand how CMD and ENTRYPOINT interact も参照のこと。

ARG / ENV

DockerFile 内や container 内で使用する変数を定義する際に用いる。
ARGENV の違いは ENV は image 内で環境変数として使われるということである。
そのため、DockerFile 内のみの変数を場合は ARG を使うのが良い。

# ENV
ENV <key>=<value> ...
# ARG
ARG <name>[=<default value>]
ARG の FROM 前での使用について

ARG instruction は唯一 (1 回目の) FROM の前に挿入して FROM 句に対する parameter として使える。
ただし、その場合の ARG は build stage の外なので、FROM より後ろで設定した parameter を使いたい場合はもう一度 ARG で呼び出す必要がある。

ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

ADD / COPY

どちらも既存のファイルなどをコピーして image に追加する instruction であるが、違いは ADD は圧縮ファイルの展開やリモート URL のサポートがあることである。
従って、ローカルの圧縮ファイルの展開をしたいのであれば ADD を使うのが良いが、それ以外は基本的には COPY を使うのが良い[2]

# ADD
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
# COPY
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

上の instruction はそれぞれ <src><dest> に追加する。
相対 path で指定した場合、それぞれの parent directory は build context と WORKDIR である。

WORKDIR について

RUN, CMD, ENTRYPOINT, COPY, ADD などで使用する working directory を指定する instruction である。
相対 path が入力された場合、WORKDIR が parent directory となる。

WORKDIR /path/to/workdir

EXPOSE

Container 外との通信を設定したいときは EXPOSE instruction を使う。
Docker に container の listen port を教えることで host との通信 (TCP or UDP) を可能にする。

EXPOSE <port> [<port>/<protocol>...]

LABEL

LABEL instruction は、作成する image に以下の形式で metadata を付与できる。

LABEL <key>=<value> <key>=<value> <key>=<value> ...

Parser Directive

#directive=value の形で DockerFile の先頭に記述する。
必須のものではないので、必要になったときに調べて再度追記する。

脚注
  1. best practice は Best practices for writing Dockerfiles ↩︎

  2. Best practices for writing Dockerfiles - ADD or COPY ↩︎

Discussion