「Dockerfile」を書いてコンテナを構築してみよう

2024年5月31日(金)
石本 達也
実践編第4回の今回は、前回解説した「VS Code」の「Dev Containers」ではなく、直接Dockerfileを書いてコンテナを構築する方法を解説します。

はじめに

前回は「Visual Studio Code」と「Dev Containers」を使って、簡単にコンテナ上に開発環境を構築する方法を紹介しました。

今回は、Dev Containerを活用せずに、「Dockerfile」を書いて直接コンテナを構築する方法を解説します。コンテナを立ち上げる方法にはいくつかありますが、一時的に利用する場合以外ではDockerfileを書いてコード化することが一般的です。

Dockerfileとは

Dockerfileとは、独自のDockerイメージを作成するための設定ファイルです。次の例のようにイメージを指定(FROM)したり、コンテナ内で実行するコマンド(RUN)を定義したりできます。これにより、ライブラリやツールのインストールからアプリケーションの実行まで、一連の作業を自動化できます。

Dockerfile
FROM python:3.9

WORKDIR /code

COPY ./requirements.txt /code/requirements.txt

RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt

COPY ./app /code/app

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を使うことにします。

  1. Dockerfileを作成します。
    mkdir dockerfile-tutorial
    cd dockerfile-tutorial
    touch Dockerfile
  2. 作成したDockerfileに、次の内容を記述します。
    Dockerfile
    FROM python:3.9
  3. ファイルが作成できたら、次にdocker buildコマンドでイメージをビルドします。
    docker build -t my-python-app .
    -tオプションはイメージにタグを付けるためのものです。my-python-appという名前のイメージを作成します。
  4. イメージのビルドが完了したら、コンテナを起動します。
    docker run -it --rm my-python-app bash
    -it(-i -tの省略形)オプションはコンテナを対話的に起動するためのものです。--rmオプションはコンテナを終了したら削除するためのものです。
  5. コンテナが起動したら、環境が起動します。
    Pythonの環境が起動していることを確認できます。

Dockerfileの書き方(応用編)

もう少し複雑なDockerfileを作成してみましょう。今回は、PythonのWebフレームワークであるFastAPIを使ってAPIサーバーを立ち上げるDockerfileを作成します。FastAPIの公式サイトに例があるので、今回はこれを使用します。

なお、ファイルの中で「Poetry」というパッケージマネージャを使っていますが、ここでは本ツールの詳しい使い方は説明しません。詳しくは公式サイトを参照してください。

FROM python:3.9 as requirements-stage

WORKDIR /tmp

RUN pip install poetry
 
COPY ./pyproject.toml ./poetry.lock* /tmp/

RUN poetry export -f requirements.txt --output requirements.txt --without-hashes

FROM python:3.9

WORKDIR /code

COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt

RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt

COPY ./app /code/app

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]

Dockerfileを書くときには様々なテクニックが活用されますが、ここでは2つのテクニックを使っています。

マルチステージビルド

マルチステージビルドは、イメージにレイヤーを追加してイメージサイズを小さくするためのテクニックです。先ほど紹介したサンプルコード上にrequirements-stageという文字がありますが、これがマルチステージビルドの記述です。前のステージで生成された必要な成果物のみを次のステージにコピーすることで、不要なファイルを含めないようにします。

FROM python:3.9 AS requirements-stage

# 1つ目のレイヤーの処理

FROM python:3.9

# 2つ目のレイヤーの処理

Dockerキャッシュの活用

Dockerはビルド時にキャッシュを使って高速化を図ります。COPYRUNなどの命令が変更されない限りキャッシュが使われます。

例えば、requirements.txtが変更されない限り、pip installの処理はキャッシュされます。キャッシュの世界は、ひと言では語り尽くせないほど奥が深いので、興味がある方は実際に試したり、調べたりしてみてください。

アプリケーションを動かしてみよう

