Python イメージのビルド

読む時間の目安: 5 分

前提条件

「はじめよう」の 1 部 に示しているガイドや設定をひととおり読んで、Docker の考え方について理解してください。

BuildKit の有効化

イメージビルドを行うにあたっては、マシン上において BuildKit が有効になっていることを確認してください。 BuildKit が有効になっていれば、Docker イメージビルドを効率的に行うことができます。 詳しくは BuildKit によるイメージ構築 を参照してください。

BuildKit は Docker Desktop 上の全ユーザーに対して、デフォルトで有効になっています。 Docker Desktop をインストールしているのであれば、手動で BuildKit を有効にする必要はありません。 Linux 上において Docker を利用している場合は、環境変数を用いるか、あるいは BuildKit がデフォルト設定となるようにして、BuildKit を有効にします。

docker buildコマンド時に環境変数 BuildKit を設定するには、以下のようにします。

$ DOCKER_BUILDKIT=1 docker build .

デフォルトで BuildKit を有効にするには、/etc/docker/daemon.json内のデーモン設定をtrueに設定して、デーモンを再起動します。 daemon.jsonファイルが存在していない場合は、このdaemon.jsonファイルを新規生成して、以下の記述を追加します。

{
  "features":{"buildkit" : true}
}

そして Docker デーモンを再起動します。

概要

ここまでに コンテナーや Docker プラットフォームについての概要を理解してきたので、ここからは初めてのイメージ作りを見ていくことにしましょう。 イメージというものは、アプリケーションを動作させるために必要なものをすべて含みます。 コード、実行モジュール、ランタイム、依存パッケージ、その他にシステムオブジェクトが必要とするあらゆるファイルです。

本チュートリアルを進めていくためには、以下が必要になります。

  • Python バージョン 3.8 またはそれ以降。 Python をダウンロードしてください。
  • ローカルで動作している Docker。 以下の手順に従って Docker のダウンロードとインストール を行ってください。
  • ファイル編集を行うための IDE またはテキストエディター。 ここでは Visual Studio Code をお勧めします。

サンプルアプリケーション

例として用いる簡単な Python アプリケーションを作っていきます。 これは Flask フレームワークを利用します。 ローカルマシン内にpython-dockerという名前のディレクトリを生成し、以下の手順により簡単なウェブサーバーを生成します。

$ cd /path/to/python-docker
$ pip3 install Flask
$ pip3 freeze | grep Flask >> requirements.txt
$ touch app.py

そこで簡単なウェブリクエストを扱うコードを追加してみます。 好みの IDE 内においてワーキングディレクトリを開いて、app.pyファイルに以下のコードを記述します。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, Docker!'

アプリケーションのテスト

ここからアプリケーションを起動させて正しく動作することを確認します。 ターミナルを開いて、上で生成したワーキングディレクトリに移動します。

$ python3 -m flask run

アプリケーションが適切に動作していることを確認するために、ブラウザーを開いてhttp://localhost:5000にアクセスします。

サーバーを起動しているターミナルに戻ります。 サーバーログとして以下のようなリクエストが表示されているはずです。 起動したマシンごとに、データ内容やタイムスタンプは異なります。

127.0.0.1 - - [22/Sep/2020 11:07:41] "GET / HTTP/1.1" 200 -

Python を用いた Dockerfile の生成

アプリケーションが正しく動作しているので、ここからは Dockerfile の生成を行っていきます。

Dockerfile とは、 Docker イメージを取得する命令を含んだテキストファイルのことです。 Docker に対してdocker buildコマンドを実行してイメージビルドを指示すると、Docker は記述された命令を読み込んで実行し、最終的に Docker イメージを作り出します。

ではアプリケーション向けに Dockerfile を生成していく手順を見ていきます。 プロジェクトのルートディレクトリにDockerfileという名前のファイルを生成して、テキストエディターでこれを開きます。

Dockerfile の名前を何にするか

Dockerfile に対して用いられるデフォルトのファイル名は、 Dockerfile です (ファイル拡張子はありません)。 このデフォルトファイル名を用いておけば、docker buildコマンドを実行する際に、コマンドラインフラグを追加して指定する必要がありません。

プロジェクトによっては、特定の目的のため、Dockerfile に別名を与える場合があります。 普通行われる慣例としては、Dockerfile.<something><something>.Dockerfileとします。 このような Dockerfile は、docker buildコマンドの実行にあたって、--fileオプション (その短縮形-f) を用いて指定します。 --fileオプションの利用方法については、docker buildリファレンス内の Dockerfile の指定 のセクションを参照してください。

プロジェクトの主となる Dockerfile には、デフォルト名 (Dockerfile) を用いることをお勧めします。 本ガイドに示すほとんどの例においては、この名前を用いています。

