QNAP に Pypi キャッシュサーバを建てる

# はじめに
[QNAP に Apt キャッシュサーバを建てる](http://qiita.com/jkawamoto/items/fe0f098642b95e60a7c2)に続き，
[QNAP](https://www.qnap.com/i/jp/) の NAS に Docker コンテナとして Pypi キャッシュサーバを建てる．
LAN 内での pip の高速化とトラフィック削減を目指す．

# Pypi キャッシュサーバ
Pipit のキャッシュサーバとして，[devpi server](http://doc.devpi.net/latest/) を用いる．Docker で運用するので，devpi のイメージを探す．
例えば，[scrapinghub/docker-devpi](https://github.com/scrapinghub/docker-devpi) などが見つかった．

[前回](http://qiita.com/jkawamoto/items/fe0f098642b95e60a7c2) にも述べた通り，手元の NAS は ARM ベースであった．
そのため，別途 ARM 用 devpi イメージを作成した．
作成したイメージの Dockerfile は，[GitHub](https://github.com/jkawamoto/dockerfiles/tree/master/armhf-devpi-server) に，イメージは [DockerHub](https://hub.docker.com/r/jkawamoto/armhf-devpi-server/) に置いてある．

# devpi server コンテナの起動
コンテナの管理には，[Container Station](https://www.qnap.com/solution/container_station/jp/)を利用する．
もしなければ，App Center からインストールする．Container Station のマニュアルは [ここ](https://www.qnap.com/i/jp/tutorial/con_show.php?op=showone&cid=179) から参照できる．

<img width="1289" alt="fig6.png" src="https://qiita-image-store.s3.amazonaws.com/0/62253/fc49ba67-ddb8-23e8-1f60-0ccc7fc4ee7c.png">

コンテナの作成タブから，利用するコンテナイメージを検索する．
デフォルトでは DockerHub にあるイメージを利用できる．

<img width="1311" alt="fig7.png" src="https://qiita-image-store.s3.amazonaws.com/0/62253/381d0182-6043-f3b1-26bb-cf1f90d6578f.png">

目的のイメージが見つかったらインストールする．

<img width="1304" alt="fig8.png" src="https://qiita-image-store.s3.amazonaws.com/0/62253/94d21844-d23f-f1ea-48dc-fd45ab92d1de.png">

インストールが終わった，あるいは既にインストール済みの場合は，
コンテナの作成画面が開く．

コンテナの名前は好きに決めれば良い．
コマンドとエントリーポイントは Dockerfile の設定をここで上書きできるが，
特に指示されていなければデフォルトで良いだろう．
CPU やメモリの制限は環境によって変わるので，ここではデフォルトのままにしておく．
次に，詳細設定のネットワーク項目を確認する．

<img width="1284" alt="fig9.png" src="https://qiita-image-store.s3.amazonaws.com/0/62253/a6d8003c-04bd-d36b-b9de-b5e0a3eabe65.png">

ネットワークの設定で，どのポートがフォワーディングされるか確認する．
devpi server はデフォルトで 3141 を利用する．
ホスト側のポート番号は，始め auto になっているので 3141 へ変更しておく．
また，ベースとなったイメージによっては関係ないポートも空いているので，削除しておく．
下の図の場合，6080 は不要なので削除する．

<img width="1289" alt="fig10.png" src="https://qiita-image-store.s3.amazonaws.com/0/62253/a4bfaf9f-992c-f720-8b5d-d93bbe8b27c6.png">

なお，共有フォルダの項目で，キャッシュサーバが利用するディレクトリをマウントすることもできる．
以上の設定が終わればコンテナを作成する．

# キャッシュサーバ利用側の設定
pip コマンドにキャッシュサーバを利用させるには，
`--index-url` オプションとしてキャッシュサーバの URL を，
`--trusted-host`オプションとしてキャッシュサーバの IP アドレスを渡す．
trusted-host が必要なのは，キャッシュサーバが https ではなく http のみで待ち受けるためである．
https をサポートさせるのは手間なので，信頼できるネットワーク内でのみ使うということにして，
trusted-host を設定する．

なお，キャッシュサーバの URL は，IP アドレスが `xxx.xxx.xxx.xxx` だとすると，
`http://xxx.xxx.xxx.xxx:3141/root/pypi` になる．
`/root/pypi` の部分は忘れやすいので注意．(`/root/pypi` 以外のURLアドレスについては[ドキュメント](http://doc.devpi.net/latest/)を参照)

しかし，毎回 pip コマンドにオプションを付けるのは手間なので，
設定ファイルに利用することもできる．
`~/.pip/pip.conf` に以下の内容を記述する．

```toml:pip.conf
[global]
index-url="http://xxx.xxx.xxx.xxx:3141/root/pypi"
trusted-host="xxx.xxx.xxx.xxx"
```

以降，pip を使うときはキャッシュサーバにアクセスするようになる．

# Docker での利用
Docker で pip を最も利用するケースは，コンテナ実行時よりはビルド時，すなわち Dockerfile の中になる．
docker build 時にキャッシュサーバを利用可否を決めるように，ARG コマンドを利用する．

```Dockerfile:Dockerfile
ARG PIP_PROXY
RUN if [ -n "$PIP_PROXY" ]; then \
      echo "Set pip proxy: $PIP_PROXY"; \
      IPPORT=${PIP_PROXY#*//}; \
      mkdir -p ~/.pip/; \
      echo "[global]\nindex-url=$PIP_PROXY/root/pypi\ntrusted-host=${IPPORT%:*}" >> ~/.pip/pip.conf; \
      cat ~/.pip/pip.conf; \
    fi
```

Dockerfile に上記のような項目を追加する．
`ARG` で定義されているのが変数であり，`docker build` 時に `—build-arg` オプションで値を渡せる．

上の Dockerfile の場合，`--build-arg PIP_PROXY=http://xxx.xxx.xxx.xxx:3141/` を
`docker build` に渡すと，`~/.pip/pip.conf` を適切に設定する．
`—build-arg` を使って `PIP_PROXY` 変数を設定しない場合はスキップされる．

以上により，ビルド時にキャッシュサーバの利用を決めることができるようになった．

# まとめ
QNAP NAS の [Container Station](https://www.qnap.com/solution/container_station/jp/) を使って，[devpi server](http://doc.devpi.net/latest/) コンテナを用意した．
ARM 環境の人は手間がかかるので，今回作成したイメージを使うと楽だろう．
また， Dockerfile の ARG コマンドを使って，ビルド時にキャッシュサーバの利用を切り替えるようにした．

今回も，[(Docker で) PyPI のキャッシュサーバを建ててまた少し幸せになった話](http://qiita.com/kitsuyui/items/8a1c464af539ef268ce6)が参考になりました．