🤔

docker+poetryでpythonを使う

2022/05/08に公開
2

はじめに

今までpythonを使う時はdocker+pipを使ってきた。
pythonのパッケージマネージャーとして、poetryの存在を知り、興味があったので使ってみた。

この記事ではpoetryで使用するpyproject.tomlファイルを作成するために、docker-compose.yamlやpoetry newコマンドを使用してますが、正直効率が悪いと思います。
poetry newコマンドで作成されるディレクトリを再利用しようと思い、このような流れになっていますが、適宜やりやすい方法でやった方が早く作業ができると思います。

前提環境

pc: MacBookPro(2017)
os: Big Sur
Homebrew 3.4.10
Docker version 20.10.13
docker-compose version 1.29.2
Dockerで作成したコンテナ上でpoetryでPython環境を構築します。
poetryは仮想環境を構築できるメリットがありますが、dockerコンテナ上では特に意味がないと思ったので、今回は仮想環境を作らないように設定します。

インストール関連(事前準備)

Homebrewのインストール

Homebrewのページからインストール用のコマンドをコピペして、ターミナルなどで実行します。

install homebrew
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
version
$ brew -v
Homebrew 3.4.10

Docker, docker-composeのインストール

下記のコマンドにより、homebrewでDocker, docker-composeをインストールしていきます。

search docker
$ brew search docker
==> Formulae
docker ✔                          docker-machine                    docker-squash
docker-clean                      docker-machine-completion         docker-swarm
docker-completion                 docker-machine-driver-hyperkit    docker2aci
docker-compose ✔                  docker-machine-driver-vmware      dockerize
docker-compose-completion         docker-machine-driver-vultr       lazydocker
docker-credential-helper          docker-machine-driver-xhyve       powerman-dockerize
docker-credential-helper-ecr      docker-machine-nfs                mockery
docker-gen                        docker-machine-parallels
docker-ls                         docker-slim

==> Casks
docker ✔            docker-edge         docker-toolbox      dockey              dozer
install Docker
$ brew install docker
install docker-compose
$ brew install docker-compose

手順やコード

手順としては下記のような流れとなります。
事前準備(poetryプロジェクト初期化) -> Dockerfile作成 -> docker-compose.yaml作成 -> build

事前準備(poetryプロジェクト初期化)

事前準備用のDockerfileとdocker-compose.yaml

Dockerfile
ARG python_image_v="python:3.10-buster"
# python3.10のイメージをダウンロード
FROM ${python_image_v}

ARG work_dir="/src/"

# コンテナにアクセスした際のデフォルトディレクトリ
WORKDIR ${work_dir}

# poetryのインストール先の指定
ENV POETRY_HOME=/opt/poetry
# poetryインストール
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python && \
# シンボリックによるpathへのpoetryコマンドの追加
    cd /usr/local/bin && \
    ln -s /opt/poetry/bin/poetry && \
# 仮想環境を作成しない設定(コンテナ前提のため,仮想環境を作らない)
    poetry config virtualenvs.create false
docker-compose.yaml
version: '3'
services:
  test-app:
    build: .
    volumes:
      - ./src:/src
#    ports:
#      - 8000:8000
    tty: true # retain container up

コンテナ上でのpoetryプロジェクト初期化

下記のdocker-composeコマンドでコンテナを作成し、コンテナにアクセスします。

$ docker-compose up -d
$ docker-compose exec test-app bash
root@a6c27148c534:/src#

poetry versionでpoetryがインストールされていることを確認したら、プロジェクトを作成します。
poetry new <project-name>でプロジェクトのディレクトリを作成してくれます。
オプションで--name <dir-name>を入れるとプロジェクトディレクトリ配下に作成されるソースコードを入れるディレクトリの名前を指定できます。
(poetry new --src <project-name> オプションでsrcディレクトリを作成できますが、ディレクトリ構成がややこしくなるので今回はやめておきます。)
対話形式でやりたい場合は、poetry initコマンドで行います。
(pyproject.tomlファイルのみ作成されます。)

poetry new
root@a6c27148c534:/src# poetry new myProject --name myFlask
Created package myflask in myProject
tree
$ tree
myProject
├── README.rst
├── myflask
│   └── __init__.py
├── pyproject.toml
└── tests
    ├── __init__.py
    └── test_myflask.py

poetry add <package-name>コマンドでパッケージの追加ができます。
今回は一例として、flaskを入れてみます。

add Flask package
root@a6c27148c534:/src/myProject# poetry add Flask
Skipping virtualenv creation, as specified in config file.
Using version ^2.1.2 for Flask

Updating dependencies
Resolving dependencies... (14.6s)

Writing lock file

Package operations: 14 installs, 0 updates, 0 removals

  • Installing markupsafe (2.1.1)
  • Installing pyparsing (3.0.8)
  • Installing attrs (21.4.0)
  • Installing click (8.1.3)
  • Installing itsdangerous (2.1.2)
  • Installing jinja2 (3.1.2)
  • Installing more-itertools (8.13.0)
  • Installing packaging (21.3)
  • Installing pluggy (0.13.1)
  • Installing py (1.11.0)
  • Installing wcwidth (0.2.5)
  • Installing werkzeug (2.1.2)
  • Installing flask (2.1.2)
  • Installing pytest (5.4.3)

pyproject.tomlファイル内にFlaskが追加されます。

pyproject.toml
root@a6c27148c534:/src/myProject# cat pyproject.toml
[tool.poetry]
name = "myFlask"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.10"
Flask = "^2.1.2"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

