$30 off During Our Annual Pro Sale. View Details »

PythonにとってのよりよいDockerfileを考える

RevComm_inc
February 07, 2023

 PythonにとってのよりよいDockerfileを考える

2022年8月に社内のTechTalkで発表したスライドです。
「良いコンテナイメージ」に関する考察とPythonを例にしたDockerfileのサンプルを示します。

RevComm_inc

February 07, 2023
Tweet

More Decks by RevComm_inc

Other Decks in Technology

Transcript

  1. Copyright © 2022 RevComm Inc.
    よりよいDockerfileを考える
    ~Pythonの実例とともに~
    RevComm
    Shota Kokado

    View Slide

  2. Copyright © 2022 RevComm Inc.
    contents
    1. はじめに
    2. “よい Dockerfile” とは
    3. Let’s practice!
    4. 実践事例の紹介

    View Slide

  3. Copyright © 2022 RevComm Inc.
    はじめに
    3
    この資料の想定読者
    ● コンテナ技術に入門済みの人
    ● Dockerfile を書いたことがある人
    ● Web アプリケーション開発者(インタプリタ言語)

    View Slide

  4. Copyright © 2022 RevComm Inc.
    はじめに
    4
    この資料の想定読者
    ● コンテナ技術に入門済みの人
    ● Dockerfile を書いたことがある人
    ● Web アプリケーション開発者(インタプリタ言語)
    この資料で話さないこと
    ● コンテナとは何か?コンテナ技術の仕組み
    ● ML/DL、バッチ処理用途のワークロード
    ● コンパイル言語向けのこと(Java, Go, Rust)
    ○ …Dockerfile の戦略が異なってくる (alpine とか distroless とか)

    View Slide

  5. Copyright © 2022 RevComm Inc.
    “よい Dockerfile” とは
    5

    View Slide

  6. Copyright © 2022 RevComm Inc.
    “よい Dockerfile” とは
    6
    コンテナイメージにとって重要な性質(抜粋)
    参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント
    ● ※“コンテナ” 自体の理解
    ● 開発利便性
    ● 可搬性 / セキュリティ

    View Slide

  7. Copyright © 2022 RevComm Inc.
    “よい Dockerfile” とは
    7
    コンテナイメージにとって重要な性質(抜粋)
    参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント
    ● ※“コンテナ” 自体の理解
    ○ 仮想マシン (VM) と同じ使い方をしない
    ○ (原則) 1コンテナ1プロセス
    ● 開発利便性
    ● 可搬性 / セキュリティ

    View Slide

  8. Copyright © 2022 RevComm Inc.
    “よい Dockerfile” とは
    8
    コンテナイメージにとって重要な性質(抜粋)
    参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント
    ● ※“コンテナ” 自体の理解
    ○ 仮想マシン(VM)と同じ使い方をしない
    ○ (原則) 1コンテナ1プロセス
    ● 開発利便性
    ○ ビルド時キャッシュを活用する
    ● 可搬性 / セキュリティ
    ○ .dockerignore で除外
    ○ レイヤの数は最小に
    ○ ベースイメージとパッケージを最新化する
    ○ 不要なパッケージをインストールしない

    View Slide

  9. Copyright © 2022 RevComm Inc.
    “よい Dockerfile” とは
    9
    コンテナイメージにとって重要な性質(抜粋)
    参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント
    ● ※“コンテナ” 自体の理解
    ○ 仮想マシン(VM)と同じ使い方をしない
    ○ (原則) 1コンテナ1プロセス
    ● 開発利便性
    ○ ビルド時キャッシュを活用する
    ● 可搬性 / セキュリティ
    ○ .dockerignore で除外
    ○ レイヤの数は最小に
    ○ ベースイメージとパッケージを最新化する
    ○ 不要なパッケージをインストールしない
    ⇒Python アプリケーションを想定した実例とともに深堀り

    View Slide

  10. Copyright © 2022 RevComm Inc.
    Let’s practice!
    10

    View Slide

  11. Copyright © 2022 RevComm Inc.
    前提
    11
    ● Django を動かすだけの簡単なアプリケーションを想定
    ● uWSGI でサーバー起動するまでを目標
    JSON で応答を返す

    View Slide

  12. Copyright © 2022 RevComm Inc.
    (再掲) “よい Dockerfile” とは
    12
    コンテナイメージにとって重要な性質(抜粋)
    参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント
    ● ※“コンテナ” 自体の理解
    ○ 仮想マシン(VM)と同じ使い方をしない
    ○ (原則) 1コンテナ1プロセス
    ● 開発利便性
    ○ ビルド時キャッシュを活用する
    ● 可搬性 / セキュリティ
    ○ .dockerignore で除外
    ○ レイヤの数は最小に
    ○ ベースイメージとパッケージを最新化する
    ○ 不要なパッケージをインストールしない

    View Slide

  13. Copyright © 2022 RevComm Inc.
    ● 先頭で COPY ./ ./ をしない
    ○ docker build で毎回(≒ソースコードを書き換える度)初めからになってしまう(= キャッシュを活用できない)
    ○ ビルド過程で必要となる requirements.txt などを先に配置する
    開発利便性 - ビルド時キャッシュを活用する(1/2)
    13

    View Slide

  14. Copyright © 2022 RevComm Inc.
    ● 先頭で COPY ./ ./ をしない
    ○ docker build で毎回(≒ソースコードを書き換える度)初めからになってしまう(= キャッシュを活用できない)
    ○ ビルド過程で必要となる requirements.txt などを先に配置する
    開発利便性 - ビルド時キャッシュを活用する(1/2)
    14
    ソースコード変更の度に「pip install …」が実行される

    View Slide

  15. Copyright © 2022 RevComm Inc.
    ● 先頭で COPY ./ ./ をしない
    ○ docker build で毎回(≒ソースコードを書き換える度)初めからになってしまう(= キャッシュを活用できない)
    ○ ビルド過程で必要となる requirements.txt などを先に配置する
    開発利便性 - ビルド時キャッシュを活用する(1/2)
    15
    ソースコード変更後は 7行目から開始

    View Slide

  16. Copyright © 2022 RevComm Inc.
    ● 先頭で COPY ./ ./ をしない
    ○ docker build で毎回(≒ソースコードを書き換える度)初めからになってしまう(= キャッシュを活用できない)
    ○ ビルド過程で必要となる requirements.txt などを先に配置する
    ○ パッケージマネージャを使用する場合
    開発利便性 - ビルド時キャッシュを活用する(1/2)
    16
    ● Pipenv
    ※以降、Pipenv を使用する前提のサンプルとする
    ● Poetry

    View Slide

  17. Copyright © 2022 RevComm Inc.
    ● 必要な定型処理を先に実行する
    ○ パッケージのインストールを先に行う
    開発利便性 - ビルド時キャッシュを活用する(2/2)
    17

    View Slide

  18. Copyright © 2022 RevComm Inc.
    ● 必要な定型処理を先に実行する
    ○ パッケージのインストールを先に行う
    開発利便性 - ビルド時キャッシュを活用する(2/2)
    18
    Pipfile / Pipfile.lock を変更した場合、
    直後のビルドは7行目から開始する

    View Slide

  19. Copyright © 2022 RevComm Inc.
    “よい Dockerfile” とは
    19
    コンテナイメージにとって重要な性質(抜粋)
    参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント
    ● ※“コンテナ” 自体の理解
    ○ 仮想マシン(VM)と同じ使い方をしない
    ○ (原則) 1コンテナ1プロセス
    ● 開発利便性
    ○ ビルド時キャッシュを活用する
    ● 可搬性 / セキュリティ
    ○ .dockerignore で除外
    ○ レイヤの数は最小に
    ○ ベースイメージとパッケージを最新化する
    ○ 不要なパッケージをインストールしない

    View Slide

  20. Copyright © 2022 RevComm Inc.
    “可搬性” (Portability)
    … コンテナイメージを軽量に保つことで複数のホスト・基盤上で共有しやすくする。
    軽量化には不要なパッケージ・ライブラリを含めないことが重要。結果、セキュリティ向上につながる。
    可搬性 / セキュリティ
    20

    View Slide

  21. Copyright © 2022 RevComm Inc.
    (再掲) “よい Dockerfile” とは
    21
    コンテナイメージにとって重要な性質(抜粋)
    参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント
    ● ※“コンテナ” 自体の理解
    ○ 仮想マシン(VM)と同じ使い方をしない
    ○ (原則) 1コンテナ1プロセス
    ● 開発利便性
    ○ ビルド時キャッシュを活用する
    ● 可搬性 / セキュリティ
    ○ .dockerignore で除外
    ○ レイヤの数は最小に
    ○ ベースイメージとパッケージを最新化する
    ○ 不要なパッケージをインストールしない

    View Slide

  22. Copyright © 2022 RevComm Inc.
    ● 同じ命令は可能な限りまとめる
    レイヤの数を最小にする
    > RUN 、 COPY 、 ADD 命令のみレイヤを作成します。他の命令では、一時的な中間イメージ(temporary intermediate images)を作成し、構築時の容
    量は増えません。
    1. RUN
    可搬性 / セキュリティ (1/2) - レイヤの数は最小に
    22
    2. COPY 3. ADD
    ※COPY が推奨される

    View Slide

  23. Copyright © 2022 RevComm Inc.
    ● 同じ命令は可能な限りまとめる
    レイヤの数を最小にする
    > RUN 、 COPY 、 ADD 命令のみレイヤを作成します。他の命令では、一時的な中間イメージ(temporary intermediate images)を作成し、構築時の容
    量は増えません。
    1. RUN
    可搬性 / セキュリティ (1/2) - レイヤの数は最小に
    23
    2. COPY 3. ADD
    ※COPY が推奨される
    1行目の中間レイヤーを保持するため
    3-4行目はイメージサイズ削減に効果が無い

    View Slide

  24. Copyright © 2022 RevComm Inc.
    (再掲) “よい Dockerfile” とは
    24
    コンテナイメージにとって重要な性質(抜粋)
    参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント
    ● ※“コンテナ” 自体の理解
    ○ 仮想マシン(VM)と同じ使い方をしない
    ○ (原則) 1コンテナ1プロセス
    ● 開発利便性
    ○ ビルド時キャッシュを活用する
    ● 可搬性 / セキュリティ
    ○ .dockerignore で除外
    ○ レイヤの数は最小に
    ○ ベースイメージとパッケージを最新化する
    ○ 不要なパッケージをインストールしない

    View Slide

  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
    ベースイメージとパッケージを最新化する
    25

    View Slide

  26. 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

    View Slide

  27. Copyright © 2022 RevComm Inc.
    (再掲) “よい Dockerfile” とは
    27
    コンテナイメージにとって重要な性質(抜粋)
    参考: Dockerfile のベスト・プラクティス — Docker-docs-ja 20.10 ドキュメント
    ● ※“コンテナ” 自体の理解
    ○ 仮想マシン(VM)と同じ使い方をしない
    ○ (原則) 1コンテナ1プロセス
    ● 開発利便性
    ○ ビルド時キャッシュを活用する
    ● 可搬性 / セキュリティ
    ○ .dockerignore で除外
    ○ レイヤの数は最小に
    ○ ベースイメージとパッケージを最新化する
    ○ 不要なパッケージをインストールしない

    View Slide

  28. Copyright © 2022 RevComm Inc.
    ● デフォルトのベースイメージには無駄なパッケージが(非常に)多い
    ⇒slim イメージを使いましょう
    ● slim イメージには gcc などビルドツール群が含まれないため、Python ライブラリをビルドできない場合がある
    ⇒マルチステージビルドを使う
    ○ slim イメージに gcc をインストールするのは
    可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない
    28

    View Slide

  29. Copyright © 2022 RevComm Inc.
    ここまでの整理
    ● slim イメージを使用する
    ○ Python ライブラリの準備を “not slim イメージ” で行う (マルチステージビルド)
    可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない
    29

    View Slide

  30. Copyright © 2022 RevComm Inc.
    ここまでの整理
    ● slim イメージを使用する
    ○ Python ライブラリの準備を “not slim イメージ” で行う (マルチステージビルド)
    ● 課題
    ○ パッケージマネージャー (Pipenv / Poetry) をアプリケーションに含めたくない
    ○ dev-packages (テスト用ツールなど)を本番用イメージから分離した
    ■ CI では dev-packages を使いたい
    可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない
    30
    アプリケーション自体に Pipenv は要らない
    (普通は)

    View Slide

  31. Copyright © 2022 RevComm Inc.
    ● 結論
    可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない
    31
    こちらでも見れます

    View Slide

  32. Copyright © 2022 RevComm Inc.
    ● 結論
    可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない
    32
    FROM [base image] AS [stage name] という形式
    1つの FROM 命令のブロックが1ステージと対応

    View Slide

  33. Copyright © 2022 RevComm Inc.
    ● 結論
    可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない
    33
    1. ステージ: export
    ○ requirements.txt 形式のライブラリ一覧を生成する
    ○ ⇒後続ステージにて Pipenv が不要となる

    View Slide

  34. Copyright © 2022 RevComm Inc.
    ● 結論
    可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない
    34
    1. ステージ: export
    ○ requirements.txt 形式のライブラリ一覧を生成する
    ○ ⇒後続ステージにて Pipenv 不要となる
    2. ステージ: builder / dev-builder
    ○ requirements.txt を元にライブラリを準備する
    ○ COPY --from=[stage name] … でステージ間でファイルを受け取る
    ○ prod 用、dev 用の2通りを用意

    View Slide

  35. Copyright © 2022 RevComm Inc.
    ● 結論
    可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない
    35
    1. ステージ: export
    ○ requirements.txt 形式のライブラリ一覧を生成する
    ○ ⇒後続ステージにて Pipenv 不要となる
    2. ステージ: builder / dev-builder
    ○ requirements.txt を元にライブラリを準備する
    ○ COPY --from=[stage name] … でステージ間でファイルを受け取る
    ○ prod 用、dev 用の2通りを用意
    3. ステージ: base
    ○ 最終的なアプリケーション用の環境をセットアップ
    ○ 必要なパッケージをインストール
    ○ ※一般ユーザーを作成、スイッチなど(割愛)
    libpq5: PostgreSQL 接続に必要
    libxml2: uWSGI 実行に必要

    View Slide

  36. Copyright © 2022 RevComm Inc.
    ● 結論
    可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない
    36
    1. ステージ: 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] .... でビルド対象ステージを
    指定する

    View Slide

  37. Copyright © 2022 RevComm Inc.
    ● 結論
    可搬性 / セキュリティ (2/2) - 不要なパッケージをインストールしない
    37

    View Slide

  38. Copyright © 2022 RevComm Inc.
    実践事例の紹介
    38

    View Slide

  39. Copyright © 2022 RevComm Inc.
    ● とあるマイクロサービスに対して実践したところ、大幅に効果があった
    Before
    実践事例の紹介
    39

    View Slide

  40. Copyright © 2022 RevComm Inc.
    ● とあるマイクロサービスに対して実践したところ、大幅に効果があった
    Before
    実践事例の紹介
    40
    ①slim イメージを使っていない
    ②アプリケーション起動が root ユーザー

    View Slide

  41. Copyright © 2022 RevComm Inc.
    After
    実践事例の紹介
    41
    ①slim イメージを使用するように変更。
    builder ステージでライブラリをでインストール・ビルドし、
    runner ステージで利用。
    ①一般ユーザーでアプリケーションを起動するように変更。
    ※80番ポートを起動できないためポート番号も変更。

    View Slide

  42. 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

    View Slide

  43. Copyright © 2022 RevComm Inc.
    Have a good container life!
    43

    View Slide