Help us understand the problem. What is going on with this article?

Dockerfileチートシートv19.03

概要

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
Dockerfile
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、基本的には指定すべき
Dockerfile
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で後述していますが、できる限りまとめて記載すべきです。

Dockerfile
FROM centos:8
RUN yum install -y httpd iproute && \
    echo "Test" > /var/www/html/index.html
ENTRYPOINT ["/usr/sbin/httpd"]

ENTRYPOINT / CMD

コンテナを起動した際に実行されるコマンドを規定します。
Dockerfileの最後の方にそれぞれ1回ずつ記述されるイメージ。

Dockerfile
FROM centos:8

ENTRYPOINT ["df"]
CMD ["-H", "."]

指定するコマンドについては、半角スペースごとにコンマで区切って記述します。例えばgo run main.goを指定したい場合は以下のようになります。

Dockerfile
ENTRYPOINT ["go", "run", "main.go"]

ENTRYPOINTとCMDの違い

ENTRYPOINTは必ずそのまま実行され、
CMDはコンテナ起動時に引数を指定すると上書き可能です。
この特性を生かして、ENTRYPOINTにはコマンド部分、CMDにはコマンドのオプション部分を記載するという書き方ができます。

e.g.
上記のDockerfileでイメージをビルドしたとして、引数ありでコンテナ起動する場合と引数なしでコンテナ起動する場合の、具体的な実行結果の違いを以下に示しておきます。

  • 引数なし : df -H .が実行されます。
Terminal
$ docker run override_test
Filesystem      Size  Used Avail Use% Mounted on
overlay          63G  2.1G   58G   4% /
  • 引数あり(--help) : df --helpが実行される。CMDの部分がコマンドラインに指定したオプションで上書きされていることがわかります。
Terminal
$ 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
Dockerfile
FROM centos:latest

RUN mkdir -p /app
# ファイル単位
COPY ./copyfile.txt /app
# ディレクトリ単位 ※コピー先のディレクトリ名称を指定する必要あり
COPY ./copydir /app/subdir
Terminal
$ 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のCOPYADDとしても同様の結果となります)
ただし、ADDを利用すると、コピーファイルが圧縮ファイルであった場合、自動的に解凍が行われます。

Dockerfile
FROM centos:8

RUN mkdir -p /app
# addtest.tarはcopydirを圧縮したファイル
ADD ./addtest.tar /app
Terminal
$ 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の場合

Terminal
$ 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

実行コンテナに環境変数を追加します。

Dockerfile
FROM centos:8                                                                                                                                                              
ENV NEW_ENV='env-value'

ENTRYPOINT ["/bin/bash"]
Terminal
$ 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状態とします。

Dockerfile
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" ]
Terminal
$ 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

Dockerfile
FROM centos:8
RUN mkdir -p /test
RUN echo "before workdir" > before.txt

WORKDIR /test
RUN echo "after workdir" > after.txt

ENTRYPOINT ["/bin/bash"]
Terminal
$ 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アプリケーションをマルチステージビルドを適用した場合とそうでない場合とのイメージサイズを比較してみます。

マルチステージビルドなし

Dockerfile
FROM golang:1.13-alpine
COPY ./main.go ./
RUN go build -o /app ./main.go
ENTRYPOINT ["/app"]

マルチステージビルドあり

Dockerfile
# 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"]

サイズ確認

Terminal
REPOSITORY     TAG                 SIZE
golang         nonmultistage       361MB
golang         multistage          7.6MB # 圧倒的に軽い

命令はできるだけまとめる

Dockerfileの命令1つにつき、イメージキャッシュがそれぞれ構築されていきます。このイメージキャッシュレイヤー数を最小化することはベストプラクティスとされており、それにより軽量化やビルド高速化につながることがあります。具体的には以下の処置を行います。

  • RUN命令は「バックスラッシュ + &&」でつなげて記載
  • COPY/ADD命令もできるだけ少なくまとめる

特にRUNに関してはビルド時キャッシュの問題もあって、yum updateyum install&&でつなげて書かないとワナにはまる等といったことがあります。(キャッシュについては本記事では記載しません。)

ADD命令にはURLも指定可能でもそうは書かない

レイヤ数を少なくし、イメージ軽量化につなげるために、URLからリソースを取得する場合はcurlまたはwgetを利用します。

Example(Best practicesより抜粋)

Dockerfile
# 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

Reference

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした