最近業務でDockerを使用する機会が多く、あまりの便利さに感動しているので調査した内容を備忘録的にまとめておこうと思いました。
本記事では、Dockerを使って、アプリを別環境で動かせるようになるまでを記載しています。
Dockerとは
Dockerとは「アプリの実行環境ごとパッケージして、どこでも同じように動かせるツール」です。
VM(仮想マシン)のようなイメージですが、VMとは異なります。
コンテナごとにOSがあるのではなく、OSはあくまでも1つで、コンテナ毎にOSを共有しています。
Docker用語説明
-
イメージ:
アプリを丸ごとパッケージ化した状態のこと。
イメージ自体はアプリとして動作しません。 -
コンテナ:
イメージを実際に起動した「動いている状態」のこと。
つまり、「イメージを作って、コンテナにする」ような感じです。
具体的に言うと、
- イメージはdocker build / docker compose buildで作成され、
- コンテナは docker run / docker compose up で起動します。
Dockerの便利なところ
アプリを動かすためにサーバーそのものに実行環境をインストールする必要がない。
今回、ローカル環境でPython3.12を使ってアプリを作りました。
これを本番環境で動かすためには、本番環境にもPython3.12をインストールする必要があります。
-
Dockerを利用しない場合
サーバーに直接Python3.12のインストールが必要。
→ 本番環境が汚れたり、別のアプリと競合する可能性があります。 -
Dockerを利用する場合
サーバーにはDockerだけインストールされていればOK。
→ イメージの中にPython3.12が入っているため、本番環境が汚れず、ほかのアプリとも競合しません。
開発環境では動くのに本番環境で動かない問題が起こらない
- 開発PCと本番環境で、異なるバージョンで開発してしまった場合
開発PC 本番サーバー
Python 3.12 Python 3.8 ← バージョンが違う
requests 2.31 requests 2.28 ← ライブラリのバージョンが違う
→ 開発環境では動くのに本番で動かない問題が発生してしまう。
イメージの中にPythonやライブラリが入っているため、本番環境にインストールされているバージョンに依存しません。
使い方も簡単
開発環境・本番環境にDockerをインストールし、アプリのディレクトリ配下にDockerfileを定義するだけで基本的には準備完了です。
イメージの準備
アプリケーションのディレクトリ配下にDockerfileを準備します。
Dockerfileとは?
「どんな環境でアプリを動かすか」を定義するファイルで、イメージを作るための設計図となります。
Dockerfile に実行環境を定義することで、どの環境でも同じコンテナが起動し、開発環境・本番環境・他のサーバーで同じように動かすことが可能となります。
例えばgitに上げる際にもDockerfileを入れておけば、他の人がどの環境でクローンしてもアプリを動かすことができるようになります。
Dockerfileを準備する
アプリケーションの中にDockerfileとrequirements.txtを作成します。
sample_app
└─sample.py
└─requirements.txt
└─Dockerfile
└─.dockerignore
└─docker-compose.yml
まずDockerfileにイメージを作る手順を記載します。
各命令ごとにレイヤーと呼ばれる層が作られます。
Dockerfileは上から順に実行され、変更があったレイヤー以降のみ再ビルドされます。
基本的に以下の手順でOKです。
#手順① ベース環境を用意する
FROM python:3.12-slim
#手順② 作業ディレクトリを作る
WORKDIR /app
#手順③ requirements.txtをコピーする
COPY requirements.txt .
#手順④ ライブラリをインストールする
# requirements.txtが変更されない限りキャッシュが使われるため高速
RUN pip install -r requirements.txt
#手順⑤ カレントディレクトリの全ファイルを /app にコピー
COPY . .
#手順⑥ 起動時のコマンドを定義する(ビルド時は実行されない)
CMD ["python", "app.py"]
requirements.txtとは?
requirements.txtとは、Pythonアプリが動くのに必要なライブラリ一覧を定義するものです。
sample_app配下で以下のコマンドを実行し、出力された結果を記載すればOKです。
これは「現在インストールされているPythonパッケージのリストを、パッケージ名==バージョンの形式で出力する」というコマンドです。
pip freeze
その他、定義したほうが良いもの
.dockerignore
.dockerignoreとは、ビルド時に無視するファイルを定義することができるファイルです。
DockerfileのCOPY . . によって、カレントディレクトリのファイルがイメージにコピーされるため、除外したいファイルは.dockerignoreに記載しましょう。
以下のようなファイルは除外することを強くおすすめします。
-
.env
パスワードやAPIキーなどの機密情報が含まれる可能性があるため。 -
venv/
ローカルのPython仮想環境。
Docker内で別途環境を構築するため不要です。含めるとイメージが肥大化します。 -
__pycache__/
Pythonが勝手に作成するキャッシュディレクトリ。イメージの中に不要なキャッシュが含まれてしまう。 -
logファイル
実行時に生成されるログファイル。
docker-compose.yml
Dockerコンテナを起動する際のコマンドや設定を簡単にまとめられるため、docker-compose.yml を定義しておくことをおすすめします。
ビルド時の話になりますが、docker runで起動する場合はオプションが多いと以下のようになります。
docker run -p 8000:8000 \
-v $(pwd):/app \
--env-file .env \
sample_app:latest
オプションが増えると管理しづらく、起動のたびに毎回長いコマンドを打つ必要があります。
docker-compose.yml に設定を書いておくことで、起動に必要な設定をまとめて管理でき、コマンドも短くて済みます。
services:
app:
build: .
ports:
- "8000:8000"
volumes:
- .:/app
env_file:
- .env
-
volumes
ホスト側のファイルやディレクトリをコンテナ内にマウントする設定。
基本的にイメージに含まれるファイルを修正したら、再度ビルドをしてイメージを再作成する必要がありますが、設定ファイルなどを頻繁に更新する場合はvolumes:に記載することで、コンテナ起動時に外から渡すファイルとして扱ってくれます。- マウントとは
ボリュームでホスト側のファイルをコンテナ内に「繋げる」操作のことです。
マウントすることでホスト側のファイルを変更するとコンテナ内にも即反映されます。
- マウントとは
-
env_file
ビルド時ではなく、コンテナ起動時に読み込まれるファイルを定義する。
ビルド・実行
本番環境に資材をコピーしたあとの手順です。
ビルドについては簡単に触れていますが、今回はあまり詳しく扱っていません。
オプション等については必要に応じて調べてみてください。
docker-compose.ymlを利用しない場合
docker-compose.ymlを利用しない場合は、ビルド→実行という流れになります。
# ビルド
cd sample_app
docker build
# 実行
docker run -p 8000:8000 sample_app #sample_appはイメージ名
ビルド時はDockerfileが存在するディレクトリ配下で実行する必要がありますが、
docker run の場合はイメージ名を指定するだけなので、どこからでも実行できます。
docker-compose.ymlを利用する場合
docker-compose.ymlを使うと、ビルドから実行まで一括で実行できます。
※ただし、すでにビルド済みのイメージがある場合は、実行だけが行われます。
cd sample_app
docker compose up
基本的にはdocker-compose.ymlがあるディレクトリで実行するのが一般的ですが、cronに設定する場合などはオプションを利用することでファイルの場所を指定することができます。
docker compose -f /sample_app/docker-compose.yml up
起動前に再ビルドしたい場合は、末尾に--buildを付けます。
docker compose up --build
おわりに
以上が、PythonアプリをDockerで動かすときの基本的な手順です。
とりあえず動かすところまでまとめてみました。
あとは必要に応じてDockerfileやdocker-compose.ymlをカスタマイズしてみてください。