O slideshow foi denunciado.
Seu SlideShare está sendo baixado. ×

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

Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio

Confira estes a seguir

1 de 83 Anúncio

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

Baixar para ler offline

2019年5月24日(金)の発表資料をベースに解説等を加えたバージョンです。
Docker Meetup Kansai #3
https://dockerkansai.connpass.com/event/129089/

2019年5月24日(金)の発表資料をベースに解説等を加えたバージョンです。
Docker Meetup Kansai #3
https://dockerkansai.connpass.com/event/129089/

Anúncio
Anúncio

Mais Conteúdo rRelacionado

Diapositivos para si (20)

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

Anúncio

Mais de Masahito Zembutsu (20)

Mais recentes (20)

Anúncio

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

  1. 1. DockerCon SF19 で発表の、基礎→マルチ・ステージ・ビルド→最新動向まで Sakura Internet, Inc. Masahito Zembutsu @zembutsu Docker Meetup Kansai #3 #dockerkansai May 24, 2019 Dockerfileを改善するための Best Practice 2019年版
  2. 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. このスライドは何? 3 ⚫ DockerCon 19 で発表された人気シリーズ “Best Practices” を日本訳+解説の追加。 ⚫ Dockerfile の改善を通して、現在利用できる マルチ・ステージ・ビルドや BuildKit 紹介。 以上の内容です。 ※ "Docker Meetup Kansai #3" 発表時のスライドをベースに、 スライドそのままでは分かりづらい部分があるため、 一部で発表時と異なる表現・補足説明を用いている場合があります。 ゴール: 「Dockerfileの改善を具体的に理解」
  4. 4. Dockerfileのベストプラクティス • "Best practices for writing Dockerfiles" https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ 4 最新の日本語訳を公開しました。 そもそもの基本となるのは、"Dockerfileを書くためのベストプラクティス"です。
  5. 5. Dockerfile解説スライドもご覧ください • ベストプラクティス日本語版に解説・図解を加えたバージョンを作りました 5 https://www.slideshare.net/zembutsu/explaining-best-practices-for-writing-dockerfiles もし、Dockerfileに慣れていないのであれば、前提として、こちらのスライドをご覧ください。
  6. 6. Dockerfile とは? 6 ド ッ カ ー フ ァ イ ル
  7. 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. 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. 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 の一部機能が既に利用できる状態です。
  10. 10. カイゼン Dockerfile 10
  11. 11. Dockerfile の改善領域 • 増大するbuild 時間 • イメージ容量 • 保守性 • 安全性 • 一貫性・反復性 11 (Incremental) build time Image size Maintainability Security Consistency/Repetability ここからは、先日開催された DockerCon SF19での発表サンプルをベースにご紹介します。 サンプルのアプリケーションを使い、5つの視点で Dockerfile を改善する流れをみていきましょう。
  12. 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. 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. 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 を入れましょう。もちろんジョークですが!
  15. 15. 増大する構築(build)時間 ビルド・キャッシュと友達になろう 15 課題と対策
  16. 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. キャッシュする順番が重要 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. キャッシュ破棄の影響を避けるため、範囲を狭く 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. キャッシュ破棄の影響を避けるため、範囲を狭く 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. 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. 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"] 古いパッケージ情報のキャッシュ利用を避ける パッケージ・マネージャを使う場合、「古い」パッケージ情報をキャッシュしがちです。 更新したいのに更新できないのを避けるには、情報の更新と、パッケージ追加・削除をまとめること。 キャッシュ有効 古いものをキャッシュしてるかも! 時間かかる場合があっても、確実に処理
  22. 22. イメージ容量を減らす より速くデプロイするには、イメージを小さく 22 課題と対策
  23. 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. 不要なパッケージマネージャのキャッシュ削除 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. 25. 保守性 できるだけシンプルに保つ 25 課題と対策
  26. 26. できるだけDocker公式(official)パッケージを使う • メンテナンスにかける時間を減らす (問題があるたびに、繰り返す更新) • 容量を減らす(イメージ間でのレイヤ共有によって) • コンテナとして使うために、予め設定済み • 賢い人達が構築している 26パッケージ・マネージャのキャッシュ情報も、アプリケーションの実行に不要なので削除
  27. 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. タグを明示 28 FROM openjdk:latest FROM openjdk:8 COPY target/app.jar /app CMD ["java", "-jar", "/app/target/app.jar"] "latest"タグは常に入れ替わる。 タグを指定しておけば、想定外の ベース・イメージ変更発生を防止。 何も指定しなければ「latest」(最新)になるため、常にタグ(主にバージョン)指定を忘れずに
  29. 29. 適切なタグを探す 29 Docker Hub のドキュメントを読もう https://hub.docker.com/_/openjdk どのようなタグがあるかは、Docker Hub上のドキュメントで確認
  30. 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 ディストリビューションがベース
  31. 31. 再利用性 Dockerfileは青写真であり、 ソースコードこそが本当の源(ソース) 31 課題と対策
  32. 32. 32
  33. 33. 一貫した環境を、ソースから構築する • Dockerfile を設計図(青写真)にしよう: • ビルド時するための構築環境を Dockerfile に記述 • 正しいバージョンのビルド・ツールをインストール • 環境ごとの違いを発生させない • システム依存はあるかもしれない • "source of truth"(本当のソース)とは、 ソースコードである。ビルド成果物ではない。 33 Image by John Dortmunder from Pixabay blue print 開発環境、テスト環境、実行環境で共通する Dockerfile を目指す
  34. 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. 一貫した環境を、ソースから構築する 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. 依存関係の解決は、ステップを分ける 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. 構築時のみの依存関係が判明 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. マルチ・ステージ・ビルドで、構築時の部分を削除 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. マルチ・ステージ・ビルドで、構築時の部分を削除 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」ステージしか処理しません。
  40. 40. マルチ・ステージ Dockerfile 単に容量を減らすためだけではない 40 応用
  41. 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. 42. マルチ・ステージの利用例 • 実行環境と構築(ビルド)環境を分ける(イメージ容量の縮小) • イメージに対する影響を最小限に(DRY) • 構築・開発・テスト・構文チェック…のような環境を明示 • 依存関係を一直線に処理しない(並列化) • プラットフォーム固有のステージ 42 一般的な目的は、最終成果物の容量を削減
  43. 43. 構築ステージを --target で指定 43 FROM image_or_stage AS stage_name … $ docker build --target stage_name 「AS ステージ名」を FROM 命令に書いておけば、「docker build」時に「--target」でステージを指定
  44. 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. 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. 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. 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. 48. 様々な環境:構築、開発、テスト、構文チェック(lint)… • ステージとしての検討例: ⁃ builder: 依存関係すべてをビルド ⁃ build(または binary): builder + ビルド成果物 ⁃ cross: 複数のプラットフォーム向けに構築 ⁃ dev: build(er) + 開発/デバッグツール ⁃ lint: 最小限の構文チェック用依存関係 ⁃ test: テストに関係する全ての依存関係 + テスト対象のビルド成果物 ⁃ release: ビルド成果物を含む、最終的な最小イメージ 48様々な「ステージ」が検討できる。ここでは定型的な例 各ステージでは、依存関係が最小となるようにする
  49. 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. 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. 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 他にも単体テスト、統合テスト用の環境も作れます。
  52. 52. 並行性(Concurrency) 52 応用
  53. 53. 一直線の Dockerfile ステージから … • 全てのステージが順番(シーケンシャル)に実行 • この図では、上から下に一直線 • BuildKitがなければ、 不要なステージも無駄に実行し、破棄する (無駄に時間がかかった) 53 s1 s2 s3 s4 s5 s6 デフォルトでは、全てのステージを順番に実行する
  54. 54. BuildKit のマルチ・ステージ graph へ • BuildKit は下(--target のステージ名)から上に辿っていくような流れ • 右図では s2, s3, s4 ステージを同時並行処理 • 不要なステージは無視できる • 右図では s5 がビルド時に 不要であれば、何もしない 54 s1 s2 s3 s4 s5s6 Docker 17.05 からマルチ・ステージ・ビルドが利用可能になった。 18.06までは experimental 、18.09 は利用できるように組み込まれている グラフ ※グラフは点と点とのつながり (関係性)を表す
  55. 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. 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. 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. 58. ベンチマーク • github.com/moby/moby Dockerfile, master ブランチ • 小さいほうが優れている 58 Dockerfile Best Practices https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices/52 キャッシュを有効にすると、7倍も速くなる
  59. 59. ベンチマーク • github.com/moby/moby Dockerfile, master ブランチ • 小さいほうが優れている 59 Dockerfile Best Practices https://www.slideshare.net/Docker/dcsf19-dockerfile-best-practices/52 最も大事なのは、ソースコードの変更時。再構築の速度はマルチ・ステージ・ビルドで 2.5 倍に改善。
  60. 60. Dockerfileの最新機能 60 新機能
  61. 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 の正式な構文では ないため
  62. 62. 詳細 • https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md 62構文の詳細は、こちらのドキュメントを参照
  63. 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. 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. 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. 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. 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. 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」コマンドを実行する
  69. 69. BuildKit キャッシュはホスト側で保持 69 Dockerfile app用ディレクトリ Build用ディレクトリ コンテナ マウント キャッシュしたあとは みーてーるーだーけーDockerのキャッシュ用ディレクトリ “docker build” の対象構築ステップ時のみ、 ホスト側のキャッシュ用ディレクトリを一時的にマウント 何度 build しても、キャッシュを有効活用できるので 高速にイメージを作りやすい また、イメージ全体の容量も削減できる docker build ※ docker builder prune で消せる
  70. 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. シークレット(これもダメな方法) 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. 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 のファイル残骸のみ)
  73. 73. BuildKit 73 Dockerfile 大事なディレクトリ Build用ディレクトリ コンテナ id指定対象を マウント 構築ステップの間だけ みーてーるーだーけー “docker build” の対象構築ステップ時のみ、 ホスト側のシークレット(機微情報)ファイルを一時的にマウント 環境変数などで取り込んで認証などに利用する 一時的にホスト側を参照するだけなので イメージ内に大事な情報を残さない docker build SECRET_FILE シークレット・マウント(v18.09+ w/ BiuldKit) secret mounts id指定
  74. 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. 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. 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. 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. 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. 79. Dockerfile 改善のまとめ • 従来 ⁃ 構築・開発・テスト環境の矛盾 ⁃ 膨れあがるイメージ容量 ⁃ 構築時間がマシマシ(キャッシュ無効)になり時間がかかる ⁃ 構築が安全ではない • これから( BuildKit の活用によって) ⁃ 構築・開発・テスト環境が一致 ⁃ イメージ容量は最小 ⁃ 構築が非常に速く、構築回数が増えていく ⁃ より安全に構築できる 79 export DOCKER_BUILDKIT=1重要なのは有効化に 新しいビルド時の一時マウント可能な bind、cache、tmpfs、secret、ssh マルチ・ステージ・ビルドの活用によって、段階ごとの FROM ステージ間のコピー機能により、最終成果物を小さく 並列ビルドの活用により、従来よりも素早い構築
  80. 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
  81. 81. 参考資料 • BuildKitによる高速でセキュアなイメージビルド https://www.slideshare.net/AkihiroSuda/buildkit 81
  82. 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. 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

×