7rikazhexde’s tech log

技術的な興味関心、備忘録、アウトプットなどを書いています。

既存のPoetry環境をベースにしたDocker環境の構築方法について

はじめに

Docker上でPoetryの依存ファイルを使用した環境を構築したいと思い調査しました。
本記事はその手順の備忘録となります。

使用するプロジェクト

以下のプロジェクトを例に説明します。 詳細は割愛しますが、pythonでdashフレームワークを使用したwebアプリのプロジェクトになります。現状はpoetryコマンドで実行環境を構築できるようにしていますが、これをdocker環境でも構築できるようにします。

github.com

方針

作業ディレクトリにプロジェクトと同じフォルダ(dlSubscanStakingRewardsHistoryDash)を構築します。
そして、Docker環境ではアプリケーションの実行で必要となる後述するソースコード類(パッケージ管理ファイルも含む)が格納されたフォルダ、および、ファイルをコピーして同期することにします。

フォルダ構成

ソースコードapp,ci,testフォルダに格納されています。
メインのプログラムはappフォルダ内のコードで、プロジェクト以下でpoetry run python appを実行することでDashフレームワークのWebアプリが起動します。

# 以下は一部記載を省略しています。
% tree -a -L 2
.
├── .DS_Store
├── .demofile
├── .git
├── .gitignore
├── .markdownlint.jsonc
├── .mypy_cache
├── .pre-commit-config.yaml
├── .ruff_cache
├── .venv
├── .vscode
├── Dockerfile
├── LICENSE
├── README.md
├── app
│   ├── __init__.py
│   ├── __main__.py
│   ├── __pycache__
│   ├── assets
│   ├── config.toml
│   ├── config_manage.py
│   ├── cryptact.py
│   ├── dcc_manage.py
│   ├── df_manage.py
│   └── subscan.py
├── ci
│   ├── run_git_tag_base_pyproject.py
│   └── update_pyproject_version.py
├── docker-compose.yaml
├── poetry.lock
├── pyproject.toml
├── requirements-dev.txt
├── requirements.txt
├── scripts
│   └── create_post-commit.sh
└── tests
    ├── __init__.py
    └── test_cryptact.py

続けて方針に従いDockerfiledocker-compose.yamlを作成します。

Dockerfile

# ベースとなるDockerイメージを指定
FROM python:3.10

# 作業ディレクトリを設定
WORKDIR /

# APTパッケージインストール / パッケージアーカイブのキャッシュ削除 / パッケージリストのキャッシュ削除
# 日本語のロケールを指定
RUN apt-get update && \
    apt-get -y --no-install-recommends install locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9

# ターミナルエミュレーターにxtermを指定する
ENV TERM xterm

# Poetryのインストール
## https://python-poetry.org/docs/#installing-with-the-official-installer
### Linux, macOS, Windows (WSL)
RUN curl -sSL https://install.python-poetry.org | python3 -

# Poetryのパスをunix指定で設定
## https://python-poetry.org/docs/#installing-with-the-official-installer
### Add Poetry to your PATH
ENV PATH /root/.local/bin:$PATH

# Poetryが仮想環境を生成しないようにする
RUN poetry config virtualenvs.create false

# 作業ディレクトリを/dlSubscanStakingRewardsHistoryDashに設定
WORKDIR /dlSubscanStakingRewardsHistoryDash

# 依存関係のコピー
COPY pyproject.toml poetry.lock ./

# 依存関係のインストール
RUN poetry install

# アプリケーションのコピー
COPY ./app ./app
COPY ./tests ./tests
COPY ./ci ./ci
COPY README.md .

補足: Dockerfile用のlinterについて

pre-commitではpre-commit hookとして以下のDockerfile linterが公開されています。
Dockerfileの構文や指定について指摘されるため、pre-commitを利用している場合は下記指定で使用できます。 実際に、Dockerfileはフックの結果を元に作成しています。個人的には有用だと感じました。

.pre-commit-config.yaml

  # https://github.com/pryorda/dockerfilelint-precommit-hooks#usage
  - repo: https://github.com/pryorda/dockerfilelint-precommit-hooks
    rev: v0.1.0
    hooks:
      - id: dockerfilelint
        stages: [commit]

docker-compose.yaml

# Docker Composeのバージョン指定
version: '3'
services:
  # アプリケーションサービスの設定
  app:
    # カレントディレクトリをベースにビルド
    build: .
    # コンテナの名前を指定
    container_name: 'dlssrhd'
    # ターミナルの割り当てを有効にする
    tty: true
    # ボリュームのマウント設定
    volumes:
      - ./app:/dlSubscanStakingRewardsHistoryDash/app
      - ./tests:/dlSubscanStakingRewardsHistoryDash/tests
      - ./ci:/dlSubscanStakingRewardsHistoryDash/ci
      - ./pyproject.toml:/dlSubscanStakingRewardsHistoryDash/pyproject.toml
      - ./poetry.lock:/dlSubscanStakingRewardsHistoryDash/poetry.lock
      - ./README.md:/dlSubscanStakingRewardsHistoryDash/README.md
    # ポートのマッピング
    ports:
      - 8050:8050
    # アプリケーションの起動コマンド実行
    command: ["poetry", "run", "python", "app"]

補足: docker compose ファイルが有効かどうかをチェックするpre-commit

pre-commitではpre-commit hookとして以下のdocker-pre-commitが公開されています。
docker-compose configコマンドを使用して、作成したdocker-compose.yamlについて環境変数の評価や、ファイルのインクルードなど、Composeファイルが正しく構成されているかどうかを確認するとができます。pre-commitを利用している場合は下記指定で使用できます。 実際に、docker-compose.yamlに適用し、指摘はありませんでしたが、docker-compose configをpre-commitで実行できるので有用だと思います。

