概要
Docker に シンプルな hook の機構をもたらしてくれる docker-plugins の使い方
docker-plugins とは
docker-plugins は Dockerのライフサイクルイベント(docker events)をトリガーに任意のプラグインを実行するのを可能にしてくれる。Docker Remote API の events stream を直接使って hook 処理 を行う事はできるが、git hooks のようにカジュアルにできるようにしたのがポイントだと思う。
利用可能な hooks
- exists -
docker-plugins
が events stream を listen し始める前から存在してるコンテナに対して起きるイベント - create
- destroy
- die
- export
- kill
- pause
- restart
- start
- stop
- unpause
- untag - サポートはされているがビルトインのプラグインでは利用しない
- delete - サポートはされているがビルトインのプラグインでは利用しない
docker-plugins の仕組み
docker-plugins も コンテナとして動かして利用することを前提に作られている。docker-plugins コンテナ起動時にホストの docker socket をマウントすることで、コンテナ内からホストの Docker daemon に対して Docker コンテナ/イメージ の操作を行う。
docker-plugins は以下の 2 つのツール/ライブラリを利用している
plugn
go と bash でできた hook システム。
下記の構造で plugins を作成し、
plugins/
├── available
│ ├── example-plugin1
│ │ ├── hookA
│ │ ├── hookB
│ │ └── plugin.toml
│ └── example-plugin2
│ ├── hookA
│ ├── hookC
│ └── plugin.toml
└── config.toml
hook を trigger する
# plugin を利用可能にする
$ plugn enable <plugin>
$ plugn trigger <hook> (<params>)
$ plugn trigger <hook>
コマンドでは、plugn
で利用可能な plugins が持つ <hook>
という名前のスクリプトを実行する。
使い方例:
# example-plugin1 を利用可能にする
$ plugn enable example-plugin1
# hookA hook をトリガーする
# これで plugins/available/example-plugin1/hookA が引数 "mike" とともに実行される
$ plugn trigger hookA mike
この仕組みで hooks システムを実現している。
dockerhook
docker の events stream を listen し、各イベントが起きると、引数で受け取った hook スクリプトにイベント対象のコンテナ ID を渡して実行する daemon といった感じ。docker-plugins では以下のように利用されている。
dockerhook "/bin/plugn trigger"
hook が実行される流れ
例えば、ビルトインの autoremove プラグインでは、docker コンテナが die すると自動的にそれを docker rm
するプラグイン。
処理の流れは以下のようになる:
- docker-plugins コンテナを起動
- コンテナの起動スクリプトにて
dockerhook "/bin/plugn trigger"
が実行される - dockerhook が docker の events stream を listen し始める
- コンテナの起動スクリプトにて
- 手動でコンテナ A を
start
する - 手動でコンテナ A を
stop
する - dockerhook が events stream より die イベントを受け取る
- dockerhook が
plugn trigger die <コンテナID>
を実行する
- dockerhook が
- plugn が利用可能なプラグインの中の
die
スクリプトを実行する- autoremove プラグインの
die
スクリプトで$ docker rm $1
が実行され、コンテナ A が rm される
- autoremove プラグインの
インストール
$ docker pull progrium/plugins
ビルトインプラグイン
$ docker run --rm progrium/plugins plugn list
autoremove 1.0 disabled Removes any container that dies, including stops and kills
envhooks 1.0 disabled Evaluates hook code from environ variables ENVHOOKS_<EVENT>
timeout 1.0 disabled Kills containers with TIMEOUT set after TIMEOUT seconds
webhooks 1.0 disabled Hits WEBHOOKS_URL or WEBHOOKS_<EVENT>_URL with a POST for each event
autoremove
コンテナの die
イベント時に、そのコンテナを docker rm
するプラグイン。
docker-plugins コンテナを起動する。
$ docker run \
--rm \
-it \
-v /var/run/docker.sock:/var/run/docker.sock \
-e "ENABLE=autoremove" \
progrium/plugins
autoremove 1.0 enabled Removes any container that dies, including stops and kills
envhooks 1.0 disabled Evaluates hook code from environ variables ENVHOOKS_<EVENT>
timeout 1.0 disabled Kills containers with TIMEOUT set after TIMEOUT seconds
webhooks 1.0 disabled Hits WEBHOOKS_URL or WEBHOOKS_<EVENT>_URL with a POST for each event
2014/11/06 01:26:04 info: listening for Docker events...
nginx コンテナを起動し、
$ docker run -d nginx:1.7.7
d81e0ef17250fc4fe095a99fb1cd7210c735bce6fd5b4f2afe615444c3e34260
$ docker ps | grep nginx
5522df9fa283 nginx:1.7.7 "nginx -g 'daemon of 11 seconds ago Up 11 seconds 443/tcp, 80/tcp nginx
nginx コンテナを stop または kill してみると
$ docker stop nginx
nginx
# rm されてる
$ docker ps | grep nginx
timeout
TIMEOUT
経過するとそのコンテナを docker kill
するプラグイン
$ docker run \
--rm \
-it \
-v /var/run/docker.sock:/var/run/docker.sock \
-e "ENABLE=timeout" \
progrium/plugins
$ docker run -d --name nginx -e TIMEOUT=5 nginx:1.7.7
# 起動直後
$ docker ps -a | grep nginx
c7b92e079d98 nginx:1.7.7 "nginx -g 'daemon of 4 seconds ago Up 4 seconds 443/tcp, 80/tcp nginx
# 5 秒後
$ docker ps -a | grep nginx
c7b92e079d98 nginx:1.7.7 "nginx -g 'daemon of 14 seconds ago Exited (-1) 9 seconds ago nginx
envhooks
利用可能な hooks ごとにシェルコマンドが実行する。
die イベント時にログに dead と出力する例
$ docker run \
--rm \
-it \
-v /var/run/docker.sock:/var/run/docker.sock \
-e "ENABLE=envhooks" \
-e "DEBUG=true" \
-e "ENVHOOKS_DIE=echo dead \$1" \
progrium/plugins
$ docker run -d --name nginx -e TIMEOUT=5 nginx:1.7.7
0e6ffc2eb50fa0487ef94942997cdac56769b6a4bb18fc976a2c1fc14aae8840
kill する
$ docker kill
docker-plugins のログ
2014/11/06 04:26:46 info: trigger: 0e6ffc2eb50f die
2014/11/06 04:26:46 info: trigger: 0e6ffc2eb50f kill
dead 0e6ffc2eb50fa0487ef94942997cdac56769b6a4bb18fc976a2c1fc14aae8840
webhooks
各イベントごとに WEBHOOKS_URL
または WEBHOOKS_<EVENT>_URL
へ POST する
$ docker run \
--rm \
-it \
-v /var/run/docker.sock:/var/run/docker.sock \
-e "ENABLE=webhooks" \
-e "WEBHOOKS_URL=http://requestb.in/151o37b1" \
progrium/plugins
autoremove と timeout の組み合わせ
$ docker run \
--rm \
-it \
-v /var/run/docker.sock:/var/run/docker.sock \
-e "ENABLE=autoremove timeout" \
progrium/plugins
$ docker run -d --name nginx -e TIMEOUT=5 nginx:1.7.7
5 秒後にはコンテナが timeout プラグインにより kill された後 autoremove プラグインにより rm される
プラグイン
プラグインのインストール
progrium/docker-plugin-demo の形式でリポジトリを作り、コンテナ起動時に INSTALL
で指定するとプラグインをインストールできる。
$ docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-e "INSTALL=https://github.com/progrium/docker-plugin-demo.git"
-e "ENABLE=docker-plugin-demo" \
progrium/plugins
Cloning into 'docker-plugin-demo'...
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 7 (delta 1), reused 7 (delta 1)
Unpacking objects: 100% (7/7), done.
Checking connectivity... done.
Starting docker-plugins with:
autoremove 1.0 disabled Removes any container that dies, including stops and kills
docker-plugin-demo 1.0 enabled Just a demo
envhooks 1.0 disabled Evaluates hook code from environ variables ENVHOOKS_<EVENT>
timeout 1.0 disabled Kills containers with TIMEOUT set after TIMEOUT seconds
webhooks 1.0 disabled Hits WEBHOOKS_URL or WEBHOOKS_<EVENT>_URL with a POST for each event
(このプラグインは DEMO_TOKEN
設定しないと動かない)
プラグインの作成
コンテナの起動時にコンテナ情報を etcd に登録し、終了時に削除する簡易なプラグインを作ってみる
$ mkdir register && cd register
$ git init
#!/bin/bash
# start
DOCKER_IMAGE="$(/bin/docker inspect $1 | jq -r '.[].Config.Image')"
etcdctl set /containers/$1 $DOCKER_IMAGE
#!/bin/bash
# die
DOCKER_IMAGE="$(/bin/docker inspect $1 | jq -r '.[].Config.Image')"
etcdctl rm /containers/$1 $DOCKER_IMAGE
[plugin]
name = "register"
description = "Register container to etcd"
version = "1.0"
maintainer_name = "Seigo Uchida"
maintainer_email = "spesnova@gmail.com"
[plugin.config]
[plugin.compatibility]
$ git add .
$ git commit -m "Initial commit"
docker-plugins 起動
$ docker run \
--rm \
-it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /home/core/register:/tmp/register \
-v /usr/bin/etcdctl:/usr/bin/etcdctl:r \
-e "ETCDCTL_PEERS=http://172.17.8.101:4001" \
-e "INSTALL=/tmp/register" \
-e "ENABLE=register" \
-e "DEBUG=true" \
progrium/plugins
# nginx コンテナを起動すると..
$ docker run -d --name nginx nginx:1.7.7
5a840eb25b12dba53f111068b270c04e94c36babb61a2adbbdc209f02a19caf4
# etcd にキーが追加されてる
$ etcdctl ls /containers
/containers/5a840eb25b12dba53f111068b270c04e94c36babb61a2adbbdc209f02a19caf4
# イメージ名が取得出来る
$ etcdctl get /containers/5a840eb25b12dba53f111068b270c04e94c36babb61a2adbbdc209f02a19caf4
nginx:1.7.7
# nginx コンテナを kill すると..
$ docker kill nginx
# キーが削除されている
$ etcdctl ls /containers
メモと感想
ちょっとしたことやるのに向いている気がする
plugin.toml
と hook スクリプト の 2 ファイル作れば好きな hook が仕込めるので楽。
逆に、複雑なことをやろうとすると大変。$ docker inspect <コンテナID>
を jq
でパースした後 bash で結構色々がんばる必要があると思う。