はじめに
前回は「Visual Studio Code」と「Dev Containers」を使って、簡単にコンテナ上に開発環境を構築する方法を紹介しました。
今回は、Dev Containerを活用せずに、「Dockerfile」を書いて直接コンテナを構築する方法を解説します。コンテナを立ち上げる方法にはいくつかありますが、一時的に利用する場合以外ではDockerfileを書いてコード化することが一般的です。
Dockerfileとは
Dockerfileとは、独自のDockerイメージを作成するための設定ファイルです。次の例のようにイメージを指定(FROM
)したり、コンテナ内で実行するコマンド(RUN
)を定義したりできます。これにより、ライブラリやツールのインストールからアプリケーションの実行まで、一連の作業を自動化できます。
06 | COPY ./requirements.txt /code/requirements.txt |
08 | RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt |
12 | CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] |
Dockerfileの書き方(基本編)
まずはイメージを指定します。イメージを取得する先のコンテナレジストリはDocker Hubを使用することが一般的ですが、最近では「GitHub Container Registry」や「AWS ECR」なども使われています。
今回はDocker Hubを使います。Docker Hubにアクセスして検索バーにpython
と入力すると、Pythonの公式イメージが表示されます。ここでは、最新のPython 3.9を使うことにします。
- Dockerfileを作成します。
1 | mkdir dockerfile-tutorial |
- 作成したDockerfileに、次の内容を記述します。
- ファイルが作成できたら、次に
docker build
コマンドでイメージをビルドします。
1 | docker build -t my-python-app . |
-t
オプションはイメージにタグを付けるためのものです。my-python-app
という名前のイメージを作成します。
- イメージのビルドが完了したら、コンテナを起動します。
1 | docker run -it --rm my-python-app bash |
-it
(-i -t
の省略形)オプションはコンテナを対話的に起動するためのものです。--rm
オプションはコンテナを終了したら削除するためのものです。
- コンテナが起動したら、環境が起動します。
Pythonの環境が起動していることを確認できます。
Dockerfileの書き方(応用編)
もう少し複雑なDockerfileを作成してみましょう。今回は、PythonのWebフレームワークであるFastAPIを使ってAPIサーバーを立ち上げるDockerfileを作成します。FastAPIの公式サイトに例があるので、今回はこれを使用します。
なお、ファイルの中で「Poetry」というパッケージマネージャを使っていますが、ここでは本ツールの詳しい使い方は説明しません。詳しくは公式サイトを参照してください。
01 | FROM python:3.9 as requirements-stage |
07 | COPY ./pyproject.toml ./poetry.lock* /tmp/ |
09 | RUN poetry export -f requirements.txt --output requirements.txt --without-hashes |
15 | COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt |
17 | RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt |
21 | CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] |
Dockerfileを書くときには様々なテクニックが活用されますが、ここでは2つのテクニックを使っています。
マルチステージビルド
マルチステージビルドは、イメージにレイヤーを追加してイメージサイズを小さくするためのテクニックです。先ほど紹介したサンプルコード上にrequirements-stage
という文字がありますが、これがマルチステージビルドの記述です。前のステージで生成された必要な成果物のみを次のステージにコピーすることで、不要なファイルを含めないようにします。
1 | FROM python:3.9 AS requirements-stage |
Dockerキャッシュの活用
Dockerはビルド時にキャッシュを使って高速化を図ります。COPY
やRUN
などの命令が変更されない限りキャッシュが使われます。
例えば、requirements.txt
が変更されない限り、pip install
の処理はキャッシュされます。キャッシュの世界は、ひと言では語り尽くせないほど奥が深いので、興味がある方は実際に試したり、調べたりしてみてください。
アプリケーションを動かしてみよう
Dockerfileを書いたら、次にアプリケーションを動かしてみましょう。公式サイトのドキュメントに簡単なアプリケーションの例があるので、それを使ってアプリケーションを動かしてみます。
app/__init__.py
を作成します。このファイルには何も書かなくても大丈夫です。
app/main.py
を作成します。各コードの詳しい説明は公式サイトのドキュメントを参照してください。
01 | from typing import Union |
03 | from fastapi import FastAPI |
10 | return {"Hello": "World"} |
13 | @app.get("/items/{item_id}") |
14 | def read_item(item_id: int, q: Union[str, None] = None): |
15 | return {"item_id": item_id, "q": q} |
pyproject.toml
を作成します。このファイルはPoetryのコマンドを実行することで生成できます。今回は、次の内容をコピーして作成します。
02 | name = "dockerfile-tutorial" |
05 | authors = ["Your Name <you>"] |
08 | [tool.poetry.dependencies] |
11 | uvicorn = {extras = ["standard"], version = "^0.29.0"} |
15 | requires = ["poetry-core"] |
16 | build-backend = "poetry.core.masonry.api"</you> |
app/__init__.py
とapp/main.py
、pyproject.toml
の3つのファイルが作成できたら準備完了です。
- Dockerfileをビルドしてコンテナを起動します。ビルドコマンドは初めに実行したコマンドと同じですが、次のコマンドを実行します。
1 | docker build -t my-python-app . |
- ビルドが完了したら、次にコンテナを起動します。
1 | docker run -it --rm -p 80:80 my-python-app |
-p
オプションはホストとコンテナのポートをマッピングするためのものです。今回はホストの80番ポートをコンテナの80番ポートにマッピングしています。ホスト側ポートを8080番に変更したい場合は、-p 8080:80
と指定します。
無事に起動できたかどうかを確認してみましょう。FastAPIには標準でSwagger UIが付属しているので、ブラウザでhttp://localhost/docs
にアクセスしてみてください。次のような画面が表示されれば成功です。
おわりに
Dockerfileを書くことで、コンテナ環境の構築を自動化できます。また、Dockerfileを使うことでコードとして管理できるため、チーム開発での共有やバージョン管理がしやすくなります。
なお、Dockerfileの書き方は様々ありますが、基本的な書き方を覚えておくと、コンテナの環境構築がスムーズになります。Dockerfileを使ったコンテナ開発に慣れてきたら、次のステップとしてイメージサイズやキャッシュ活用などにチャレンジしてみてください。