.pre-commit-config.yaml

  # https://github.com/IamTheFij/docker-pre-commit#installation
  - repo: https://github.com/iamthefij/docker-pre-commit
    rev: v3.0.1
    hooks:
      - id: docker-compose-check

Docker環境の構築

docker-compose upappサービスのdlssrhdコンテナを起動する

処理の流れ

  1. docker-compose upコマンド実行:

    • docker-compose upコマンドを実行すると、Docker Composeは指定されたdocker-compose.yamlを読み込みます。
  2. ベースとなるDockerイメージのダウンロード:

    • python:3.10というベースイメージがなければ、このイメージをDocker Hubからダウンロードします。
  3. Dockerfileのビルド:

    • ダウンロードしたベースイメージを元に、指定されたDockerfileがビルドされます。
    • ビルドの際には、各命令が順番に実行されます。
  4. 作業ディレクトリとロケールの設定:

  5. Poetryのインストール:

  6. Poetryのパスの設定:

    • インストールしたPoetryのパスを環境変数に追加します。
  7. Poetryの仮想環境生成設定:

    • Poetryが仮想環境を生成しないように設定します。
  8. 作業ディレクトリの変更:

    • 以降の作業は、/dlSubscanStakingRewardsHistoryDashディレクトリで行います。
  9. 依存関係のコピーとインストール:

    • pyproject.tomlpoetry.lockがコピーされ、Poetryを使用して依存関係がインストールされます。
  10. アプリケーションのコピー:

    • ./app./tests./ciREADME.mdがコピーされます。
  11. ポートのバインドとコンテナの起動:

    • docker-compose.yamlで指定されたポート8050がホストマシンのポート8050にバインドされ、dlssrhdという名前のコンテナが起動されます。
  12. Pseudo-TTYの割り当て:

    • tty: trueが指定されているため、コンテナ内で対話的なシェルを使用できるようになります。
  13. docker-compose.yamlのcommandの指定:

    • docker-compose.yamlcommand: ["poetry", "run", "python", "app"]の指定からデフォルトのコンテナ起動時のコマンドとして使用されます。

これにより、docker-compose upコマンドによって、指定されたサービス(appサービス)に基づいたコンテナが構築、起動され、アプリケーションが実行されます。

補足:CMDcommandの使い分け

プロジェクトの運用方針にもよりますが、CMDcommandの両方を使用する場合はdocker-compose.yamlcommandが優先されるようなので、個人的にはcommandでコマンド実行するのが良いと思います。

参考記事

qiita.com

  1. CMDのみを使用する場合:

    • CMD ["poetry", "run", "python", "app"]
      • コンテナが実行されるときに、何もコマンドが指定されなかった場合はCMDで指定されたコマンドが実行されます。
  2. commandのみを使用する場合:

    • command: ["poetry", "run", "python", "app"]
      • docker-compose.yamlcommandでコンテナの起動時のコマンドを上書きします。
      • CMDにコマンドの指定がある場合でも、docker-compose.yamlcommandで指定したコマンドが優先されます。
  3. CMDとcommandの両方を使用する場合:

    • CMD ["poetry", "run", "python", "app"]
    • command: ["poetry", "run", "python", "app"]
      • この場合、docker-compose.yamlcommandが優先されます。

補足:コンテナの起動方法

参考記事

qiita.com

コンテナ一覧を表示

% docker ps -a                
CONTAINER ID   IMAGE                                    COMMAND                   CREATED       STATUS                      PORTS                    NAMES
5c41a6c6a8ca   dlsubscanstakingrewardshistorydash-app   "poetry run python a…"   5 hours ago   Up 22 seconds               0.0.0.0:8050->8050/tcp   dlssrhd

単一コンテナ起動: docker start "CONTAINER ID"

 % docker start 5c41a6c6a8ca           

5c41a6c6a8ca

複数コンテナ(アプリケーション)の起動: docker compose up

appサービスのdlssrhdコンテナを起動する

% docker compose up
[+] Building 0.0s (0/0)                                                                                        docker:desktop-linux
[+] Running 1/0
 ✔ Container dlssrhd  Created                                                                                                  0.0s 
Attaching to dlssrhd
dlssrhd  | Skipping virtualenv creation, as specified in config file.
dlssrhd  | Dash is running on http://0.0.0.0:8050/
dlssrhd  | 
dlssrhd  |  * Serving Flask app '__main__'
dlssrhd  |  * Debug mode: on

# Ctrl+Cで停止
^CGracefully stopping... (press Ctrl+C again to force)
Aborting on container exit...
[+] Stopping 1/1
 ✔ Container dlssrhd  Stopped                                                                                                  0.3s 
canceled

# -d(デタッチドモード) でバックグラウンドでサービスを実行
% docker compose up -d     
[+] Building 0.0s (0/0)                                                                                        docker:desktop-linux
[+] Running 1/1
 ✔ Container dlssrhd  Started 

まとめ

実際のプロジェクトを例にPoetryを使用したDocker環境の構築方法について紹介しました。

環境構築について、プロジェクトではフォルダ構成を維持して、全てのファイルをコピーせず、必要なファイルをコピーしたかったので、実際に作成することで処理の流れを理解することができました。

Docker環境のインストールは必要ですが、実際にMacWindows(WSL)でgitクローンしてdocker-compose upコマンドを実行してwebアプリを起動できることを確認できました。

今後や他のプログラミング言語やDBと組み合わせたサービスについても、調査して作成してみたいと思います。

以上です。