この前作ったMeCabのgRPCサーバ(mecab-grpc)のDockerイメージサイズが1.27GBとそこそこの大きさなので出来る限りサイズを小さくしてみたいと思います。
Dockerfile
FROM python:3.5.6-alpine3.9
LABEL Name=mecab-grpc Version=1.0.0
RUN apk add --no-cache git make g++ swig
WORKDIR /
RUN git clone https://github.com/taku910/mecab.git
WORKDIR /mecab/mecab
RUN ./configure --enable-utf8-only \
&& make \
&& make check \
&& make install
WORKDIR /mecab/mecab-ipadic
RUN ./configure --with-charset=utf8 \
&& make \
&& make install
WORKDIR /mecab/mecab-jumandic
RUN ./configure --with-charset=utf8 \
&& make \
&& make install
COPY . /mecab-grpc
WORKDIR /mecab-grpc
RUN python -m pip install --upgrade pip \
&& python -m pip install -r requirements.txt \
&& sh protoc-gen.sh
CMD ["python", "server.py"]
現状はこんな感じ。ベースイメージは既にalpineを使っているので、ベースイメージはそのままで不要なパッケージを削除したりレイヤー数を抑えたりする感じになると思います。(頑張ればマルチステージビルドとdistrolessみたいな方向性で極限まで削れるんだろうけど、今回のケースは手間に対して得られる効果が薄そうなのでalpineと古典的な削減方法で妥協)
調べる
削減の方法はググればいっぱい出てくる。実際に作業する前に調べて得たポイントを書き出してみる。
- 小さいベースイメージを使う
- レイヤーの数を最小限にする
- デバッグ用のツールをインストールしない
- 不要になったツールは同じレイヤーで削除
- 適切な
.dockerignore
apt-get install -y --no-install-recommends …
rm -rf /var/lib/apt/lists/*
apt-get purge -y --auto-remove …
apk add --no-cache …
apk add —-virtual …
apk del --purge …
どのサイトもこんな感じで同じようなことを言ってた。まとめると、
- アプリの動作にとって不要なものはインストールしない
- ツール(とかソース、キャッシュ、その他一時的なファイル)の追加と削除は1つのコンテキストと考えて、1つのレイヤーで完結させる
という感じだと思う。
やってみる
まずどのレイヤーでどのくらいサイズが増えているのか見たいので docker history
をする。
IMAGE CREATED CREATED BY SIZE COMMENT
67eb23360bbf 4 hours ago /bin/sh -c #(nop) CMD ["python" "server.py"] 0B
2978157db9ba 4 hours ago /bin/sh -c python -m pip install --upgrade p… 216MB
ad947e372dde 4 hours ago /bin/sh -c #(nop) WORKDIR /mecab-grpc 0B
6842b17a252e 4 hours ago /bin/sh -c #(nop) COPY dir:7ff7416db8e5d3d9d… 3.85kB
b7a768bcc5b6 4 hours ago /bin/sh -c ./configure --with-charset=utf8 … 297MB
f08be6cb767a 4 hours ago /bin/sh -c #(nop) WORKDIR /mecab/mecab-juman… 0B
db55cd3ac0c1 4 hours ago /bin/sh -c ./configure --with-charset=utf8 … 106MB
33cd77a79206 4 hours ago /bin/sh -c #(nop) WORKDIR /mecab/mecab-ipadic 0B
c77fe2139ae9 4 hours ago /bin/sh -c ./configure --enable-utf8-only … 10.7MB
05277449a505 4 hours ago /bin/sh -c #(nop) WORKDIR /mecab/mecab 0B
1c7466774cb7 4 hours ago /bin/sh -c git clone https://github.com/taku… 379MB
b5e81d428587 4 hours ago /bin/sh -c #(nop) WORKDIR / 0B
7471a9d7fcf5 4 hours ago /bin/sh -c apk add --no-cache git make g++ s… 188MB
3201b5481ba9 4 hours ago /bin/sh -c #(nop) LABEL Name=mecab-grpc Ver… 0B
be8ce886a36a 6 days ago /bin/sh -c #(nop) CMD ["python3"] 0B
<missing> 6 days ago /bin/sh -c set -ex; wget -O get-pip.py 'ht… 6.04MB
<missing> 6 days ago /bin/sh -c #(nop) ENV PYTHON_PIP_VERSION=19… 0B
<missing> 3 weeks ago /bin/sh -c cd /usr/local/bin && ln -s idle3… 32B
<missing> 3 weeks ago /bin/sh -c set -ex && apk add --no-cache --… 61.8MB
<missing> 3 weeks ago /bin/sh -c #(nop) ENV PYTHON_VERSION=3.5.6 0B
<missing> 3 weeks ago /bin/sh -c #(nop) ENV GPG_KEY=97FC712E4C024… 0B
<missing> 3 weeks ago /bin/sh -c apk add --no-cache ca-certificates 551kB
<missing> 3 weeks ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0B
<missing> 3 weeks ago /bin/sh -c #(nop) ENV PATH=/usr/local/bin:/… 0B
<missing> 4 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 4 weeks ago /bin/sh -c #(nop) ADD file:2a1fc9351afe35698… 5.53MB
サイズが増えているところをピックアップして見てゆく。
apk add
しているところで188MB増。 g++
パッケージ以外( git
、make
、swig
)はビルド&インストールする時しか必要ないので使い終わったら削除できる。( g++
にはロードされる共有ライブラリが含まれる。あとコンパイラも含まれてるから改善の余地あり)
MeCabのリポジトリをクローンしているところで379MB増。これはMeCab本体と辞書のインストールが終わったら必要なくなるので削除できる。
MeCab本体のインストールで10.7MB増、ipadicのインストールで106MB増、jumandicのインストールで297MB増。jumandicは正直使ったことが無いので削除。(辞書は人それぞれ。というか辞書によってはサイズが爆増するので、イメージサイズを頑張って削減するぞ!的な試みがほぼ無意味になるという)
最後に pip
のアップグレードと依存パッケージのインストールとgRPCのスタブ諸々の生成で216MB増。そもそも pip
はベースイメージの段階で最新のバージョンが入るっぽいからアップグレードは必要ない(削減とはあまり関係ないけど)。あとは依存パッケージのインストール時に pip
のキャッシュを作らないために --no-cache-dir
を追加する。
というわけで、書き直した Dockerfile が以下。
FROM python:3.5.6-alpine3.9
LABEL Name=mecab-grpc Version=1.0.0
COPY . /opt/mecab-grpc
RUN set -x \
&& apk add --no-cache --virtual .run-deps \
g++ \
\
&& apk add --no-cache --virtual .build-deps \
git \
make \
swig \
\
&& mkdir -p /usr/local/src \
&& cd /usr/local/src \
&& git clone https://github.com/taku910/mecab.git \
\
&& cd /usr/local/src/mecab/mecab \
&& ./configure --enable-utf8-only && make && make check && make install \
\
&& cd /usr/local/src/mecab/mecab-ipadic \
&& ./configure --with-charset=utf8 && make && make check && make install \
\
&& cd /opt/mecab-grpc \
&& pip install \
--disable-pip-version-check \
--no-cache-dir \
-r requirements.txt \
&& sh protoc-gen.sh \
\
&& apk del --purge .build-deps \
&& rm -rf /usr/local/src/mecab
WORKDIR /opt/mecab-grpc
CMD ["python", "server.py"]
いくつかに分散していた RUN
と WORKDIR
が1つ RUN
にまとまっている(その都合上 COPY
を RUN
の前に持ってくる)。 RUN
の中ではまず set -x
をする。これで単一の RUN
で複数のコマンドを実行する時の出力が見やすくなる。公式のDockerfileがやってるのを真似した(サブシェルとかパイプを使う場合は set -ex
としたり)。
apt add
では --virtual
でパッケージ群の意図の把握と管理がしやすいように名前を付ける。( .run-deps
は後で何かをするわけじゃないけど、今回は分かりやすいようにあえて)
MeCab本体と辞書のインストールは特に変わらない(jumandicは削除)。 pip
はバージョンチェックのスキップをするために --disable-pip-version-check
を追加(新しいバージョンの pip
があるぜ的なメッセージを出さないために)。 --no-cache-dir
は上述の通り。
そんで RUN
の最後に忘れずに apk del
で .build-deps
を削除して、MeCabのソースコードも削除。
こんな感じでイメージサイズの合計が442MB。約1/3に削減できた。