LoginSignup
2
0

More than 5 years have passed since last update.

docker-pyで docker daemonを遠隔操作してみようのコーナー

Posted at

どうもはにおかさいです。
dockerをLinuxサーバーにおいて、別の端末やサーバーからdockerを操作したくなりました。
dockerではrestfulなAPIが提供されていまして、それをdocker-pyから呼び出すことができます

前準備

Docker側でTCPを受け付けられるようにします。
今回のやり方だとだれでもアクセスできるので、危険です。実験用のローカル以外ではどうにか適宜対策すること。

tcpをあける

TCPで待ち受けするために、Dockerの起動時オプションを変更する必要があります。
その方法についてはこちらをご覧くださいませ。

CentOS7のわたしの環境の場合:/usr/lib/systemd/system/docker.service
ExecStart に -H 0.0.0.0:5555 と追記してください。 とするとポート5555でhttp通信を受け付けることができます。

docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H 0.0.0.0:5555

読み込み

[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# service docker restart

Python側から遠隔操作してみよう

上から下に読むと変数の意味がわかります。また、 stack はstringのcontainer IDです。

イニシャライズ

docker APIを初期化するには、下記のようなコードが必要です。

# coding: utf-8
import docker

c = docker.APIClient(base_url='tcp://192.168.1.4:5555', timeout=5)

情報の表示

print(c.version())
print(c.info())
print(docker.version)
{'Platform': {'Name': 'Docker Engine - Community'}, 'Components': [{'Name': 'Engine', 'Version': '18.09.5', 'Details': {'ApiVersion': '1.39', 'Arch': 'amd64', 'BuildTime': '2019-04-11T04:13:40.000000000+00:00', 'Experimental': 'false', 'GitCommit': 'e8ff056', 'GoVersion': 'go1.10.8', 'KernelVersion': '3.10.0-957.10.1.el7.x86_64', 'MinAPIVersion': '1.12', 'Os': 'linux'}}], 'Version': '18.09.5', 'ApiVersion': '1.39', 'MinAPIVersion': '1.12', 'GitCommit': 'e8ff056', 'GoVersion': 'go1.10.8', 'Os': 'linux', 'Arch': 'amd64', 'KernelVersion': '3.10.0-957.10.1.el7.x86_64', 'BuildTime': '2019-04-11T04:13:40.000000000+00:00'}
{'ID': 'UL47:X6SQ:GP6M:V6PT:IKC2:UBSO:T4HK:SQM3:HG34:T5Q5:3JPW:VS5Q', 'Containers': 0, 'ContainersRunning': 0, 'ContainersPaused': 0, 'ContainersStopped': 0, 'Images': 4, 'Driver': 'overlay2', 'DriverStatus': [['Backing Filesystem', 'xfs'], ['Supports d_type', 'true'], ['Native Overlay Diff', 'true']], 'SystemStatus': None, 'Plugins': {'Volume': ['local'], 'Network': ['bridge', 'host', 'macvlan', 'null', 'overlay'], 'Authorization': None, 'Log': ['awslogs', 'fluentd', 'gcplogs', 'gelf', 'journald', 'json-file', 'local', 'logentries', 'splunk', 'syslog']}, 'MemoryLimit': True, 'SwapLimit': True, 'KernelMemory': True, 'CpuCfsPeriod': True, 'CpuCfsQuota': True, 'CPUShares': True, 'CPUSet': True, 'IPv4Forwarding': True, 'BridgeNfIptables': True, 'BridgeNfIp6tables': True, 'Debug': False, 'NFd': 22, 'OomKillDisable': True, 'NGoroutines': 38, 'SystemTime': '2019-04-18T23:06:19.983023943+09:00', 'LoggingDriver': 'json-file', 'CgroupDriver': 'cgroupfs', 'NEventsListener': 0, 'KernelVersion': '3.10.0-957.10.1.el7.x86_64', 'OperatingSystem': 'CentOS Linux 7 (Core)', 'OSType': 'linux', 'Architecture': 'x86_64', 'IndexServerAddress': 'https://index.docker.io/v1/', 'RegistryConfig': {'AllowNondistributableArtifactsCIDRs': [], 'AllowNondistributableArtifactsHostnames': [], 'InsecureRegistryCIDRs': ['127.0.0.0/8'], 'IndexConfigs': {'docker.io': {'Name': 'docker.io', 'Mirrors': [], 'Secure': True, 'Official': True}}, 'Mirrors': []}, 'NCPU': 2, 'MemTotal': 1907965952, 'GenericResources': None, 'DockerRootDir': '/var/lib/docker', 'HttpProxy': '', 'HttpsProxy': '', 'NoProxy': '', 'Name': 'localhost.localdomain', 'Labels': [], 'ExperimentalBuild': False, 'ServerVersion': '18.09.5', 'ClusterStore': '', 'ClusterAdvertise': '', 'Runtimes': {'runc': {'path': 'runc'}}, 'DefaultRuntime': 'runc', 'Swarm': {'NodeID': '', 'NodeAddr': '', 'LocalNodeState': 'inactive', 'ControlAvailable': False, 'Error': '', 'RemoteManagers': None}, 'LiveRestoreEnabled': False, 'Isolation': '', 'InitBinary': 'docker-init', 'ContainerdCommit': {'ID': 'bb71b10fd8f58240ca47fbb579b9d1028eea7c84', 'Expected': 'bb71b10fd8f58240ca47fbb579b9d1028eea7c84'}, 'RuncCommit': {'ID': '2b18fe1d885ee5083ef9f0838fee39b62d653e30', 'Expected': '2b18fe1d885ee5083ef9f0838fee39b62d653e30'}, 'InitCommit': {'ID': 'fec3683', 'Expected': 'fec3683'}, 'SecurityOptions': ['name=seccomp,profile=default'], 'ProductLicense': 'Community Engine', 'Warnings': ["WARNING: API is accessible on http://0.0.0.0:5555 without encryption.\n         Access to the remote API is equivalent to root access on the host. Refer\n         to the 'Docker daemon attack surface' section in the documentation for\n         more information: https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface"]}
4.0.0-dev

コンテナ作成

stack = c.create_container(image='uphy/ubuntu-desktop-jp:18.04',detach=True,host_config=c.create_host_config(port_bindings={8080: ('0.0.0.0', 8080)},storage_opt={"size":"1g"}),stdin_open=True)
print(stack["Id"])
print(stack["Warnings"])
print(c.start(stack))

ポートは{コンテナ内ポート:(IP,ホストのポート)}で一対一対応とします。
また、storage-optはホストが対応していれば利用可能です。原則としてすべてのargsがdocker-pyに実装されています。
image.png

イメージのpull

for line in c.pull("uphy/ubuntu-desktop-jp:18.04", stream=True):
 print(line)

forで回して待機しておかないと、あとあとの処理のときにimage取得完了前にコンテナを作成しかねません。

コンテナのexport

con = c.export("eee126213653")
f = open('./busybox-latest.tar', 'wb')
for chunk in con:
 f.write(chunk)
f.close()

コンテナIDを指定することで「export」できます。exportはdocker exportと似た振る舞いをします。

コンテナのimport

何故か読み込み方法が複数あります。
repository,tagは指定しないと以下のようになります。

#<none>              <none>              c49ad9dfe280        12 minutes ago      1.42MB

tarとして読み込む

c.import_image_from_file(filename="./busybox-latestgetimage.tar",repository="title-busy",tag="latest")

dataとして読み込み。

c.import_image_from_data(data=open("./busybox-latestgetimage.tar","rb").read())

urlで指定して、tarfileを取得。

c.import_image_from_url(url="foo.com")

イメージのsave

image = c.get_image("busybox:latest")

f = open('./busybox-latestgetimage.tar', 'wb')
for chunk in image:
 f.write(chunk)
f.close()

image save tarballでイメージを取得. docker saveに似ています。 https://github.com/docker/docker-py/blob/02e660da12ecb5cf755b17ffd850ce3da21ecec0/docker/models/images.py#L87

イメージのload

docker.api.image.ImageApiMixin.get_image (docker save)で取得したデータをdocker loadのように読み込めます

tar = tarfile.open("./busybox-latestgetimage.tar", "r|")
print(c.load_image(data=tar))
#busybox             latest              af2f74c517aa        11 days ago         1.2MB
c.load_image(data=open("./busybox-latestgetimage.tar","rb").read())

コンテナ内の指定ディレクトリでtarを作る

f = open('./sh_bin.tar', 'wb')
bits, stat = c.get_archive(stack, '/dirname')
print(stat)
for chunk in bits:
 f.write(chunk)
f.close()

コンテナ内の指定ディレクトリにtarballを展開する

c.put_archive(stack,"/path",tardata)

tardataはbytesです。

コンテナの停止

c.stop(stack)

サンプル

参考

https://nisenabe.hatenablog.com/entry/2014/01/21/104701
https://docker-py.readthedocs.io/en/1.2.3/port-bindings/

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0