コンテンツにスキップ

Dockerfileのベストプラクティス

この章ではDockerfileを記述する際のベストプラクティスについて記述します。

軽量なイメージを作る

Docker Image はレイヤーが少なくサイズが軽いものが良いものだとされています。
レイヤーを増やすことはオーバーヘッドに繋がり、サイズはImageのpullの速度に繋がります。

どのようなアプローチでDocker Image を作成すると良いかを見ていきましょう。

最小限の構成にする

例えばPHPの環境を構築するのにCentOSのベースイメージで、phpenvを入れて、MySQLを入れて、、といったことは非推奨です。
DockerはいままでのVMとは思想が異なります。1コンテナ1プロセスになるように設計を行いましょう。
複数のプロセスを使用したい場合はそれぞれコンテナに分け、オーケストレーションツールを使用してコンテナを協調させて動かしましょう。

軽量なベースイメージを使用する

Alpine OS という非常に軽量なOSが存在します。
まずはこのAlpine OS がベースとなっているDocker Image を使いましょう。

メジャーな言語は一通りAlpineOSに対応しており、Node.jsも例に漏れず対応しております。
(nodeのDocker Image が12倍の差があります) node alpine

.dockerignoreを使う

Dockerのビルド時に無視するファイル/ディレクトリを指定することができます。
".git" のようなコンテナ内に不要な情報、 "node_modules" のような上書きされると困るものを記述します。
.dockerignore は基本的に .gitignore と同じ書き方が可能です。

.git/
node_modules/

Build

キャッシュを意識する

Docker Image は各コマンド毎にキャッシュを作成します(これを中間レイヤーと呼びます)。
ビルド後に、コマンドの変更・ファイルの追加/更新など、なにか変化を起こすと、変化が起こったレイヤーの直前のキャッシュからビルドを実行します。

例えば単純な依存ライブラリのインストールだけ行えば問題ないNode.jsのアプリケーションがあったとします。
開発中は頻繁にコードの変更を行うはずです。コードの変更を行えばせっかく作成したキャッシュが効かなくなってしまい、ビルドのし直しになってしまいます。
単純な npm install が必要なアプリケーションであれば package.jsonpackage-lock.json だけをコンテナ上へコピーして、その後スクリプトのコピーを行うと高速なビルドを実現できるでしょう。

  FROM node:slim

  WORKDIR /scripts

- COPY . .
+ COPY ./package.json ./package-lock.json /scripts/

  RUN npm install

+ COPY . .

  CMD ["npm", "start"]

Multi-Stage Build

Golangのようなビルドを行い成果物をバイナリとして吐き出す言語であれば、最小限のOSと成果物のバイナリの2つだけで動作します。
この2つだけの最低限の環境を用意するために活躍するのがMulti-Stage Buildです。

Multi-Stage Buildは複数のDocker Image を作り、最終的にその複数のDocker Image から任意のファイルだけを抽出して1つのDocker Image にします。

#==================================================
# Build Layer
FROM golang:1.12-alpine as build

WORKDIR /go/app

COPY . .

RUN apk add --no-cache git \
  && go build -o app

#==================================================
# Run Layer
FROM alpine

WORKDIR /app

COPY --from=build /go/app/app .

RUN addgroup go \
  && adduser -D -G go go \
  && chown -R go:go /app/app

CMD ["./app"]

おまけ

軽量かつプロダクションを意識したイメージの例

Dockerfile

FROM node:alpine

ARG UID=991
ARG GID=991

ENV NODE_ENV=production

WORKDIR /scripts

COPY package.json package-lock.json /scripts/

RUN npm install --production --no-progress

COPY . .

RUN addgroup app -g ${GID} \
  && adduser -D -G app -u ${UID} app \
  && mv /root/.config /home/app/ \
  && chown -R app:app /scripts /home/app/.config

USER app

EXPOSE 3000

CMD ["npm", "start"]

.dockerignore

.git/
node_modules/

イメージの確認

$ docker build -t myapp:4 .
   :
$ docker images myapp
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myapp               4                   9eb407f3406a        9 seconds ago       90MB
myapp               3                   87ea6e63c875        32 minutes ago      160MB
myapp               2                   919f447ae003        About an hour ago   160MB
myapp               1                   61d3ff752744        About an hour ago   916MB
$ docker history myapp:4
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
9eb407f3406a        24 seconds ago      /bin/sh -c #(nop)  CMD ["npm" "start"]          0B
32d79cd696ee        25 seconds ago      /bin/sh -c #(nop)  EXPOSE 3000                  0B
ba11be6658e8        25 seconds ago      /bin/sh -c #(nop)  USER app                     0B
10aaa4ed01f2        25 seconds ago      |2 GID=991 UID=991 /bin/sh -c addgroup app -…   6.04MB
beba948fcdf5        28 seconds ago      /bin/sh -c #(nop) COPY dir:4031660d2eb87dd44…   30.1kB
d208f024298c        4 minutes ago       |2 GID=991 UID=991 /bin/sh -c npm install --…   7.8MB
499f23e1c8b9        4 minutes ago       /bin/sh -c #(nop) COPY multi:7bbf60d9ee13776…   26.2kB
0989832456ac        4 minutes ago       /bin/sh -c #(nop) WORKDIR /scripts              0B
8b17881bf747        4 minutes ago       /bin/sh -c #(nop)  ENV NODE_ENV=production      0B
6a42c9764263        4 minutes ago       /bin/sh -c #(nop)  ARG GID=991                  0B
b740f7e04562        4 minutes ago       /bin/sh -c #(nop)  ARG UID=991                  0B
953c516e1466        30 hours ago        /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>           30 hours ago        /bin/sh -c apk add --no-cache --virtual .bui…   5.1MB
<missing>           30 hours ago        /bin/sh -c #(nop)  ENV YARN_VERSION=1.15.2      0B
<missing>           30 hours ago        /bin/sh -c addgroup -g 1000 node     && addu…   65.4MB
<missing>           31 hours ago        /bin/sh -c #(nop)  ENV NODE_VERSION=11.13.0     0B
<missing>           3 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>           3 weeks ago         /bin/sh -c #(nop) ADD file:88875982b0512a9d0…   5.53MB