はじめに
あなたは Docker イメージを作る際に start.sh
や startup.sh
というファイルを作った経験はないだろうか。「Dockerfile の CMD
だと 1 コマンドしか書けないから、シェルスクリプトで頑張る」というやつだ。例えば、環境変数を受け取って設定ファイルを動的に作ったり、メインプロセス起動前に何かしら初期化処理をするなど。そんなあなたに Entrykit というツールを紹介しよう。
Entrykit とは
Entrykit は一言でいうと、コンテナ内のプロセス起動時に便利な軽量 init システムだ。Docker の起動コマンドに Entrykit を使うと、起動時にテンプレートファイルを元に設定ファイルをレンダリングしてくれたり、メインプロセスの前にコマンドを実行できたりする。
- 公式サイト - https://github.com/progrium/entrykit
- 読み方 - エントリーキット
- 一言でいうと - コンテナ内のプロセス起動時に便利な軽量 init システム
- 公式ドキュメント - https://github.com/progrium/entrykit/blob/master/README.md
- 最新バージョン - Releases · progrium/entrykit を参照
この記事では Entrykit を使うと何ができて、どう嬉しいかを例とともに紹介する。
インストール
Entrykit は Go で書かれていて、GitHub にバイナリリリースが置いてあるため、それをコンテナの中に置いて使う。
FROM gliderlabs/alpine:3.2
MAINTAINER Seigo Uchida <spesnova@gmail.com> (@spesnova)
ENV ENTRYKIT_VERSION 0.4.0
WORKDIR /
RUN apk-install openssl \
&& wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& mv entrykit /bin/entrykit \
&& chmod +x /bin/entrykit \
&& entrykit --symlink
entrykit --symlink
はサブコマンドのシンボリックリンクを作ってくれる。
$ entrykit --symlink
entrykit
Creating symlink /bin/entrykit ...
Creating symlink /bin/codep ...
Creating symlink /bin/prehook ...
Creating symlink /bin/render ...
Creating symlink /bin/switch ...
render - テンプレートのレンダリング
render
コマンドは、指定したテンプレートをコンテナ起動時にレンダリングしてから、ルートプロセスを起動してくれる。特にコンテナ起動時に環境変数で設定ファイルの中身を上書きしたいときに便利だ。
簡単な例
例えば、nginx.conf
をレンダリングしたいとしよう。
まずはテンプレートとなる nginx.conf.tmpl
を用意する。テンプレートエンジンは sigil が用いられる。
daemon off;
error_log /dev/stdout notice;
user nobody nogroup;
worker_processes {{ var "WORKER_PROCESSES" | default "1" }};
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
access_log /dev/stdout;
root /;
location / {
index /index.html;
}
}
}
4 行目の部分がテンプレートを使って置換したいところ。
コンテナ起動時に WORKER_PROCESSES
という環境変数で設定できるようにする。
worker_processes {{ var "WORKER_PROCESSES" | default "1" }};
表示する HTML ファイル(index.html
) も用意しておこう。
hello world
次に Dockerfile
を用意しよう。
FROM gliderlabs/alpine:3.2
MAINTAINER Seigo Uchida <spesnova@gmail.com> (@spesnova)
ENV ENTRYKIT_VERSION 0.4.0
WORKDIR /
# Install Entrykit
RUN apk-install openssl \
&& rm -rf /var/cache/apk/* \
&& wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& mv entrykit /bin/entrykit \
&& chmod +x /bin/entrykit \
&& entrykit --symlink
RUN apk-install nginx
COPY ./nginx.conf.tmpl /etc/nginx/nginx.conf.tmpl
COPY ./index.html /index.html
ENTRYPOINT ["render", "/etc/nginx/nginx.conf", "--", "/usr/sbin/nginx"]
ポイントは 18 行目。テンプレートファイルをコンテナの中に入れておく必要がある。
COPY ./nginx.conf.tmpl /etc/nginx.conf.tmpl
そして、entrykit の render
コマンドにレンダリング先ファイル nginx.conf
を指定する。
ENTRYPOINT ["render", "/etc/nginx.conf", "--", "/usr/sbin/nginx"]
これで entrykit を使った nginx イメージをビルドして起動してみる。
$ docker build -t=entrykit-example .
$ docker run -it --rm -p 80:80 entrykit-example
2016/02/18 08:10:46 [notice] 1#0: using the "epoll" event method
2016/02/18 08:10:46 [notice] 1#0: nginx/1.8.1
2016/02/18 08:10:46 [notice] 1#0: OS: Linux 4.1.13-boot2docker
2016/02/18 08:10:46 [notice] 1#0: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2016/02/18 08:10:46 [notice] 1#0: start worker processes
2016/02/18 08:10:46 [notice] 1#0: start worker process 11
2016/02/18 08:10:46 [notice] 1#0: start worker process 11
Nginx の worker プロセスは 1 つである。環境変数 WORKER_PROCESSES
を使って worker プロセス数を変えてみよう。
$ docker run -it --rm -p 80:80 -e WORKER_PROCESSES=2 entrykit-example
2016/02/18 08:11:54 [notice] 1#0: using the "epoll" event method
2016/02/18 08:11:54 [notice] 1#0: nginx/1.8.1
2016/02/18 08:11:54 [notice] 1#0: OS: Linux 4.1.13-boot2docker
2016/02/18 08:11:54 [notice] 1#0: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2016/02/18 08:11:54 [notice] 1#0: start worker processes
2016/02/18 08:11:54 [notice] 1#0: start worker process 8
2016/02/18 08:11:54 [notice] 1#0: start worker process 9
プロセスが 2 つになったことがわかる。
複数ファイルのレンダリング
render
コマンドは、レンダリングするファイルを複数指定することも可能だ。
index.html
を index.html.tmpl
にリネームして以下のように変えてみよう。
{{ var "MESSAGE" | default "hello world" }}
index.html
の代わりに index.html.tmpl
をコンテナイメージに追加して、render
コマンドが受け取るレンダリング先ファイルを 1 つ追加する。
FROM gliderlabs/alpine:3.2
MAINTAINER Seigo Uchida <spesnova@gmail.com> (@spesnova)
ENV ENTRYKIT_VERSION 0.4.0
WORKDIR /
RUN apk-install openssl \
&& rm -rf /var/cache/apk/* \
&& wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& mv entrykit /bin/entrykit \
&& chmod +x /bin/entrykit \
&& entrykit --symlink
RUN apk-install nginx
COPY ./nginx.conf.tmpl /etc/nginx/nginx.conf.tmpl
COPY ./index.html.tmpl /index.html.tmpl # updated line
ENTRYPOINT ["render", "/etc/nginx/nginx.conf", "/index.html", "--", "/usr/sbin/nginx"] # updated line
ビルドしなおして、起動してみる。
$ docker build -t=entrykit-example .
$ docker run -it --rm -p 80:80 -e WORKER_PROCESSES=2 entrykit-example
nginx コンテナにアクセスしてみると hello world
と表示されているはずだ。
次は環境変数 MESSAGE
を設定して起動してみよう。
$ docker run -it --rm -p 80:80 -e WORKER_PROCESSES=2 -e MESSAGE='hello entrykit' entrykit-example
今度は hello entrykit
と表示されるはずだ。
switch - 起動コマンドの切り替え
switch
は簡単に言うと ENTRYPOINT
を切り替えるコマンド。普段は ENTRYPOINT
に指定したプロセスを起動するが、シェルを起動してコンテナの中に入り、デバッグしたいケースもあるだろう。
簡単な例
Entrykit の入った Nginx コンテナを使って、以下の 3 パターンを実行してみよう。
- Nginx を通常通り起動する
- シェルを起動してコンテナの中に入る
- Nginx のバージョンを確認する
Entrykit の入った Nginx コンテナを用意する。
FROM gliderlabs/alpine:3.2
MAINTAINER Seigo Uchida <spesnova@gmail.com> (@spesnova)
ENV ENTRYKIT_VERSION 0.4.0
WORKDIR /
RUN apk-install add openssl \
&& rm -rf /var/cache/apk/* \
&& wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& mv entrykit /bin/entrykit \
&& chmod +x /bin/entrykit \
&& entrykit --symlink
RUN apk-install nginx
ADD ./nginx.conf /etc/nginx/nginx.conf
ADD index.html /index.html
ENTRYPOINT [ \
"switch", \
"shell=/bin/sh", \
"version=nginx -v", "--", \
"/usr/sbin/nginx" \
]
何もコマンドを指定せずに起動すると --
の後の /usr/sbin/nginx
が実行される
$ docker run -it --rm -p 80:80 entrykit-example
2016/02/18 08:49:20 [notice] 1#0: using the "epoll" event method
2016/02/18 08:49:20 [notice] 1#0: nginx/1.8.1
2016/02/18 08:49:20 [notice] 1#0: OS: Linux 4.1.13-boot2docker
2016/02/18 08:49:20 [notice] 1#0: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2016/02/18 08:49:20 [notice] 1#0: start worker processes
2016/02/18 08:49:20 [notice] 1#0: start worker process 8
ENTRYPOINT
を指定しているのでもちろん、-h
を渡すと Nginx のヘルプが出る
$ docker run -it --rm -p 80:80 entrykit-example -h
nginx version: nginx/1.8.1
Usage: nginx [-?hvVtq] [-s signal] [-c filename] [-p prefix] [-g directives]
Options:
-?,-h : this help
-v : show version and exit
-V : show version and configure options then exit
-t : test configuration and exit
-q : suppress non-error messages during configuration testing
-s signal : send signal to a master process: stop, quit, reopen, reload
-p prefix : set prefix path (default: /usr/share/nginx/)
-c filename : set configuration file (default: /etc/nginx/nginx.conf)
-g directives : set global directives out of configuration file
さて今度は shell
を渡してみよう。
$ docker run -it --rm -p 80:80 entrykit-example shell
/ #
switch
コマンドに渡した shell
タスク = /bin/sh
が実行されていることがわかる。
同様に、version
を指定すると
$ docker run -it --rm -p 80:80 entrykit-example version
nginx version: nginx/1.8.1
バージョンが表示された。バージョンを表示するだけだとさほど旨みはないかもしれないが、長いコマンドを実行するケースでは switch
タスクが活きてくるはずだ。
prehook - プレフックの実行
prehook
はメインプロセス起動前にコマンドを 1 つまたは複数実行できる
簡単な例
Nginx の例を示そう。
Nginx サーバプロセスをメインプロセスとして、それの起動前に Nginx バージョンを表示してみよう。
FROM gliderlabs/alpine:3.2
MAINTAINER Seigo Uchida <spesnova@gmail.com> (@spesnova)
ENV ENTRYKIT_VERSION 0.4.0
WORKDIR /
RUN apk-install openssl \
&& rm -rf /var/cache/apk/* \
&& wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& mv entrykit /bin/entrykit \
&& chmod +x /bin/entrykit \
&& entrykit --symlink
RUN apk-install nginx
ADD ./nginx.conf /etc/nginx/nginx.conf
ADD index.html /index.html
ENTRYPOINT [ \
"prehook", \
"nginx -v", "--", \
"/usr/sbin/nginx" \
]
コンテナをビルドして実行してみよう。
$ docker run -it --rm -p 80:80 entrykit-example
nginx version: nginx/1.8.1
2016/02/18 09:02:45 [notice] 1#0: using the "epoll" event method
2016/02/18 09:02:45 [notice] 1#0: nginx/1.8.1
2016/02/18 09:02:45 [notice] 1#0: OS: Linux 4.1.13-boot2docker
2016/02/18 09:02:45 [notice] 1#0: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2016/02/18 09:02:45 [notice] 1#0: start worker processes
2016/02/18 09:02:45 [notice] 1#0: start worker process 10
サーバ起動前にバージョンが表示されてるのがわかる。
複数のプレコマンドを実行することもできる
ENTRYPOINT [ \
"prehook", \
"echo hello world", \
"nginx -v", "--", \
"/usr/sbin/nginx" \
]
$ docker run -it --rm -p 80:80 entrykit-example
hello world
nginx version: nginx/1.8.1
2016/02/18 09:07:00 [notice] 1#0: using the "epoll" event method
2016/02/18 09:07:00 [notice] 1#0: nginx/1.8.1
2016/02/18 09:07:00 [notice] 1#0: OS: Linux 4.1.13-boot2docker
2016/02/18 09:07:00 [notice] 1#0: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2016/02/18 09:07:00 [notice] 1#0: start worker processes
2016/02/18 09:07:00 [notice] 1#0: start worker process 12
codep - 複数プロセスの並列起動
codep
は複数プロセスを並列に起動してくれるコマンドだ。一般的な init システムと違って、codep
は並列に起動しているプロセスのどれかが死んだ場合に他のプロセスを kill する。送られてくるシグナルは両方にプロキシされる。
codep
は confd とか consul-template といった、メインプロセス起動後も設定ファイルを更新しつづけるプロセスを並列に走らせるのに便利だ。
簡単な例
Nginx サーバプロセスと並列に hello world
と echo し続けるプロセスを走らせてみよう。
hello
というスクリプトを用意する。
#!/bin/sh
while true; do
sleep 2
echo 'hello world'
done
FROM gliderlabs/alpine:3.2
MAINTAINER Seigo Uchida <spesnova@gmail.com> (@spesnova)
ENV ENTRYKIT_VERSION 0.4.0
WORKDIR /
RUN apk-install openssl \
&& rm -rf /var/cache/apk/* \
&& wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& mv entrykit /bin/entrykit \
&& chmod +x /bin/entrykit \
&& entrykit --symlink
RUN apk-install nginx
ADD ./nginx.conf /etc/nginx/nginx.conf
ADD index.html /index.html
ADD hello /bin/hello
RUN chmod +x /bin/hello
ENTRYPOINT [ \
"codep", \
"/bin/hello", \
"/usr/sbin/nginx" \
]
$ docker run -it --rm -p 80:80 entrykit-example
2016/02/18 09:25:15 [notice] 10#0: using the "epoll" event method
2016/02/18 09:25:15 [notice] 10#0: nginx/1.8.1
2016/02/18 09:25:15 [notice] 10#0: OS: Linux 4.1.13-boot2docker
2016/02/18 09:25:15 [notice] 10#0: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2016/02/18 09:25:15 [notice] 10#0: start worker processes
2016/02/18 09:25:15 [notice] 10#0: start worker process 14
hello world
hello world
hello world
hello world
docker top
の結果はこうなる
$ docker top d90cebcb55d6
UID PID PPID C STIME TTY TIME CMD
root 6836 943 0 09:38 ? 00:00:00 codep /bin/hello /usr/sbin/nginx
root 6846 6836 0 09:38 ? 00:00:00 /bin/sh /bin/hello
root 6848 6836 0 09:38 ? 00:00:00 nginx: master process /usr/sbin/nginx
nobody 6849 6848 0 09:38 ? 00:00:00 nginx: worker process
root 6855 6846 0 09:38 ? 00:00:00 sleep 2
複数のルートプロセスが起動していることがわかる
Entrykit コマンドのチェイン
entrykit の command は chain させることが可能だ。
簡単な例
FROM alpine:3.2
MAINTAINER Seigo Uchida <spesnova@gmail.com> (@spesnova)
ENV ENTRYKIT_VERSION 0.4.0
WORKDIR /
RUN apk --update add openssl \
&& rm -rf /var/cache/apk/* \
&& wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& mv entrykit /bin/entrykit \
&& chmod +x /bin/entrykit \
&& entrykit --symlink
RUN apk --update add nginx
ADD ./nginx.conf.tmpl /etc/nginx/nginx.conf.tmpl
ADD index.html /index.html
ADD hello /bin/hello
RUN chmod +x /bin/hello
ENTRYPOINT [ \
"switch", \
"shell=/bin/sh", \
"version=nginx -v", "--", \
"render", \
"/etc/nginx/nginx.conf", "--", \
"prehook", \
"nginx -v", "--", \
"codep", \
"/bin/hello", \
"/usr/sbin/nginx" \
]
起動してみる
$ docker run -it --rm -p 80:80 -e WORKER_PROCESSES=2 entrykit-example
nginx version: nginx/1.8.1
2016/02/18 09:52:26 [notice] 19#0: using the "epoll" event method
2016/02/18 09:52:26 [notice] 19#0: nginx/1.8.1
2016/02/18 09:52:26 [notice] 19#0: OS: Linux 4.1.13-boot2docker
2016/02/18 09:52:26 [notice] 19#0: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2016/02/18 09:52:26 [notice] 19#0: start worker processes
2016/02/18 09:52:26 [notice] 19#0: start worker process 22
2016/02/18 09:52:26 [notice] 19#0: start worker process 23
hello world
hello world
hello world
- prehook
- render
- codep
が動いてるのがわかる
$ docker run -it --rm -p 80:80 -e WORKER_PROCESSES=2 entrykit-example shell
/ #
switch も動いている
まとめ
Entrykit というツールの存在を紹介し、具体的にどう使えばいいのかを解説した。実際に自分も start.sh
を作ったことがあるし、他人のイメージでよく見かける。それでもシェルスクリプトじゃないと実現できないってことも当然あるだろうけど、置き換えられるものも多々あるかなという印象。とりあえず Entrykit、オススメです。