はじめに
最近、検証環境を構築するにあたり Dockerfile
を書き始めるところから入るカワハラです。おはようございます。で、結局、当初の検証目的を忘れて Dockerfile
書きに没頭してしまって色々と滞っております...つきましては、Dockerfile
を書くにあたってのメモを残しておきたいと思います。あくまでも「私的」なベストプラクティスですのでご注意下さい...
参考
以下のサイトを参考にさせて頂きました。
- Dockerfile Best Practices
- http://docs.docker.io/en/latest/examples/
- Build Images (Dockerfile Reference)
- DockerでJava Webアプリケーションの検証環境を構築する
- How to Use Entrypoint in Docker Builder
「私的」なベストプラクティス
MAINTAINER は入れましょう
どこの誰が作ったかをハッキリした方が良いと思うので MAINTAINER
は指定しましょう。
MAINTAINER YOHEI KAWAHARA inokappa
上記のような感じ。
ENV
RUN
したい時にパスが通っていないと悲しいのでパスが通ってなさそうだなーと思ったら、とりあえず以下のように RUN
の前あたりで ENV
でパスをしてしてあげると大概の場合は RUN
が通りました。特にディレクトリごとバイナリをコピーした時等。
ENV PATH $PATH:/usr/local/jruby/bin RUN jgem install hoge
上記の例は PATH
に /usr/local/jruby/bin
を追加している。この後に jgem
や jruby
等が使えるようになる。
EXPOSE
EXPOSE
は直訳すると「晒す」。コンテナを起動した際に外部に公開するポートを指定することが出来る。docker run
する時にオプションで指定する -p
と同じ役割をする。例えば、Jenkins
のデフォルトポート 8080
を晒す場合には以下のようにする。
EXPOSE 8080 CMD /usr/bin/java -jar /usr/share/jenkins/jenkins.war --webroot=/var/cache/jenkins/war --httpPort=8080 --ajp13Port=-1
こんな感じにしておく。ちなみに、EXPOSE 8080:12345
としておくとコンテナをホストしている localhost
の 12345
にバインドさせることも出来る。(はずなんだけど、何度試しても 12345
だけがバインドされてしまうので引き続き調べる)
CMD とか ENTRYPOINT とか
まだ、ちゃんと整理しきれていない部分もあるが、CMD とか ENTRYPOINT で docker run
した時の挙動が異なるのでざっくりと書いてみる。
それぞれ、以下のような Dockerfile
を作成して実行してみる。
以下はパターン 1。
# test1 FROM inokappa/wheezy-7.2-basic MAINTAINER YOHEI KAWAHARA inokappa ENTRYPOINT echo
以下はパターン 2。
# test2 FROM inokappa/wheezy-7.2-basic MAINTAINER YOHEI KAWAHARA inokappa CMD echo
どちらも echo
を実行するだけ。とりあえず docker build -t inokappa/test1 .
と docker build -t inokappa/test2 .
で構築してから docker run
してみる。
docker run inokappa/test1
以下のように表示される。
[なんにも表示されない]
同様に...
docker run inokappa/test2
以下のように表示される。
[なんにも表示されない]
これだけだとどちらも変わらない。「なんや、CMD
と ENTRYPOINT
は一緒やん...」と思ったが...
docker run inokappa/test1 a
以下のように表示される。
[なんにも表示されない]
では、docker run inokappa/test2 a
とすると...
2013/12/28 03:06:58 Unable to locate a
というエラーが表示される。なるほど、なるほど、a
がコマンドとして認識されてるっぽいぞ。ちなみに終了コードを比較してみる。
さらに docker inspect
で確認してみる。(一部、抜粋)
// docker run inokappa/test1 a { "Config": { "Entrypoint": [ "/bin/sh", "-c", "echo" ], "Cmd": [ "a" ] }, "Args": [ "-c", "echo", "a" ], "Path": "/bin/sh" }
ほうほう、ENTRYPOINT
で指定された場合に Path
に /bin/sh -c
が補完されており、docker run
で指定したコマンドの a
は Cmd
に設定されている。
// docker run inokappa/test2 a { "Config": { "Entrypoint": null, "Cmd": [ "a" ] }, "Args": [], "Path": "a" }
CMD
は Path
には何も設定されず Cmd
に docker run
で引数につけた a
が設定されているので a
のみが実行される状態にエラーとなる。
ということで、CMD
と ENTRYPOINT
の違いは以下のような点が挙げられるかなと。
CMD
でコマンドを指定した場合にはそのコマンドの引数をdocker run
時に渡したら別のコマンドとして認識されるCMD
はDockerfile
内で完結させる必要がある(完結してしまう)ENTRYPOINT
でコマンドを指定した場合にはそのコマンドの引数をdocker run
時に渡すと引数として認識されるENTRYPOINT
とCMD
の合わせ技を用いることが出来る
なんかあった時の為にログイン出来るユーザーは作っておく
どうせコンテナなんだし上手く動かなければ捨てて Dockerfile
を修正して作りなおせば良いし、docker attach
も使えたりするのだが、コンテナにログイン出来る手順は複数あった方が良いというのが持論。ということで、以下のように sshd
のインストールとログイン用のユーザーも作っておく。
RUN apt-get install openssh-server RUN mkdir -p /var/run/sshd RUN useradd -d /home/hogehoge -m -s /bin/bash hogehoge RUN echo hogehoge:${your_pass} | chpasswd RUN echo 'hogepass ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
docker run する時
docker run
した後にすぐにコンテナの IP
を確認したい時が多々あるので、以下のように docker ps -a
してわざわざコンテナの ID
を調べなくて便利だったりする。
con=`docker run -d ${container/image}` docker inspect $con | jq '.[].NetworkSettings.IPAddress'
以下のような感じになる。(※.[].NetworkSettings.IPAddress
でも .[]|.NetworkSettings.IPAddress
でもイケてしまう)
最後に
Dockerfile
を通してdocker
の動作について少しだけ理解を深めることが出来た(気がする)CMD
とENTRYPOINT
の違いについてはまだまだモヤっとしているので使いながらフォローアップしていく