21
16

Poetry+DockerでのPython環境構築(2023年版)

Last updated at Posted at 2023-10-03

私が普段使っているPythonの開発環境の構築について説明します。DockerPoetryを利用して構築し、1人または少人数で利用することを想定した開発環境です。

本稿では、はじめに最低限の構成でJupyterLabを起動する方法までを説明します。後にオプションとしてVS CodeやGitHub Codespacesの各種設定やフォーマッター、テスト、API構築などのトピックを扱います。使っているエディタなどに合わせて、必要なところだけ参考にしてください。

なぜDocker+Poetryという構成が良いのか、他の選択肢にはどういった物があるのかという点については、Pythonの開発環境の3つの観点をおさえようという記事に詳しく説明されています。

実行環境

  • Debian 11.7
  • Docker version 24.0.6
  • Docker Compose version v2.21.0

Linux(ChromeOS)およびWindows 11 WSL2を想定しています。
ホストコンピュータにPythonやPoetryをインストールする必要はありません。

Python環境構築

poetry new

Poetryを使う場合、最初にpoetry newでプロジェクトを初期化する必要があります。ですがPoetryはPythonに依存しており、Python環境構築前にPoetryは使えません。
そのため、Dockerで一時的なコンテナを使ってpoetry newを実行します。

$ docker run \
  --volume $(pwd):/app \
  --workdir /app \
  python:3.10-slim bash -c \
  'pip install poetry && poetry new my_python_project && cd $_ && poetry install'
$ cd my_python_project

以下はお好みで変更してください。

  • Dockerイメージ(Pythonのバージョン) python:3.10-slim
  • プロジェクト名 my_python_project

Pythonのバージョンは、2023年10月時点で最新版である3.12がリリースされていますが、どのバージョンを選択するかはいくつかの方針があります。
最新版にすると最新の機能が利用できますが、有名なライブラリ等が対応していない時もあります。そういったトラブルを避ける必要がある場合はひとつ前のバージョンを選ぶのが安定すると思います。

方針 現時点のバージョン
最新版にする 3.12
最新版のひとつ前にする 3.11
Google Colabに合わせる 3.10

Gitの設定

$ git init .
$ wget --output-document=.gitignore https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore

お好みでlockファイルをバイナリ扱いにします。
賛否が分かれる設定だと思いますが、最近BunがLockファイルをバイナリとしたのもあって、一定の支持はある考え方のようです。

$ echo "poetry.lock binary" >> .gitattributes # お好みで

Dockerの設定

Docker Composeで起動する設定をします。

# 最初に構築したイメージと合わせる
FROM python:3.10-slim
WORKDIR /app

# お好みで好きなパッケージを追加
RUN apt-get update && apt-get install -y \
    build-essential \
    git \
    curl \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

RUN git config --global --add safe.directory /app

RUN pip install poetry \
  && poetry config virtualenvs.create false

COPY ./pyproject.toml ./poetry.lock* ./
RUN poetry install
compose.yaml
services:
  app:
    build: .
    volumes:
      - .:/app
    command: sleep infinity

ビルドしておきます。

$ docker compose build

パッケージのインストールと起動

次に、パッケージをインストールします。ここでは例としてJupyterLabをインストールし、起動設定をします。

$ docker compose run --rm app poetry add --group dev jupyterlab ipywidgets

compose.yamlに起動設定をします。JupyterLabしか起動しないコンテナなのであれば、compose.yamlではなくDockerfileCMDで指定する方が良いと思います。

compose.yaml
services:
 app:
   build: .
   volumes:
     - .:/app
-  command: sleep infinity
+  ports:
+    - 8888:8888
+  command: poetry run jupyter lab --no-browser --ip=0.0.0.0 --port=8888 --allow-root --NotebookApp.token='' --NotebookApp.password='' --NotebookApp.disable_check_xsrf=True

なおVS CodeからNotebookを使う場合などは、実は8888ポート開放の必要はありません。複数プロジェクトを同時に起動する必要がある場合などにポートの競合が起きなくなり便利です。

$ docker compose up --build

image.png

JupyterLabを起動できたら成功です。

