それが僕には楽しかったんです。

いろんなレイヤーに居ます

Docker Remote APIを使いたかった。

はじめに

最近マッサージ機を使っている時にふくらはぎのツボを見つけた、けんつです。

この時期は、Muroran Institute of Technology Advent Calendar 2018 16日目の記事です。

今取り組んでいるプロジェクトで、最近話題のk8sを使わずにdockerコンテナをいい感じに制御する必要があり 「Docker Remote API」 なるものを使い倒しているので今回はその概要と利用方法をざっくりとまとめようかなと思っています。

具体的な例は後日、別日のアドカレで書きます。

Docker の 基本的な仕組み

Dockerの仕組みといっても、大体は以下の記事に書いてあることを中心に特に Docker Remote API に関することを書きます。
というか、なぜ Docker Remote API があるのか。みたいなことを話すのに Docker の超基本的な仕組みを紹介する必要があるからです。

docs.docker.com


Docker は以下のようなクライアント-サーバアーキテクチャを採用している。

f:id:RabbitFoot141:20181215214310p:plain


これらは主要な3つのコンポーネントによって成立している。

  • デーモンプロセス:サーバの役割を果たす
  • REST API:クライアントとサーバの仲立ちの役割を果たす。
  • CLIクライアント:文字通りクライアントの役割を果たす。

よく、 docker ps などコマンドを使うと思うがこれはコマンドがクライアントの役割を担っていて Docker Remote API を叩くことで Docker Daemon が処理を行うという構成になっている。

Docker の アーキテクチャ


基本的な仕組みは上で解説したとおりで、次は重要となるアーキテクチャ部分を詳解する。

Docker のアーキテクチャは以下のようになっている。

f:id:RabbitFoot141:20181215215123p:plain


それぞれの役割を以下にまとめる。

Docker Daemon

Docker Daemon(dockerd) は Docker APIコール を listen して、コンテナ、ボリューム、ネットワークなど Docker で必要となるオブジェクトを管理する。
そしてこの Daemonは他のDaemonと通信することも出来る。

Docker Client

Docker Client(docker command) はユーザが Docker を使用する上で主な方法と成る。
これらは Docker API をコールして Docker Daemon の機能を利用している。

Docker Registries

Docker Registries は Docker Image を管理している。特によく使用する Docker Hub は誰もが利用できる公開レジストリでデフォルトではこの Docker Hub を Docker は参照するようになっている。
docker pull, docker run などで必要なイメージがレジストリから取得される。

Docker Remote API とは

上記の説明でわかったと思うが、実際に docker コマンドなどがコールしているAPIがそれに当てはまる。
Docker コマンドは単にクライアントにすぎないのでDocker コマンドでなくてもこのAPIをコールすることができ、コンテナの作成削除、はたまたコンテナ内の標準入出力なども取得することができる。

ここでは、その利用方法と簡単な例をあげようと思う。

準備

Docker Remote APIはそのまま使うことはできない。
というのも、デフォルトでは Unix Domain Socketでのみ利用可能になっているからだ。


まず、Docker Remote API を利用する場合に Unix Domain Socket だけでなく TCP を使えるようにする必要がある。


Ubuntu の場合は /etc/init/docker.conf に

DOCKERD=/usr/bin/dockerd
DOCKER_OPTS="-d -H=tcp://127.0.0.1:4243"

とある部分の、 DOCKER_OPTSで 127.0.0.1 、 4243ポートでTCPを許可する一文をいれてからデーモンを再起動することで使えるようになる。
TCPでいけるということは curl などを使ってHTTPリクエストでいけるということになる。


これで行けない場合が存在するらしいがその時は /lib/systemd/system/docker.service で

ExecStart=/usr/bin/dockerd -H unix:// -H tcp://0.0.0.0:4243

とするといけるそう。

実際のシステムで運用する際などには、これではちょっとまずいことになるので証明書などを導入するべき。
Protect the Docker daemon socket | Docker Documentation

そこら辺が面倒ならUnix Domain Socketを利用するとよいかもしれない。

これで準備は完了した。

利用例

