SlideShare a Scribd company logo
1 of 83
Download to read offline
DockerCon SF19 で発表の、基礎→マルチ・ステージ・ビルド→最新動向まで
Sakura Internet, Inc.
Masahito Zembutsu @zembutsu
Docker Meetup Kansai #3 #dockerkansai
May 24, 2019
Dockerfileを改善するための
Best Practice 2019年版
DockerCon SF19 での発表に基づく内容
• Dockerfile Best Practices
https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices
2
• 動画もご覧ください
https://www.docker.com/dockercon/2019-videos?watch=dockerfile-best-practices
オリジナルの発表は Tibor Vass 氏( @tiborvass) および Sebastian van Stijin 氏(@thajeztha)
による、 DockerCon 毎回恒例人気セッション"Dockerfile Best Practices" であり、両者に感謝します。
I thank you both for your excellent presentation of DockerCon. I also appreciate your permission to translate the content.
Please refer to this slides. And, this presentation video
will also be helpful.
このスライドは何?
3
⚫ DockerCon 19 で発表された人気シリーズ
“Best Practices” を日本訳+解説の追加。
⚫ Dockerfile の改善を通して、現在利用できる
マルチ・ステージ・ビルドや BuildKit 紹介。
以上の内容です。
※ "Docker Meetup Kansai #3" 発表時のスライドをベースに、
スライドそのままでは分かりづらい部分があるため、
一部で発表時と異なる表現・補足説明を用いている場合があります。
ゴール:
「Dockerfileの改善を具体的に理解」
Dockerfileのベストプラクティス
• "Best practices for writing Dockerfiles"
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
4
最新の日本語訳を公開しました。
そもそもの基本となるのは、"Dockerfileを書くためのベストプラクティス"です。
Dockerfile解説スライドもご覧ください
• ベストプラクティス日本語版に解説・図解を加えたバージョンを作りました
5
https://www.slideshare.net/zembutsu/explaining-best-practices-for-writing-dockerfiles
もし、Dockerfileに慣れていないのであれば、前提として、こちらのスライドをご覧ください。
Dockerfile とは?
6
ド ッ カ ー フ ァ イ ル
Dockerfile
• Docker は Dockerfile から命令を読み込み、イメージを自動構築
• テキスト形式のドキュメント
• docker build / docker image build で Dockerfile を読み込み構築
• 「Docker イメージを作るための設計図」
• FROM、ADD、CMD など命令文で構成
• 例) 何のイメージをもとに、何を実行するか?
• 誰でも確実にイメージを構築できる
• イメージの構築過程を確認できる
• Dockerfileは広く使われている
• GitHub上に Dockerfile は100万以上
7
automate build
blueprint
Image by John Dortmunder from Pixabay
「Dockerfile」はDockerイメージを自動構築するために、必ず使うファイルです。
BuildKit: builder v2
• docker build は「イメージ・キャッシュ」があるため、素早く開発できる
• しかし、いくつかの制限があったため、buildKit プロジェクトで根本的に構築
• https://github.com/moby/buildkit
• "ゴールは Docker build のデフォルト"
• BuildKit: 特長
• 同時へいれt性(concurrency)
• 断片的なコンテキスト・アップロード (lazy context upload)
• キャッシュの改良
• 新しい Dockerfile 機能の追加
8
⚫並列性が無い
⚫docker run と同様に root で動作する必要性
⚫ボリューム機能が無い
高速にイメージを作れる BuildKit という汎用ツールが開発途上です。
Docker BuildKit を使う方法
• クライアントの環境変数
• Docker デーモン設定ファイル /etc/docker/daemon.json
• 現時点では Windows は対応していない
• Windows support coming soon!
9
{ "features": { "buildkit": true }}
export DOCKER_BUILDKIT=1
BiuldKit は Docker とは別のプロジェクト (https://github.com/moby/buildkit ) ですが、
現在の Docker CE v18.09 では、BuildKit の一部機能が既に利用できる状態です。
カイゼン Dockerfile
10
Dockerfile の改善領域
• 増大するbuild 時間
• イメージ容量
• 保守性
• 安全性
• 一貫性・反復性
11
(Incremental) build time
Image size
Maintainability
Security
Consistency/Repetability
ここからは、先日開催された DockerCon SF19での発表サンプルをベースにご紹介します。
サンプルのアプリケーションを使い、5つの視点で Dockerfile を改善する流れをみていきましょう。
サンプルの Dockerfile をカイゼンするぞ!
12
FROM debian
COPY . /app
RUN apt-get update
RUN apt-get –y install openjdk-8-jdk ssh emacs
CMD ["java", "-jar", "/app/target/app.jar"]
これは、Javaで"Hello world"を表示するための、サンプルプログラム用の Dockerfile です。
サンプルの Dockerfile をカイゼンするぞ!
13
FROM debian
COPY . /app
RUN apt-get update
RUN apt-get –y install openjdk-8-jdk ssh emacs
CMD ["java", "-jar", "/app/target/app.jar"]
vim
まずすべきは、emacs を消して、vim を入れましょう。もちろんジョークですが!
サンプルの Dockerfile をカイゼンするぞ!
14
FROM debian
COPY . /app
RUN apt-get update
RUN apt-get –y install openjdk-8-jdk ssh emacs
CMD ["java", "-jar", "/app/target/app.jar"]
vim
まずすべきは、emacs を消して、vim を入れましょう。もちろんジョークですが!
増大する構築(build)時間
ビルド・キャッシュと友達になろう
15
課題と対策
キャッシュする順番が重要
16
FROM debian
COPY . /app
RUN apt-get update
RUN apt-get –y install openjdk-8-jdk ssh vim
COPY . /app
CMD ["java", "-jar", "/app/target/app.jar"]
頻繁に変更するものを後ろへ
構築時のキャッシュとは、変更箇所があれば破棄されます。この例の COPY では「.」(カレント)にある
ファイルに変更があれば、毎回「apt-get update」と「install」が走るので、時間がかかってしまいます。
せっかくあrキャッシュを有効活用するには、頻繁に更新する可能性があるものを後ろにおきます。
キャッシュする順番が重要
17
FROM debian
COPY . /app
RUN apt-get update
RUN apt-get –y install openjdk-8-jdk ssh vim
COPY . /app
CMD ["java", "-jar", "/app/target/app.jar"]
頻繁に変更するものを後ろへ
構築時のキャッシュとは、変更箇所があれば破棄されます。この例の COPY では「.」(カレント)にある
ファイルに変更があれば、毎回「apt-get update」と「install」が走るので、時間がかかってしまいます。
せっかくあrキャッシュを有効活用するには、頻繁に更新する可能性があるものを後ろにおきます。
キャッシュ有効
キャッシュ破棄 … ホスト側「 ./ 」に変更時
キャッシュ破棄 … ホスト側「 ./ 」に変更時
時間がかかるので、キャッシュ活用すべし
時間かかる
キャッシュ破棄の影響を避けるため、範囲を狭く
18
FROM debian
RUN apt-get update
RUN apt-get –y install openjdk-8-jdk ssh vim
COPY . /app
COPY target/app.jar /app
CMD ["java", "-jar", "/app/target/app.jar"]
コピーに必要なものを明示する。
可能であれば "COPY ."を避ける
コピーすべき場所を絞っておけば、キャッシュは破棄されません。
この例では「app.jar」しか「COPY」しませんので、他のファイルに変更があってもキャッシュを保持。
キャッシュ破棄の影響を避けるため、範囲を狭く
19
FROM debian
RUN apt-get update
RUN apt-get –y install openjdk-8-jdk ssh vim
COPY . /app
COPY target/app.jar /app
CMD ["java", "-jar", "/app/target/app.jar"]
コピーすべき場所を絞っておけば、キャッシュは破棄されません。
この例では「app.jar」しか「COPY」しませんので、他のファイルに変更があってもキャッシュを保持。
キャッシュ有効
キャッシュ有効
キャッシュ有効
ちょっとキャッシュする範囲が拡がるかもね
キャッシュ対象の明確化
コピーに必要なものを明示する。
可能であれば "COPY ."を避ける
行をまとめる: apt-get update & install
20
FROM debian
RUN apt-get update
RUN apt-get –y install openjdk-8-jdk ssh vim
RUN apt-get update ¥
&& apt-get -y install ¥
openjdk-8-jdk ssh vim
COPY target/app.jar /app
CMD ["java", "-jar", "/app/target/app.jar"]
古いパッケージ情報のキャッシュ利用を避ける
パッケージ・マネージャを使う場合、「古い」パッケージ情報をキャッシュしがちです。
更新したいのに更新できないのを避けるには、情報の更新と、パッケージ追加・削除をまとめること。
行をまとめる: apt-get update & install
21
FROM debian
RUN apt-get update
RUN apt-get –y install openjdk-8-jdk ssh vim
RUN apt-get update ¥
&& apt-get -y install ¥
openjdk-8-jdk ssh vim
COPY target/app.jar /app
CMD ["java", "-jar", "/app/target/app.jar"]
古いパッケージ情報のキャッシュ利用を避ける
パッケージ・マネージャを使う場合、「古い」パッケージ情報をキャッシュしがちです。
更新したいのに更新できないのを避けるには、情報の更新と、パッケージ追加・削除をまとめること。
キャッシュ有効
古いものをキャッシュしてるかも!
時間かかる場合があっても、確実に処理
イメージ容量を減らす
より速くデプロイするには、イメージを小さく
22
課題と対策
不要な依存関係を削除
23
FROM debian
RUN apt-get update ¥
&& apt-get -y install --no-install-recommends ¥
openjdk-8-jdk ssh vim
COPY target/app.jar /app
CMD ["java", "-jar", "/app/target/app.jar"]
デプロイを素早くするためには、イメージ容量の削減が重要であり、必須課題。
推奨パッケージ(必須ではない)を避けるためには、「--no-install-recommends」フラグを付ける。
Javaの実行に不要なパッケージも入れないので「ssh」「vim」も消す。
不要なパッケージマネージャのキャッシュ削除
24
FROM debian
RUN apt-get update ¥
&& apt-get -y install –no-install-recommends ¥
openjdk-8-jdk ¥
&& rm –rf /var/lib/apt/lists/*
COPY target/app.jar /app
CMD ["java", "-jar", "/app/target/app.jar"]
パッケージ・マネージャのキャッシュ情報も、アプリケーションの実行に不要なので削除
保守性
できるだけシンプルに保つ
25
課題と対策
できるだけDocker公式(official)パッケージを使う
• メンテナンスにかける時間を減らす
(問題があるたびに、繰り返す更新)
• 容量を減らす(イメージ間でのレイヤ共有によって)
• コンテナとして使うために、予め設定済み
• 賢い人達が構築している
26パッケージ・マネージャのキャッシュ情報も、アプリケーションの実行に不要なので削除
27
FROM debian
RUN apt-get update ¥
&& apt-get -y install –no-install-recommends ¥
openjdk-8-jdk ¥
&& rm –rf /var/lib/apt/lists/*
FROM openjdk
COPY target/app.jar /app
CMD ["java", "-jar", "/app/target/app.jar"]
できるだけDocker公式(official)パッケージを使う
Javaの実行であれば、「openjdk」イメージがあるため、debianでセットアップする必要はない
タグを明示
28
FROM openjdk:latest
FROM openjdk:8
COPY target/app.jar /app
CMD ["java", "-jar", "/app/target/app.jar"]
"latest"タグは常に入れ替わる。
タグを指定しておけば、想定外の
ベース・イメージ変更発生を防止。
何も指定しなければ「latest」(最新)になるため、常にタグ(主にバージョン)指定を忘れずに
適切なタグを探す
29
Docker Hub のドキュメントを読もう
https://hub.docker.com/_/openjdk
どのようなタグがあるかは、Docker Hub上のドキュメントで確認
必要最小限のものを探す
30
REPOSITORY TAG SIZE
openjdk 8 624MB
openjdk 8-jre 443MB
openjdk 8-jre-slim 204MB
openjdk 8-jre-alpie 83MB
ベース・イメージの変更だけで
540MBも削減
どのイメージ(タグ)を選ぶかによって、容量がかなり異なる。
alpineタグは Alpine Linux という約 5MB の Linux ディストリビューションがベース
再利用性
Dockerfileは青写真であり、
ソースコードこそが本当の源(ソース)
31
課題と対策
32
一貫した環境を、ソースから構築する
• Dockerfile を設計図(青写真)にしよう:
• ビルド時するための構築環境を Dockerfile に記述
• 正しいバージョンのビルド・ツールをインストール
• 環境ごとの違いを発生させない
• システム依存はあるかもしれない
• "source of truth"(本当のソース)とは、
ソースコードである。ビルド成果物ではない。
33
Image by John Dortmunder from Pixabay
blue print
開発環境、テスト環境、実行環境で共通する Dockerfile を目指す
一貫した環境を、ソースから構築する
34
FROM openjdk:8-jre-alpine
FROM maven:3.6-jdk-8-alpine
WORKDIR /app
COPY app.jar/app
COPY pom.xml .
COPY src ./src
RUN mvn –e –B package
CMD ["java", "-jar", "/app/target/app.jar"]
openjdk にかわり、Javaプロジェクト管理ツールであるMavenを開発環境として入れる
一貫した環境を、ソースから構築する
35
FROM maven:3.6-jdk-8-alpine
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn –e –B package
CMD ["java", "-jar", "/app/target/app.jar"]
このように、開発環境向けの Dockerfile を整えていく。
依存関係の解決は、ステップを分ける
36
FROM maven:3.6-jdk-8-alpine
WORKDIR /app
COPY pom.xml .
RUN mvn –e –B dependency:resolve
COPY src ./src
RUN mvn –e –B package
CMD ["java", "-jar", "/app/target/app.jar"]
開発関係だけに必要な依存関係を追加。
構築時のみの依存関係が判明
37
FROM maven:3.6-jdk-8-alpine
WORKDIR /app
COPY pom.xml .
RUN mvn –e –B dependency:resolve
COPY src ./src
RUN mvn –e –B package
CMD ["java", "-jar", "/app/target/app.jar"]
しかし、この黄色い部分は「開発環境の構築」段階しか使わないものであり、本番稼働では無駄
マルチ・ステージ・ビルドで、構築時の部分を削除
38
FROM maven:3.6-jdk-8-alpine AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn –e –B dependency:resolve
COPY src ./src
RUN mvn –e –B package
CMD ["java", "-jar", "/app/target/app.jar"]
FROM openjdk:8-jre-alpine
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app/target/app.jar"]
そこで、マルチ・ステージ・ビルドで複数の「FROM」を使い、構築時(AS bilder)と実行時を分離
←「builder」ステージから
ファイルをコピー
←ステージ「builder」と名前を付ける
マルチ・ステージ・ビルドで、構築時の部分を削除
39
FROM maven:3.6-jdk-8-alpine AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn –e –B dependency:resolve
COPY src ./src
RUN mvn –e –B package
FROM openjdk:8-jre-alpine
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app/target/app.jar"]
マルチ・ステージ・ビルドでは、「docker build」は全てのビルドを実行しますし、
「docker build –target builder」と指定すると「AS builder」の「FROM」ステージしか処理しません。
マルチ・ステージ Dockerfile
単に容量を減らすためだけではない
40
応用
プロジェクトごとに多くのステージがある
• Moby: 16 ステージ
https://github.com/moby/moby/blob/master/Dockerfile
• BuildKit: 44 ステージ
https://github.com/moby/buildkit/blob/master/hack/dockerfiles
/test.buildkit.Dockerfile
41Docker 関連プロジェクトの Dockerfile にも、多くのステージ(FROM命令)がある
マルチ・ステージの利用例
• 実行環境と構築(ビルド)環境を分ける(イメージ容量の縮小)
• イメージに対する影響を最小限に(DRY)
• 構築・開発・テスト・構文チェック…のような環境を明示
• 依存関係を一直線に処理しない(並列化)
• プラットフォーム固有のステージ
42
一般的な目的は、最終成果物の容量を削減
構築ステージを --target で指定
43
FROM image_or_stage AS stage_name
…
$ docker build --target stage_name
「AS ステージ名」を FROM 命令に書いておけば、「docker build」時に「--target」でステージを指定
イメージの flavor を変える
44
FROM maven:3.6-jdk-8-alpine AS builder
...
FROM openjdk:8-jre-jessie AS release-jessie
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app.jar"]
FROM openjdk:8-jre-jessie AS release-alpine
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app.jar"]
$ docker build --target release-jessie .
Debian jessie (8.x) をベースとするイメージと、Alpine Linux をベースとするイメージ。
特色(風味)
45
FROM maven:3.6-jdk-8-alpine AS builder
...
FROM openjdk:8-jre-jessie AS release-jessie
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app.jar"]
FROM openjdk:8-jre-jessie AS release-alpine
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app.jar"]
$ docker build --target release-jessie .
イメージの flavor を変える
特色(風味)
しかし、 Dockerfile をよく見ると問題があり、
46
FROM maven:3.6-jdk-8-alpine AS builder
...
FROM openjdk:8-jre-jessie AS release-jessie
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app.jar"]
FROM openjdk:8-jre-jessie AS release-alpine
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app.jar"]
$ docker build --target release-jessie .
このように各ステージで、同じ命令が重複する箇所がある。
イメージの flavor を変える
特色(風味)
どちらも
同じ
47
ARG flavor=alpine
FROM maven:3.6-jdk-8-alpine AS builder
...
FROM openjdk:8-jre-$flavor AS release
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app.jar"]
$ docker build --target release
--build-arg flavor=jessie .
「ARG」を Dockerfile で指定しておくと、docker build 時に「--build-arg」を通して、変数のように展開
--build-arg で $flavor=xxx があれば、$flavor に xxx 代入
↓何もなければ「flavor=alpine」が適用
イメージの flavor を変える (DRY / 汎用的な ARG)
特色(風味)
※ DRY = “Don’t Repeat Yourself”(自分では繰り返さない)という
ソフトウェア開発の手法
引数
←ARG命令は、docker build時に指定できる変数を定義
書式は「変数名」または「変数名=デフォルト値」。
様々な環境:構築、開発、テスト、構文チェック(lint)…
• ステージとしての検討例:
⁃ builder: 依存関係すべてをビルド
⁃ build(または binary): builder + ビルド成果物
⁃ cross: 複数のプラットフォーム向けに構築
⁃ dev: build(er) + 開発/デバッグツール
⁃ lint: 最小限の構文チェック用依存関係
⁃ test: テストに関係する全ての依存関係 + テスト対象のビルド成果物
⁃ release: ビルド成果物を含む、最終的な最小イメージ
48様々な「ステージ」が検討できる。ここでは定型的な例
各ステージでは、依存関係が最小となるようにする
様々な環境:構築、開発、テスト、構文チェック(lint)…
49
FROM maven:3.6-jdk-8-alpine AS builder
...
FROM openjdk:8-jre-alpine AS lint
RUN wget https://github.com/checkstyle/checkstyle/releases/download/checkstyle-8.15/checkstyle-8.15-all.jar
COPY checks.xml .
COPY src /src
RUN java –jar checkstyle-8.15-all.jar –c checks.xml /src
これはシンプルな Java 構文チェック(リント)の確認用の Dockerfile を「AS lint」と指定
様々な環境:構築、開発、テスト、構文チェック(lint)…
50
FROM maven:3.6-jdk-8-alpine AS builder
...
FROM openjdk:8-jre-alpine AS release
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app.jar"]
FROM builder AS dev
RUN apk add --no-cache strace vim tcpdump
ENTRYPOINT ["ash"]
開発用デバッグ環境であれば、「builder」ステージをベースにしながら「AS dev」と明示し
シンプルにエディタ(vim)と tcpdump を入れている
様々な環境:構築、開発、テスト、構文チェック(lint)…
51
FROM maven:3.6-jdk-8-alpine AS builder
...
RUN mvn –e –B package –DskipTests
FROM builder AS unit-test
RUN mvn –e –B test
FROM release AS integration-test
RUN apk add --no-cache curl
RUN ./test/run.sh
他にも単体テスト、統合テスト用の環境も作れます。
並行性(Concurrency)
52
応用
一直線の Dockerfile ステージから …
• 全てのステージが順番(シーケンシャル)に実行
• この図では、上から下に一直線
• BuildKitがなければ、
不要なステージも無駄に実行し、破棄する
(無駄に時間がかかった)
53
s1
s2
s3
s4
s5
s6
デフォルトでは、全てのステージを順番に実行する
BuildKit のマルチ・ステージ graph へ
• BuildKit は下(--target のステージ名)から上に辿っていくような流れ
• 右図では s2, s3, s4 ステージを同時並行処理
• 不要なステージは無視できる
• 右図では s5 がビルド時に
不要であれば、何もしない
54
s1
s2 s3 s4
s5s6
Docker 17.05 からマルチ・ステージ・ビルドが利用可能になった。
18.06までは experimental 、18.09 は利用できるように組み込まれている
グラフ
※グラフは点と点とのつながり
(関係性)を表す
Multi-srage: 並行ビルド (build concurrently)
55
FROM maven:3.6-jdk-8-alpine AS builder
...
FROM tiborvass/whalesay AS assets
RUN whalesay "Hello DockerCon!" > /out/assets.html
FROM openjdk:8-jre-alpine AS release
COPY --from=builder /app/target/app.jar /
COPY --from=assets /out /assets
CMD ["java", "-jar", "/app.jar"]
「assets」は最終イメージ(release)に必要だが、別のステージとして処理できる
そして、この Dockerfile は「builder」と「assets」のステージからのコピーを並行処理
Multi-srage: 並行ビルド (build concurrently)
56
FROM maven:3.6-jdk-8-alpine AS builder-base
…
FROM gcc:8-alpine AS builder-someClib
…
RUN git clone … ¥
./configure --prefix=/out && make && make install
FROM g++:8-alpine AS builder-someCPPlib
…
RUN git clone … ¥
cmake …
FROM builder-base AS builder
COPY --from=builder-someClib /out /
COPY --from=builder-someCPPlib /out /
複数の COPY 命令を使う時には、特に効果を発揮し、時間を節約できる
並行処理が有用な典型的なパターンが
複数の COPY --from … の繰り返し
ベンチマーク
• github.com/moby/moby Dockerfile, master ブランチ
• 小さいほうが優れている
57
Dockerfile Best Practices
https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices/52
v18.03 の docker build と BuiltKit では2倍の速さ
ベンチマーク
• github.com/moby/moby Dockerfile, master ブランチ
• 小さいほうが優れている
58
Dockerfile Best Practices
https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices/52
キャッシュを有効にすると、7倍も速くなる
ベンチマーク
• github.com/moby/moby Dockerfile, master ブランチ
• 小さいほうが優れている
59
Dockerfile Best Practices
https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices/52
最も大事なのは、ソースコードの変更時。再構築の速度はマルチ・ステージ・ビルドで 2.5 倍に改善。
Dockerfileの最新機能
60
新機能
新機能を有効にするには?
61
# syntax=docker/dockerfile:1.0-experimental
FROM maven:3.6-jdk-8-alpine AS builder
WORKDIR /app
COPY . /app
RUN mvn -e –B package
FROM openjdk:8-jre-alpine
COPY --from=builder /app/target/app.jar /
CMD ["java", "-jar", "/app.jar"]
Dockerfile の新機能を有効にするには、この行を1行目に追加する必要がある
※experimental というのは、まだ
Dockerfile の正式な構文では
ないため
詳細
• https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md
62構文の詳細は、こちらのドキュメントを参照
コンテキスト・マウント(v18.09+ w/ BuildKit)
63
# syntax=docker/dockerfile:1.0-experimental
FROM maven:3.6-jdk-8-alpine AS builder
WORKDIR /app
COPY . /app
RUN --mount=target=. mvn -e –B package -DoutputDirectory=/
FROM openjdk:8-jre-alpine
COPY --from=builder /app/app.jar /
CMD ["java", "-jar", "/app.jar"]
新機能を使って Dockerfile を書き換えていく。まずはマウント・オプション。
実行時に「.」(カレント・ディレクトリ内容)を Build Context として送信せず、マウントできる
※ Docker v18.09 以上かつ、BuildKit 有効化の状態で利用可能
contest mounts
Dockerfile
hello
コンテキスト・マウント(v18.09+ w/ BuildKit)
64
ディレクトリ内容をコンテナに COPY しなくても、
ビルド時に docker build コマンドを実行しているディレクトリ(ここでは 「target=.」で指定)を
rw(読み書き可能な状態)で直接コンテナにマウントし、
コンテナ内で「cat hello > /hello.txt」を実行
→ 構築したイメージの“/hello.txt” に “Hello world!” が書かれたファイルが作成される
# syntax=docker/dockerfile:1.0-experimental
FROM alpine
RUN --mount=type=bind,target=.,rw cat hello > /hello.txt
Hello world!
ソースコードなど、ビルド・コンテクストのコピーが不要となり、より早いビルドができる
contest mounts
• Dockerfile の mount 例:
ホスト側の docker build する場所に、
このファイルがあるとします
65
コンテキスト・マウント(v18.09+ w/ BuildKit)
contest mounts
従来 “COPY . /app” BuildKit
Dockerfile
app用ディレクトリ
Build用ディレクトリ
“docker build” 時、 「.」 以下の内容を
Docker イメージ用レイヤとして構築するため
全て Docker に対して送る必要があった
※結果として Docker イメージの不本意な増加
※あるいはマルチ・ステージ・ビルドで回避
Dockerfile
app用ディレクトリ
Build用ディレクトリ
コンテナ
コピー マウント
よいしょ…
みーてーるーだーけー
“docker build” の特定ステップのみ参照するため、
・ build 全体の時間を削減
・ イメージ・レイヤの肥大化を抑制
キャッシュの保持(BuildKitが無ければ)
66
FROM maven:3.6-jdk-8-alpine AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn -e –B dependency: resolve
COPY src ./src
RUN mvn –e –B package
CMD ["java", "-jar", "/app.jar"]
docker build 時にキャッシュが破棄されると、毎回依存関係の準備に時間がかかる
キャッシュの保持(v18.09+ w/ BuildKit)
67
# syntax=docker/dockerfile:1.0-experimental
FROM maven:3.6-jdk-8-alpine AS builder
WORKDIR /app
RUN --mount=target=. --mount=type=cache,target=/root/.m2 ¥
&& mvn package –DoutputDirectory=/
FROM openjdk:8-jre-alpine
COPY --from=builder /app.jar /
CMD ["java", "-jar", "/app.jar"]
apt: /var/lib/apt/lists
go: ~/.cache/go-build
go-modules: $GOPATH/pkg/mod
npm: ~/.npm
pip: ~/.cache/pip
キャッシュ用ディレクトリをマウントできるので、依存関係の準備にかかる時間を削減できる
キャッシュの保持
• --mount=type=cache 例:
68
Dockerfile
# syntax=docker/dockerfile:1.0-experimental
FROM ubuntu
RUN --mount=type=cache,target=/var/cache/apt ¥
--mount=type=cache,target=/var/lib/apt ¥
apt-get update && apt-get install -y wget curl
「type=cache」で指定した「target」のディレクトリは、docker build を実行したホスト上でキャッシュ
この例では apt-get install を含む「RUN」命令を書き換えたとしても、
一度キャッシュ(ビルド)済みであれば次回から高速なビルドが可能になる
キャッシュ情報をクリアするには「docker builder prune」コマンドを実行する
BuildKit
キャッシュはホスト側で保持
69
Dockerfile
app用ディレクトリ
Build用ディレクトリ
コンテナ
マウント
キャッシュしたあとは
みーてーるーだーけーDockerのキャッシュ用ディレクトリ
“docker build” の対象構築ステップ時のみ、
ホスト側のキャッシュ用ディレクトリを一時的にマウント
何度 build しても、キャッシュを有効活用できるので
高速にイメージを作りやすい
また、イメージ全体の容量も削減できる
docker build
※ docker builder prune で消せる
シークレット(これはダメな方法)
70
FROM baseimage
RUN …
ENV AWS_ACCESS_KEY_ID=…
ENV AWS_SECRET_ACCESS_KEY=…
RUN ./fetch-aseets-from-s3.sh
RUN ./build-scripts.sh
シークレット(secret)とは、パスワードやAPIキー、SSH 鍵などの認証情報(機微情報)
ENV に書くと docker history で丸見えですし、
シークレット(これもダメな方法)
71
FROM baseimage
RUN …
ARG AWS_ACCESS_KEY_ID
ARG AWS_SECRET_ACCESS_KEY
RUN ./fetch-aseets-from-s3.sh
RUN ./build-scripts.sh
$ docker build --build-arg ¥
AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID …
ARGでも途中のRUNで変数の情報をダンプできますし、
シェルのhistoryからも辿れるリスクが出てきます(記録しない方法もありますが、運用カバー系作業)
docker history
シークレット(v18.09+ w/ BiuldKit では、こうします)
72
# syntax=docker/dockerfile:1-experimental
FROM baseimage
RUN …
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials,
required ./fetch-assets-from-s3.sh
RUN ./build-scripts.sh
$ docker build --secret id=aws,src=~/.aws/credentials .
最終イメージに混入させないための手法が「secret」としてのマウント。
対象ステップのみデータを参照できるよう一時的にマウントし、次のステップではマウントしません。
↓idは docker build 時の –secret オプションで識別するため
↑targetで、この構築ステップのみ、
コンテナ内のこの場所に
一時的にファイルを設置する指示
↑これはホスト側に存在するファイル。
~より、 $HOME や絶対パスのほうが安全かも
↑次のステップでは、先の id=aws でマウントしたシークレットは見えない( 0 byte のファイル残骸のみ)
BuildKit
73
Dockerfile
大事なディレクトリ
Build用ディレクトリ
コンテナ
id指定対象を
マウント
構築ステップの間だけ
みーてーるーだーけー
“docker build” の対象構築ステップ時のみ、
ホスト側のシークレット(機微情報)ファイルを一時的にマウント
環境変数などで取り込んで認証などに利用する
一時的にホスト側を参照するだけなので
イメージ内に大事な情報を残さない
docker
build
SECRET_FILE
シークレット・マウント(v18.09+ w/ BiuldKit)
secret mounts
id指定
• Dockerfile の mount secret 例:
Dockerfile
$HOME/secret
シークレット・マウント(v18.09+ w/ BiuldKit)
74
# syntax=docker/dockerfile:1.0-experimental
FROM alpine
RUN --mount=type=secret,id=check,target=/root/secret,required ¥
cat /root/secret > /data.txt
MYID=secretpass
Dockerfile で当該 RUN 命令行のビルド時のみ、一時的にホスト側ファイルをマウントできる
secret mounts
ホスト側にIDとパスワードのような、
いわゆるシークレット(機微情報)を記録するファイルを設置
もちろん、パーミッションは 600 にするなど、十分なセキュリティ配慮が必要
$ docker build --secret id=check,src=$HOME/.data/credentials -t myimage .
↑srcで指定したパスはホスト上のファイル
プライベート git リポジトリ(ダメぜったい)
75
FROM baseimage
COPY ./keys/private.pem /root/.ssh/private.pem
ARG REPO_REF=19ba8bcd9976ef8a9bd086187df19ba7bcd997f2
RUN git clone git@githubcom:org/repo /work && cd /work ¥
&& git checkout –b $REPO_REF
これもイメージ内に大事なファイルが残ってしまう NG 例
プライベート git リポジトリ(v18.09+ w/BuildKitの場合)
76
FROM alpine
RUN apk add --no-cache openssh-client
RUN mkdir –p –m 0700 ~/.ssh && ssh-keyscan github.com >>
~/.ssh/known_hosts
ARG REPO REF=19ba8bcd9976ef8a9bd086187df19ba7bcd997f2
RUN –-mount-type=ssh,required ¥
git clone git@github.com:org/repo /work && cd /work ¥
&& git checkout –b $REPO_REF
$ eval $(ssh-agent)
$ ssh-add ~/.ssh/id_rsa
$ docker build --ssh=default .
この RUN 命令実行時のみ、
ホスト側の SSH 認証情報を読み込む方法が
利用できる
BuildKit
77
SSH マウント(v18.09+ w/ BiuldKit)
mounts
Dockerfile
.ssh
Build用ディレクトリ
コンテナ
鍵情報
参照
このステップがある間だけ
みーてーるーだーけー
“docker build” の対象構築ステップ時のみ、
ホスト側のSSH agent 情報を一時的にマウント
シークレット・マウントに近いけれど
SSH の利用に特化
こんてなから GitHub や SSH 接続したい場合に有用
docker
build
id_rsa ssh-agent
--mount=type=ssh
SSH
リモート・ホストや
GitHub / GitLab 等
• Dockerfile の mount=SSH 例:
Dockerfile
SSH マウント(v18.09+ w/ BiuldKit)
78
# syntax=docker/dockerfile:1.0-experimental
FROM alpine
RUN apk update && apk add openssh
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan <host_IP> > ~/.ssh/known_hosts
RUN --mount=type=ssh,required ¥
ssh <user>@<host_IP> ls -al / > /ls.txt
GitHubやGitLabの認証だけでなk、別のホストにこのようにしてログイン&操作も可能
mounts
$ eval $(ssh-agent)
$ ssh-add ~/.ssh/id_rsa
(パスフレーズを入力)
$ docker build --ssh default=$SSH_AUTH_SOCK -t <image_name> .
Dockerfile 改善のまとめ
• 従来
⁃ 構築・開発・テスト環境の矛盾
⁃ 膨れあがるイメージ容量
⁃ 構築時間がマシマシ(キャッシュ無効)になり時間がかかる
⁃ 構築が安全ではない
• これから( BuildKit の活用によって)
⁃ 構築・開発・テスト環境が一致
⁃ イメージ容量は最小
⁃ 構築が非常に速く、構築回数が増えていく
⁃ より安全に構築できる
79
export DOCKER_BUILDKIT=1重要なのは有効化に
新しいビルド時の一時マウント可能な bind、cache、tmpfs、secret、ssh
マルチ・ステージ・ビルドの活用によって、段階ごとの FROM
ステージ間のコピー機能により、最終成果物を小さく
並列ビルドの活用により、従来よりも素早い構築
参考資料
• Advanced multi-stage build patterns – Tõnis Tiigi – Medium
https://medium.com/@tonistiigi/advanced-multi-stage-build-patterns-6f741b852fae
• Build secrets and SSH forwarding in Docker 18.09 – Tõnis Tiigi – Medium
https://medium.com/@tonistiigi/build-secrets-and-ssh-forwarding-in-docker-18-09-
ae8161d066
• Introducing BuildKit – Moby Blog
https://blog.mobyproject.org/introducing-buildkit-17e056cc5317
• docker builder prune | Docker Documentation
https://docs.docker.com/engine/reference/commandline/builder_prune/
• Docker v18.09 新機能 (イメージビルド&セキュリティ) – nttlabs – Medium
https://medium.com/nttlabs/docker-v18-09-%E6%96%B0%E6%A9%9F%E8%83%BD-
%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%83%93%E3%83%AB%E3%83%89-
%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3-
9534714c26e2
80
参考資料
• BuildKitによる高速でセキュアなイメージビルド
https://www.slideshare.net/AkihiroSuda/buildkit
81
DockerCon での発表
• Dockerfile Best Practices
https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices
82
• 動画
https://www.docker.com/dockercon/2019-videos?watch=dockerfile-best-practices
Q&A
• 何か気になるところはありますか?
• Twitter: @zembutsu
• https://slideshare.net/zembutsu
• Dockerドキュメント日本語訳
http://docs.docker.jp
• Docker Composeドキュメント日本語訳
http://docs.docker.jp/compose/
• 公式ドキュメント
https://docs.docker.com
83

More Related Content

What's hot

SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021Hiroshi Tokumaru
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Akihiro Suda
 
マイクロサービス 4つの分割アプローチ
マイクロサービス 4つの分割アプローチマイクロサービス 4つの分割アプローチ
マイクロサービス 4つの分割アプローチ増田 亨
 
忙しい人の5分で分かるMesos入門 - Mesos って何だ?
忙しい人の5分で分かるMesos入門 - Mesos って何だ?忙しい人の5分で分かるMesos入門 - Mesos って何だ?
忙しい人の5分で分かるMesos入門 - Mesos って何だ?Masahito Zembutsu
 
Docker道場オンライン#1 Docker基礎概念と用語の理解
Docker道場オンライン#1 Docker基礎概念と用語の理解Docker道場オンライン#1 Docker基礎概念と用語の理解
Docker道場オンライン#1 Docker基礎概念と用語の理解Masahito Zembutsu
 
分散システムについて語らせてくれ
分散システムについて語らせてくれ分散システムについて語らせてくれ
分散システムについて語らせてくれKumazaki Hiroki
 
Grafana LokiではじめるKubernetesロギングハンズオン(NTT Tech Conference #4 ハンズオン資料)
Grafana LokiではじめるKubernetesロギングハンズオン(NTT Tech Conference #4 ハンズオン資料)Grafana LokiではじめるKubernetesロギングハンズオン(NTT Tech Conference #4 ハンズオン資料)
Grafana LokiではじめるKubernetesロギングハンズオン(NTT Tech Conference #4 ハンズオン資料)NTT DATA Technology & Innovation
 
忙しい人の5分で分かるDocker 2017年春Ver
忙しい人の5分で分かるDocker 2017年春Ver忙しい人の5分で分かるDocker 2017年春Ver
忙しい人の5分で分かるDocker 2017年春VerMasahito Zembutsu
 
DockerコンテナでGitを使う
DockerコンテナでGitを使うDockerコンテナでGitを使う
DockerコンテナでGitを使うKazuhiro Suga
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugMasatoshi Tada
 
Dockerの期待と現実~Docker都市伝説はなぜ生まれるのか~
Dockerの期待と現実~Docker都市伝説はなぜ生まれるのか~Dockerの期待と現実~Docker都市伝説はなぜ生まれるのか~
Dockerの期待と現実~Docker都市伝説はなぜ生まれるのか~Masahito Zembutsu
 
Cassandraのしくみ データの読み書き編
Cassandraのしくみ データの読み書き編Cassandraのしくみ データの読み書き編
Cassandraのしくみ データの読み書き編Yuki Morishita
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話Koichiro Matsuoka
 
え、まって。その並列分散処理、Kafkaのしくみでもできるの? Apache Kafkaの機能を利用した大規模ストリームデータの並列分散処理
え、まって。その並列分散処理、Kafkaのしくみでもできるの? Apache Kafkaの機能を利用した大規模ストリームデータの並列分散処理え、まって。その並列分散処理、Kafkaのしくみでもできるの? Apache Kafkaの機能を利用した大規模ストリームデータの並列分散処理
え、まって。その並列分散処理、Kafkaのしくみでもできるの? Apache Kafkaの機能を利用した大規模ストリームデータの並列分散処理NTT DATA Technology & Innovation
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!mosa siru
 
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】Masahito Zembutsu
 
初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!Tetsutaro Watanabe
 
こわくない Git
こわくない Gitこわくない Git
こわくない GitKota Saito
 
速習!論理レプリケーション ~基礎から最新動向まで~(PostgreSQL Conference Japan 2022 発表資料)
速習!論理レプリケーション ~基礎から最新動向まで~(PostgreSQL Conference Japan 2022 発表資料)速習!論理レプリケーション ~基礎から最新動向まで~(PostgreSQL Conference Japan 2022 発表資料)
速習!論理レプリケーション ~基礎から最新動向まで~(PostgreSQL Conference Japan 2022 発表資料)NTT DATA Technology & Innovation
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪Takuto Wada
 

What's hot (20)

SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
マイクロサービス 4つの分割アプローチ
マイクロサービス 4つの分割アプローチマイクロサービス 4つの分割アプローチ
マイクロサービス 4つの分割アプローチ
 
忙しい人の5分で分かるMesos入門 - Mesos って何だ?
忙しい人の5分で分かるMesos入門 - Mesos って何だ?忙しい人の5分で分かるMesos入門 - Mesos って何だ?
忙しい人の5分で分かるMesos入門 - Mesos って何だ?
 
Docker道場オンライン#1 Docker基礎概念と用語の理解
Docker道場オンライン#1 Docker基礎概念と用語の理解Docker道場オンライン#1 Docker基礎概念と用語の理解
Docker道場オンライン#1 Docker基礎概念と用語の理解
 
分散システムについて語らせてくれ
分散システムについて語らせてくれ分散システムについて語らせてくれ
分散システムについて語らせてくれ
 
Grafana LokiではじめるKubernetesロギングハンズオン(NTT Tech Conference #4 ハンズオン資料)
Grafana LokiではじめるKubernetesロギングハンズオン(NTT Tech Conference #4 ハンズオン資料)Grafana LokiではじめるKubernetesロギングハンズオン(NTT Tech Conference #4 ハンズオン資料)
Grafana LokiではじめるKubernetesロギングハンズオン(NTT Tech Conference #4 ハンズオン資料)
 
忙しい人の5分で分かるDocker 2017年春Ver
忙しい人の5分で分かるDocker 2017年春Ver忙しい人の5分で分かるDocker 2017年春Ver
忙しい人の5分で分かるDocker 2017年春Ver
 
DockerコンテナでGitを使う
DockerコンテナでGitを使うDockerコンテナでGitを使う
DockerコンテナでGitを使う
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
 
Dockerの期待と現実~Docker都市伝説はなぜ生まれるのか~
Dockerの期待と現実~Docker都市伝説はなぜ生まれるのか~Dockerの期待と現実~Docker都市伝説はなぜ生まれるのか~
Dockerの期待と現実~Docker都市伝説はなぜ生まれるのか~
 
Cassandraのしくみ データの読み書き編
Cassandraのしくみ データの読み書き編Cassandraのしくみ データの読み書き編
Cassandraのしくみ データの読み書き編
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
 
え、まって。その並列分散処理、Kafkaのしくみでもできるの? Apache Kafkaの機能を利用した大規模ストリームデータの並列分散処理
え、まって。その並列分散処理、Kafkaのしくみでもできるの? Apache Kafkaの機能を利用した大規模ストリームデータの並列分散処理え、まって。その並列分散処理、Kafkaのしくみでもできるの? Apache Kafkaの機能を利用した大規模ストリームデータの並列分散処理
え、まって。その並列分散処理、Kafkaのしくみでもできるの? Apache Kafkaの機能を利用した大規模ストリームデータの並列分散処理
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
 
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
 
初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!
 
こわくない Git
こわくない Gitこわくない Git
こわくない Git
 
速習!論理レプリケーション ~基礎から最新動向まで~(PostgreSQL Conference Japan 2022 発表資料)
速習!論理レプリケーション ~基礎から最新動向まで~(PostgreSQL Conference Japan 2022 発表資料)速習!論理レプリケーション ~基礎から最新動向まで~(PostgreSQL Conference Japan 2022 発表資料)
速習!論理レプリケーション ~基礎から最新動向まで~(PostgreSQL Conference Japan 2022 発表資料)
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 

Similar to Dockerfileを改善するためのBest Practice 2019年版

Appsody でnodejsのアプリを立ち上げよう!
Appsody でnodejsのアプリを立ち上げよう!Appsody でnodejsのアプリを立ち上げよう!
Appsody でnodejsのアプリを立ち上げよう!Daisuke Hiraoka
 
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014Masahiro Nagano
 
Dockerハンズオン
DockerハンズオンDockerハンズオン
DockerハンズオンKazuyuki Mori
 
Docker実践入門
Docker実践入門Docker実践入門
Docker実践入門hiro nemu
 
今だからこそ知りたい Docker Compose/Swarm 入門
今だからこそ知りたい Docker Compose/Swarm 入門今だからこそ知りたい Docker Compose/Swarm 入門
今だからこそ知りたい Docker Compose/Swarm 入門Masahito Zembutsu
 
Newcomer2020 Docker研修
Newcomer2020 Docker研修Newcomer2020 Docker研修
Newcomer2020 Docker研修Suguru Yazawa
 
Docker handson
Docker handsonDocker handson
Docker handsonkoda3
 
Introduce that Best practices for writing Dockerfiles
Introduce that Best practices for writing DockerfilesIntroduce that Best practices for writing Dockerfiles
Introduce that Best practices for writing DockerfilesYukiya Hayashi
 
Docker講習会資料
Docker講習会資料Docker講習会資料
Docker講習会資料teruyaono1
 
Docker ComposeでMastodonが必要なものを梱包する話
Docker ComposeでMastodonが必要なものを梱包する話Docker ComposeでMastodonが必要なものを梱包する話
Docker ComposeでMastodonが必要なものを梱包する話Masahito Zembutsu
 
Building production server on docker
Building production server on dockerBuilding production server on docker
Building production server on dockerHiroshi Miura
 
Building production server on docker
Building production server on dockerBuilding production server on docker
Building production server on dockerHiroshi Miura
 
Docker入門: コンテナ型仮想化技術の仕組みと使い方
Docker入門: コンテナ型仮想化技術の仕組みと使い方Docker入門: コンテナ型仮想化技術の仕組みと使い方
Docker入門: コンテナ型仮想化技術の仕組みと使い方Yuichi Ito
 
Docker Swarm モード にゅうもん
Docker Swarm モード にゅうもんDocker Swarm モード にゅうもん
Docker Swarm モード にゅうもんMasahito Zembutsu
 
Webアプリケーション開発者のためのDockerハンズオン
Webアプリケーション開発者のためのDockerハンズオンWebアプリケーション開発者のためのDockerハンズオン
Webアプリケーション開発者のためのDockerハンズオン虎の穴 開発室
 
Webアプリケーション開発者のためのDockerハンズオン20210519
Webアプリケーション開発者のためのDockerハンズオン20210519Webアプリケーション開発者のためのDockerハンズオン20210519
Webアプリケーション開発者のためのDockerハンズオン20210519虎の穴 開発室
 
ラズパイ2で動く Docker PaaSを作ってみたよ
ラズパイ2で動く Docker PaaSを作ってみたよラズパイ2で動く Docker PaaSを作ってみたよ
ラズパイ2で動く Docker PaaSを作ってみたよnpsg
 
Nseg20120929
Nseg20120929Nseg20120929
Nseg20120929hiro345
 

Similar to Dockerfileを改善するためのBest Practice 2019年版 (20)

Appsody でnodejsのアプリを立ち上げよう!
Appsody でnodejsのアプリを立ち上げよう!Appsody でnodejsのアプリを立ち上げよう!
Appsody でnodejsのアプリを立ち上げよう!
 
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014
Dockerで遊んでみよっかー YAPC::Asia Tokyo 2014
 
Dockerハンズオン
DockerハンズオンDockerハンズオン
Dockerハンズオン
 
Docker実践入門
Docker実践入門Docker実践入門
Docker実践入門
 
今だからこそ知りたい Docker Compose/Swarm 入門
今だからこそ知りたい Docker Compose/Swarm 入門今だからこそ知りたい Docker Compose/Swarm 入門
今だからこそ知りたい Docker Compose/Swarm 入門
 
Newcomer2020 Docker研修
Newcomer2020 Docker研修Newcomer2020 Docker研修
Newcomer2020 Docker研修
 
Docker handson
Docker handsonDocker handson
Docker handson
 
Introduce that Best practices for writing Dockerfiles
Introduce that Best practices for writing DockerfilesIntroduce that Best practices for writing Dockerfiles
Introduce that Best practices for writing Dockerfiles
 
Docker講習会資料
Docker講習会資料Docker講習会資料
Docker講習会資料
 
Docker ComposeでMastodonが必要なものを梱包する話
Docker ComposeでMastodonが必要なものを梱包する話Docker ComposeでMastodonが必要なものを梱包する話
Docker ComposeでMastodonが必要なものを梱包する話
 
Building production server on docker
Building production server on dockerBuilding production server on docker
Building production server on docker
 
Building production server on docker
Building production server on dockerBuilding production server on docker
Building production server on docker
 
Docker入門: コンテナ型仮想化技術の仕組みと使い方
Docker入門: コンテナ型仮想化技術の仕組みと使い方Docker入門: コンテナ型仮想化技術の仕組みと使い方
Docker入門: コンテナ型仮想化技術の仕組みと使い方
 
Docker Swarm モード にゅうもん
Docker Swarm モード にゅうもんDocker Swarm モード にゅうもん
Docker Swarm モード にゅうもん
 
Webアプリケーション開発者のためのDockerハンズオン
Webアプリケーション開発者のためのDockerハンズオンWebアプリケーション開発者のためのDockerハンズオン
Webアプリケーション開発者のためのDockerハンズオン
 
Webアプリケーション開発者のためのDockerハンズオン20210519
Webアプリケーション開発者のためのDockerハンズオン20210519Webアプリケーション開発者のためのDockerハンズオン20210519
Webアプリケーション開発者のためのDockerハンズオン20210519
 
Docker社内勉強会
Docker社内勉強会Docker社内勉強会
Docker社内勉強会
 
Docker Swarm入門
Docker Swarm入門Docker Swarm入門
Docker Swarm入門
 
ラズパイ2で動く Docker PaaSを作ってみたよ
ラズパイ2で動く Docker PaaSを作ってみたよラズパイ2で動く Docker PaaSを作ってみたよ
ラズパイ2で動く Docker PaaSを作ってみたよ
 
Nseg20120929
Nseg20120929Nseg20120929
Nseg20120929
 

More from Masahito Zembutsu

忙しい人のための Rocky Linux 入門〜Rocky LinuxはCentOSの後継者たり得るか?〜
忙しい人のための Rocky Linux 入門〜Rocky LinuxはCentOSの後継者たり得るか?〜忙しい人のための Rocky Linux 入門〜Rocky LinuxはCentOSの後継者たり得るか?〜
忙しい人のための Rocky Linux 入門〜Rocky LinuxはCentOSの後継者たり得るか?〜Masahito Zembutsu
 
自由検証環境提供宣言+Docker Compose V2 GA
自由検証環境提供宣言+Docker Compose V2 GA自由検証環境提供宣言+Docker Compose V2 GA
自由検証環境提供宣言+Docker Compose V2 GAMasahito Zembutsu
 
CentOS Linux 8 の EOL と対応策の検討
CentOS Linux 8 の EOL と対応策の検討CentOS Linux 8 の EOL と対応策の検討
CentOS Linux 8 の EOL と対応策の検討Masahito Zembutsu
 
さくらインターネットのコミュニティ with COVID-19
さくらインターネットのコミュニティ with COVID-19さくらインターネットのコミュニティ with COVID-19
さくらインターネットのコミュニティ with COVID-19Masahito Zembutsu
 
ブックトーク@CROSS ~SF編~ 発表資料「攻殻機動隊」「導きの星」
ブックトーク@CROSS ~SF編~ 発表資料「攻殻機動隊」「導きの星」ブックトーク@CROSS ~SF編~ 発表資料「攻殻機動隊」「導きの星」
ブックトーク@CROSS ~SF編~ 発表資料「攻殻機動隊」「導きの星」Masahito Zembutsu
 
インターネットでウェブサイトを表示している裏側の話
インターネットでウェブサイトを表示している裏側の話インターネットでウェブサイトを表示している裏側の話
インターネットでウェブサイトを表示している裏側の話Masahito Zembutsu
 
3分で分かる「プログラミング教育・情報教育」
3分で分かる「プログラミング教育・情報教育」3分で分かる「プログラミング教育・情報教育」
3分で分かる「プログラミング教育・情報教育」Masahito Zembutsu
 
ようこそオンラインの展示会場へ
ようこそオンラインの展示会場へようこそオンラインの展示会場へ
ようこそオンラインの展示会場へMasahito Zembutsu
 
小学校プログラミング教育に対する企業の取り組みと課題 #KOF2020
小学校プログラミング教育に対する企業の取り組みと課題 #KOF2020小学校プログラミング教育に対する企業の取り組みと課題 #KOF2020
小学校プログラミング教育に対する企業の取り組みと課題 #KOF2020Masahito Zembutsu
 
オンライン発表で気を付けているポイント~姿勢編
オンライン発表で気を付けているポイント~姿勢編オンライン発表で気を付けているポイント~姿勢編
オンライン発表で気を付けているポイント~姿勢編Masahito Zembutsu
 
Docker 9 tips~意外と知られていない日常で役立つ便利技
Docker 9 tips~意外と知られていない日常で役立つ便利技Docker 9 tips~意外と知られていない日常で役立つ便利技
Docker 9 tips~意外と知られていない日常で役立つ便利技Masahito Zembutsu
 
クリスマスに工場(Factorio)を作るゲームをしよう
クリスマスに工場(Factorio)を作るゲームをしようクリスマスに工場(Factorio)を作るゲームをしよう
クリスマスに工場(Factorio)を作るゲームをしようMasahito Zembutsu
 
2020年から始まる小学校プログラミング教育の話 #osc19os
2020年から始まる小学校プログラミング教育の話 #osc19os2020年から始まる小学校プログラミング教育の話 #osc19os
2020年から始まる小学校プログラミング教育の話 #osc19osMasahito Zembutsu
 
CNCF Updates 2019 Winter version and Knative
CNCF Updates 2019  Winter version and KnativeCNCF Updates 2019  Winter version and Knative
CNCF Updates 2019 Winter version and KnativeMasahito Zembutsu
 
[1C5] Docker Comose & Swarm mode Orchestration (Japan Container Days - Day1)
[1C5] Docker Comose & Swarm mode Orchestration (Japan Container Days - Day1)[1C5] Docker Comose & Swarm mode Orchestration (Japan Container Days - Day1)
[1C5] Docker Comose & Swarm mode Orchestration (Japan Container Days - Day1)Masahito Zembutsu
 
CNCFアップデート情報~2018年のCNCFを振り返る
CNCFアップデート情報~2018年のCNCFを振り返るCNCFアップデート情報~2018年のCNCFを振り返る
CNCFアップデート情報~2018年のCNCFを振り返るMasahito Zembutsu
 
Docker道場「Dockerの基本概念」0825インフラ勉強会資料
Docker道場「Dockerの基本概念」0825インフラ勉強会資料Docker道場「Dockerの基本概念」0825インフラ勉強会資料
Docker道場「Dockerの基本概念」0825インフラ勉強会資料Masahito Zembutsu
 
コンテナ導入概要資料2018
コンテナ導入概要資料2018コンテナ導入概要資料2018
コンテナ導入概要資料2018Masahito Zembutsu
 

More from Masahito Zembutsu (20)

忙しい人のための Rocky Linux 入門〜Rocky LinuxはCentOSの後継者たり得るか?〜
忙しい人のための Rocky Linux 入門〜Rocky LinuxはCentOSの後継者たり得るか?〜忙しい人のための Rocky Linux 入門〜Rocky LinuxはCentOSの後継者たり得るか?〜
忙しい人のための Rocky Linux 入門〜Rocky LinuxはCentOSの後継者たり得るか?〜
 
自由検証環境提供宣言+Docker Compose V2 GA
自由検証環境提供宣言+Docker Compose V2 GA自由検証環境提供宣言+Docker Compose V2 GA
自由検証環境提供宣言+Docker Compose V2 GA
 
CentOS Linux 8 の EOL と対応策の検討
CentOS Linux 8 の EOL と対応策の検討CentOS Linux 8 の EOL と対応策の検討
CentOS Linux 8 の EOL と対応策の検討
 
さくらインターネットのコミュニティ with COVID-19
さくらインターネットのコミュニティ with COVID-19さくらインターネットのコミュニティ with COVID-19
さくらインターネットのコミュニティ with COVID-19
 
Docker Chronicle 2021.09
Docker Chronicle  2021.09Docker Chronicle  2021.09
Docker Chronicle 2021.09
 
ブックトーク@CROSS ~SF編~ 発表資料「攻殻機動隊」「導きの星」
ブックトーク@CROSS ~SF編~ 発表資料「攻殻機動隊」「導きの星」ブックトーク@CROSS ~SF編~ 発表資料「攻殻機動隊」「導きの星」
ブックトーク@CROSS ~SF編~ 発表資料「攻殻機動隊」「導きの星」
 
インターネットでウェブサイトを表示している裏側の話
インターネットでウェブサイトを表示している裏側の話インターネットでウェブサイトを表示している裏側の話
インターネットでウェブサイトを表示している裏側の話
 
3分で分かる「プログラミング教育・情報教育」
3分で分かる「プログラミング教育・情報教育」3分で分かる「プログラミング教育・情報教育」
3分で分かる「プログラミング教育・情報教育」
 
ようこそオンラインの展示会場へ
ようこそオンラインの展示会場へようこそオンラインの展示会場へ
ようこそオンラインの展示会場へ
 
小学校プログラミング教育に対する企業の取り組みと課題 #KOF2020
小学校プログラミング教育に対する企業の取り組みと課題 #KOF2020小学校プログラミング教育に対する企業の取り組みと課題 #KOF2020
小学校プログラミング教育に対する企業の取り組みと課題 #KOF2020
 
オンライン発表で気を付けているポイント~姿勢編
オンライン発表で気を付けているポイント~姿勢編オンライン発表で気を付けているポイント~姿勢編
オンライン発表で気を付けているポイント~姿勢編
 
Jitsi Meetとは?
Jitsi Meetとは?Jitsi Meetとは?
Jitsi Meetとは?
 
Docker 9 tips~意外と知られていない日常で役立つ便利技
Docker 9 tips~意外と知られていない日常で役立つ便利技Docker 9 tips~意外と知られていない日常で役立つ便利技
Docker 9 tips~意外と知られていない日常で役立つ便利技
 
クリスマスに工場(Factorio)を作るゲームをしよう
クリスマスに工場(Factorio)を作るゲームをしようクリスマスに工場(Factorio)を作るゲームをしよう
クリスマスに工場(Factorio)を作るゲームをしよう
 
2020年から始まる小学校プログラミング教育の話 #osc19os
2020年から始まる小学校プログラミング教育の話 #osc19os2020年から始まる小学校プログラミング教育の話 #osc19os
2020年から始まる小学校プログラミング教育の話 #osc19os
 
CNCF Updates 2019 Winter version and Knative
CNCF Updates 2019  Winter version and KnativeCNCF Updates 2019  Winter version and Knative
CNCF Updates 2019 Winter version and Knative
 
[1C5] Docker Comose & Swarm mode Orchestration (Japan Container Days - Day1)
[1C5] Docker Comose & Swarm mode Orchestration (Japan Container Days - Day1)[1C5] Docker Comose & Swarm mode Orchestration (Japan Container Days - Day1)
[1C5] Docker Comose & Swarm mode Orchestration (Japan Container Days - Day1)
 
CNCFアップデート情報~2018年のCNCFを振り返る
CNCFアップデート情報~2018年のCNCFを振り返るCNCFアップデート情報~2018年のCNCFを振り返る
CNCFアップデート情報~2018年のCNCFを振り返る
 
Docker道場「Dockerの基本概念」0825インフラ勉強会資料
Docker道場「Dockerの基本概念」0825インフラ勉強会資料Docker道場「Dockerの基本概念」0825インフラ勉強会資料
Docker道場「Dockerの基本概念」0825インフラ勉強会資料
 
コンテナ導入概要資料2018
コンテナ導入概要資料2018コンテナ導入概要資料2018
コンテナ導入概要資料2018
 

Dockerfileを改善するためのBest Practice 2019年版

  • 1. DockerCon SF19 で発表の、基礎→マルチ・ステージ・ビルド→最新動向まで Sakura Internet, Inc. Masahito Zembutsu @zembutsu Docker Meetup Kansai #3 #dockerkansai May 24, 2019 Dockerfileを改善するための Best Practice 2019年版
  • 2. DockerCon SF19 での発表に基づく内容 • Dockerfile Best Practices https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices 2 • 動画もご覧ください https://www.docker.com/dockercon/2019-videos?watch=dockerfile-best-practices オリジナルの発表は Tibor Vass 氏( @tiborvass) および Sebastian van Stijin 氏(@thajeztha) による、 DockerCon 毎回恒例人気セッション"Dockerfile Best Practices" であり、両者に感謝します。 I thank you both for your excellent presentation of DockerCon. I also appreciate your permission to translate the content. Please refer to this slides. And, this presentation video will also be helpful.
  • 3. このスライドは何? 3 ⚫ DockerCon 19 で発表された人気シリーズ “Best Practices” を日本訳+解説の追加。 ⚫ Dockerfile の改善を通して、現在利用できる マルチ・ステージ・ビルドや BuildKit 紹介。 以上の内容です。 ※ "Docker Meetup Kansai #3" 発表時のスライドをベースに、 スライドそのままでは分かりづらい部分があるため、 一部で発表時と異なる表現・補足説明を用いている場合があります。 ゴール: 「Dockerfileの改善を具体的に理解」
  • 4. Dockerfileのベストプラクティス • "Best practices for writing Dockerfiles" https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ 4 最新の日本語訳を公開しました。 そもそもの基本となるのは、"Dockerfileを書くためのベストプラクティス"です。
  • 6. Dockerfile とは? 6 ド ッ カ ー フ ァ イ ル
  • 7. Dockerfile • Docker は Dockerfile から命令を読み込み、イメージを自動構築 • テキスト形式のドキュメント • docker build / docker image build で Dockerfile を読み込み構築 • 「Docker イメージを作るための設計図」 • FROM、ADD、CMD など命令文で構成 • 例) 何のイメージをもとに、何を実行するか? • 誰でも確実にイメージを構築できる • イメージの構築過程を確認できる • Dockerfileは広く使われている • GitHub上に Dockerfile は100万以上 7 automate build blueprint Image by John Dortmunder from Pixabay 「Dockerfile」はDockerイメージを自動構築するために、必ず使うファイルです。
  • 8. BuildKit: builder v2 • docker build は「イメージ・キャッシュ」があるため、素早く開発できる • しかし、いくつかの制限があったため、buildKit プロジェクトで根本的に構築 • https://github.com/moby/buildkit • "ゴールは Docker build のデフォルト" • BuildKit: 特長 • 同時へいれt性(concurrency) • 断片的なコンテキスト・アップロード (lazy context upload) • キャッシュの改良 • 新しい Dockerfile 機能の追加 8 ⚫並列性が無い ⚫docker run と同様に root で動作する必要性 ⚫ボリューム機能が無い 高速にイメージを作れる BuildKit という汎用ツールが開発途上です。
  • 9. Docker BuildKit を使う方法 • クライアントの環境変数 • Docker デーモン設定ファイル /etc/docker/daemon.json • 現時点では Windows は対応していない • Windows support coming soon! 9 { "features": { "buildkit": true }} export DOCKER_BUILDKIT=1 BiuldKit は Docker とは別のプロジェクト (https://github.com/moby/buildkit ) ですが、 現在の Docker CE v18.09 では、BuildKit の一部機能が既に利用できる状態です。
  • 11. Dockerfile の改善領域 • 増大するbuild 時間 • イメージ容量 • 保守性 • 安全性 • 一貫性・反復性 11 (Incremental) build time Image size Maintainability Security Consistency/Repetability ここからは、先日開催された DockerCon SF19での発表サンプルをベースにご紹介します。 サンプルのアプリケーションを使い、5つの視点で Dockerfile を改善する流れをみていきましょう。
  • 12. サンプルの Dockerfile をカイゼンするぞ! 12 FROM debian COPY . /app RUN apt-get update RUN apt-get –y install openjdk-8-jdk ssh emacs CMD ["java", "-jar", "/app/target/app.jar"] これは、Javaで"Hello world"を表示するための、サンプルプログラム用の Dockerfile です。
  • 13. サンプルの Dockerfile をカイゼンするぞ! 13 FROM debian COPY . /app RUN apt-get update RUN apt-get –y install openjdk-8-jdk ssh emacs CMD ["java", "-jar", "/app/target/app.jar"] vim まずすべきは、emacs を消して、vim を入れましょう。もちろんジョークですが!
  • 14. サンプルの Dockerfile をカイゼンするぞ! 14 FROM debian COPY . /app RUN apt-get update RUN apt-get –y install openjdk-8-jdk ssh emacs CMD ["java", "-jar", "/app/target/app.jar"] vim まずすべきは、emacs を消して、vim を入れましょう。もちろんジョークですが!
  • 16. キャッシュする順番が重要 16 FROM debian COPY . /app RUN apt-get update RUN apt-get –y install openjdk-8-jdk ssh vim COPY . /app CMD ["java", "-jar", "/app/target/app.jar"] 頻繁に変更するものを後ろへ 構築時のキャッシュとは、変更箇所があれば破棄されます。この例の COPY では「.」(カレント)にある ファイルに変更があれば、毎回「apt-get update」と「install」が走るので、時間がかかってしまいます。 せっかくあrキャッシュを有効活用するには、頻繁に更新する可能性があるものを後ろにおきます。
  • 17. キャッシュする順番が重要 17 FROM debian COPY . /app RUN apt-get update RUN apt-get –y install openjdk-8-jdk ssh vim COPY . /app CMD ["java", "-jar", "/app/target/app.jar"] 頻繁に変更するものを後ろへ 構築時のキャッシュとは、変更箇所があれば破棄されます。この例の COPY では「.」(カレント)にある ファイルに変更があれば、毎回「apt-get update」と「install」が走るので、時間がかかってしまいます。 せっかくあrキャッシュを有効活用するには、頻繁に更新する可能性があるものを後ろにおきます。 キャッシュ有効 キャッシュ破棄 … ホスト側「 ./ 」に変更時 キャッシュ破棄 … ホスト側「 ./ 」に変更時 時間がかかるので、キャッシュ活用すべし 時間かかる
  • 18. キャッシュ破棄の影響を避けるため、範囲を狭く 18 FROM debian RUN apt-get update RUN apt-get –y install openjdk-8-jdk ssh vim COPY . /app COPY target/app.jar /app CMD ["java", "-jar", "/app/target/app.jar"] コピーに必要なものを明示する。 可能であれば "COPY ."を避ける コピーすべき場所を絞っておけば、キャッシュは破棄されません。 この例では「app.jar」しか「COPY」しませんので、他のファイルに変更があってもキャッシュを保持。
  • 19. キャッシュ破棄の影響を避けるため、範囲を狭く 19 FROM debian RUN apt-get update RUN apt-get –y install openjdk-8-jdk ssh vim COPY . /app COPY target/app.jar /app CMD ["java", "-jar", "/app/target/app.jar"] コピーすべき場所を絞っておけば、キャッシュは破棄されません。 この例では「app.jar」しか「COPY」しませんので、他のファイルに変更があってもキャッシュを保持。 キャッシュ有効 キャッシュ有効 キャッシュ有効 ちょっとキャッシュする範囲が拡がるかもね キャッシュ対象の明確化 コピーに必要なものを明示する。 可能であれば "COPY ."を避ける
  • 20. 行をまとめる: apt-get update & install 20 FROM debian RUN apt-get update RUN apt-get –y install openjdk-8-jdk ssh vim RUN apt-get update ¥ && apt-get -y install ¥ openjdk-8-jdk ssh vim COPY target/app.jar /app CMD ["java", "-jar", "/app/target/app.jar"] 古いパッケージ情報のキャッシュ利用を避ける パッケージ・マネージャを使う場合、「古い」パッケージ情報をキャッシュしがちです。 更新したいのに更新できないのを避けるには、情報の更新と、パッケージ追加・削除をまとめること。
  • 21. 行をまとめる: apt-get update & install 21 FROM debian RUN apt-get update RUN apt-get –y install openjdk-8-jdk ssh vim RUN apt-get update ¥ && apt-get -y install ¥ openjdk-8-jdk ssh vim COPY target/app.jar /app CMD ["java", "-jar", "/app/target/app.jar"] 古いパッケージ情報のキャッシュ利用を避ける パッケージ・マネージャを使う場合、「古い」パッケージ情報をキャッシュしがちです。 更新したいのに更新できないのを避けるには、情報の更新と、パッケージ追加・削除をまとめること。 キャッシュ有効 古いものをキャッシュしてるかも! 時間かかる場合があっても、確実に処理
  • 23. 不要な依存関係を削除 23 FROM debian RUN apt-get update ¥ && apt-get -y install --no-install-recommends ¥ openjdk-8-jdk ssh vim COPY target/app.jar /app CMD ["java", "-jar", "/app/target/app.jar"] デプロイを素早くするためには、イメージ容量の削減が重要であり、必須課題。 推奨パッケージ(必須ではない)を避けるためには、「--no-install-recommends」フラグを付ける。 Javaの実行に不要なパッケージも入れないので「ssh」「vim」も消す。
  • 24. 不要なパッケージマネージャのキャッシュ削除 24 FROM debian RUN apt-get update ¥ && apt-get -y install –no-install-recommends ¥ openjdk-8-jdk ¥ && rm –rf /var/lib/apt/lists/* COPY target/app.jar /app CMD ["java", "-jar", "/app/target/app.jar"] パッケージ・マネージャのキャッシュ情報も、アプリケーションの実行に不要なので削除
  • 26. できるだけDocker公式(official)パッケージを使う • メンテナンスにかける時間を減らす (問題があるたびに、繰り返す更新) • 容量を減らす(イメージ間でのレイヤ共有によって) • コンテナとして使うために、予め設定済み • 賢い人達が構築している 26パッケージ・マネージャのキャッシュ情報も、アプリケーションの実行に不要なので削除
  • 27. 27 FROM debian RUN apt-get update ¥ && apt-get -y install –no-install-recommends ¥ openjdk-8-jdk ¥ && rm –rf /var/lib/apt/lists/* FROM openjdk COPY target/app.jar /app CMD ["java", "-jar", "/app/target/app.jar"] できるだけDocker公式(official)パッケージを使う Javaの実行であれば、「openjdk」イメージがあるため、debianでセットアップする必要はない
  • 28. タグを明示 28 FROM openjdk:latest FROM openjdk:8 COPY target/app.jar /app CMD ["java", "-jar", "/app/target/app.jar"] "latest"タグは常に入れ替わる。 タグを指定しておけば、想定外の ベース・イメージ変更発生を防止。 何も指定しなければ「latest」(最新)になるため、常にタグ(主にバージョン)指定を忘れずに
  • 30. 必要最小限のものを探す 30 REPOSITORY TAG SIZE openjdk 8 624MB openjdk 8-jre 443MB openjdk 8-jre-slim 204MB openjdk 8-jre-alpie 83MB ベース・イメージの変更だけで 540MBも削減 どのイメージ(タグ)を選ぶかによって、容量がかなり異なる。 alpineタグは Alpine Linux という約 5MB の Linux ディストリビューションがベース
  • 32. 32
  • 33. 一貫した環境を、ソースから構築する • Dockerfile を設計図(青写真)にしよう: • ビルド時するための構築環境を Dockerfile に記述 • 正しいバージョンのビルド・ツールをインストール • 環境ごとの違いを発生させない • システム依存はあるかもしれない • "source of truth"(本当のソース)とは、 ソースコードである。ビルド成果物ではない。 33 Image by John Dortmunder from Pixabay blue print 開発環境、テスト環境、実行環境で共通する Dockerfile を目指す
  • 34. 一貫した環境を、ソースから構築する 34 FROM openjdk:8-jre-alpine FROM maven:3.6-jdk-8-alpine WORKDIR /app COPY app.jar/app COPY pom.xml . COPY src ./src RUN mvn –e –B package CMD ["java", "-jar", "/app/target/app.jar"] openjdk にかわり、Javaプロジェクト管理ツールであるMavenを開発環境として入れる
  • 35. 一貫した環境を、ソースから構築する 35 FROM maven:3.6-jdk-8-alpine WORKDIR /app COPY pom.xml . COPY src ./src RUN mvn –e –B package CMD ["java", "-jar", "/app/target/app.jar"] このように、開発環境向けの Dockerfile を整えていく。
  • 36. 依存関係の解決は、ステップを分ける 36 FROM maven:3.6-jdk-8-alpine WORKDIR /app COPY pom.xml . RUN mvn –e –B dependency:resolve COPY src ./src RUN mvn –e –B package CMD ["java", "-jar", "/app/target/app.jar"] 開発関係だけに必要な依存関係を追加。
  • 37. 構築時のみの依存関係が判明 37 FROM maven:3.6-jdk-8-alpine WORKDIR /app COPY pom.xml . RUN mvn –e –B dependency:resolve COPY src ./src RUN mvn –e –B package CMD ["java", "-jar", "/app/target/app.jar"] しかし、この黄色い部分は「開発環境の構築」段階しか使わないものであり、本番稼働では無駄
  • 38. マルチ・ステージ・ビルドで、構築時の部分を削除 38 FROM maven:3.6-jdk-8-alpine AS builder WORKDIR /app COPY pom.xml . RUN mvn –e –B dependency:resolve COPY src ./src RUN mvn –e –B package CMD ["java", "-jar", "/app/target/app.jar"] FROM openjdk:8-jre-alpine COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app/target/app.jar"] そこで、マルチ・ステージ・ビルドで複数の「FROM」を使い、構築時(AS bilder)と実行時を分離 ←「builder」ステージから ファイルをコピー ←ステージ「builder」と名前を付ける
  • 39. マルチ・ステージ・ビルドで、構築時の部分を削除 39 FROM maven:3.6-jdk-8-alpine AS builder WORKDIR /app COPY pom.xml . RUN mvn –e –B dependency:resolve COPY src ./src RUN mvn –e –B package FROM openjdk:8-jre-alpine COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app/target/app.jar"] マルチ・ステージ・ビルドでは、「docker build」は全てのビルドを実行しますし、 「docker build –target builder」と指定すると「AS builder」の「FROM」ステージしか処理しません。
  • 41. プロジェクトごとに多くのステージがある • Moby: 16 ステージ https://github.com/moby/moby/blob/master/Dockerfile • BuildKit: 44 ステージ https://github.com/moby/buildkit/blob/master/hack/dockerfiles /test.buildkit.Dockerfile 41Docker 関連プロジェクトの Dockerfile にも、多くのステージ(FROM命令)がある
  • 42. マルチ・ステージの利用例 • 実行環境と構築(ビルド)環境を分ける(イメージ容量の縮小) • イメージに対する影響を最小限に(DRY) • 構築・開発・テスト・構文チェック…のような環境を明示 • 依存関係を一直線に処理しない(並列化) • プラットフォーム固有のステージ 42 一般的な目的は、最終成果物の容量を削減
  • 43. 構築ステージを --target で指定 43 FROM image_or_stage AS stage_name … $ docker build --target stage_name 「AS ステージ名」を FROM 命令に書いておけば、「docker build」時に「--target」でステージを指定
  • 44. イメージの flavor を変える 44 FROM maven:3.6-jdk-8-alpine AS builder ... FROM openjdk:8-jre-jessie AS release-jessie COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app.jar"] FROM openjdk:8-jre-jessie AS release-alpine COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app.jar"] $ docker build --target release-jessie . Debian jessie (8.x) をベースとするイメージと、Alpine Linux をベースとするイメージ。 特色(風味)
  • 45. 45 FROM maven:3.6-jdk-8-alpine AS builder ... FROM openjdk:8-jre-jessie AS release-jessie COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app.jar"] FROM openjdk:8-jre-jessie AS release-alpine COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app.jar"] $ docker build --target release-jessie . イメージの flavor を変える 特色(風味) しかし、 Dockerfile をよく見ると問題があり、
  • 46. 46 FROM maven:3.6-jdk-8-alpine AS builder ... FROM openjdk:8-jre-jessie AS release-jessie COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app.jar"] FROM openjdk:8-jre-jessie AS release-alpine COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app.jar"] $ docker build --target release-jessie . このように各ステージで、同じ命令が重複する箇所がある。 イメージの flavor を変える 特色(風味) どちらも 同じ
  • 47. 47 ARG flavor=alpine FROM maven:3.6-jdk-8-alpine AS builder ... FROM openjdk:8-jre-$flavor AS release COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app.jar"] $ docker build --target release --build-arg flavor=jessie . 「ARG」を Dockerfile で指定しておくと、docker build 時に「--build-arg」を通して、変数のように展開 --build-arg で $flavor=xxx があれば、$flavor に xxx 代入 ↓何もなければ「flavor=alpine」が適用 イメージの flavor を変える (DRY / 汎用的な ARG) 特色(風味) ※ DRY = “Don’t Repeat Yourself”(自分では繰り返さない)という ソフトウェア開発の手法 引数 ←ARG命令は、docker build時に指定できる変数を定義 書式は「変数名」または「変数名=デフォルト値」。
  • 48. 様々な環境:構築、開発、テスト、構文チェック(lint)… • ステージとしての検討例: ⁃ builder: 依存関係すべてをビルド ⁃ build(または binary): builder + ビルド成果物 ⁃ cross: 複数のプラットフォーム向けに構築 ⁃ dev: build(er) + 開発/デバッグツール ⁃ lint: 最小限の構文チェック用依存関係 ⁃ test: テストに関係する全ての依存関係 + テスト対象のビルド成果物 ⁃ release: ビルド成果物を含む、最終的な最小イメージ 48様々な「ステージ」が検討できる。ここでは定型的な例 各ステージでは、依存関係が最小となるようにする
  • 49. 様々な環境:構築、開発、テスト、構文チェック(lint)… 49 FROM maven:3.6-jdk-8-alpine AS builder ... FROM openjdk:8-jre-alpine AS lint RUN wget https://github.com/checkstyle/checkstyle/releases/download/checkstyle-8.15/checkstyle-8.15-all.jar COPY checks.xml . COPY src /src RUN java –jar checkstyle-8.15-all.jar –c checks.xml /src これはシンプルな Java 構文チェック(リント)の確認用の Dockerfile を「AS lint」と指定
  • 50. 様々な環境:構築、開発、テスト、構文チェック(lint)… 50 FROM maven:3.6-jdk-8-alpine AS builder ... FROM openjdk:8-jre-alpine AS release COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app.jar"] FROM builder AS dev RUN apk add --no-cache strace vim tcpdump ENTRYPOINT ["ash"] 開発用デバッグ環境であれば、「builder」ステージをベースにしながら「AS dev」と明示し シンプルにエディタ(vim)と tcpdump を入れている
  • 51. 様々な環境:構築、開発、テスト、構文チェック(lint)… 51 FROM maven:3.6-jdk-8-alpine AS builder ... RUN mvn –e –B package –DskipTests FROM builder AS unit-test RUN mvn –e –B test FROM release AS integration-test RUN apk add --no-cache curl RUN ./test/run.sh 他にも単体テスト、統合テスト用の環境も作れます。
  • 53. 一直線の Dockerfile ステージから … • 全てのステージが順番(シーケンシャル)に実行 • この図では、上から下に一直線 • BuildKitがなければ、 不要なステージも無駄に実行し、破棄する (無駄に時間がかかった) 53 s1 s2 s3 s4 s5 s6 デフォルトでは、全てのステージを順番に実行する
  • 54. BuildKit のマルチ・ステージ graph へ • BuildKit は下(--target のステージ名)から上に辿っていくような流れ • 右図では s2, s3, s4 ステージを同時並行処理 • 不要なステージは無視できる • 右図では s5 がビルド時に 不要であれば、何もしない 54 s1 s2 s3 s4 s5s6 Docker 17.05 からマルチ・ステージ・ビルドが利用可能になった。 18.06までは experimental 、18.09 は利用できるように組み込まれている グラフ ※グラフは点と点とのつながり (関係性)を表す
  • 55. Multi-srage: 並行ビルド (build concurrently) 55 FROM maven:3.6-jdk-8-alpine AS builder ... FROM tiborvass/whalesay AS assets RUN whalesay "Hello DockerCon!" > /out/assets.html FROM openjdk:8-jre-alpine AS release COPY --from=builder /app/target/app.jar / COPY --from=assets /out /assets CMD ["java", "-jar", "/app.jar"] 「assets」は最終イメージ(release)に必要だが、別のステージとして処理できる そして、この Dockerfile は「builder」と「assets」のステージからのコピーを並行処理
  • 56. Multi-srage: 並行ビルド (build concurrently) 56 FROM maven:3.6-jdk-8-alpine AS builder-base … FROM gcc:8-alpine AS builder-someClib … RUN git clone … ¥ ./configure --prefix=/out && make && make install FROM g++:8-alpine AS builder-someCPPlib … RUN git clone … ¥ cmake … FROM builder-base AS builder COPY --from=builder-someClib /out / COPY --from=builder-someCPPlib /out / 複数の COPY 命令を使う時には、特に効果を発揮し、時間を節約できる 並行処理が有用な典型的なパターンが 複数の COPY --from … の繰り返し
  • 57. ベンチマーク • github.com/moby/moby Dockerfile, master ブランチ • 小さいほうが優れている 57 Dockerfile Best Practices https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices/52 v18.03 の docker build と BuiltKit では2倍の速さ
  • 58. ベンチマーク • github.com/moby/moby Dockerfile, master ブランチ • 小さいほうが優れている 58 Dockerfile Best Practices https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices/52 キャッシュを有効にすると、7倍も速くなる
  • 59. ベンチマーク • github.com/moby/moby Dockerfile, master ブランチ • 小さいほうが優れている 59 Dockerfile Best Practices https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices/52 最も大事なのは、ソースコードの変更時。再構築の速度はマルチ・ステージ・ビルドで 2.5 倍に改善。
  • 61. 新機能を有効にするには? 61 # syntax=docker/dockerfile:1.0-experimental FROM maven:3.6-jdk-8-alpine AS builder WORKDIR /app COPY . /app RUN mvn -e –B package FROM openjdk:8-jre-alpine COPY --from=builder /app/target/app.jar / CMD ["java", "-jar", "/app.jar"] Dockerfile の新機能を有効にするには、この行を1行目に追加する必要がある ※experimental というのは、まだ Dockerfile の正式な構文では ないため
  • 63. コンテキスト・マウント(v18.09+ w/ BuildKit) 63 # syntax=docker/dockerfile:1.0-experimental FROM maven:3.6-jdk-8-alpine AS builder WORKDIR /app COPY . /app RUN --mount=target=. mvn -e –B package -DoutputDirectory=/ FROM openjdk:8-jre-alpine COPY --from=builder /app/app.jar / CMD ["java", "-jar", "/app.jar"] 新機能を使って Dockerfile を書き換えていく。まずはマウント・オプション。 実行時に「.」(カレント・ディレクトリ内容)を Build Context として送信せず、マウントできる ※ Docker v18.09 以上かつ、BuildKit 有効化の状態で利用可能 contest mounts
  • 64. Dockerfile hello コンテキスト・マウント(v18.09+ w/ BuildKit) 64 ディレクトリ内容をコンテナに COPY しなくても、 ビルド時に docker build コマンドを実行しているディレクトリ(ここでは 「target=.」で指定)を rw(読み書き可能な状態)で直接コンテナにマウントし、 コンテナ内で「cat hello > /hello.txt」を実行 → 構築したイメージの“/hello.txt” に “Hello world!” が書かれたファイルが作成される # syntax=docker/dockerfile:1.0-experimental FROM alpine RUN --mount=type=bind,target=.,rw cat hello > /hello.txt Hello world! ソースコードなど、ビルド・コンテクストのコピーが不要となり、より早いビルドができる contest mounts • Dockerfile の mount 例: ホスト側の docker build する場所に、 このファイルがあるとします
  • 65. 65 コンテキスト・マウント(v18.09+ w/ BuildKit) contest mounts 従来 “COPY . /app” BuildKit Dockerfile app用ディレクトリ Build用ディレクトリ “docker build” 時、 「.」 以下の内容を Docker イメージ用レイヤとして構築するため 全て Docker に対して送る必要があった ※結果として Docker イメージの不本意な増加 ※あるいはマルチ・ステージ・ビルドで回避 Dockerfile app用ディレクトリ Build用ディレクトリ コンテナ コピー マウント よいしょ… みーてーるーだーけー “docker build” の特定ステップのみ参照するため、 ・ build 全体の時間を削減 ・ イメージ・レイヤの肥大化を抑制
  • 66. キャッシュの保持(BuildKitが無ければ) 66 FROM maven:3.6-jdk-8-alpine AS builder WORKDIR /app COPY pom.xml . RUN mvn -e –B dependency: resolve COPY src ./src RUN mvn –e –B package CMD ["java", "-jar", "/app.jar"] docker build 時にキャッシュが破棄されると、毎回依存関係の準備に時間がかかる
  • 67. キャッシュの保持(v18.09+ w/ BuildKit) 67 # syntax=docker/dockerfile:1.0-experimental FROM maven:3.6-jdk-8-alpine AS builder WORKDIR /app RUN --mount=target=. --mount=type=cache,target=/root/.m2 ¥ && mvn package –DoutputDirectory=/ FROM openjdk:8-jre-alpine COPY --from=builder /app.jar / CMD ["java", "-jar", "/app.jar"] apt: /var/lib/apt/lists go: ~/.cache/go-build go-modules: $GOPATH/pkg/mod npm: ~/.npm pip: ~/.cache/pip キャッシュ用ディレクトリをマウントできるので、依存関係の準備にかかる時間を削減できる
  • 68. キャッシュの保持 • --mount=type=cache 例: 68 Dockerfile # syntax=docker/dockerfile:1.0-experimental FROM ubuntu RUN --mount=type=cache,target=/var/cache/apt ¥ --mount=type=cache,target=/var/lib/apt ¥ apt-get update && apt-get install -y wget curl 「type=cache」で指定した「target」のディレクトリは、docker build を実行したホスト上でキャッシュ この例では apt-get install を含む「RUN」命令を書き換えたとしても、 一度キャッシュ(ビルド)済みであれば次回から高速なビルドが可能になる キャッシュ情報をクリアするには「docker builder prune」コマンドを実行する
  • 70. シークレット(これはダメな方法) 70 FROM baseimage RUN … ENV AWS_ACCESS_KEY_ID=… ENV AWS_SECRET_ACCESS_KEY=… RUN ./fetch-aseets-from-s3.sh RUN ./build-scripts.sh シークレット(secret)とは、パスワードやAPIキー、SSH 鍵などの認証情報(機微情報) ENV に書くと docker history で丸見えですし、
  • 71. シークレット(これもダメな方法) 71 FROM baseimage RUN … ARG AWS_ACCESS_KEY_ID ARG AWS_SECRET_ACCESS_KEY RUN ./fetch-aseets-from-s3.sh RUN ./build-scripts.sh $ docker build --build-arg ¥ AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID … ARGでも途中のRUNで変数の情報をダンプできますし、 シェルのhistoryからも辿れるリスクが出てきます(記録しない方法もありますが、運用カバー系作業) docker history
  • 72. シークレット(v18.09+ w/ BiuldKit では、こうします) 72 # syntax=docker/dockerfile:1-experimental FROM baseimage RUN … RUN --mount=type=secret,id=aws,target=/root/.aws/credentials, required ./fetch-assets-from-s3.sh RUN ./build-scripts.sh $ docker build --secret id=aws,src=~/.aws/credentials . 最終イメージに混入させないための手法が「secret」としてのマウント。 対象ステップのみデータを参照できるよう一時的にマウントし、次のステップではマウントしません。 ↓idは docker build 時の –secret オプションで識別するため ↑targetで、この構築ステップのみ、 コンテナ内のこの場所に 一時的にファイルを設置する指示 ↑これはホスト側に存在するファイル。 ~より、 $HOME や絶対パスのほうが安全かも ↑次のステップでは、先の id=aws でマウントしたシークレットは見えない( 0 byte のファイル残骸のみ)
  • 74. • Dockerfile の mount secret 例: Dockerfile $HOME/secret シークレット・マウント(v18.09+ w/ BiuldKit) 74 # syntax=docker/dockerfile:1.0-experimental FROM alpine RUN --mount=type=secret,id=check,target=/root/secret,required ¥ cat /root/secret > /data.txt MYID=secretpass Dockerfile で当該 RUN 命令行のビルド時のみ、一時的にホスト側ファイルをマウントできる secret mounts ホスト側にIDとパスワードのような、 いわゆるシークレット(機微情報)を記録するファイルを設置 もちろん、パーミッションは 600 にするなど、十分なセキュリティ配慮が必要 $ docker build --secret id=check,src=$HOME/.data/credentials -t myimage . ↑srcで指定したパスはホスト上のファイル
  • 75. プライベート git リポジトリ(ダメぜったい) 75 FROM baseimage COPY ./keys/private.pem /root/.ssh/private.pem ARG REPO_REF=19ba8bcd9976ef8a9bd086187df19ba7bcd997f2 RUN git clone git@githubcom:org/repo /work && cd /work ¥ && git checkout –b $REPO_REF これもイメージ内に大事なファイルが残ってしまう NG 例
  • 76. プライベート git リポジトリ(v18.09+ w/BuildKitの場合) 76 FROM alpine RUN apk add --no-cache openssh-client RUN mkdir –p –m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts ARG REPO REF=19ba8bcd9976ef8a9bd086187df19ba7bcd997f2 RUN –-mount-type=ssh,required ¥ git clone git@github.com:org/repo /work && cd /work ¥ && git checkout –b $REPO_REF $ eval $(ssh-agent) $ ssh-add ~/.ssh/id_rsa $ docker build --ssh=default . この RUN 命令実行時のみ、 ホスト側の SSH 認証情報を読み込む方法が 利用できる
  • 77. BuildKit 77 SSH マウント(v18.09+ w/ BiuldKit) mounts Dockerfile .ssh Build用ディレクトリ コンテナ 鍵情報 参照 このステップがある間だけ みーてーるーだーけー “docker build” の対象構築ステップ時のみ、 ホスト側のSSH agent 情報を一時的にマウント シークレット・マウントに近いけれど SSH の利用に特化 こんてなから GitHub や SSH 接続したい場合に有用 docker build id_rsa ssh-agent --mount=type=ssh SSH リモート・ホストや GitHub / GitLab 等
  • 78. • Dockerfile の mount=SSH 例: Dockerfile SSH マウント(v18.09+ w/ BiuldKit) 78 # syntax=docker/dockerfile:1.0-experimental FROM alpine RUN apk update && apk add openssh RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan <host_IP> > ~/.ssh/known_hosts RUN --mount=type=ssh,required ¥ ssh <user>@<host_IP> ls -al / > /ls.txt GitHubやGitLabの認証だけでなk、別のホストにこのようにしてログイン&操作も可能 mounts $ eval $(ssh-agent) $ ssh-add ~/.ssh/id_rsa (パスフレーズを入力) $ docker build --ssh default=$SSH_AUTH_SOCK -t <image_name> .
  • 79. Dockerfile 改善のまとめ • 従来 ⁃ 構築・開発・テスト環境の矛盾 ⁃ 膨れあがるイメージ容量 ⁃ 構築時間がマシマシ(キャッシュ無効)になり時間がかかる ⁃ 構築が安全ではない • これから( BuildKit の活用によって) ⁃ 構築・開発・テスト環境が一致 ⁃ イメージ容量は最小 ⁃ 構築が非常に速く、構築回数が増えていく ⁃ より安全に構築できる 79 export DOCKER_BUILDKIT=1重要なのは有効化に 新しいビルド時の一時マウント可能な bind、cache、tmpfs、secret、ssh マルチ・ステージ・ビルドの活用によって、段階ごとの FROM ステージ間のコピー機能により、最終成果物を小さく 並列ビルドの活用により、従来よりも素早い構築
  • 80. 参考資料 • Advanced multi-stage build patterns – Tõnis Tiigi – Medium https://medium.com/@tonistiigi/advanced-multi-stage-build-patterns-6f741b852fae • Build secrets and SSH forwarding in Docker 18.09 – Tõnis Tiigi – Medium https://medium.com/@tonistiigi/build-secrets-and-ssh-forwarding-in-docker-18-09- ae8161d066 • Introducing BuildKit – Moby Blog https://blog.mobyproject.org/introducing-buildkit-17e056cc5317 • docker builder prune | Docker Documentation https://docs.docker.com/engine/reference/commandline/builder_prune/ • Docker v18.09 新機能 (イメージビルド&セキュリティ) – nttlabs – Medium https://medium.com/nttlabs/docker-v18-09-%E6%96%B0%E6%A9%9F%E8%83%BD- %E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%83%93%E3%83%AB%E3%83%89- %E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3- 9534714c26e2 80
  • 82. DockerCon での発表 • Dockerfile Best Practices https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices 82 • 動画 https://www.docker.com/dockercon/2019-videos?watch=dockerfile-best-practices
  • 83. Q&A • 何か気になるところはありますか? • Twitter: @zembutsu • https://slideshare.net/zembutsu • Dockerドキュメント日本語訳 http://docs.docker.jp • Docker Composeドキュメント日本語訳 http://docs.docker.jp/compose/ • 公式ドキュメント https://docs.docker.com 83