JupyterLabを使う必要がないがコンテナを起動したい場合、コマンドをsleep infinityなどのように指定しておくことで起動状態を維持できます。

オプション

以下はオプションの設定です。お好みで必要な設定を取り入れてください。

OpenAI API環境変数の追加

ChatGPTを利用したアプリケーションを利用したい場合のAPIキーなどは、環境変数として配置します。APIキーやChatGPTについての詳細はここでは省略します。

$ echo "OPENAI_API_KEY=XXXXXXXX" >> .env
compose.yaml
services:
  app:
    build: .
    volumes:
      - .:/app
+   env_file:
+     - .env
    command: sleep infinity

.envgitignore/Python.gitignoreに含まれているため、手順通り設定している場合はリポジトリに含まれません。

VS CodeのPython設定追加

VS Codeを使っている場合は起動中のコンテナ内にアタッチして使うことで、Pythonランタイムに沿った補完などの恩恵が得られたり、ターミナルでコンテナ内のコマンドを実行できるようになります。

VS Codeを起動したらコマンドパレットを開いて>Dev Containers: Attatch to Running Containerコマンドなどで、Dev Containersで接続します。

image.png

コンテナ内でワークスペースを開いたら、/app(WORKDIRで設定したディレクトリ)を開きます。
コンテナ内でms-python.pythonms-toolsai.jupyterの拡張機能をインストールします。推奨事項に追加する設定をすると、インストールされていない環境でVS Codeを開くと通知が来るようになります。

.vscode/extensions.json
{
  "recommendations": [
    "ms-python.python",
    "ms-toolsai.jupyter"
  ]
}

拡張機能をインストールしたら、>Developer: Reload Windowコマンドでリロードします。
VS Code上のNotebookでPythonを実行などができたら成功です。

image.png

なおVS Code内からのみNotebookを使うためWebブラウザからアクセスできる必要が無いという場合、JupyterLabのための8888ポート開放の必要が無くなります。複数プロジェクトを同時に起動する必要がある場合などにポートの競合が起きなくなったりして少し便利です。

compose.yaml
services:
 app:
   build: .
   volumes:
     - .:/app
-  ports:
-    - 8888:8888
   command: poetry run jupyter lab --no-browser --ip=0.0.0.0 --port=8888 --allow-root --NotebookApp.token='' --NotebookApp.password='' --NotebookApp.disable_check_xsrf=True

Dev Container

記述したDockerfilecompose.yamlを利用して、Development Containers(Dev Container)に沿って定義することもできます。

.devcontainer/devcontainer.json
{
  "dockerComposeFile": "../compose.yaml",
  "workspaceFolder": "/app",
  "service": "app",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-toolsai.jupyter",
        "ms-python.flake8",
        "ms-python.black-formatter"
      ]
    }
  }
}

VS Codeを使う上では、Dev Containerを使うことでワークスペースディレクトリ/appを指定しなくても良くなったり、拡張機能を自動でインストールできる等のメリットがあります。$ dockerのCLIコマンドを使わずに起動できるという点は、好みが分かれると思います。

GitHub Codespaces

先述のDev Containerの定義を利用して、GitHub Codespacesで開発環境を動作させることもできます。

その際、先述の設定ファイルではenv_fileを用意できなくて環境が立ち上がりません。GitHubのsecretsに設定した上でファイルを作成する必要があります。

.devcontainer/devcontainer.json
  "initializeCommand": "if [ ! -e .env ]; then echo OPENAI_API_KEY=`echo $OPENAI_API_KEY` > .env; fi"

また、Codespacesのログは>Codespaces: Export Logsコマンドを使うとzipで固めた状態で取得できるため、コンテナ内にunzipをインストールするようDockerfileに追記すると便利だと思います。

VS CodeによるType Checking

VS CodeのPython拡張機能があれば、Pythonの型チェックを有効にできます。

.vscode/settings.json
{
  "python.analysis.typeCheckingMode": "basic",
}

image.png

Flake8およびBlackの導入

リンターとフォーマッターを導入します。本稿ではリンターにFlake8、フォーマッターにBlackを利用します。

VS Code拡張機能

VS Codeを介してのみFlake8とBlackを利用する場合は、拡張機能をインストールするのみでOKです。$ pip install flake8などをする必要はありません。

