Docker時代のビルド手法について考える

## Dockerの登場によって「ビルド」の再考が求められている
2013年3月に登場したDockerによって、IaaS,PaaS, Linuxディストリビューション、プロビジョニングツール、など様々なものが、役割の再定義を迫られています。

コンパイル・ビルドという行為もまたその１つです。
伝統的な`./configure && make && make install`というお作法はDocker登場以前に考えられた方法なので、Docker Imageを構築する場合にいろいろ非効率なことがあります。

例えばファイルサイズです。
Docker Imageはレイヤーを重ねて構築されるので、ベースとなるイメージのサイズが大きいと、そこから派生する全てのイメージが肥大化してしまいます。
伝統的ビルド手法は、コンパイル時間の短縮や生成されたバイナリの実行速度向上に注力されることはあっても、生成物のファイルサイズにはわりと無頓着なことが多いのではないでしょうか。(make cleanがなかったり、あったとしてもmake cleanで不要なものが全部消えるわけではない)
今後ソフトウェアの配信方法としてDocker Image(またはその代替品)が主流になってくれば、ビルドは誰かが１回やったものを使い回せばよいので、ビルド時間よりも生成物のファイルサイズの方が重要になってくるかもしれません。

今まで全く意識されてこなかった「ビルド結果を小さくするノウハウ」が求められる時代が来ました。

では具体例をあげて、どうやってサイズを小さくするか考えてみましょう。

## 例：Perlをビルドしてみる

perlをビルドする素朴な例を考えてみました。

* Perl-Buildを使ってperlをビルド
* cpanmをインストール
* cartonをインストール

Dockerfileはこんな感じになるかと思います。

```text:Dockerfile
FROM centos:centos6
MAINTAINER DQNEO

# install required packages
RUN yum -y update; yum clean all
RUN yum -y install perl tar bzip2 gcc make; yum clean all

# install perl
RUN curl --silent https://raw.githubusercontent.com/tokuhirom/Perl-Build/master/perl-build | perl - 5.20.1 /opt/perl

# install cpanm
RUN curl -L http://cpanmin.us | /opt/perl/bin/perl -  App::cpanminus

# install carton
RUN /opt/perl/bin/cpanm Carton

# show perl version
CMD /opt/perl/bin/perl -v
```

ごくごく普通のビルド手順だと思いますが、この方法でビルドするとImageサイズが401MBになります。

```
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
dqneo/perl          0                   42a948187215        30 minutes ago       401.2 MB
centos              centos6             25c5298b1a36        7 days ago           215.8 MB
```

## ビルド時に不要ファイルを削減してみる

さて、今度はビルド時にRUNの末尾で不要ファイルの削除をやってみましょう。
具体的には、manとか一時ファイルを削除します。

```text:Dockerfile
FROM centos:centos6
MAINTAINER DQNEO

# install required packages
RUN yum -y update; yum clean all
RUN yum -y install perl tar bzip2 gcc make; yum clean all

# install perl
RUN (curl --silent https://raw.githubusercontent.com/tokuhirom/Perl-Build/master/perl-build | perl - 5.20.1 /opt/perl ) \
    && rm -rf /opt/perl/man/

# install cpanm
RUN (curl -L http://cpanmin.us | /opt/perl/bin/perl -  App::cpanminus) \
    && rm -rf /root/.cpanm/work/*

# install carton
RUN /opt/perl/bin/cpanm Carton \
    && rm -rf /root/.cpanm/work/*

# show perl version
CMD /opt/perl/bin/perl -v
```

docker buildして、生成物のサイズを見てみましょう。

```
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
dqneo/perl          1                   5221cef3751e        About a minute ago   372 MB
dqneo/perl          0                   42a948187215        30 minutes ago       401.2 MB
centos              centos6             25c5298b1a36        7 days ago           215.8 MB
```

イメージサイズが372MBに縮まりました。
rmコマンドを3回書くだけで20MB減リました。

## もっとがんばってみる
さらに、`perl-build`を使わずに素のtarballからの`./Configure && make && make instal`してみます。

```text:Dockerfile
FROM centos:centos6
MAINTAINER DQNEO

# install required packages
RUN yum -y update; yum clean all
RUN yum -y install tar bzip2 gcc make; yum clean all

RUN mkdir /usr/src/perl
WORKDIR /usr/src/perl

RUN curl -SL https://cpan.metacpan.org/authors/id/S/SH/SHAY/perl-5.20.1.tar.bz2 -o perl-5.20.1.tar.bz2 \
    && tar --strip-components=1 -xjf perl-5.20.1.tar.bz2 -C /usr/src/perl \
    && rm perl-5.20.1.tar.bz2 \
    &&./Configure  -Dprefix=/opt/perl -Duse64bitall  -des \
        && make -j$(nproc) \
      # && TEST_JOBS=$(nproc) make test_harness \
        && make install \
        && make veryclean \
        && rm -rf /opt/perl/man \
        && rm -rf /usr/src/perl

# install cpanm
RUN (curl -L http://cpanmin.us | /opt/perl/bin/perl -  App::cpanminus) \
    && rm -rf /root/.cpanm/work/*

# install carton
RUN /opt/perl/bin/cpanm Carton \
    && rm -rf /root/.cpanm/work/*

WORKDIR /root

CMD ["/opt/perl/bin/perl","-v"]
```

結果

```
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
dqneo/perl          2                   b598201ae1e0        About a minute ago   337.3 MB
```

今度は337MBになりました。
最初の素朴な例から、実に63MB削減できました。

## これはやる意味のあることなの？バッドノウハウでは？

議論の分かれるところだと思いますが、
この程度のhackならやってもいいんじゃないかと思います。
この程度で可読性が損なわれるとは思いません。
ソフトウェアによっては、ドキュメントのサイズが大きい場合もあり、その場合は削除の効果が大きいでしょう。

不要なものを消すにはそのソフトウェアのビルドの内部動作を多少知っている必要があるので、逆に勉強になるというメリットもあります。

## まとめ

いろんなものを小さくビルドするノウハウを共有しましょう！

## 参考
今回作ったDockerfile
https://github.com/DQNEO/docker-perl

Dockerイメージの最適化
http://wazanova.jp/items/1454