poetry+docker+devcontainerでpython環境を構築する

今までの大学生活ではコードを共有して複数人が利用するという状況があまりなかったことと、自分のガサツな性格のせいでローカルのpython環境をいくら汚しても気になっていなかったため、pipとcondaのチャンポンで環境を構築していたが、流石に社会人になるのにそれはまずいだろうということでdockerとpoetryを利用した環境構築に手をつけたので、その備忘録を残しておく。ついでにvscodeでjupyter notebookを動かせた方がラクだろうということでdevcontainerを使った環境構築もしてみた。

この記事では次のような人をターゲットにしている。
- python, poetry, vscodeのインストールはできている
- 細かいことはいいからコピペでpython環境を構築したい

# 大まかなイメージ
大体のイメージとして、dockerで素のpython環境を用意して、poetryでパッケージのバージョン管理をして、devcontainerがvscode上でpython環境コンテナにアクセスできるようにしてくれるというイメージ。

# ディレクトリ構成
ディレクトリ構成は次のようになる。
```
.
└── project_file/
    ├── .devcontainer/
    │   └── devcontainer.json
    ├── docker-compose.yml
    ├── Dockerfile
    ├── pyproject.toml
    ├── poetry.lock
    └── README.md
```
docker-compose.yml, Dockerfileはdocker環境構築に、pyproject.toml, poetry.lock, README.mdはpoetryに、.devcontainerはdevcontainerに必要なものである。

# project fileの作成
project fileの親ディレクトリで次のコマンドを打つとproject fileが作成される。
```
poetry new [project名]
```
poetryによって作成されたproject fileは次のような構成になっている。
```
.
└── project_file/
    ├── project_file/
    │   └── __init__.py
    ├── test/
    │   └── __init__.py
    ├── pyproject.toml
    └── README.md
```
project_fileとtestは使わないようなら消してしまっても構わない。

すでにproject_fileがある場合には、project_file内で次のコマンドを打てば良い。
```
poetry init
```
このコマンドはproject_file内にpyproject.tomlを作成する。

## pyproject.tomlの中身
pyproject.tomlの中身は次のようになっている。
```
[tool.poetry]
name = "project name"
version = "0.1.0"
description = ""
authors = ["****"]
readme = "README.md"
packages = [{include = "project name"}]

[tool.poetry.dependencies]
python = "^3.9"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
```
パッケージを追加するとtool.poetry.dependenciesに追加されていく。pythonのバージョンを使いたいバージョンに変える必要がある。

# devcontainerのインストール
vscode上で次のdev containersをインストールする。
![スクリーンショット 2024-04-30 18.52.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3243092/9e44cc6b-d212-12ec-3a53-f767b9f71271.png)

## devcontainer.jsonの作成
project file内に.devcontainer/devcontainer.jsonを作成する。devcontainer.jsonの中身は次のようになっている。
```json
{
    "dockerComposeFile": ["../docker-compose.yml"],
    "workspaceFolder": "/app",
    "service": "app",
    "customizations": {
      "vscode": {
        "settings": {
          "extensions.verifySignature": false
        },
        "extensions": [
          "ms-python.python",
          "ms-toolsai.jupyter",
          "ms-python.flake8",
          "ms-python.black-formatter"
        ]
      }
    }
  }
```
各要素の説明は次のようになる。
- dockerComposeFile: 利用するdocker-compose.ymlのパスを指定する
- workspaceFolder: ローカルで開いているワークスペースをコンテナ内のどのパスに位置付けるかを指定する
- service: docker-compose.yml内で定義したservice名を指定
- customizations: vscodeの設定を記述する
- extensions.verifySignature: 拡張機能をインストールする際に重要。特にm1 macでは重要になる
- extensions: devcontainerがコンテナを作成する際にインストールする拡張機能を指定する。ここではpythonでよく利用する拡張機能を追加している

# Dockerfileとdocker-compose.ymlを作成する
Dockerfileはpython環境のimageを定義し、docker-compose.ymlはコンテナが接続するボリュームなどの設定を定義する。
## Dockerfile
```Dockerfile
# pythonのバージョンを指定
FROM python:3.9-slim

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

# rustのインストール
ENV PATH=$PATH:/root/.cargo/bin
RUN curl https://sh.rustup.rs -sSf > /rust.sh && sh /rust.sh -y && \
    rustup install stable

# poetryのインストール
WORKDIR /home
RUN pip install poetry && \
    poetry config virtualenvs.create false

# gitの設定
RUN git config --global --add safe.directory /app  
```
ubuntuのパッケージとrustの部分は適当に書き換えてください。pythonのバージョンはpoetryで指定したものと同じにしてください。tensorflowなどで利用するcudaなどはここで書くか、適当なimageを利用してください。

## docker-compose.yml
```yaml
services:
  app:
    build: .
    volumes:
      - .:/app
    command: sleep infinity
```
service名はdevcontainer.jsonを同一にし、buildはDockerfileを利用するためカレントディレクトリを指定してください。volumeはカレントディレクトリをコンテナ内のappディレクトリに接続してください。commandはsleep infinityにして常にコンテナを起動しておきます。

# コンテナの起動とアタッチ
vscodeの左下にあるdevcontainerのタブをクリックし、「コンテナを再度開く」をクリックすることでコンテナが起動し、アタッチされる。
![スクリーンショット 2024-04-30 19.42.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3243092/7abf0f54-30af-b275-4a92-7359ba7608f0.png)

# パッケージのインストール
すでに用意されているpyproject.tomlを利用する場合は次のコマンドで全てのパッケージがインストールされる。
```
poetry install
```
このコマンドでpoetry.lockが作成される。
新しくパッケージをインストールする場合は次のコマンドでインストールできる。
```
poetry add [パッケージ名]
```
パッケージの削除は次のコマンド
```
poetry remove [パッケージ名]
```
全パッケージのアップデートは次のコマンド
```
poetry update
```

# まとめ
Dockerfileとdocker-compose.yml, devcontainer.jsonは一回作成すればコピペで他のプロジェクトにも流用できる。
自分の環境はM1 macなのでtensorflowなどをインストールするのはかなりめんどくさいので諦めました。