PyCharmでDocker(Hyper-V)を使う on Windows 10(2017/8時点)

Windows10でPyCharmにDockerを使おうとしたら、パスの解決等、色々とハマったのでメモしておきます。
試行錯誤の結果であるため、余計な手順があるかもしれません。

## できるようになること
* Docker(Hyper-V)上での、PyCharmからテストのRun/Debug

## できないこと
* 1テストケースを選択してのRun/Debug(できるが、設定が大変)
* Coverageの収集

## 試した環境
* Windows 10 Pro
* Hyper-Vマネージャ バージョン: 10.0.15063.0
* docker-machine version 0.12.2, build 9371605
* Docker version 17.06.0-ce, build 02c1d87
* PyCharm 2017.2 Build #PY-172.3317.103, built on July 26, 2017

PyCharmは、2017.1だとうまくできませんでした。
Windowsでない方や、WindowsだけどHyper-Vを使わない方は [PyCharmでの開発でDockerを使う](http://www.rhoboro.com/2017/01/21/pycharm-docker.html) を参考にされると良いかと思われます。

## 1. Docker環境の準備
### 1.1. Docker Toolboxのインストール
WindowsでDockerを使う場合はDocker for WindowsとDocker Toolboxがありますが、**Docker Toolbox**を入れます。下記ページにて`Get Docker Toolbox for Windows`のボタンを選択し、インストールしてください。

https://docs.docker.com/toolbox/overview/

初めはHyper-Vを使うためにDocker for Windowsをインストールしたのですが、後述するCertificates folderの設定が出来ず、諦めました[^1]。
また、Docker ToolboxでもHyper-Vは使えました。


### 1.2. Hyper-Vの有効化
以下を参考にHyper-Vを有効化してください。
[Windows 10 上に Hyper-V をインストールする](https://docs.microsoft.com/ja-jp/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v)


### 1.3. Boot2Docker virtual machineの作成
下記を参考にVMを作成します。
https://docs.docker.com/machine/drivers/hyper-v/

```bash
$ docker-machine create --driver hyperv --hyperv-memory 4096 --hyperv-cpu-count 4 default
```
上記コマンドを実行すると、下記設定でVMが作成されます。

|設定項目 | option |上記例 |
|:-------|--------|----------|
|ドライバ | --driver | hyperv |
|メモリ容量 | --hyperv-memory | 4096 |
|CPU数 | --hyperv-cpu-count | 4 |
|マシン名 |  | default |

その他オプションや詳細は上記リンク先をご参照ください。
この辺の設定値はHyver-Vマネージャで後から変更できるようです。

### 1.4. Hyper-V の設定
このままのVMでは外部ネットワークに接続できません。[Docker Machine 入門(Hyper-Vの場合)](http://qiita.com/ohhara_shiojiri/items/801a24a2c94ff93be82d#hyper-v%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A) の **Hyper-V環境設定** を参考に、仮想スイッチ及びネットワークアダプタを設定してください。

この際の注意点として、**外部、内部ネットワークの両方を設定するようにしてください。**外部ネットワークだけにしてしまうと、別のネットワークに接続した際にアドレスが変わり、動かなくなります。

以下のような状態になっていれば良いです。

<img width="541" alt="virtual-machineのネットワーク設定.png" src="https://qiita-image-store.s3.amazonaws.com/0/3770/95c073e6-6416-03da-7be3-73750a0ae319.png">


### 1.5. Windows共有フォルダの作成

[Windows 10の共有フォルダへ特定の人のみアクセスさせる設定方法(後編)](http://yakudate.com/windows10-share-ntfs2/)

上記ページを参考に、下記の設定を行います。

* VMからWindows共有フォルダへアクセスする際のユーザを追加
* 共有フォルダへのアクセス権を設定

共有フォルダには**開発するプロジェクトが含まれる**ようにしてください。
ここで設定したユーザ名、パスワードは次節で使います。
(ユーザ追加は本当は必要ないかも。ここは自信無いです。)

### 1.6. VM上で共有フォルダをマウント
Windowsの共有フォルダをcifsとしてVMからアクセスできるようにします。
まずVMへcifs-utilsをインストールします。`docker-machine ssh`でVMへログインし、以下を実行してください。([参考](http://haishibai.blogspot.jp/2014/10/walkthrough-use-boot2docker-on-windows.html))

```bash
$ wget http://distro.ibiblio.org/tinycorelinux/5.x/x86/tcz/cifs-utils.tcz
$ tce-load -i cifs-utils.tcz
```
そして、以下を参考に設定した共有フォルダをマウントします。
[Ubuntu から Windows の共有フォルダをマウントして利用する](http://qiita.com/mdstoy/items/54925cdcbca6d558b666)

#### 設定の永続化

VMの再起動時にも自動でマウントされるように設定します。
VM上で`/var/lib/boot2docker/bootlocal.sh`を下記内容で新規作成します。

```bash:bootlocal.sh
# /var/lib/boot2docker/に格納するとdocker-machine起動時に実行される

ip addr add 172.29.32.2/20 dev eth0 # HostOSと同じサブネット内で固定IPアドレスを設定する

uid=1000              # dockerユーザ
gid=50                # staffグループ
user=share            # HostOSで共有を許可したユーザ名
password=xxxxxxxxxxxx # 共有を許可したユーザのパスワード
host_ip=//172.29.32.1 # HostOSのIPアドレス

mkdir -p /home/docker/src
mount -t cifs -o uid=${uid},gid=${gid},username=${user},password=${password} ${host_ip}/src /home/docker/src
```

ここでのuser, passwordは 「1.5. Windows共有フォルダの作成」で設定したものです。

`host_ip`はWindowsPCのIPアドレスです。ネットワーク変化の影響を受けないよう、内部ネットワークのIPアドレスを指定します。私の環境では以下の状態になっていました。

```bat
> ipconfig

Windows IP 構成

イーサネット アダプター vEthernet (HNS Internal NIC) 2:

   接続固有の DNS サフィックス . . . . .:
   リンクローカル IPv6 アドレス. . . . .: fe80::68fe:336d:b69b:e392%24
   IPv4 アドレス . . . . . . . . . . . .: 172.29.32.1  ★これをHostOSのIPアドレスとして使う
   サブネット マスク . . . . . . . . . .: 255.255.240.0 ★サブネットにはこれ
   デフォルト ゲートウェイ . . . . . . .:

・・・省略
```

### 1.7. VMへの固定IPアドレスの設定
先に出てしまいましたが、上述の`bootlocal.sh`に記載していた下記一行で固定IPアドレスを設定します。

```bash
ip addr add 172.29.32.2/20 dev eth0 # HostOSと同じサブネット内で固定IPアドレスを設定する
```
サブネットマスクはWindows上での`ipconfig`の結果に合わせます。
PyCharmからは、ここで設定したIPアドレスへアクセスすることになります。

`docker-machine env`の`DOCKER_HOST`がここで指定したIPアドレスになっていればOKです。

```
$ docker-machine env default
You can further specify your shell with either 'cmd' or 'powershell' with the --shell flag.

SET DOCKER_TLS_VERIFY=1
SET DOCKER_HOST=tcp://172.29.32.2:2376
SET DOCKER_CERT_PATH=C:\Users\uj\.docker\machine\machines\default
SET DOCKER_MACHINE_NAME=default
SET COMPOSE_CONVERT_WINDOWS_PATHS=true
```

### 1.8. Python用Docker imageの用意
お好みのimageを用意しておきます。私は以下から取ってきました。
https://hub.docker.com/_/python/

```
> docker pull python:3.5
```

## 2. PyCharmでの設定
ようやくPyCharm上での設定に入ります。

### 2.1. VMの指定
`File > Settings` から設定画面を開き、`Build,Execution, Deployment > Docker`を開きます。
＋ボタンを押して追加したら、下記画面のように入力します。
<img width="762" alt="pycharm-virtual-machine設定2.png" src="https://qiita-image-store.s3.amazonaws.com/0/3770/15768f8f-6541-a64f-084d-03062d8ae2e7.png">

* `Engine API URL`: 「1.7. VMへの固定IPアドレスの設定」で指定したIPアドレスを`ip_addr`とすると、`https://${ip_addr}:2376`のように入力します。`DOCKER_HOST`のIPアドレスと一致している必要があります。プロトコルをtcpやhttpとしている情報もありましたが、**https**でないとうまく行きませんでした。
* `Certificates folder`: `docker-machine env`の`DOCKER_CERT_PATH`と同じパス。ここではバックスラッシュではなく**スラッシュ区切り**にします。
* `Path mappings`: デフォルトで何か入っていた気がしますが、この後の設定がうまく行かなくなるため**全て消します。**

成功するとウィンドウの真ん中あたりに`Connection successful`と表示されます。
なお、Docker for Windowsではこの`Certificates folder`が指定できませんでした。[^1]

### 2.2. Project Interpreterの指定
引き続き、設定画面から`Project: 自身のプロジェクト > Project Interpreter`を開きます。
ウィンドウ右上の歯車アイコンをクリックして、下記の通り設定します。
<img width="761" alt="pycharm-interpreter設定.png" src="https://qiita-image-store.s3.amazonaws.com/0/3770/a7898a90-7f79-c640-16ed-d4962af19381.png">

* ラジオボタンで`Docker`を選択
* `Server`: 「2.1. VMの指定」で指定したもの
* `Image name`: 「1.8. Python用Docker imageの用意」で用意したもの
* `Python interpreter path`: そのまま

うまく設定できて、暫く(3~4分?)待つと、指定したimageで有効なPackageが表示されます。

<img width="762" alt="pycharm-interpreter設定2.png" src="https://qiita-image-store.s3.amazonaws.com/0/3770/3da41cc0-8cb3-11a0-b2a0-649476413ee5.png">


### 2.3. Run/Debug Configurations
最後にテスト実行用の設定をします。

#### 2.3.1 実行設定の追加
メニューバーから`Run > Edit Configurations`を選択し、Run/Debug Configurations画面を表示します。
＋ボタンを押し、`Python tests > Unittests`を選択します（Unittestsの箇所はお好みのもので良いと思います）。
<img width="540" alt="pycharm-test-add.png" src="https://qiita-image-store.s3.amazonaws.com/0/3770/80118262-7b67-e653-72e2-4e5da624a8b7.png">

#### 2.3.2 実行設定
ここが一番ややこしいです。
**Windows上のパスか、VM上のパスかを区別して**設定する必要があります。
なお、パスは適宜自身の環境に合わせて読み替えてください。

以降の例では、プロジェクトパスは以下です。
`C:\Users\uj\src\github.com\ujiro99\auto_logger`

<img width="540" alt="pycharm-test設定.png" src="https://qiita-image-store.s3.amazonaws.com/0/3770/3f795d07-4ac2-bf88-241d-ac52ac645a1d.png">

* `Target`: ラジオボタンで`Path`を選択。Pathには、プロジェクトのtestファイルを保存したディレクトリを選択。
* `Python interpreter`: 「2.2. Project Interpreterの指定」で選択したInterpreterを選択。
* `Working directory`: **Windows上での**プロジェクトのパスを指定。
* `Path mappings`: `...`を押してダイアログを表示。
    * `Local path`は**Windows上での**プロジェクトのパスを指定。  
    * `Remote Path`はデフォルトのまま。
* `Docker container settings`: `...`を押してダイアログを表示。以下の通り設定。

<img width="263" alt="pycharm-test-container設定.png" src="https://qiita-image-store.s3.amazonaws.com/0/3770/f5ea042d-5fb0-2dbc-9a42-c2bea87432eb.png">

* `Volume bindings`: `>>`を押してダイアログを表示。
  * `Container path`はそのまま。
  * `Host path`は**VM上での**プロジェクトのパスを指定。

なお、「2.1. VMの指定」で`Path mappings`の設定を残していると**パスが意図せず変換**されてしまい、うまく行きません。

## 3. テスト実行

ここまでの設定が完了すると、テスト実行ができるようになります。
今回の例ですと、testファイル群を`プロジェクトパス/tests/`配下に格納しておき、テスト実行すると下記の通り実行されます。デバッグ実行、ブレークポイントの利用等も可能です。

<img width="841" alt="pycharm-test.png" src="https://qiita-image-store.s3.amazonaws.com/0/3770/b8fdb6f6-25bc-bffb-51fb-6e0c4409b28f.png">

## まとめ
Docker(Hyper-V)環境でPyCharmを使う方法を紹介しました。
正直、設定がかなり面倒なので、現時点では素直にLocal上へPython環境をこさえた方が宜しいかと存じます。
今後のJetbransの(というかDocker for Windowsの)発展に期待します。

[^1]: [`docker-machine regenerate-certs`](https://docs.docker.com/machine/reference/regenerate-certs/)を実行していればDocker for Windowsでも良かったかもしれません。