あえてdockerにpipenv環境を作る

私がpythonを始めた7月頃にはpipenvを活用するチュートリアルがそこそこに登場していました。pythonにもphpで言うところのcomposerがあることに感動しましたが、まずは実績のあるpipを活用してきました。pipでも事足りることが分かったのですが、ライブラリの依存関係が分かりにくい事に不満があります。そこで、dockerにpipenvで環境を構築してみたいと思います。

# この記事でしないこと
- pipenv の細かな説明はしない
    - pipenvを知る上で以下がとても役立ったので、是非そちらを参照ください
        - [Pipenv: 人間のためのPython開発ワークフロー — pipenv 2018.7.1.dev0 ドキュメント](https://pipenv-ja.readthedocs.io/ja/translate-ja/)
        - [DeepLearning なプロジェクトで Pipenv を導入しようとして感じた良いところ悪いところ - Qiita](https://qiita.com/halhorn/items/2fba53cf65e994b7de76)
        - [きたない requirements.txt から Pipenv への移行 | 株式会社カブク](https://www.kabuku.co.jp/developers/python-pipenv-graph)
        - [Pipenvの基本的な使い方 — pipenv 2018.11.15.dev0 ドキュメント](https://pipenv-ja.readthedocs.io/ja/translate-ja/basics.html)

# pipenvの導入目的
- `pipenv graph` でライブラリの依存関係をわかりやすくする事

# dockerでpipenvは2重の仮想化になるでのはないかの問いに関して

- [こちらの記事](https://qiita.com/Aruneko/items/796d7eeb61e1f36ae4a0) で知ったが、`--system`オプションを使うことで、pipenvの仮想環境ではなくシステムの pipコマンドを使用したインストールができるのことが分かった
- ただし、`--system`すると`pipenv graph`するのに一手間が増えた(後述)

# 作成した環境で動作確認したこと
- `pipenv graph`
- pipenv.lockの更新
- test.pyを実行してhello world

# `pipenv graph`できたので、構成を説明する

## ディレクトリ構成

```
$ tree
.
├── Makefile
├── docker
│   ├── Dockerfile
│   ├── Dockerfile_base
│   ├── Dockerfile_pipenv_gragh
│   └── Dockerfile_update_dependency
└── main
    ├── Pipfile
    ├── Pipfile.lock
    └── test.py
```

## 各ファイルの内容
### Makefile

- やっていること 
    - application-run
        - pythonファイルの実行
    - application-update-dependency
        - Pipfile.lock を更新する
    - application-graph
        - `pipenv graph`する

``` Makefile:Makefile
PYTHON_FILE := $(PYTHON_FILE)

IMAGE_NAME = python_application
IMAGE_NAME_BASE = $(IMAGE_NAME)_base
IMAGE_NAME_PIPENV = $(IMAGE_NAME)_pipenv
CONTAINER_NAME = python_application

application-build-base:
	docker build . -f docker/Dockerfile_base\
	 -t $(IMAGE_NAME_BASE):0.1

application-build: application-build-base
	docker build . -f docker/Dockerfile\
	 -t $(IMAGE_NAME):0.1

application-build-update-dependency: application-build-base
	docker build . -f docker/Dockerfile_update_dependency\
	 -t $(IMAGE_NAME):0.1

application-build-pipenv-gragh: application-build-base
	docker build . -f docker/Dockerfile_pipenv_gragh\
	 --no-cache \
	 -t $(IMAGE_NAME_PIPENV):0.1

application-update-dependency: application-build-update-dependency
	docker run -it --rm --name $(CONTAINER_NAME) \
	-v `pwd`/main:/main \
	$(IMAGE_NAME):0.1 \
	pipenv lock

application-run: application-build
	docker run -it --rm --name $(CONTAINER_NAME) \
	-v `pwd`/main:/main \
	$(IMAGE_NAME):0.1 \
	python $(PYTHON_FILE)

application-graph: application-build-pipenv-gragh
	docker run -it --rm --name $(CONTAINER_NAME) \
	-v `pwd`/main:/main \
	$(IMAGE_NAME_PIPENV):0.1 \
	pipenv graph
```

### Dockerfile_base
- やっていること 
    - pipenv のinstall

``` Dockerfile:Dockerfile_base
FROM python:3.7

RUN pip install --upgrade pip && pip --version
RUN pip install pipenv && pipenv --version
```

### Dockerfile
- やっていること 
    - Pipfile.lockを元にしたシステムpipでのinstall

``` Dockerfile:Dockerfile
FROM python_application_base:0.1

COPY main /main
WORKDIR main

COPY main/Pipfile Pipfile
COPY main/Pipfile.lock Pipfile.lock

RUN set -x && \
    pipenv install --system
```

### Dockerfile_pipenv_gragh
- やっていること 
    - Pipfile.lockを元にしたpipenv仮想環境へのinstall

``` Dockerfile:Dockerfile_pipenv_gragh
FROM python_application_base:0.1

COPY main /main
WORKDIR main

COPY main/Pipfile Pipfile
COPY main/Pipfile.lock Pipfile.lock

RUN set -x && \
    pipenv install
```

### Dockerfile_update_dependency
- やっていること 
    - Pipfileを元にしたシステムpipでのinstall

``` Dockerfile:Dockerfile_update_dependency
FROM python_application_base:0.1

COPY main /main
WORKDIR main

COPY main/Pipfile Pipfile

RUN set -x && \
    pipenv install --system --skip-lock
```

## Pipfile
- やっていること
    - pandasのinstall

``` Pipfile:Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
pandas="*"

[requires]
python_version = "3.7"

```


# 動作確認結果

## `pipenv graph`
`pipenv gragh`するときだけ、pipenvの仮想環境を使うことで、pipenv graghできた。

```
pandas==0.23.4
  - numpy [required: >=1.9.0, installed: 1.15.4]
  - python-dateutil [required: >=2.5.0, installed: 2.7.5]
    - six [required: >=1.5, installed: 1.11.0]
  - pytz [required: >=2011k, installed: 2018.7]

```


systemオプション使用すると以下のエラーが出た

```
Warning: No virtualenv has been created for this project yet! Consider running `pipenv install` first to automatically generate one for you or see`pipenv install --help` for further instructions.
make: *** [application-graph] Error 1
```


## pipenv.lockの更新
- 問題なく更新できた

## test.pyを実行してhello world
- `make application-run PYTHON_FILE:=test.py` すると、hello worldできた

# 感想

`pipenv gragh`するときはpipenvの仮想環境が必要なので思ってたんと違った。多少複雑になったが、ライブラリの依存関係が表現のメリットが勝りそうなので使っていきたい。