Dockerでnode.jsアプリを作るときにイメージ選択で悩んだ話

## 結果

採用したイメージは`node:4.4.5-slim`にしました。

## 考慮した点

- [公式のイメージ](https://hub.docker.com/_/node/)を使用する
- 出来る限り小さいイメージ
- [必要に応じてツールを追加する](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#avoid-installing-unnecessary-packages)

ベースとなるOSを用意して作成しても良かったのですがNodeに関しては公式のサポートがはやく新しいバージョンが出てから1日ほどで更新されるので公式のイメージ使わない理由がなかったので使用することにしました。

しかし、公式のdockerhubを見たらわかると思うのですが種類が複数あるのです。具体的には下記の4パターンです。

- node:4.4.5
- node:4.4.5-onbuild
- node:4.4.5-slim
- node:4.4.5-wheezy

それぞれ元となるDockerfileを見てどれを使うべきか判断していきます。バージョンは4.4.5で比較します。

まず、node:4.4.5の[コード](https://github.com/nodejs/docker-node/blob/fdcbbd6445c70290c50396cba2b2b67357a00629/4.4/Dockerfile)を見ていきましょう。

---

## node: 4.4.5

気になるところはベースとなるイメージです。最深部まで追っていきましょう。

node:4.4.5  
↓  
[buildpack-deps:jessie](https://github.com/docker-library/buildpack-deps/blob/e7534be05255522954f50542ebf9c5f06485838d/jessie/Dockerfile)  
↓  
[buildpack-deps:jessie-scm](https://github.com/docker-library/buildpack-deps/blob/1845b3f918f69b4c97912b0d4d68a5658458e84f/jessie/scm/Dockerfile)  
↓  
[buildpack-deps:jessie-curl](https://github.com/docker-library/buildpack-deps/blob/a0a59c61102e8b079d568db69368fb89421f75f2/jessie/curl/Dockerfile)  
↓  
[debian:jessie](https://github.com/tianon/docker-brew-debian/blob/35057319055fbeda72447ee824e5677c33db7bf9/jessie/Dockerfile)  
↓  
[scratch](https://hub.docker.com/_/scratch/)

追いかけることに成功しました。

### scratchとは

次にscratchが何をするのか見ていきましょう。

[Short Description](https://hub.docker.com/_/scratch/)を見る限りscratchは、明示的な空のイメージで、ゼロから構築するときに使用します。

### debianとは

次にdebian:jessieが何をするのか見ていきましょう。

`FROM scratch` はscratchで説明しました。  
`ADD rootfs.tar.xz /` これは何？

[github](https://github.com/tianon/docker-brew-debian/tree/35057319055fbeda72447ee824e5677c33db7bf9/jessie)を眺めていると答えがわかりました。
[build-command.txt](https://github.com/tianon/docker-brew-debian/blob/35057319055fbeda72447ee824e5677c33db7bf9/jessie/build-command.txt)内に使用するシェルとコマンドが存在したので試しに実行手順をまとめました。

```
$ vagrant init ubuntu/trusty64; vagrant up --provider virtualbox
$ vagrant ssh
$ sudo su -
$ apt-get install -y git debootstrap
$ git clone --depth 1 https://github.com/docker/docker.git
$ cd docker/contrib/
$ ./mkimage.sh -d ../../ debootstrap --variant=minbase --components=main --include=inetutils-ping,iproute2 jessie http://httpredir.debian.org/debian
$ cd ../../
$ tree . -L 1
.
├── docker
├── Dockerfile
└── rootfs.tar.xz
$ cat Dockerfile
FROM scratch
ADD rootfs.tar.xz /
CMD ["/bin/bash"]
```

何が起きたのか説明すると`debootstrap`コマンドを用いてjessieの環境を作りrootfs.tar.xzに固めてDockerfileを出力しています。
独自のイメージが作りたい場合はrootfs.tar.xzを展開してchrootして設定変更等を行うことができます。

`ADD`を使うことで`/`にrootfs.tar.xzを展開したファイルをImageに焼き付け、`CMD`で`/bin/bash`を起動しています。

`ADD`を使うべきか`COPY`を使うべきかは[ベストプラクティス](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#add-or-copy)を参考に展開を含む場合は`ADD`を使い単純なコピーの場合は`COPY`を使うようにしましょう。

### buildpack-deps:jessie-curlとは

次にbuildpack-deps:jessie-curlが何をするのか見ていきましょう。
単純にツールを追加しているだけで下記でした。ダウンロードはするけどgit等からチェックアウトすることは無いときに最適なイメージのようです。

- ca-certificates
- curl
- wget

また、イメージが膨らまないように`rm -rf /var/lib/apt/lists/*`と`--no-install-recommends`をしています。ツールを追加するときはイメージサイズを気にしましょう。

### buildpack-deps:jessie-scmとは

次にbuildpack-deps:jessie-scmが何をするのか見ていきましょう。  
こちらも単純にツールを追加しているだけで下記でした。bzr, git, hg, svnがサポートされるようです。チェックアウトするがビルドはしない時に最適なイメージのようです。

CVSは意図的に対象外にしているようです。バックスラッシュのみの行が存在するのは意図的に対象外にしている暗示かもしれないです(謎)

- bzr
- git
- mercurial
- openssh-client
- subversion
- procps

### buildpack-deps:jessieとは

次にbuildpack-deps:jessieが何をするのか見ていきましょう。  
こちらも更にツールを追加しているだけで下記でした。ビルドツールなどを導入しています。イメージの容量は大きくなるけどビルド失敗して困ることは減ると思います。

- autoconf
- automake
- bzip2
- file
- g++
- gcc
- imagemagick
- libbz2-dev
- libc6-dev
- libcurl4-openssl-dev
- libdb-dev
- libevent-dev
- libffi-dev
- libgeoip-dev
- libglib2.0-dev
- libjpeg-dev
- liblzma-dev
- libmagickcore-dev
- libmagickwand-dev
- libmysqlclient-dev
- libncurses-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

ようやく本題のnode:4.4.5が何をするか見ていきましょう。  
curlでtar ball持ってきて安全確認のためにチェックサム検証して解凍して配置、後処理をしているみたいですね。

まとめると、Debian 8 Jessieをベースイメージにcurlやgit、ビルドツールなど、プラスnode環境が含まれているということです。

## node: onbuild

次に、onbuildの
[コード](https://github.com/nodejs/docker-node/blob/fdcbbd6445c70290c50396cba2b2b67357a00629/4.4/onbuild/Dockerfile)
を見ていきましょう。

`FROM node:4.4.5` 先ほど調べたイメージを使用していることが分かりました。

下記の書き方は現在は推奨されていなかったはず(`WORKDIR`は対象ディレクトリが存在しない場合ディレクトリを生成するため実行ステップ数が減らせるため)なんですがなぜかこう書かれていました。

```
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
```

また、`ONBUILD COPY`が使われているようです。ビルド時に`COPY`が走るので、キャッシュが効かなくなりビルド時間が長くなるので個人的には使いたくないコマンドです。別のプロジェクトでも同じDockerfileを使えるのが良い点だと感じました。

## node: 4.4.5-slim

次に、4.4.5-slimの[コード](https://github.com/nodejs/docker-node/blob/fdcbbd6445c70290c50396cba2b2b67357a00629/4.4/slim/Dockerfile)を見ていきましょう。

`FROM buildpack-deps:jessie-curl` 先ほど調べたイメージを使用していることが分かりました。

ダウンロードのみに特化しているイメージだったので後は`xz-utils`を導入してファイル展開できるようにしています。もちろん使用後は`apt-get purge -y --auto-remove xz-utils`で必要なくなったツールを削除しています。

## node: 4.4.5-wheezy

最後に、4.4.5-wheezyの[コード](https://github.com/nodejs/docker-node/blob/fdcbbd6445c70290c50396cba2b2b67357a00629/4.4/wheezy/Dockerfile)を見ていきましょう。

4.4.5で見た内容とほぼほぼ同じで違いは`build-command.txt`のビルド方法がwheezyに変わっているだけでした。

## まとめ

- 4.4.5は、Debian 8 Jessieをベースイメージに`curl`や`git`、ビルドツールなど、プラスnode環境が含まれている
- 4.4.5-onbuildは、4.4.5をベースイメージとして`package.json`の導入や`npm install`をビルドごとに導入することができる
- 4.4.5-slimは、Debian 8 Jessieをベースイメージに`curl`と、プラスnode環境が含まれている
- 4.4.5-wheezyは、Debian 7 Wheezyをベースイメージに`curl`や`git`、ビルドツールなど、プラスnode環境が含まれている

考慮した結果採用したイメージは`4.4.5-slim`にしました。