周回遅れのDocker 6: コマンドラインプログラムをサービス提供するコンテナ


あるコマンドラインプログラムを Docker コンテナとしてカプセル化して利用するといった手段の可能性について調べた。


## コンテナが起動（実行）される際の処理について

指定するイメージからコンテナを動かした際に実行されるコマンド（プログラム）は、Dockerfile 内の定義として `CMD` あるいは `ENTRYPOINT` で指定することができる。両者の違いはドキュメントと、日本語で明確な解説が書かれた記事がわかりやすい。


* [Dockerfile reference](https://docs.docker.com/reference/builder/)
* [dockerのENTRYPOINTとCMDの書き方と使い分け、さらに併用](http://qiita.com/hnakamur/items/afddaa3dbe48ad2b8b5c)
* [Dockerfileの RUN, CMD, ENTRYPOINT について](http://nbyst.hatenadiary.jp/entry/2014/06/22/152306)

両者は似ているようで微妙に違っていて、`ENTRYPOINT` の内容変更は docker run 時の --entrypoint で指定するのに対して `CMD` の方は docker run の引数であること、両者を指定するとコマンド実行が "`ENTRYPOINT` `CMD`" という解決が行われること、ということで `CMD` の方を比較的カジュアルに差し替えることを想定しているように思われる。

従って今回のような内部でコマンドラインを実行する用途や目的で考えた場合 `ENTRYPOINT` + `CMD` の組み合わせの上で、プログラムの実行パスを `ENTRYPOINT`, そのプログラムに渡すオプションや引数を `CMD` で指定するのが良さそう。


## 実験1: Ping サービス

ping を実行してその結果を返すサービスを提供するコンテナを考える。
次のような Dockerfile を用意した。

```Dockerfile
FROM busybox
ENTRYPOINT [ "ping" ]
CMD [ "--help" ]
```

このイメージから作られるコンテナを実行すると、引数を与えない限り `ping --help` が実行されることになる。早速試してみる。

```bash
$ docker build -t ping-service .
$ docker run --rm ping-service

BusyBox v1.22.1 (2014-05-22 23:22:11 UTC) multi-call binary.

Usage: ping [OPTIONS] HOST

Send ICMP ECHO_REQUEST packets to network hosts

        -c CNT          Send only CNT pings
        -s SIZE         Send SIZE data bytes in packets (default:56)
        -t TTL          Set TTL
        -I IFACE/IP     Use interface or IP address as source
        -W SEC          Seconds to wait for the first response (default:10)
                        (after all -c CNT packets are sent)
        -w SEC          Seconds until ping exits (default:infinite)
                        (can exit earlier with -c CNT)
        -q              Quiet, only displays output at start
                        and when finished
```

（BusyBox とか出力されているので）Docker コンテナの中で `ping --help` したであろう出力内容が表示された。ping-service を利用するには、HOST を指定しなければならないとわかる。

それでは HOST や OPTIONS を docker run 時に指定して再びサービスを実行してみる。

```bash
$ docker run --rm ping-service -c 5 google.com

PING google.com (216.58.220.238): 56 data bytes
64 bytes from 216.58.220.238: seq=0 ttl=61 time=23.672 ms
64 bytes from 216.58.220.238: seq=1 ttl=61 time=24.050 ms
64 bytes from 216.58.220.238: seq=2 ttl=61 time=23.656 ms
64 bytes from 216.58.220.238: seq=3 ttl=61 time=24.878 ms
64 bytes from 216.58.220.238: seq=4 ttl=61 time=24.027 ms

--- google.com ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 23.656/24.056/24.878 ms
```

今度は ping を実行した結果が出力された。オプション指定した -c 5 も正しく機能している。


## 実験2: ImageMagick サービス

続いて、ファイルを指定してファイルを出力するサービスの例として ImageMagick による画像変換を行う。

次のような Dockerfile を用意した。通常（何も指定しなかった場合）は --help を出力するのは先ほどと同じ。

```Dockerfile
FROM ubuntu:trusty
RUN apt-get update && apt-get install -y imagemagick

ENTRYPOINT [ "convert" ]
CMD [ "--help" ]
```

イメージを作ってコンテナを実行する。

実行時に渡す入力画像のファイルパス、および出力先となるファイルパスは Docker エンジンのホストではなくローカルマシン上にあるはずなので、Data volume としてローカルパスをそのままの内容でマウントする。こうすると同一パスが Docker エンジンホスト側にあると見なすことができる。

```bash
$ docker build -t imagemagick .
$ docker run --rm -v $PWD:$PWD imagemagick $PWD/image.png $PWD/image.jpg
```

この方法だと、入力ファイルと出力ファイルのディレクトリパスが異なる場合は当然ながらそれぞれのディレクトリを Data volume としてマウントする必要がある。

このようなシチュエーション下でより良い方法が知りたい。

## 備考

今回作成したようなサービス提供コンテナをベースにした拡張コンテナを作成する場合、その Dockerfile 内で `ENTRYPOINT`, `CMD` を定義すれば上書きされ、逆に定義しなければベース側の内容が採用される。普通に docker run する際に --entrypoint や引数を指定して `ENTRYPOINT`, `CMD` を上書きして実行しているのと同じといえる。