Dockerfileを書いたら、次にアプリケーションを動かしてみましょう。公式サイトのドキュメントに簡単なアプリケーションの例があるので、それを使ってアプリケーションを動かしてみます。

.  
├── app  
│   ├── __init__.py  
│   └── main.py  
├── Dockerfile
└── pyproject.toml  
  1. app/__init__.pyを作成します。このファイルには何も書かなくても大丈夫です。
  2. app/main.pyを作成します。各コードの詳しい説明は公式サイトのドキュメントを参照してください。
    from typing import Union
    
    from fastapi import FastAPI
    
    app = FastAPI()
    
    
    @app.get("/")
    def read_root():
        return {"Hello": "World"}
    
    
    @app.get("/items/{item_id}")
    def read_item(item_id: int, q: Union[str, None] = None):
        return {"item_id": item_id, "q": q}
  3. pyproject.tomlを作成します。このファイルはPoetryのコマンドを実行することで生成できます。今回は、次の内容をコピーして作成します。
    [tool.poetry]
    name = "dockerfile-tutorial"
    version = "0.1.0"
    description = ""
    authors = ["Your Name "]
    readme = "README.md"
    
    [tool.poetry.dependencies]
    python = "^3.9"
    fastapi = "^0.110.1"
    uvicorn = {extras = ["standard"], version = "^0.29.0"}
    
    
    [build-system]
    requires = ["poetry-core"]
    build-backend = "poetry.core.masonry.api"
    app/__init__.pyapp/main.pypyproject.tomlの3つのファイルが作成できたら準備完了です。
  4. Dockerfileをビルドしてコンテナを起動します。ビルドコマンドは初めに実行したコマンドと同じですが、次のコマンドを実行します。
    docker build -t my-python-app .
  5. ビルドが完了したら、次にコンテナを起動します。
    docker run -it --rm -p 80:80 my-python-app
    -pオプションはホストとコンテナのポートをマッピングするためのものです。今回はホストの80番ポートをコンテナの80番ポートにマッピングしています。ホスト側ポートを8080番に変更したい場合は、-p 8080:80と指定します。
  6. 無事に起動できたかどうかを確認してみましょう。FastAPIには標準でSwagger UIが付属しているので、ブラウザでhttp://localhost/docsにアクセスしてみてください。次のような画面が表示されれば成功です。

    おわりに

    Dockerfileを書くことで、コンテナ環境の構築を自動化できます。また、Dockerfileを使うことでコードとして管理できるため、チーム開発での共有やバージョン管理がしやすくなります。

    なお、Dockerfileの書き方は様々ありますが、基本的な書き方を覚えておくと、コンテナの環境構築がスムーズになります。Dockerfileを使ったコンテナ開発に慣れてきたら、次のステップとしてイメージサイズやキャッシュ活用などにチャレンジしてみてください。

日本仮想化技術株式会社
Sierやベンチャー企業を経て、現在は日本仮想化技術でDevOps支援サービス「かんたんDevOps」のDev側を担当。「DevOpsを通じて開発者体験を最大化する」をミッションに理想的な開発環境の実現を目指して技術調査や仕組み作りを行っている。

連載バックナンバー

設計/手法/テスト技術解説
第25回

AWSの監視サービス「CloudWatch」でサーバー監視を試してみよう

2024/8/9
本連載も今回で最終回となります。今回は、AWSの監視サービス「CloudWatch」を使って、簡単なサーバー監視を試してみましょう。
設計/手法/テスト技術解説
第24回

CI環境を構築して「ESLint」で静的解析を実行してみよう

2024/7/26
実践編第8回の今回は、「Dev Containers」でCI環境を構築し、静的解析ツール「ESLint」で静的解析を実行するまでの流れを解説します。
設計/手法/テスト技術解説
第23回

テストコードを書いて「GitHub Actions」でCIを実行してみよう

2024/7/12
実践編第7回の今回は、Webフロントエンド開発を例に、テストコードを書いて「GitHub Actions」でCIを実行するまでの流れを解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています