自分の Python イメージを構築

事前準備

Docker の概念を理解するため、導入ガイド Part 1 での説明を読み、準備をしてください。

BuildKit 有効化

イメージの構築を始める前に、マシン上で BuildKit を有効化します。 BuildKit があれば Docker イメージを効率的に構築できます。詳しい情報は、 BuildKit でイメージ構築 をご覧ください。

Docker Desktop 上のすべてのユーザは、デフォルトで BuildKit は有効化されています。Docker Desktop をインストール済みの場合、手動で BuildKit を有効化する必要はありません。 Linux 上で Docker を動かしている場合は、 BuildKit を有効化するため、環境変数を設定するか、 BuildKit がデフォルトになるよう設定を変更します。

docker build コマンドの実行時、 BuildKit 環境編集を設定するには、次のように実行します。

$ DOCKER_BUILDKIT=1 docker build .

Docker で BuildKit をデフォルトで有効化するには、 /etc/docker/daemon.json のデーモン設定で機能を true にし、デーモンを再起動します。 daemon.json ファイルが存在しなければ、 daemon.json という名前でファイルを作成し、ファイル内に以下を追加します。

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

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

概要

これでコンテナと Docker プラットフォームの概要が分かりましたので、初めてのイメージを構築しましょう。イメージにはアプリケーションの実行に必要な全てを含みます。具体的には、コードやバイナリ、ランタイム、依存関係、必要なその他のファイルシステムです。

このチュートリアルを終えるには、以下が必要です。

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

例として使える Flask フレームワークを使い、簡単な Python アプリケーションを作りましょう。ローカルマシン内に 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 build コマンドを実行し、 Docker に対してイメージ構築を命令すると、 Docker はこれらの命令を読み込み、命令を実行し、その結果を Docker イメージとして作成します。

アプリケーションのために Dockerfile を作成する流れを見ていきましょう。プロジェクトのルートで、 Dockerfile という名前のファイルを作成し、このファイルをテキストエディタで開きます。

注釈

Dockerfile の名前はどうしますか?

Dockerfile のデフォルトファイル名は Dockerfile です(拡張子はありません)。デフォルトの名前を使えば、 docker build コマンドの実行し、コマンドにフラグの追加が不要です。

