Entrykit のすすめ

  • 232
    いいね
  • 0
    コメント

はじめに

あなたは Docker イメージを作る際に start.shstartup.sh というファイルを作った経験はないだろうか。「Dockerfile の CMD だと 1 コマンドしか書けないから、シェルスクリプトで頑張る」というやつだ。例えば、環境変数を受け取って設定ファイルを動的に作ったり、メインプロセス起動前に何かしら初期化処理をするなど。そんなあなたに Entrykit というツールを紹介しよう。

Entrykit とは

Entrykit は一言でいうと、コンテナ内のプロセス起動時に便利な軽量 init システムだ。Docker の起動コマンドに 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.htmlindex.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、オススメです。