Docker ファイルの 1 行めに書くのは # syntaxパーサーディレクティブ です。 これは 任意の記述 ではありますが、Dockerfile の解析にあたって Docker ビルダーがどの文法を採用するのかを指示します。 また古い Docker バージョンにおいて BuildKit を利用する際に、ビルド前にパーサーをアップグレードするようになります。 パーサーディレクティブ は Dockerfile において、いずれのコメント、空行、Dockerfile 命令よりも前に、つまり第一に記述することが必要です。

# syntax=docker/dockerfile:1

docker/dockerfile:1を用いることをお勧めします。 こうしておくと、常に文法バージョン 1 の最新リリース版を指し示すことになります。 BuildKit は、ビルド処理の前に文法に更新がないかを自動的にチェックし、最新バージョンが用いられていることを確認します。

Dockerfile にその次に加えるのは、ベースイメージに何を用いるのかを指定します。 そのベースイメージを利用してアプリケーションを構築します。

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

Docker イメージというものは、別のイメージを継承することができます。 したがって独自のベースイメージを作るのではなく、公式の Python イメージを利用することにします。 そのイメージには、Python アプリケーションの実行に必要となるツールやパッケージがすでに含まれています。

メモ

独自のベースイメージ作りについて学びたい場合は、ベースイメージの生成 を参照してください。

これ以降のコマンド実行をやりやすくするように、ここでワーキングディレクトリを生成します。 Docker に対してこれを指示しておけば、この後に続くコマンドにおいてデフォルトディレクトリとして用いられます。 これによりファイルのフルパスを記述する必要がなくなり、ワーキングディレクトリからの相対パスを用いることができます。

WORKDIR /app

通常は Python で書かれたプロジェクトのダウンロード後、一番に行っておくことがpipパッケージのインストールです。 これを行っておくとアプリケーションの依存パッケージがすべてインストールされます。

pip installを実行する前には、requirements.txtファイルをイメージ内にコピーしておくことが必要です。 COPYコマンドを使ってこれを行います。 COPYコマンドには引数が 2 つあります。 1 つめの引数は、Docker に対してイメージ内にコピーしたい元のファイルを指示します。 2 つめの引数は、Docker に対してそのファイルをイメージ内のどこにコピーするかを指示します。 ここではrequirements.txtをワーキングディレクトリ/appにコピーします。

COPY requirements.txt requirements.txt

requirements.txtファイルをイメージ内に置いたらRUNコマンドによってpip3 installを行います。 この際の処理はpip3 installをマシン内でローカルに実行しているかのようにして動作します。 ただしこのときの Python モジュール類はイメージ内にインストールされます。

RUN pip3 install -r requirements.txt

ここまでに Python バージョン 3.8 に基づくイメージを構築して、依存パッケージのインストールを行いました。 次に行うのは、イメージ内にソースコードを置くことです。 先に行ったrequirements.txtファイルと同じようにCOPYコマンドを用いることにします。

COPY . .

このCOPYコマンドは、カレントディレクトリにあるファイルすべてをイメージ内にコピーします。 最後に行うのは、このイメージがコンテナー内において実行される際に実行させたいコマンドを指定します。 これを行うにはCMDコマンドを用います。 なおここでアプリケーションは外部から(つまりコンテナーの外から)アクセスできるようにすることが必要であるため、--host=0.0.0.0を指定します。

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

以下が完全な Dockerfile です。

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

ディレクトリ構造

ここまでの作業をまとめてみると、ローカルマシン内にpython-dockerというディレクトリを生成して、Flask フレームワークを利用した簡単な Python アプリケーションを生成しました。 またrequirements.txtファイルを利用して依存パッケージを取得し、またイメージビルドのためのコマンドを記述した Dokerfile を生成しました。 Python アプリケーションディレクトリは以下のような構造となります。

python-docker
|____ app.py
|____ requirements.txt
|____ Dockerfile

イメージのビルド

Dockerfile を生成したので、ここからイメージをビルドします。 これを行うにはdocker buildコマンドを使います。 docker buildコマンドは Dockerfile と「コンテキスト(context)」から Docker イメージをビルドします。 ビルドのコンテキストとは、指定されているパスや URL 内にある一連のファイルのことです。 Docker のビルド処理においては、コンテキスト内にあるファイルはどれにでもアクセスすることができます。

build コマンドにはオプションとして--tagフラグをつけることができます。 タグ(tag)はイメージ名とオプションとなるタグ名をname:tagという書式で指定します。 話を単純にするため、ここでは「タグ」は用いないことにします。 タグを指定しなければ Docker はデフォルトのタグ名として「latest」を用います。

では初めての Docker イメージをビルドしてみます。