プロジェクトによっては、特定の目的に対して Dockerfile を分ける必要があるでしょう。一般的な慣習として、名前を Dockerfile.<何か><何か>.Dockerfile にします。このような Dockerfile は docker build コマンドで --file ( や省略形 -f` )オプションを渡して利用できます。 --file オプションについて学ぶには、 docker build リファレンスの Dockerfile の指定(-f) を参照ください。

このガイドの大部分の例でも使われているように、プロジェクトで主となる Dockerfile には、デフォルト( Dockerfile )の利用を推奨します。

Dockerfile の1行目に追加するのは、 # syntax パーサ ディレクティブ です。この命令は「オプション」ですが、Docker ビルダがどの Dockerfile を使って解釈するかを指定できます。さらに、古い BuildKit が入っている Docker のバージョンで構築する前に、アップグレードをできるようにします。 パーサ ディレクティブ は、 Dockerfile 内であらゆるコメント、空白、 Dockerfile より前に書く必要があるため、 Dockerfile では1行目に書くべきです。

# syntax=docker/dockerfile:1

私たちは docker/dockerfile:1 の指定を推奨します。これは、バージョン1構文の最新リリースを常に示します。 BuildKit は構築前、自動的に構文を確認するため、直近の現行バージョンを使えるようにします。

次は、Docker にアプリケーションが何のベースイメージを使うかを伝えるため、 Dockerfile に行を追加する必要があります。

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

Docker イメージは他のイメージを 継承(inherit) できます。そのため、自分でベースイメージを作成するのではなく、公式の Python イメージを使います。イメージには Python アプリケーションの実行に必要なツールとパッケージが全て入っています。

注釈

自分でベースイメージを作成する方法についての情報は ベース イメージの作成 をご覧ください。

以降のコマンドを実行しやすくるため、作業ディレクトリを作成しましょう。この命令は、以降すべてのコマンドを実行するデフォルトの場所として、指定したパスを使うよう Docker に対して伝えます。この方法によりフルパスを入力する必要がなくなりますが、その作業ディレクトリを基準とした相対パスで記述する必要があります。

WORKDIR /app

通常、 Node.js で書かれたプロジェクトをダウンロードして最初にするのは、 pip パッケージのインストールです。これにより、アプリケーションが必要とするすべての依存関係がインストールされます。

pip3 install を実行する前に、 requirements.txt ファイルをイメージの中に入れる必要があります。そのためには COPY 命令が使えます。 COPY 命令は2つのパラメータ、 srcdest を使います。1つめのパラメータ src は、 Docker に対して何のファイル(群)をイメージにコピーするかを伝えます。2つめのパラメータ dest は、 Docker に対してファイル(群)をどこにコピーしたいか伝えます。作業ディレクトリ内へ requirements.txt をコピーします。

COPY requirements.txt requirements.txt

既に requirements.txt ファイルはイメージ内ですので、 RUN 命令を使って pip3 install を実行できるようになります。これは、自分のマシン上でローカルに pip3 install を実行するのと全く同じ挙動です。ですが、今回は各モジュールはイメージ内へインストールされます。

RUN pip3 install -r requirements.txt

この時点で、私たちのイメージは Python バージョン 3.8 をベースにし、必要となる依存関係をインストールしました。次に必要なのは、ソースコードをイメージの中に追加します。先ほど package.json ファイルで行ったように、 COPY コマンドを使います。

COPY . .

この COPY コマンドは、現在のディレクトリ内にある全てのファイルを取得し、すべてをイメージの中にコピーします。次は、イメージの実行時、コンテナ内で実行したいコマンドが何かを Docker に伝える必要があります。これを 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 を使用し、イメージを構築するための命令を含む Dockerfile を作成しました。Python アプリケーションのディレクトリ構成は、次のようになります。

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

イメージ構築

これで Dockerfile が作成できましたので、イメージを構築しましょう。そのためには docker build コマンドを使います。 docker build コマンドは Dockerfile と "コンテクスト" からイメージを構築します。構築コンテクストとは、指定したパスまたは URL 内に置かれているファイル群です。 Docker 構築プロセスは、コンテクスト内に置かれているあらゆるファイルにアクセス可能です。

build コマンドは、オプションで --tag フラグを付けられます。 タグ(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 でイメージ一覧を見てみましょう。

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

$ docker images
REPOSITORY      TAG               IMAGE ID       CREATED         SIZE
python-docker   latest            8cae92a8fbd6   3 minutes ago   123MB

リストには少なくとも1つの構築したイメージ python-docker:latest が見えるでしょう。

イメージにタグ付け

イメージ名は、スラッシュ記号で区切られた名前の要素で構成されます。名前の要素には、小文字の文字列、数字、 セパレータ(separator) (区切り文字)を含みます。セパレータとして定義されているのは、ピリオド、1つまたは2つのアンダースコア、1つまたは2つのダッシュです。名前の要素では、初めと終わりにセパレータを使えません。

イメージは マニフェスト(manifest) と一連のレイヤによって構成されます。簡単に言うと、「タグ」が示すのは、これら アーティファクト(artifact) (訳者注:完成したイメージのこと。成果物)の組み合わせを示します。イメージは複数のタグを持てます。構築済みのイメージに2つめのタグを作成し、レイヤをみてみましょう。

先ほど構築したイメージに新しいタグを作成するには、以下のコマンドを実行します。

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

docker tag コマンドはイメージに新しいタグを作成しますが、新しいイメージは作成しません。タグが示すのは同じイメージであり、そのイメージを別の方法で参照しているだけです。

次は 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つのイメージの値は同じに見えますので、どちらも同じイメージだと分かります。

先ほど作成したタグを消しましょう。そのためには、 rmi コマンドを使います。rmi コマンドは「 イメージ削除(remove image) を表します。」

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

Docker の応答から分かるのは、イメージは削除しておらず、単に「 タグを削除済み(untagged) 」です。 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 アプリケーションの設定方法を説明しました。また、Docker イメージ構築に使う Dockerfile を作成しました。それから、イメージにタグをつけ、イメージからタグを削除する方法を説明しました。次の章では、コンテナとしてイメージを実行する方法を説明します。

フィードバック

フィードバックを通し、このトピックの改善を支援ください。考えがあれば、 Docker Docs GitHub リポジトリに issue を作成して教えてください。あるいは、更新の提案のために RP を作成 してください。