Dockerfile
畑田です。
チームのDockerfileを作成するときに、できるだけdocker社の意図したものに近いわかりやすいものを作りたくてドキュメントを紐解いています。
一次情報の旨味を消さないように気をつけて書いています。
DockerfileのRUN
とCMD
とENTRYPOINT
について
RUN
RUN
はビルド時(docker build
のとき)にコンテナ内で実行されます。
一つのDockerfileは複数のRUN
を持ち、ビルドステップに応じて呼び出されます。
RUN
の数だけレイヤー(中間生成コンテナ)が生成されるので少ないに越したことはないです。
CMD
CMD
は完成したイメージからコンテナを作成するときに実行されます。
つまりdocker run
の時に実行されます。
1つのDockerfileにつき1つのCMD
が呼び出され、複数書いてあったとしても最後に書かれたものが実行されます。
ただし、docker run
の引数としてcommandが指定されていた場合、これが最後のコマンドと捉えられ、これに上書きされてしまいます。
ENTRYPOINT
ENTRYPOINT
はCMD
同様、イメージからコンテナを作成するときに実行されるのですが、ENTRYPOINT
はdocker run
の引数のcommandに上書きされません。
ENTRYPOINT
はCMD
と組み合わせて使います。
ENTRYPOINT
とCMD
が両方書かれているとき、書く順番によらず、ENTRYPOINT
を実行するコマンドとして、CMD
をそのコマンドのargumentとして、実行されます。
ENTRYPOINT
はdocker container run
の引数やdocker-compose.yml
の- command
に上書きされないがCMD
は上書きされるという性質を利用して、引数のみを上書き可能にしたコマンドを用意することもできます。
DockerfileのVOLUME
について
まず、Dockerfile内ではコンテナ側のdirectoryしか指定することができません。
これはいかなる環境においても同様の挙動をさせたいというDockerの設計思想による仕様です。
開発のためにボリュームをマウントしたい場合はdocker-composeを使うしかないようです。
おそらくdocker-composeを使っていないプロジェクトはあまりないのでそれほど問題にはならないですが、deployのときなどには気をつける必要があるかもしれません。
DockerfileのADD
とCOPY
について
ADD
とCOPY
は機能が似ていますが、COPY
が単にlocalのファイルをcontainerにコピーするだけなのに対して、ADD
はtarファイルの展開やremote URLからのデータの取得をサポートしています。
したがって、localファイルのコピーを意図しているときにはより限定的な意味のCOPY
を用いることがdockerによって推奨されています。
さらに、ADD
によってremote URLを用いてリソースを取得することは非推奨とされており、curl
やwget
をRUN
コマンドによって実行することが推奨されます。
このため、ADDコマンドはtar展開を含んだcontainerへのリソース追加に適用することが推奨されていると考えられます。
この性質を知っていると、Dockerfileの可読性がぐんと上がりますが、そもそもこれを意識せず書かれたDockerfileが世に溢れており、却って生きづらくなるかもしれません。
DockerfileのCOPY
における注意点
上の項目でも推奨しているCOPY
について、directoryをコピーするときに注意する必要があります。
まず、以下のようにファイルをコピーするときにはなんら問題は生じません。
COPY ./package*.json ./
しかし、次のようにdirectory自体をコピーしようとするときに問題は生じます。
COPY ./src/ ./
このように書いてしまうと、host machineの./src/
の中身がcontainer machineの./
のなかに展開されてコピーされてしまいます。(イメージはJavaScriptの[...data]
です。違うか。)
つまり、container machineの./
が空であれば、container machineの./
はhost machineの./src/
と全く同じものになるし、もともと中身が入っていればmergeされてしまうということです。
展開せずにcontainer machineの./
の下に./src
としてコピーしたい場合は以下のように記述してください。
COPY ./src/ ./src/
この仕様に若干の違和感があるのは、Linux系共通コマンドである以下のものと仕様が異なるからだと思います。
$ cp -r DIRECTORY_NAME DESTINATION