Docker Remote APIを使ってみる #apijp

このエントリは「Web API Advent Calendar 2014」の12/3担当です。次は「YosAwed」さんです。

最近話題のDockerですが、みなさんもう使ってますよね?

Dockerはクライアント-サーバーアーキテクチャを採用しており、DockerクライアントとDockerサーバーがRemote API経由で接続されています。つまり、「docker ps」などのコマンドはすべてサーバーに送られて処理されているということです。
このRemote API、実は(ほぼ)RESTになっていて、Dockerクライアント以外からでも利用することが可能です。そこで、Remote APIを直接使う方法についてご紹介したいと思います。

テストした環境

Mac OS X(Yosemite)のBoot2Dockerを利用しました。Linux環境でもDockerサーバーの設定だけ追加すれば大丈夫なはずです。

$ docker version
Client version: 1.3.2
Client API version: 1.15
Go version (client): go1.3.3
Git commit (client): 39fa2fa
OS/Arch (client): darwin/amd64
Server version: 1.3.2
Server API version: 1.15
Go version (server): go1.3.3
Git commit (server): 39fa2fa

$ boot2docker version
Boot2Docker-cli version: v1.3.2
Git commit: e41a9ae

Docker Remote APIを使うための設定

LinuxのDockerサーバーは、デフォルトではUnixドメインソケット(unix:///var/run/docker.sock)をリスンするように設定されています。
このままではリモートからTCP接続できないので、Dockerサーバーの起動時オプションに '-H tcp://0.0.0.0:2376' のようにしてTCPのリスンポートを追加する必要があります。

設定方法としては、dockerコマンドの起動時オプションで指定するか、設定ファイルに追記するなど、いくつかやり方があります。
例えばUbuntu14.04では /etc/init/docker.conf に

DOCKER_OPTS=' -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock'

を設定し、

$ sudo service docker restart

で反映します。
詳しくはDockerの公式ドキュメントを参照してください。
https://docs.docker.com/articles/basics/#bind-docker-to-another-hostport-or-a-unix-socket

Boot2Dockerの場合

Boot2Dockerの場合はもともとTCPで接続するようになっているので、特に設定は必要ありません。
DockerサーバーのTCP接続先は「boot2docker shellinit」で確認できます。

$ boot2docker shellinit
Writing /Users/nobusue/.boot2docker/certs/boot2docker-vm/ca.pem
Writing /Users/nobusue/.boot2docker/certs/boot2docker-vm/cert.pem
Writing /Users/nobusue/.boot2docker/certs/boot2docker-vm/key.pem
    export DOCKER_CERT_PATH=/Users/nobusue/.boot2docker/certs/boot2docker-vm
    export DOCKER_TLS_VERIFY=1
    export DOCKER_HOST=tcp://192.168.59.103:2376

TCP接続先はBoot2DockerのVMを再起動すると変わることがあるので、以下のコマンドで環境変数を毎回設定しましょう。

$ $(boot2docker shellinit 2>/dev/null)

また、Boot2Docker VMIPアドレスを毎回手打ちするのは面倒なので、以下のコマンドでhostsに登録しておきましょう。(Mac限定)

$ sudo sed -i -e '/dockerhost/d' /etc/hosts
$ echo $(boot2docker ip 2>/dev/null) dockerhost | sudo tee -a /etc/hosts

これでBoot2Docker VMを「dockerhost」で参照できます。
以降はこの設定を前提として記載しますので、未設定の方は適宜IPアドレスに読み替えてください。

Docker Remote APIでイメージ一覧を取得する

当然ですが、事前にDockerサーバーを起動しておいてください。(Boot2Dockerの方は boot2docker up しておいてください。)
また、wgetcurlなど、RESTのリクエストを送信できるコマンドをインストールしておいてください。
(筆者の環境ではcurlは証明書の処理でエラーが発生したため、wgetを利用しています。また、JSONを見やすく整形するためにjqコマンドを利用します。)

Dockerを初めて使う方は「docker pull」で適当なイメージをダウンロードしておいてくださいね。その上でイメージ一覧を確認しておきましょう。

$ docker images
REPOSITORY                   TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
oddpoet/zookeeper            latest              33a69da2484d        5 days ago          909.3 MB
nobusue/kafka                0.8.1.1             29836f0457d5        5 days ago          808.1 MB
nobusue/java7                u72                 140a4d7d1cea        5 days ago          500.6 MB

それではRESTで同じ情報が取れるか確認してみましょう。

まず、DockerサーバーのTCP接続先を確認します。
ここでは、「DOCKER_HOST=tcp://192.168.59.103:2376」を前提としてコマンドを実行例を記載します。

次に、おもむろにRESTリクエストを送信してみましょう。

$ wget http://dockerhost:2376/images/json -O - -q | jq .

Linux環境ではJSONが表示されたと思いますが、Boot2Docker環境では何も表示されないはずです。

実は、Boot2Docker環境ではTLS暗号化が行われており、証明書と秘密鍵の指定が必要です。以下のようにしてください。

$ wget --no-check-certificate --certificate=$DOCKER_CERT_PATH/cert.pem --private-key=$DOCKER_CERT_PATH/key.pem https://dockerhost:2376/images/json -O - -q | jq .

正常に実行できれば、こんな感じのレスポンスが返ってきます。

[
  {
    "Created": 1417099585,
    "Id": "33a69da2484d70d6e9b9e590b6286a4fe6fba5ab91a7444ba6d970a7c97e10d8",
    "ParentId": "47f7486b7c978fda52628680274d3e0aeaa504c027b48fca1b898755ef21eedd",
    "RepoTags": [
      "oddpoet/zookeeper:latest"
    ],
    "Size": 0,
    "VirtualSize": 909336287
  },
  {
    "Created": 1417088378,
    "Id": "29836f0457d5f82b748c42d3ed8f798086f8116b09b700e3c1f36d058404de62",
    "ParentId": "d53de445c803da52a60af0cde9f8448f4f1b34422129e5234c1e2ffa6ec09b67",
    "RepoTags": [
      "nobusue/kafka:0.8.1.1"
    ],
    "Size": 0,
    "VirtualSize": 808064814
  },
  {
    "Created": 1417080614,
    "Id": "140a4d7d1cea6982b3d62921d3edb509f2602c8bda69f99a2c6ca0e055f39f1c",
    "ParentId": "0d7c4caeafc408f94009cc6f8d6cf52a8412a81dd6158ee40dde18715ce115de",
    "RepoTags": [
      "nobusue/java7:u72"
    ],
    "Size": 0,
    "VirtualSize": 500624405
  }
]

「docker images」コマンドと同等の情報が得られることが確認できました。

Docker Remote APIでコンテナ一覧を取得する

次に、コンテナ一覧を取得してみます。

先に適当なコンテナを起動しておきましょう。

$ docker run -ti -d ubuntu
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
91d01d7df230        ubuntu:latest       "/bin/bash"         7 seconds ago       Up 6 seconds                            cranky_ardinghelli   

RESTでコンテナ一覧を取得してみます。

$ wget --no-check-certificate --certificate=$DOCKER_CERT_PATH/cert.pem --private-key=$DOCKER_CERT_PATH/key.pem https://dockerhost:2376/containers/json -O - -q | jq .
[
  {
    "Command": "/bin/bash",
    "Created": 1417537292,
    "Id": "91d01d7df23053c8bac8b3ccdd8a02015027da69a0b57a8a1e8b6b66f1477c7f",
    "Image": "ubuntu:latest",
    "Names": [
      "/cranky_ardinghelli"
    ],
    "Ports": [],
    "Status": "Up 22 seconds"
  }
]

簡単ですね!

その他のAPIの使い方

公式リファレンスを参照してください。
Docker Remote API - Docker Documentation
なお、Remote APIにはバージョンがあり、DockerサーバーとDockerクライアントでAPIバージョンが異なると接続できません。
自分でRESTを叩く場合には問題ないかもしれませんが、独自のクライアントを実装する場合などは注意してください。

(補足)Unixドメインソケットを覗き見する

socatコマンドでドメインソケットを覗き見できます。デバッグのときなどにどうぞ。

How to set the name of a Docker container using REST API - Stack Overflow