2022年8月に社内のTechTalkで発表したスライドです。 「良いコンテナイメージ」に関する考察とPythonを例にしたDockerfileのサンプルを示します。
Copyright © 2022 RevComm Inc.よりよいDockerfileを考える~Pythonの実例とともに~RevCommShota Kokado
View Slide
Copyright © 2022 RevComm Inc.contents1. はじめに2. “よい Dockerfile” とは3. Let’s practice!4. 実践事例の紹介
Copyright © 2022 RevComm Inc.はじめに3この資料の想定読者● コンテナ技術に入門済みの人● Dockerfile を書いたことがある人● Web アプリケーション開発者(インタプリタ言語)
Copyright © 2022 RevComm Inc.はじめに4この資料の想定読者● コンテナ技術に入門済みの人● Dockerfile を書いたことがある人● Web アプリケーション開発者(インタプリタ言語)この資料で話さないこと● コンテナとは何か?コンテナ技術の仕組み● ML/DL、バッチ処理用途のワークロード● コンパイル言語向けのこと(Java, Go, Rust)○ …Dockerfile の戦略が異なってくる (alpine とか distroless とか)
Copyright © 2022 RevComm Inc.“よい Dockerfile” とは5
Copyright © 2022 RevComm Inc.“よい Dockerfile” とは6コンテナイメージにとって重要な性質(抜粋)参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント● ※“コンテナ” 自体の理解● 開発利便性● 可搬性 / セキュリティ
Copyright © 2022 RevComm Inc.“よい Dockerfile” とは7コンテナイメージにとって重要な性質(抜粋)参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント● ※“コンテナ” 自体の理解○ 仮想マシン (VM) と同じ使い方をしない○ (原則) 1コンテナ1プロセス● 開発利便性● 可搬性 / セキュリティ
Copyright © 2022 RevComm Inc.“よい Dockerfile” とは8コンテナイメージにとって重要な性質(抜粋)参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント● ※“コンテナ” 自体の理解○ 仮想マシン(VM)と同じ使い方をしない○ (原則) 1コンテナ1プロセス● 開発利便性○ ビルド時キャッシュを活用する● 可搬性 / セキュリティ○ .dockerignore で除外○ レイヤの数は最小に○ ベースイメージとパッケージを最新化する○ 不要なパッケージをインストールしない
Copyright © 2022 RevComm Inc.“よい Dockerfile” とは9コンテナイメージにとって重要な性質(抜粋)参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント● ※“コンテナ” 自体の理解○ 仮想マシン(VM)と同じ使い方をしない○ (原則) 1コンテナ1プロセス● 開発利便性○ ビルド時キャッシュを活用する● 可搬性 / セキュリティ○ .dockerignore で除外○ レイヤの数は最小に○ ベースイメージとパッケージを最新化する○ 不要なパッケージをインストールしない⇒Python アプリケーションを想定した実例とともに深堀り
Copyright © 2022 RevComm Inc.Let’s practice!10
Copyright © 2022 RevComm Inc.前提11● Django を動かすだけの簡単なアプリケーションを想定● uWSGI でサーバー起動するまでを目標JSON で応答を返す
Copyright © 2022 RevComm Inc.(再掲) “よい Dockerfile” とは12コンテナイメージにとって重要な性質(抜粋)参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント● ※“コンテナ” 自体の理解○ 仮想マシン(VM)と同じ使い方をしない○ (原則) 1コンテナ1プロセス● 開発利便性○ ビルド時キャッシュを活用する● 可搬性 / セキュリティ○ .dockerignore で除外○ レイヤの数は最小に○ ベースイメージとパッケージを最新化する○ 不要なパッケージをインストールしない
Copyright © 2022 RevComm Inc.● 先頭で COPY ./ ./ をしない○ docker build で毎回(≒ソースコードを書き換える度)初めからになってしまう(= キャッシュを活用できない)○ ビルド過程で必要となる requirements.txt などを先に配置する開発利便性 - ビルド時キャッシュを活用する(1/2)13
Copyright © 2022 RevComm Inc.● 先頭で COPY ./ ./ をしない○ docker build で毎回(≒ソースコードを書き換える度)初めからになってしまう(= キャッシュを活用できない)○ ビルド過程で必要となる requirements.txt などを先に配置する開発利便性 - ビルド時キャッシュを活用する(1/2)14ソースコード変更の度に「pip install …」が実行される
Copyright © 2022 RevComm Inc.● 先頭で COPY ./ ./ をしない○ docker build で毎回(≒ソースコードを書き換える度)初めからになってしまう(= キャッシュを活用できない)○ ビルド過程で必要となる requirements.txt などを先に配置する開発利便性 - ビルド時キャッシュを活用する(1/2)15ソースコード変更後は 7行目から開始
Copyright © 2022 RevComm Inc.● 先頭で COPY ./ ./ をしない○ docker build で毎回(≒ソースコードを書き換える度)初めからになってしまう(= キャッシュを活用できない)○ ビルド過程で必要となる requirements.txt などを先に配置する○ パッケージマネージャを使用する場合開発利便性 - ビルド時キャッシュを活用する(1/2)16● Pipenv※以降、Pipenv を使用する前提のサンプルとする● Poetry
Copyright © 2022 RevComm Inc.● 必要な定型処理を先に実行する○ パッケージのインストールを先に行う開発利便性 - ビルド時キャッシュを活用する(2/2)17
Copyright © 2022 RevComm Inc.● 必要な定型処理を先に実行する○ パッケージのインストールを先に行う開発利便性 - ビルド時キャッシュを活用する(2/2)18Pipfile / Pipfile.lock を変更した場合、直後のビルドは7行目から開始する
Copyright © 2022 RevComm Inc.“よい Dockerfile” とは19コンテナイメージにとって重要な性質(抜粋)参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント● ※“コンテナ” 自体の理解○ 仮想マシン(VM)と同じ使い方をしない○ (原則) 1コンテナ1プロセス● 開発利便性○ ビルド時キャッシュを活用する● 可搬性 / セキュリティ○ .dockerignore で除外○ レイヤの数は最小に○ ベースイメージとパッケージを最新化する○ 不要なパッケージをインストールしない
Copyright © 2022 RevComm Inc.“可搬性” (Portability)… コンテナイメージを軽量に保つことで複数のホスト・基盤上で共有しやすくする。軽量化には不要なパッケージ・ライブラリを含めないことが重要。結果、セキュリティ向上につながる。可搬性 / セキュリティ20
Copyright © 2022 RevComm Inc.(再掲) “よい Dockerfile” とは21コンテナイメージにとって重要な性質(抜粋)参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント● ※“コンテナ” 自体の理解○ 仮想マシン(VM)と同じ使い方をしない○ (原則) 1コンテナ1プロセス● 開発利便性○ ビルド時キャッシュを活用する● 可搬性 / セキュリティ○ .dockerignore で除外○ レイヤの数は最小に○ ベースイメージとパッケージを最新化する○ 不要なパッケージをインストールしない
Copyright © 2022 RevComm Inc.● 同じ命令は可能な限りまとめるレイヤの数を最小にする> RUN 、 COPY 、 ADD 命令のみレイヤを作成します。他の命令では、一時的な中間イメージ(temporary intermediate images)を作成し、構築時の容量は増えません。1. RUN可搬性 / セキュリティ (1/2) - レイヤの数は最小に222. COPY 3. ADD※COPY が推奨される
Copyright © 2022 RevComm Inc.● 同じ命令は可能な限りまとめるレイヤの数を最小にする> RUN 、 COPY 、 ADD 命令のみレイヤを作成します。他の命令では、一時的な中間イメージ(temporary intermediate images)を作成し、構築時の容量は増えません。1. RUN可搬性 / セキュリティ (1/2) - レイヤの数は最小に232. COPY 3. ADD※COPY が推奨される1行目の中間レイヤーを保持するため3-4行目はイメージサイズ削減に効果が無い
Copyright © 2022 RevComm Inc.(再掲) “よい Dockerfile” とは24コンテナイメージにとって重要な性質(抜粋)参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント● ※“コンテナ” 自体の理解○ 仮想マシン(VM)と同じ使い方をしない○ (原則) 1コンテナ1プロセス● 開発利便性○ ビルド時キャッシュを活用する● 可搬性 / セキュリティ○ .dockerignore で除外○ レイヤの数は最小に○ ベースイメージとパッケージを最新化する○ 不要なパッケージをインストールしない
Copyright © 2022 RevComm Inc.● ベースイメージ○ “buster” イメージを使っていませんか?(python:3.x-buster、python:3.x-slim-buster)○ No!最新は “bullseye” です(= Debian 11.0)※2022年11月時点○ ベースイメージには debian バージョンを指定しなくても良いかも(alias があるので)■ python:3.10.8 = python:3.10.8-bullseye■ python:3.10.8-slim = python:3.10.8-slim-bullseyeベースイメージとパッケージを最新化する25
Copyright © 2022 RevComm Inc.● ベースイメージ○ “buster” イメージを使っていませんか?(python:3.x-buster、python:3.x-slim-buster)○ No!最新は “bullseye” です(= Debian 11.0)※2022年11月時点○ ベースイメージには debian バージョンを指定しなくても良いかも(alias があるので)■ python:3.10.8 = python:3.10.8-bullseye■ python:3.10.8-slim = python:3.10.8-slim-bullseye● パッケージ○ Docker 公式ドキュメントで「Dockerfile の中で apt-get upgrade しないように」と書かれていたのは昔の話○ OWASP が提唱して、Docker コミュニティも修正済み○ Not installing security updates is bad advice by itamarst · Pull Request #614 · OWASP/CheatSheetSeries○ Stop telling people not to install security updates by itamarst · Pull Request #12571 · docker/docs○ パッケージは必ず最新化しましょう!○ 定期的にイメージを更新しましょう!ベースイメージとパッケージを最新化する26
Copyright © 2022 RevComm Inc.(再掲) “よい Dockerfile” とは27コンテナイメージにとって重要な性質(抜粋)参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント● ※“コンテナ” 自体の理解○ 仮想マシン(VM)と同じ使い方をしない○ (原則) 1コンテナ1プロセス● 開発利便性○ ビルド時キャッシュを活用する● 可搬性 / セキュリティ○ .dockerignore で除外○ レイヤの数は最小に○ ベースイメージとパッケージを最新化する○ 不要なパッケージをインストールしない
Copyright © 2022 RevComm Inc.● デフォルトのベースイメージには無駄なパッケージが(非常に)多い⇒slim イメージを使いましょう● slim イメージには gcc などビルドツール群が含まれないため、Python ライブラリをビルドできない場合がある⇒マルチステージビルドを使う○ slim イメージに gcc をインストールするのは可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない28
Copyright © 2022 RevComm Inc.ここまでの整理● slim イメージを使用する○ Python ライブラリの準備を “not slim イメージ” で行う (マルチステージビルド)可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない29
Copyright © 2022 RevComm Inc.ここまでの整理● slim イメージを使用する○ Python ライブラリの準備を “not slim イメージ” で行う (マルチステージビルド)● 課題○ パッケージマネージャー (Pipenv / Poetry) をアプリケーションに含めたくない○ dev-packages (テスト用ツールなど)を本番用イメージから分離した■ CI では dev-packages を使いたい可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない30アプリケーション自体に Pipenv は要らない(普通は)
Copyright © 2022 RevComm Inc.● 結論可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない31こちらでも見れます
Copyright © 2022 RevComm Inc.● 結論可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない32FROM [base image] AS [stage name] という形式1つの FROM 命令のブロックが1ステージと対応
Copyright © 2022 RevComm Inc.● 結論可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない331. ステージ: export○ requirements.txt 形式のライブラリ一覧を生成する○ ⇒後続ステージにて Pipenv が不要となる
Copyright © 2022 RevComm Inc.● 結論可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない341. ステージ: export○ requirements.txt 形式のライブラリ一覧を生成する○ ⇒後続ステージにて Pipenv 不要となる2. ステージ: builder / dev-builder○ requirements.txt を元にライブラリを準備する○ COPY --from=[stage name] … でステージ間でファイルを受け取る○ prod 用、dev 用の2通りを用意
Copyright © 2022 RevComm Inc.● 結論可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない351. ステージ: export○ requirements.txt 形式のライブラリ一覧を生成する○ ⇒後続ステージにて Pipenv 不要となる2. ステージ: builder / dev-builder○ requirements.txt を元にライブラリを準備する○ COPY --from=[stage name] … でステージ間でファイルを受け取る○ prod 用、dev 用の2通りを用意3. ステージ: base○ 最終的なアプリケーション用の環境をセットアップ○ 必要なパッケージをインストール○ ※一般ユーザーを作成、スイッチなど(割愛)libpq5: PostgreSQL 接続に必要libxml2: uWSGI 実行に必要
Copyright © 2022 RevComm Inc.● 結論可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない361. ステージ: export○ requirements.txt 形式のライブラリ一覧を生成する○ ⇒後続ステージにて Pipenv 不要となる2. ステージ: builder / dev-builder○ requirements.txt を元にライブラリを準備する○ COPY --from=[stage name] … でステージ間でファイルを受け取る○ prod 用、dev 用の2通りを用意3. ステージ: base○ 最終的なアプリケーション用の環境をセットアップ○ 必要なパッケージをインストール○ ※一般ユーザーを作成、スイッチなど(割愛)4. ステージ: dev / prod○ Python ライブラリをインストール○ ソースコードを配置○ ※dev / prod で CMD を分ける、など適宜○ docker build --target [stage-name] .... でビルド対象ステージを指定する
Copyright © 2022 RevComm Inc.● 結論可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない37
Copyright © 2022 RevComm Inc.実践事例の紹介38
Copyright © 2022 RevComm Inc.● とあるマイクロサービスに対して実践したところ、大幅に効果があったBefore実践事例の紹介39
Copyright © 2022 RevComm Inc.● とあるマイクロサービスに対して実践したところ、大幅に効果があったBefore実践事例の紹介40①slim イメージを使っていない②アプリケーション起動が root ユーザー
Copyright © 2022 RevComm Inc.After実践事例の紹介41①slim イメージを使用するように変更。builder ステージでライブラリをでインストール・ビルドし、runner ステージで利用。①一般ユーザーでアプリケーションを起動するように変更。※80番ポートを起動できないためポート番号も変更。
Copyright © 2022 RevComm Inc.● 開発利便性と軽量性/セキュリティ は両立できる(相反する性質ではない)● なるべく slim イメージを使用しましょう● マルチステージビルドを活用することで「アプリケーションに必要十分な環境」が作りやすくなる○ 開発 / 本番用イメージを同一 Dockerfile で管理できる(DRY 原則を保てる)※必要に応じて Dockerfile を分離して可読性を維持する(要はバランス)リンク● サンプルコード: GitHub revcomm/public-slide-docker-multistage-build-for-python○ dev コンテナ: ユニットテスト実行○ prod コンテナ:アプリケーション起動、動作確認● 仕事でPythonコンテナをデプロイする人向けのDockerfile (1): オールマイティ編 | フューチャー技術ブログ● 軽量Dockerイメージに安易にAlpineを使うのはやめたほうがいいという話 - inductor's blogまとめ42
Copyright © 2022 RevComm Inc.Have a good container life!43