DockerfileでPoetryを使用して依存関係パッケージをインストールする

この記事は[Zeals Advent Calendar2020](https://qiita.com/advent-calendar/2020/zeals)の3日目の記事です。見ていただきありがとうございます！

今回はPipenvの代替やら色々言われているPoetryをDocker上で使用する方法を書いていきます。
また、この記事はほとんど公式ドキュメントやGithubリポジトリのissueを参考にしています。正確な情報を知りたいのであれば、ぜひ公式ドキュメントを読んでみてください。
https://python-poetry.org/

## 記事を書いたときの動作環境

* MacOS Big Sur 11.0.1
* Docker for Mac 2.5.0.1

## この記事の流れ
今回はこのような流れで開発していきます。
[Poetryのインストール方法](#Poetryのインストール方法)
[Poetryの設定](#Poetryの設定)
[Poetryの環境を作成](#Poetryの環境を作成)
[pyproject.tomlへのパッケージ追加](#pyproject.tomlへのパッケージ追加)
[DockerFileを書く](#DockerFileを書く)

## Poetryのインストール方法
PoetryのインストールはcURL推奨です。pipを使ってインストールのほうが楽ではあるんですが、非推奨である理由として、

1. 使用したいPythonのバージョンごとにPoetryをインストールして切り替える必要がある
2. コンフリクトする可能性のあるパッケージをインストールしてしまうから

らしいです。

ローカル上でdependencieなどを記載していきたいため、まずはローカルにPoetryをインストールしていきます。

```sh
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
```

次に、PATHを通します。

```sh
export PATH="$HOME/.poetry/bin:$PATH"
```

これでPoetryを使える準備が出来ました。

<br>

## Poetryの設定
### Poetryの環境を作成
このコマンドを叩くことで、`pyproject.toml`をインタラクティブに作成できます。好みに合わせて答えていきましょう。

```sh
poetry init
```

https://python-poetry.org/docs/cli/#init

<br>

pyproject.tomlが生成されたので、次にdependenciesに使用するPythonのVersionを宣言します。明示的に宣言しないと、Poetryが処理できないためです。

```sh
[tool.poetry.dependencies]
python = "~3.8"
```

このチルダは、Pythonのバージョンを `>=3.8.0 <3.9.0` の間でバージョンを指定しています。
詳しくは公式ドキュメントを読んでください。

https://python-poetry.org/docs/dependency-specification/#tilde-requirements

また、このバージョンの書き方について詳しく知りたい方は、SemVer(セマンティック バージョニング)で調べてみてください。

<br>

一旦ここまでのpyproject.tomlはこんな感じです

```md
[tool.poetry]
name = "test"
version = "0.1.0"
description = "test"
authors = ["ssab"]

[tool.poetry.dependencies]
python = "~3.8"  # docker imageのpython versionと統一しておく
requests = "~2.24.0"


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

<br>

### pyproject.tomlへのパッケージ追加

pyproject.tomlへパッケージを追加するときは下記コマンドを叩きます。

```sh
poetry add <package name>
```

<br>

バージョンを指定してインストールをしたいときは、下記のようにします。

```sh
poetry add requests@2.24.0
```

インストールに成功すると、パッケージが自動的に`pyproject.toml`に記載されます。また、自動的に依存関係のパッケージのバージョンが記載された`poetry.lock`ファイルが生成されます。  
`poetry.lock`ファイルはgitで管理することで、他の開発者とも依存関係のバージョンを強制することができます。そのため、ぜひ使っていくといいと思います。
poetry.lockの管理の是非については、公式ドキュメントにいいことが書いてあるので、色々疑問などがある方は是非読んでみてください。https://python-poetry.org/docs/basic-usage/#installing-without-poetrylock

そして補足です。no-dependenciesは、本番環境に上げる必要はないよ！みたいなパッケージを追加するといいと思います。たとえばflake8やpytestなどです。
なぜかというと、

```sh
poetry install --no-dev
```

と書くことで、no-dependenciesに書いたパッケージはインストールせずに済むからです。

<br>

## Dockerfileを書く
```dockerfile
FROM python:3.8-slim AS builder
RUN apt-get update && apt-get -y install \
    curl

RUN curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python -
ENV PATH="/root/.poetry/bin:$PATH"
WORKDIR /tmp
COPY pyproject.toml poetry.lock /tmp/
RUN poetry config virtualenvs.create false
RUN poetry install
```

こんな感じです。わりとシンプルに。

## Dockerfileからイメージの作成
Dockerfileからイメージを作成できるか試してみます。

```sh
docker build .
```

これでエラーが出なければ、無事イメージの作成に成功です。

## 終わりに
今回Poetryに移行した理由として、[Trivy](https://github.com/aquasecurity/trivy)でDocker image全体の脆弱性スキャンをするためでした。
Poetryを導入することでTrivyを使うことができるメリットも享受できるので、興味を持った方はぜひ！