.vscode/extensions.json
 {
   "recommendations": [
     "ms-python.python",
     "ms-toolsai.jupyter",
+    "ms-python.flake8",
+    "ms-python.black-formatter",
   ]
 }

VS Codeで以下のように設定します。

.vscode/settings.json
{
  "[python]": {
    "editor.formatOnSave": true,
    "editor.formatOnSaveMode": "modifications",
    "editor.defaultFormatter": "ms-python.black-formatter",
  },
  "black-formatter.args": ["--line-length", "120"],
  "flake8.args": ["--max-line-length", "120"],
}

リンターによる指摘が表示され、>Format Documentコマンドでフォーマッターを使えるようになります。

image.png

CLI

リンターやフォーマッターをCLIで利用したい場合は、インストールが必要です。

$ docker compose exec app poetry add --group dev black flake8

以下のように設定をします。

setup.cfg
[flake8]
max-line-length = 120
pyproject.toml
+
+[tool.black]
+line-length = 120

以下のようなコマンドで実行します。

$ docker compose exec app poetry run flake8 ./my_python_project/
Skipping virtualenv creation, as specified in config file.
./my_python_project/nicegui_main.py:4:1: E302 expected 2 blank lines, found 1
$ docker compose exec app poetry run black ./my_python_project/
Skipping virtualenv creation, as specified in config file.
reformatted /app/my_python_project/nicegui_main.py

All done! ✨ 🍰 ✨
1 file reformatted, 1 file left unchanged.

実際にはGitHub ActionsなどのCI上で実行するように設定するのが良いと思います。

アプリケーション実行環境の追加

FastAPIの追加

Pythonで開発したものをWeb APIとして実行できる環境も用意したい場合があります。本稿ではFastAPIを起動できる環境を用意します。

$ docker compose exec app poetry add fastapi uvicorn

以下のように記載します。

compose.yaml
services:
  app:
    build: .
    volumes:
      - .:/app
    command: poetry run jupyter lab --no-browser --ip=0.0.0.0 --port=8888 --allow-root --NotebookApp.token='' --NotebookApp.password='' --NotebookApp.disable_check_xsrf=True
+  fastapi:
+    extends:
+      service: app
+    ports:
+      - 8081:80
+    command: uvicorn my_python_project.fastapi_main:app --host 0.0.0.0 --port 80 --reload

FastAPI以外を使う場合もだいたい似たような設定で行けると思います。起動ホストを0.0.0.0で指定するのがポイントです。

my_python_project/fastapi_main.py
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: str = None):
    return {"item_id": item_id, "q": q}

コンテナを立ち上げ直し、$ curl localhost:8081などで{"Hello": "World"}と表示されたら成功です。

NiceGUIの追加

WebAPIだけではなくUIが欲しい場合もあります。要件や好みによってStreamlitGradioを選びますが、本稿ではより柔軟にアプリケーションを構築できるNiceGUIの例を記載します。

$ docker compose exec app poetry add nicegui

以下のように記載します。

compose.yaml
services:
  app:
    build: .
    volumes:
      - .:/app
    command: poetry run jupyter lab --no-browser --ip=0.0.0.0 --port=8888 --allow-root --NotebookApp.token='' --NotebookApp.password='' --NotebookApp.disable_check_xsrf=True
+  nicegui:
+    extends:
+      service: app
+    ports:
+      - 8080:8080
+    command: poetry run python my_python_project/nicegui_main.py
my_python_project/nicegui_main.py
from nicegui import ui

ui.label('Hello NiceGUI!')

ui.run()

pytest

開発が進むとテストコードが欲しくなります。ここではpytestを導入します。

$ docker compose exec app poetry add --group dev pytest

tests/test_mymodule.pyのような名前でテストファイルを作成しますが、そのままではテストを実行できません。pyproject.tomlに、pytestの設定を記述する必要があります。

pyproject.toml
+
+[tool.pytest.ini_options]
+testpaths = ["tests",]
+pythonpath = "my_python_project"

$ poetry run pytestでテストが実行できます。

補足

本記事は3年前に書いた記事のリメイクです。

21
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
16