利用例の一部を紹介する。

コンテナ一覧を取得

試しに、redisコンテナを起動させた状態でやってみた。

$ curl http://127.0.0.1:4243/containers/json
[
	{
		"Id": "249a982a42823380db8703966915dca91548f945bb109beaff31926f85d007a8",
		"Names": [
			"/accepted_redis_1_b573c8ffd990"
		],
		"Image": "redis:latest",
		"ImageID": "sha256:c188f257942c5263c7c3063363c0b876884eb458d212cf57aa2c063219016ace",
		"Command": "docker-entrypoint.sh redis-server --appendonly yes",
		"Created": 1544365476,
		"Ports": [
			{
				"IP": "0.0.0.0",
				"PrivatePort": 6379,
				"PublicPort": 6379,
				"Type": "tcp"
			}
		],
		"Labels": {
			"com.docker.compose.config-hash": "69b24a42ee98408e3e9622311382cc47736de52c1bdc8a578731e06673db2f26",
			"com.docker.compose.container-number": "1",
			"com.docker.compose.oneoff": "False",
			"com.docker.compose.project": "accepted",
			"com.docker.compose.service": "redis",
			"com.docker.compose.slug": "b573c8ffd9903731bccd479f3086b206bee4d6ac4e66e89da012e25b21743bb",
			"com.docker.compose.version": "1.23.0"
		},
		"State": "running",
		"Status": "Up 6 days",
		"HostConfig": {
			"NetworkMode": "accepted_default"
		},
		"NetworkSettings": {
			"Networks": {
				"accepted_default": {
					"IPAMConfig": null,
					"Links": null,
					"Aliases": null,
					"NetworkID": "da9a9b579158911ba56881af6149f37f2a9a7cc35cf2348183ddf9db75642cab",
					"EndpointID": "5a4ea6f575af66bb0ee307dcc82ea6aeb85517f5daf3a02e58f447336ad75631",
					"Gateway": "172.18.0.1",
					"IPAddress": "172.18.0.2",
					"IPPrefixLen": 16,
					"IPv6Gateway": "",
					"GlobalIPv6Address": "",
					"GlobalIPv6PrefixLen": 0,
					"MacAddress": "02:42:ac:12:00:02",
					"DriverOpts": null
				}
			}
		},
		"Mounts": [
			{
				"Type": "volume",
				"Name": "d5c83c863897ed4a0f6bab5dc55e56744fb5edf3a284fcae0e7161f61b1670aa",
				"Source": "",
				"Destination": "/data",
				"Driver": "local",
				"Mode": "",
				"RW": true,
				"Propagation": ""
			}
		]
	}
]
コンテナを作成
$ curl -X POST -H "Content-Type: application/json" http://localhost:4243/containers/create -d '{"Hostname":"","User":"","Memory":0,"MemorySwap":0,"AttachStdin":false,"AttachStdout":true,"AttachStderr":true,"PortSpecs":null,"Privileged": false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Dns":null,"Image":"nginx","Volumes":{},"VolumesFrom":[],"WorkingDir":""}'

{"Id":"2bdb551799ccff923a417dac1135c190dcb16b9429eca9cb9eb5daffd4a69751","Warnings":null}

これで起動すると docker ps ではコンテナが見えないらしく docker ps -a で全てのコンテナを参照する必要がある。

また手元にイメージのないコンテナは一度 pull 必要がある。

$ docker ps -a
CONTAINER ID        IMAGE                          COMMAND                  CREATED              STATUS                      PORTS                                      NAMES
2bdb551799cc        nginx                          "nginx -g 'daemon of…"   45 seconds ago       Created    
コンテナを止める

さっきたてたコンテナを止める

curl -X POST http://localhost:4243/containers/2bdb551799ccff923a417dac1135c190dcb16b9429eca9cb9eb5daffd4a69751/stop

これで止まる。コンテナIDではなくコンテナ名でもよさげ

おわりに

Docker Remote APIを使えばこれ以外にも色々できるため、リクエストに応じてコンテナを立てたり消したりするオンラインジャッジシステムの一部が組めるようになるのではと思っている