もっと軽いOpenCVコンテナを求めて

# TL; DR
dockerでOpenCVが使えるpythonのコンテナが欲しかった。
適当にpythonのイメージを拾ってきて作ると1.07gbになってしまったが
ちょっと頑張ると440MBまで小さくなった。

# 概要
railsで作っているサービスに画像処理の機能を追加することになったが、
画像処理ならpythonの方が使い勝手が良さそうだと思った(偏見)ので、pythonコンテナを用意することにした。
何も考えずに用意すると、イメージサイズが1gbを超えてしまった。
railsや他のコンテナのイメージサイズと合わせると、酷いことになってしまったので、
頑張ってサイズを小さくした。


# 初めに用意したコンテナ
はじめに用意したDockerfileはかなりシンプル。

```Dockerfile
FROM python:3.7.0

WORKDIR /app
COPY requirements.txt /app/

RUN pip install --no-cache-dir -r requirements.txt
```
特に何事もなく動作確認ができたので、そのまま完了報告をしようかと思ったが
何か重たいなと感じ、

```shell
$ docker images
```
と叩いてみると
> 1.07GB

と表示されてしまった。
「え？デカ過ぎないか？」
python:3.7.0のイメージサイズが923MBとなっていたのでベースのイメージからして
重過ぎることはすぐに分かったが

```shell
$ docker history $image_name
```
とすると、サイズが大きくなってしまう原因を絞り込めると最近知ったので調べて見た。
するといくつか怪しそうなものが。。。

```
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
82ef4046d424        3 minutes ago       /bin/sh -c pip install --no-cache-dir -r req…   149MB
47b14d8c6138        About an hour ago   /bin/sh -c #(nop) COPY file:459af46ed1f18f20…   262B
c77fb443d300        About an hour ago   /bin/sh -c #(nop) WORKDIR /app             0B
a9d071760c82        2 weeks ago         /bin/sh -c #(nop)  CMD ["python3"]              0B
<missing>           2 weeks ago         /bin/sh -c set -ex;   wget -O get-pip.py 'ht…   5.93MB
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PYTHON_PIP_VERSION=18…   0B
<missing>           2 weeks ago         /bin/sh -c cd /usr/local/bin  && ln -s idle3…   32B
<missing>           2 weeks ago         /bin/sh -c set -ex   && wget -O python.tar.x…   69.9MB
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PYTHON_VERSION=3.7.0     0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV GPG_KEY=0D96DF4D4110E…   0B
<missing>           2 weeks ago         /bin/sh -c apt-get update && apt-get install…   17MB
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PATH=/usr/local/bin:/…   0B
<missing>           2 weeks ago         /bin/sh -c set -ex;  apt-get update;  apt-ge…   556MB
<missing>           2 weeks ago         /bin/sh -c apt-get update && apt-get install…   142MB
<missing>           2 weeks ago         /bin/sh -c set -ex;  if ! command -v gpg > /…   7.8MB
<missing>           2 weeks ago         /bin/sh -c apt-get update && apt-get install…   23.2MB
<missing>           2 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 weeks ago         /bin/sh -c #(nop) ADD file:58d5c21fcabcf1eec…   101MB
```

因みに、以下にオプションをつけると各行のコマンドを省略せずに確認できた。

```shell
$ docker history $image_name --no-trunc=true
```

```
<missing> 2 weeks ago /bin/sh -c set -ex;  apt-get update;
apt-get install -y --no-install-recommends
autoconf   automake   bzip2   dpkg-dev   file   g++   gcc   imagemagick   libbz2-dev   libc6-dev   libcurl4-openssl-dev   libdb-dev   libevent-dev   libffi-dev   libgdbm-dev   libgeoip-dev   libglib2.0-dev   libjpeg-dev   libkrb5-dev   liblzma-dev   libmagickcore-dev   libmagickwand-dev   libncurses5-dev   libncursesw5-dev   libpng-dev   libpq-dev   libreadline-dev   libsqlite3-dev   libssl-dev   libtool   libwebp-dev   libxml2-dev   libxslt-dev   libyaml-dev   make   patch   xz-utils   zlib1g-dev
$(if apt-cache show 'default-libmysqlclient-dev' 2>/dev/null | grep -q '^Version:'; then
  echo 'default-libmysqlclient-dev';
else
  echo 'libmysqlclient-dev';
fi   )  ;
rm -rf /var/lib/apt/lists/*
556MB
```
```
<missing> 2 weeks ago /bin/sh -c apt-get update &&
apt-get install -y --no-install-recommends   bzr   git   mercurial   openssh-client   subversion     procps  && rm -rf /var/lib/apt/lists/*
142MB
```
```
<missing> 2 weeks ago /bin/sh -c #(nop) ADD file:
58d5c21fcabcf1eec94e8676a3b1e51c5fdc2db5c7b866a761f907fa30ede4d8 in /
101MB
```
# alpine(非推奨かつ未完了)
python:3.7.0のイメージには不要なものが混ざっているようだったので、
もう少し小さいものから作ることにした。
調べているとalpineではpip installでopencv-pythonをインストールは出来なくて
自分でbuildしないといけないという話があったので、やろうとして見た。
まだ完成していないが、大体はこんな感じで良さそうだった。

```Dockerfile
FROM python:3.7.0-alpine

WORKDIR /app
COPY requirements.txt /app/

RUN apk upgrade --no-cache && \
  apk add --update --no-cache \
  redis \
  build-base \
  cmake \
  gtk+3.0 \
  pkgconfig \
  linux-headers

RUN mkdir /opencv && \
  cd /opencv && \
  wget https://github.com/Itseez/opencv/archive/3.4.3.zip && \
  unzip 3.4.3.zip && \
  cd opencv-3.4.3 && \
  mkdir build && \
  cd build && \
  cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_IPP=ON .. && \
  make -j7 && \
  make install && \
  rm -rf /opencv

RUN pip install -r requirements.txt
```
ただ、buildにかなりの時間がかかってしまうことと、まだいくつかエラーが残ってしまっているので、
やる気がなくなり、alpineはやめることにした。


# slim
最終的に採用したのはpython:3.7.0-slim。
OpenCVを使うためにはいくつかlibが足りなかったので、手動で入れる必要があったものの
alpineの時ほど頭をかかえる必要もなく、用意ができた。
そしてサイズも440MBと最初の半分以下になってくれた！
[もっと小さくする方法](https://github.com/docker-slim/docker-slim)もあるようだが、まぁこれは一旦は良いかと思った。
これをベースにして、足りないものが出てきたら適宜追加していくことにする。

```Dockerfile
FROM python:3.7.0-slim

WORKDIR /app
COPY requirements.txt /app/

ENV LIB="libswscale-dev \
  libtbb2 \
  libtbb-dev \
  libjpeg-dev \
  libpng-dev \
  libtiff-dev \
  libglib2.0-0 \
  libsm6 \
  libxext6 \
  libavformat-dev \
  libpq-dev"

RUN apt-get update \
  && apt-get -y upgrade \
  && apt-get install --no-install-recommends -qy $LIB \
  && apt-get clean \
  && apt-get autoclean \
  && apt-get autoremove \
  && pip install --no-cache-dir -r requirements.txt \
  && rm -rf /tmp/* /var/tmp/* \
  && rm -rf /var/lib/apt/lists/* \
  rm -rf /var/lib/apt/lists/*
```

# まとめ
dockerのイメージサイズはもっと手軽に小さくしていきたい。
あと、このままだとOpenCVで日本語が文字化けするので、適当にフォントを拾ってこないといけなさそう。
~~railsのイメージサイズは重たいまま~~