概要
Dockerfile
命令をまとめました。
※すべて網羅しているわけではありません。
- Dockerバージョン:v19.03
早見表
命令 | 概要 |
---|---|
FROM | ベースイメージを指定 |
RUN | 指定コマンドを実行 |
ENTRYPOINT | コンテナ実行時のコマンドを指定 |
CMD | コンテナ実行時のコマンドを指定(上書き可) |
COPY | ホストマシンからコンテナイメージへファイル/ディレクトリを単純コピー |
ADD | COPY+解凍 / URLからダウンロード(非推奨) |
ENV | 環境変数を追加 |
EXPOSE | 指定ポートを開ける |
WORKDIR | カレントディレクトリを変更 |
MAINTAINER |
deprecated 現在は LABEL maintainer="maintainer@example.com" と指定すべき |
FROM
ベースイメージを指定して取得します。1行目はこのコマンド。
Docker HubやプライベートDockerリポジトリからイメージを指定します。
- タグ指定なし :
latest
でpull
FROM centos
ENTRYPOINT ["/bin/bash"]
$ docker image build --file Dockerfile -t testrepo:test .
$ docker image ls --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
REPOSITORY TAG SIZE
testrepo test 220MB # <- 今回作成されたイメージ
centos latest 220MB # <- ベースイメージ
- タグ指定あり :指定したタグでpull、基本的には指定すべき
FROM centos:7.7.1908
ENTRYPOINT [ "/bin/bash" ]
$ docker image build --file Dockerfile -t testrepo:test .
$ docker image ls --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
REPOSITORY TAG SIZE
testrepo test 204MB # <- 今回作成されたイメージ
centos 7.7.1908 204MB # <- ベースイメージ
RUN
指定したコマンドを実行します。
Tipsで後述していますが、できる限りまとめて記載すべきです。
FROM centos:8
RUN yum install -y httpd iproute && \
echo "Test" > /var/www/html/index.html
ENTRYPOINT ["/usr/sbin/httpd"]
ENTRYPOINT / CMD
コンテナを起動した際に実行されるコマンドを規定します。
Dockerfileの最後の方にそれぞれ1回ずつ記述されるイメージ。
FROM centos:8
ENTRYPOINT ["df"]
CMD ["-H", "."]
指定するコマンドについては、半角スペースごとにコンマで区切って記述します。例えばgo run main.go
を指定したい場合は以下のようになります。
ENTRYPOINT ["go", "run", "main.go"]
ENTRYPOINTとCMDの違い
ENTRYPOINT
は必ずそのまま実行され、
CMD
はコンテナ起動時に引数を指定すると上書き可能です。
この特性を生かして、ENTRYPOINT
にはコマンド部分、CMD
にはコマンドのオプション部分を記載するという書き方ができます。
e.g.
上記のDockerfileでイメージをビルドしたとして、引数ありでコンテナ起動する場合と引数なしでコンテナ起動する場合の、具体的な実行結果の違いを以下に示しておきます。
- 引数なし :
df -H .
が実行されます。
$ docker run override_test
Filesystem Size Used Avail Use% Mounted on
overlay 63G 2.1G 58G 4% /
- 引数あり(
--help
) :df --help
が実行される。CMDの部分がコマンドラインに指定したオプションで上書きされていることがわかります。
$ docker run docker_test --help
Usage: df [OPTION]... [FILE]...
Show information about the file system on which each FILE resides,
or all file systems by default.
Mandatory arguments to long options are mandatory for short options too.
-a, --all include pseudo, duplicate, inaccessible file systems
# 以下略
COPY
ホストマシンのファイルまたはディレクトリをコンテナ内にコピーします。
.
├── Dockerfile
├── copydir
│ ├── subfile.txt
│ └── subfile2.txt
└── copyfile.txt
FROM centos:latest
RUN mkdir -p /app
# ファイル単位
COPY ./copyfile.txt /app
# ディレクトリ単位 ※コピー先のディレクトリ名称を指定する必要あり
COPY ./copydir /app/subdir
$ docker image build --file Dockerfile -t centos:copytest .
$ docker container run centos:copytest ls -ltR /app
/app:
total 8
drwxr-xr-x 2 root root 4096 Jan 12 13:27 subdir
-rw-r--r-- 1 root root 6 Jan 12 13:18 copyfile.txt
/app/subdir:
total 4
-rw-r--r-- 1 root root 0 Jan 12 13:25 subfile2.txt
-rw-r--r-- 1 root root 8 Jan 12 13:19 subfile.txt
ADD
COPY
同様、ホストマシンのファイルをコンテナ内へコピーします。(上記DockerfileのCOPY
をADD
としても同様の結果となります)
ただし、ADD
を利用すると、コピーファイルが圧縮ファイルであった場合、自動的に解凍が行われます。
FROM centos:8
RUN mkdir -p /app
# addtest.tarはcopydirを圧縮したファイル
ADD ./addtest.tar /app
$ docker image build --file add/Dockerfile -t centos:addtest .
$ docker container run centos:addtest ls -lR /app
/app:
total 4
drwxr-xr-x 2 501 games 4096 Jan 12 13:25 copydir
/app/copydir:
total 4
-rw-r--r-- 1 501 games 8 Jan 12 13:19 subfile.txt
-rw-r--r-- 1 501 games 0 Jan 12 13:25 subfile2.txt
# tarファイルそのものはなく、解凍されたものが存在している
e.f. COPY
の場合
$ docker container run testrepo:copy ls -lR /app
/app:
total 4
-rw-r--r-- 1 root root 177 Jan 12 13:31 addtest.tar # 圧縮ファイルはそのまま
COPYとの棲み分け
- 単純にコピーのみ行う場合 => COPY
- コピーして解凍も行う場合 => ADD
ENV
実行コンテナに環境変数を追加します。
FROM centos:8
ENV NEW_ENV='env-value'
ENTRYPOINT ["/bin/bash"]
$ docker image build --file Dockerfile -t centos:envtest .
$ docker container run -it centos:envtest
[root@de6dc0f2577e /]# env
LANG=en_US.UTF-8
HOSTNAME=de6dc0f2577e
NEW_ENV=env-value # fileで追加した環境変数
EXPOSE
指定したポートをLISTEN状態とします。
FROM python:3.8-slim-buster
COPY . /
RUN pip install -r /app/requirements.txt
EXPOSE 9876
ENTRYPOINT [ "gunicorn", "flask_app:app" ]
CMD [ "-c", "/app/config/gunicorn_settings.py" ]
$ docker image build --file Dockerfile -t python:flask .
$ docker container run --name exposetest python:flask
$ docker container ps --format "table {{.Image}}\t{{.Ports}}"
IMAGE PORTS NAMES
flask 9876/tcp exposetest
a2556b3a812 worktest
# PORTSにLISTENEDのポート番号が表示される
WORKDIR
作業ディレクトリを変更します。
Dockerfileにおけるcd
。
FROM centos:8
RUN mkdir -p /test
RUN echo "before workdir" > before.txt
WORKDIR /test
RUN echo "after workdir" > after.txt
ENTRYPOINT ["/bin/bash"]
$ docker image build --file Dockerfile -t centos:workdir .
$ docker container run -it --name workdirtest centos:workdir
[root@4cd5accab742 test]# pwd
/test # 最後にWORKDIRしたパスがコンテナ起動時のベースパスとなる
[root@4cd5accab742 test]# ls .. | grep before
before.txt # beforeはWORKDIR前に作成したのでルートディレクトリに存在
[root@4cd5accab742 test]# ls .
after.txt # afterはWORKDIR後に作成したため/testディレクトリに存在
Tips
マルチステージビルドで軽量化
マルチステージビルドとは、最終完成イメージの軽量化をはかるためのテクニックのようなものです。
一例としてGo
アプリケーションをマルチステージビルドを適用した場合とそうでない場合とのイメージサイズを比較してみます。
マルチステージビルドなし
FROM golang:1.13-alpine
COPY ./main.go ./
RUN go build -o /app ./main.go
ENTRYPOINT ["/app"]
マルチステージビルドあり
# 1段階め処理(ビルドを行う)
FROM golang:1.13-alpine as builder
COPY ./main.go ./
RUN go build -o /app ./main.go
# 2段階め(これが完成イメージ)
# ベースに最低限の要件を満たす軽量イメージを選択する
FROM alpine:3.11
# 1段階めのコンテナから必要物をコピー
COPY --from=builder /app .
ENTRYPOINT ["./app"]
サイズ確認
REPOSITORY TAG SIZE
golang nonmultistage 361MB
golang multistage 7.6MB # 圧倒的に軽い
命令はできるだけまとめる
Dockerfileの命令1つにつき、イメージキャッシュがそれぞれ構築されていきます。このイメージキャッシュレイヤー数を最小化することはベストプラクティスとされており、それにより軽量化やビルド高速化につながることがあります。具体的には以下の処置を行います。
- RUN命令は「バックスラッシュ + &&」でつなげて記載
- COPY/ADD命令もできるだけ少なくまとめる
特にRUN
に関してはビルド時キャッシュの問題もあって、yum update
とyum install
は&&
でつなげて書かないとワナにはまる等といったことがあります。(キャッシュについては本記事では記載しません。)
ADD命令にはURLも指定可能でもそうは書かない
レイヤ数を少なくし、イメージ軽量化につなげるために、URLからリソースを取得する場合はcurl
またはwget
を利用します。
Example(Best practicesより抜粋)
# Good
RUN mkdir -p /usr/src/things \
&& curl -SL http://example.com/big.tar.xz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all
# Bad
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all