Dockerfile, docker-compose.yamlの作成

Dockerfile
ARG python_image_v="python:3.10-buster"
# python3.10のイメージをダウンロード
FROM ${python_image_v}

ARG work_dir="/work/"
#ARG project_name="myProject"
#ARG src_dir="myflask"
RUN mkdir /work && mkdir /work/src && mkdir /work/tests

# コンテナにアクセスした際のデフォルトディレクトリ
WORKDIR ${work_dir}

# poetryのインストール先の指定
ENV POETRY_HOME=/opt/poetry
# poetryインストール
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python && \
# シンボリックによるpathへのpoetryコマンドの追加
    cd /usr/local/bin && \
    ln -s /opt/poetry/bin/poetry && \
# 仮想環境を作成しない設定(コンテナ前提のため,仮想環境を作らない)
    poetry config virtualenvs.create false
# 仮想環境をprojectディレクトリで作成する
#RUN poetry config virtualenvs.in-project true

# poetryの定義ファイルをコピー (./src/配下に存在すると想定)
COPY ./src/myProject/pyproject.toml* ./src/myProject/poetry.lock* ./

# poetryでライブラリをインストール (pyproject.tomlが既にある場合)
 RUN if [ -f pyproject.toml ]; then poetry install; else poetry new ${project_name} --name ${src_dir}; fi
docker-compose.yaml
version: '3'
services:
  test-app:
    build: .
    volumes:
      - ./src/myProject/myflask:/work/src
      - ./src/myProject/tests:/work/tests
#    ports:
#      - 8000:8000
    tty: true # retain container up

buildとflaskの確認、パッケージの追加

下記のコマンドでコンテナを起動します。

build
$ docker-compose up -d

コンテナにアクセスし、対話形式でimport flaskを実施するとインポートできます。

flaskの確認
$ docker-compose exec test-app bash
root@ab019553223e:/work# python
Python 3.10.4 (main, Apr 20 2022, 18:32:02) [GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import flask
>>>

パッケージを追加または削除する場合は下記のコマンドpoetry add(remove) <package-name>で実施可能です。
(今回はpyproject.tomlファイルがあるディレクトリで行いましたが、違うディレクトリでも可能?仮想環境を作っていないため?仮想環境を作っている場合はそのディレクトリでないとダメ?試していないため未確認です。)

add(remove) package
root@ab019553223e:/work# ls
poetry.lock  pyproject.toml  src  tests
root@ab019553223e:/work# cat pyproject.toml
[tool.poetry]
name = "myFlask"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.10"
Flask = "^2.1.2"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

root@ab019553223e:/work# poetry add selenium
Skipping virtualenv creation, as specified in config file.
Using version ^4.1.5 for selenium

Updating dependencies
Resolving dependencies... (0.3s)

Writing lock file

Package operations: 17 installs, 0 updates, 0 removals

  • Installing pycparser (2.21)
  • Installing cffi (1.15.0)
  • Installing async-generator (1.10)
  • Installing cryptography (37.0.2)
  • Installing h11 (0.13.0)
  • Installing idna (3.3)
  • Installing outcome (1.1.0)
  • Installing sniffio (1.2.0)
  • Installing sortedcontainers (2.4.0)
  • Installing certifi (2021.10.8)
  • Installing pyopenssl (22.0.0)
  • Installing pysocks (1.7.1)
  • Installing trio (0.20.0)
  • Installing wsproto (1.1.0)
  • Installing trio-websocket (0.9.2)
  • Installing urllib3 (1.26.9)
  • Installing selenium (4.1.5)
  
root@ab019553223e:/work# poetry remove selenium
Skipping virtualenv creation, as specified in config file.
Updating dependencies
Resolving dependencies... (0.1s)

Writing lock file

Package operations: 0 installs, 0 updates, 17 removals

  • Removing async-generator (1.10)
  • Removing certifi (2021.10.8)
  • Removing cffi (1.15.0)
  • Removing cryptography (37.0.2)
  • Removing h11 (0.13.0)
  • Removing idna (3.3)
  • Removing outcome (1.1.0)
  • Removing pycparser (2.21)
  • Removing pyopenssl (22.0.0)
  • Removing pysocks (1.7.1)
  • Removing selenium (4.1.5)
  • Removing sniffio (1.2.0)
  • Removing sortedcontainers (2.4.0)
  • Removing trio (0.20.0)
  • Removing trio-websocket (0.9.2)
  • Removing urllib3 (1.26.9)
  • Removing wsproto (1.1.0)

コンテナを停止する場合は下記のコマンドでできます。オプションで--rmi allを付けるとdocker-composeコマンドで作成されたコンテナとイメージなどを削除してくれます。

down
$ docker-compose down (--rmi all)

その他

poetryでのversion指定

poetryでパッケージをインストールする際にバージョン指定が可能ですが、今回は指定していません。
詳しくはpoetryドキュメントを見てください。

docker内でのpoetry操作の自動化について

今回はpoetryによるpoetry installコマンドをpyproject.tomlがある前提でDockerfileやdocker-compose.yamlなどを書いた。しかし、poetry new or initなどの操作も自動化したい。他の記事もpyproject.tomlを前提に書いているのが多いため、現在お勉強中です。
下記のサイトが参考になるかも。
Poetry と Docker を併用する試み

docker-composeでのvolumeのタイミング

RUN -> volume -> CMDらしい?
なので、マウントするvolume上で何かしらのコマンドを実行したい場合は、CMDの方が良い?

(# 最後に)

参考

Discussion