!バージョンによって挙動が全然違うので注意してください!
通常 docker は daemon として待ち受けていて、unix domain socket を listen している。実はこの socket に流れるプロトコルは REST/HTTP で、-H
option で tcp に bind させることもできて、webサーバになる。仕様は ["Docker Remote API"] (http://docs.docker.io/reference/api/docker_remote_api/) に書いてある。そして各種 language client も存在する。
そんな python client である docker-py のお話。
まずは通常の使い方。
import docker
dc = docker.Client()
# docker ps
dc.containers()
# docker images
dc.images()
# docker run -ti ubuntu bash
c = dc.create_container("ubuntu", stdin_open=True, tty=True)
dc.start(c["Id"])
print "docker attach %s" % c["Id"]
ところが。
「あ…ありのまま 今 起こった事を話すぜ」とばかりに、激しい落とし穴があったりするので注意。
/container/create
の ExposedPorts の組立の部分。listなのかtupleなのか。listが正しいかと思いきや、tupleを要求されるところもある。しかも正しい組み合わせでなければ正しい値にならない。その上、普通に使ってるとエラーも出ないという罠。
import traceback
import docker
dc = docker.Client()
tests = (
[22,], # OK
(22,), # NG
["22/tcp",], # NG
[(22, "tcp"),], # OK
[[22, "tcp"],] # NG
)
for ports in tests:
c = dc.create_container("ubuntu", stdin_open=True, tty=True, command="bash", ports=ports)
dc.start(c["Id"])
info = dc.inspect_container(c["Id"])
print ports, info["Config"]["ExposedPorts"]
try:
assert {'22/tcp': {}} == info["Config"]["ExposedPorts"]
except:
traceback.print_exc()
finally:
dc.stop(c["Id"])
次は /containers/(id)/start
の PortBindings。うん…なんだろう。この一貫性の無さは。
import traceback
import docker
dc = docker.Client()
tests = (
# automatic allocation
{22:None}, # OK
{"22/tcp": None}, # OK
# manual allocation
{22:10080}, # OK
{"22/tcp": 10080}, # OK
{22: ":10080"}, # NG
{22: dict(HostPort=10080, HostIp="0.0.0.0")}, # NG
{22: ["0.0.0.0", 10080]}, # NG
{22: ("0.0.0.0", 10080)} # OK
)
for port_bindings in tests:
c = dc.create_container("ubuntu", stdin_open=True, tty=True, command="bash", ports=[22,])
dc.start(c["Id"], port_bindings=port_bindings)
info = dc.inspect_container(c["Id"])
print port_bindings, info["HostConfig"]["PortBindings"]
try:
assert '22/tcp' in info["HostConfig"]["PortBindings"]
except:
traceback.print_exc()
finally:
dc.stop(c["Id"])