$ docker build --tag python-docker .
[+] Building 2.7s (10/10) FINISHED
 => [internal] load build definition from Dockerfile
 => => transferring dockerfile: 203B
 => [internal] load .dockerignore
 => => transferring context: 2B
 => [internal] load metadata for docker.io/library/python:3.8-slim-buster
 => [1/6] FROM docker.io/library/python:3.8-slim-buster
 => [internal] load build context
 => => transferring context: 953B
 => CACHED [2/6] WORKDIR /app
 => [3/6] COPY requirements.txt requirements.txt
 => [4/6] RUN pip3 install -r requirements.txt
 => [5/6] COPY . .
 => [6/6] CMD [ "python3", "-m", "flask", "run", "--host=0.0.0.0"]
 => exporting to image
 => => exporting layers
 => => writing image sha256:8cae92a8fbd6d091ce687b71b31252056944b09760438905b726625831564c4c
 => => naming to docker.io/library/python-docker

ローカルイメージの確認

ローカルマシン内にあるイメージの一覧を見るには 2 つの方法があります。 1 つは CLI を用いる方法、もう 1 つは Docker Desktop を用いる方法です。 これまでターミナルを使って作業を進めてきていますから、イメージ一覧は CLI を使って取得することにします。

イメージを一覧表示するには、単純にdocker imagesコマンドを実行します。

$ docker images
REPOSITORY      TAG               IMAGE ID       CREATED         SIZE
python-docker   latest            8cae92a8fbd6   3 minutes ago   123MB
python          3.8-slim-buster   be5d294735c6   9 days ago      113MB

一覧には少なくとも 2 つのイメージが表示されるはずです。 1 つはベースイメージ3.8-slim-busterであり、もう 1 つはpython-docker:latestとしてビルドしたイメージです。

イメージへのタグづけ

イメージ名はスラッシュによって区切られた名称により構成されます。 この名称には、英字の小文字、数字、セパレーター文字が利用可能です。 このセパレーター文字とは、1 つのピリオド、1 つまたは 2 つのアンダースコア、いくつかのダッシュ、のいずれかです。 各名称のはじめと終わりにセパレーター文字を用いることはできません。

イメージとは、マニフェストと複数レイヤーによって構成されるものです。 マニフェストとレイヤーに関しては、この時点で深く気にとめておく必要はありません。 「タグ」によってこういった構成要素が結び合わせれている、と捉えておくだけで十分です。 イメージに対しては複数のタグを設定できます。 作り上げてきたイメージに対する 2 つめのタグを生成します。 そしてそのレイヤー構成を見てみます。

イメージに対して新たなタグを生成するには、以下のコマンドを実行します。

$ docker tag python-docker:latest python-docker:v1.0.0

docker tagコマンドはイメージに対するタグを生成します。 新たなイメージが作り出されるわけではありません。 このタグもまた同じイメージを指していて、イメージを参照するもう 1 つの手段が出来上がったことになります。

そこでdocker imagesコマンドを実行して、ローカルイメージの一覧を確認します。

$ docker images
REPOSITORY      TAG               IMAGE ID       CREATED         SIZE
python-docker   latest            8cae92a8fbd6   4 minutes ago   123MB
python-docker   v1.0.0            8cae92a8fbd6   4 minutes ago   123MB
python          3.8-slim-buster   be5d294735c6   9 days ago      113MB

python-dockerで始まるイメージが一覧に 2 つ表示されています。 IMAGE ID列を見てみれば、その 2 つのイメージは同一のものであることがわかります。 2 つのイメージの ID 値は同じだからです。

では今生成したタグを削除してみます。 これを行うにはrmiコマンドを使います。 rmiコマンドは「remove image」を表しています。

$ docker rmi python-docker:v1.0.0
Untagged: python-docker:v1.0.0

Docker の出力結果からわかるように、イメージは削除されたわけではなく「タグづけ解除」が行われただけです。 docker imagesコマンドを実行して確認してみます。

$ docker images
REPOSITORY      TAG               IMAGE ID       CREATED         SIZE
python-docker   latest            8cae92a8fbd6   6 minutes ago   123MB
python          3.8-slim-buster   be5d294735c6   9 days ago      113MB

:v1.0.0としてタグづけを行ったイメージは削除されましたが、マシン上にはpython-docker:latestというタグを通じてイメージが参照可能です。

次のステップ

本節ではサンプル Python アプリケーションの設定を行いました。 これはこの先のチュートリアルを通じて利用していきます。 また Dockerfile を生成して Docker イメージのビルドに利用しました。 そしてイメージへのタグづけ、タグづけ解除を行いました。 次節では以下のことを行います。

コンテナーとしてイメージ実行

フィードバック

本トピック改善のためにフィードバックをお寄せください。 お気づきの点があれば Docker Docs の GitHub リポジトリに issue をあげてください。 あるいは PR の生成 により変更の提案を行ってください。


python, build, images, dockerfile