前言

  本篇博客围绕官方提供的python版的docker Engine API(Docker SDK for Python),结合具体示例,详细介绍如何使用python来自动化操作docker

  建议本篇文章同《Docker常用指令汇总》一起食用效果更佳!!!

1. docker客户端

  要与Docker服务器进行通信,首先需要实例化一个客户端,要做到这一点,最简单的方法是调用 from_env()函数 ,当然,如果使用ssh来操作远程的docker,也可以通过实例化 DockerClient 类。
  常规client实例化:

# pip install docker
import docker

client = docker.from_env()

  使用ssh远程操作docker,初始化client可以这样做:

client = docker.Dockerclient("ssh://username@yourip", use_ssh_client=True)
# 执行之后会让用户输入密码

# base_url(str) -- 指向Docker服务器的URL。例如, unix:///var/run/docker.sock 或 tcp://127.0.0.1:1234 
# version(str) -- 要使用的API的版本。设为 auto 自动检测服务器的版本。默认: 1.35
# timeout(int) -- API调用的默认超时时间,单位为秒
# tls(bool or TLSConfig) -- 启用TLS。经过 True 使用默认选项启用它,或将 TLSConfig 对象以使用自定义配置
# user_agent(str) -- 为对服务器的请求设置自定义用户代理
# credstore_env(dict) -- 调用凭据存储进程时覆盖环境变量
# use_ssh_client (bool) -- 如果设置为 True ,通过向ssh客户端发出命令来建立ssh连接。确保在主机上安装并配置了ssh客户端
# max_pool_size (int) -- 要保存在池中的最大连接数

  得到的client是一个DockerClient对象,常用的属性及方法有:

属性功能备注
containers用于管理服务器上的容器
images用于管理服务器上的镜像
close()关闭当前连接
events()从服务器获取实时事件docker events
info()显示系统范围的信息docker info
login()使用注册表进行身份验证docker login
version()从服务器返回版本信息docker version

在这里插入图片描述

2. docker镜像

  client.images是一个ImageCollection对象,用来管理服务器上的镜像,其主要有以下方法:

属性功能备注
list(**kwargs)列出服务器上的图像docker images
get(name)根据镜像名字来获取镜像
load(data)加载镜像docker load 需要注意的是,data参数为镜像的二进制数据,而非路径
prune(filters=None)删除未使用的图像
pull(repository, tag=None, all_tags=False, **kwargs)拉取给定名称的镜像并将其返回docker pull
push(repository, tag=None, **kwargs)将镜像推送到注册表docker push
remove(*args, **kwargs)删除镜像docker rmi
search(*args, **kwargs)Docker Hub上搜索镜像docker search
client.list()
client.images.get('myubuntu:python')

# 加载镜像
with open(image_path, 'rb') as f:
    img = client.images.load(f)
    img_obj = img[0]
    print('Image Info: ', img_obj.short_id, img_obj.tags)

# 拉取最新的ubuntu镜像
image = client.images.pull('ubuntu')
# 如果 tag 是 None 或为空,则设置为 latest
# 如果 all_tags 已设置,则 tag 参数被忽略,并且将拉取所有镜像标签

在这里插入图片描述
  Image对象是服务器上的映像,其主要有以下方法:

属性功能备注
attrs服务器中此对象的原始表示形式
id镜像的ID
labels作为字典的镜像的标签
short_id截断为12个字符的图像的ID,加上 sha256: 前缀
tags镜像的标签
history()显示图像的历史记录
reload()再次从服务器加载此对象并更新 attrs 使用新的数据
save(chunk_size=2097152, named=False)保存镜像docker save
tag(repository, tag=None, **kwargs)将此镜像的标签添加到存储库中docker tag
img = client.images.get('myubuntu:python')
print(img.attrs)
print(img.id)
print(img.labels)
print(img.short_id)
print(img.tags)

在这里插入图片描述

3. docker容器

  client.containers是一个ContainerCollection对象,在服务器上运行和管理容器,其主要有以下方法:

属性功能备注
run(image, command=None, **kwargs)运行一个容器,默认情况下,它将等待容器完成并返回其日志docker run ,如果 detach 参数是 True ,它将启动容器并立即返回一个 Container 对象,类似于 docker run -d
create(image, command=None, **kwargs)在不启动的情况下创建容器docker create
get(id_or_name)按名称或ID获取容器docker create
list(**kwargs)列出容器docker ps
prune(filters=None)删除停止的容器

  下面将详细介绍一下run()函数,主要有以下参数:

  • image(str) ---- 要运行的映像
  • command(str or list) ---- 要在容器中运行的命令
  • auto_remove(bool) ---- 当容器的进程退出时,在守护程序端启用容器的自动删除
  • cap_add(list of str) ---- 添加内核功能,例如,["SYS_ADMIN", "MKNOD"]
  • cap_drop(list of str) ---- 放弃内核功能
  • cpu_count(int) ---- 可用CPU数(仅限windows)
  • cpu_percent(int) ---- 可用CPU的可用百分比(仅限windows)
  • cpu_period(int) ---- 以微秒为单位的CPU周期长度
  • cpu_shares(int) ---- CPU占有率(相对权重)
  • cpuset_cpus(str) ---- 允许执行的CPUs (0-3, 0, 1)
  • detach(bool) ---- 在后台运行容器并返回一个 Container 对象
  • devices(list) ---- 将主机设备作为以下形式的字符串列表公开给容器:<path_on_host>:<path_in_container>:<cgroup_permissions>
  • device_requests(list) ---- 将主机资源(如GPU)公开给容器,作为 docker.types.DeviceRequest 实例
  • dns(list) ---- 设置自定义的DNS服务器
  • dns_opt(list) ---- 要添加到容器的 resolv.conf 文件
  • dns_search(list) ---- 域名系统搜索域
  • domainname(str or list) ---- 设置自定义DNS搜索域
  • entrypoint(str or list) ---- 容器的入口点
  • environment(dict or list) ---- 要在容器内设置的环境变量,格式为字典或字符串列表 ["PYTHONPATH=xxx"]
  • extra_hosts(dict) ---- 要在容器内解析的其他主机名,作为主机名到IP地址的映射
  • group_add(list) ---- 容器进程将以其身份运行的其他组名或ID的列表
  • healthcheck(dict) ---- 指定要执行的测试以检查容器是否正常
  • hostname(str) ---- 容器的可选主机名
  • isolation(str) ---- 使用隔离技术,默认为 None
  • kernel_memory(int or str) ---- 内核内存限制
  • labels(dict or list) ---- 以字典或者列表(空值)的形式,例如 {"label1": "value1", "label2": "value2"}["label1", "label2"]
  • links(dict) ---- 链接的映射使用 {'container': 'alias'} 格式化,别名是可选的
  • log_config(LogConfig) ---- 日志记录配置
  • mac_address(str) ---- 要分配给容器的MAC
  • mem_limit(int or str) ---- 内存限制
  • name(str) ---- 给容器起的名称
  • network(str) ---- 此容器在创建时将连接到的网络的名称
  • network_disabled(bool) ---- 禁用网络
  • network_mode(str) ---- 以下其中一项: 桥接模式bridge、主机模式hostnonecontainer:<name|id>
  • ports(dict) ---- 要绑定到容器内部的端口,字典形式:{'2222/tcp': 3333} 会将容器内的端口2222暴露为主机上的端口3333
  • privileged(bool) ---- 将扩展权限授予此容器
  • read_only(bool) ---- 将容器的根文件系统挂载为只读
  • remove(bool) ---- 当容器运行完毕后,将其移除。默认为 False
  • restart_policy(dict) ---- 容器退出时重新启动,配置为带有关键字的词典:Name取值只能是 on-failurealwaysMaximumRetryCount 为失败时重新启动容器的次数。例如: {"Name": "on-failure", "MaximumRetryCount": 5}
  • shm_size(str or int) ---- /dev/shm的大小,比如1G
  • stdin_open(bool) ---- 保持 STDIN 打开,即使没有连接
  • stdout(bool) ---- 当detach=False时日志从STDOUT 返回 ,默认为 True
  • stderr(bool) ---- 当detach=False时日志从STDERR 返回 ,默认为 False
  • stream(bool) ---- 如果设置为True,且 detachFalse,则返回日志生成器而不是字符串,如果detachTrue将忽略。默认为 False
  • sysctls(dict) ---- 要在容器中设置的内核参数
  • tmpfs(dict) ---- 要挂载的临时文件系统,作为将容器内的路径映射到该路径的选项的字典。例如:{'/mnt/vol2': '', '/mnt/vol1': 'size=3G,uid=1000'}
  • tty(bool) ---- 分配一个伪TTY
  • user(str or int) ---- 在容器内运行命令的用户名或UID
  • version(str) ---- 要使用的API的版本。设为 auto 自动检测服务器的版本。默认为1.35
  • volume_driver(str) ---- 卷驱动程序/插件的名称
  • volumes(dict or list) ---- 用于配置装载在容器内的卷的字典。键是主机路径或卷名,值是包含键的字典,比如:{'/home/user1/': {'bind': '/mnt/vol2', 'mode': 'rw'},'/var/www': {'bind': '/mnt/vol1', 'mode': 'ro'}},其中bind表示在容器内挂载卷的路径,mode要么为rw 以读/写方式装载卷,或 ro 以只读方式装载它。也可以是列表形式直接映射:['/home/user1/:/mnt/vol2','/var/www:/mnt/vol1']
  • working_dir(str) ---- 容器的工作目录的路径

  上述的参数并没有完全列举完,只对可能用到的进行了介绍,下面看下具体如何使用:

container = client.containers.run(image='ubuntu:latest',
                                  command='/bin/bash',
                                  user='root',
                                  name='docker_test',
                                  volumes=['/home/liyanpeng/docker-test:/home/liyanpeng/docker-test'],
                                  working_dir='/home/liyanpeng',
                                  tty=True,
                                  detach=True,
                                  stdin_open=True,
                                  environment=['PYTHONPATH=xxxxx:$PYTHONPATH'])
# 如果detach=True,那么在容器内的输出信息需要通过container.logs()来查看
# 如果detach=False,可以直接print(container)来查看信息

  Container对象是容器对象的本地表示形式,其主要有以下方法:

属性功能备注
attrs容器的属性信息
id容器的id
image容器的容器的图像
labels作为词典的容器的标签
name容器的名称
short_id对象的ID,截断为12个字符
status容器的状态,例如runningexited
attach(**kwargs)附在这个容器上
commit(repository=None, tag=None, **kwargs)将容器提交到映像docker commit
diff()检查容器的文件系统上的更改
exec_run(cmd)在该容器内运行命令docker exec
export(chunk_size=2097152)将容器的文件系统的内容导出为TAR压缩包
logs(**kwargs)logs(**kwargs)docker logs
remove(**kwargs)移除这个容器docker rm
rename(name)重命名此容器docker rename
restart(**kwargs)重新启动此容器docker restart
start(**kwargs)启动这个容器docker start
stats(**kwargs)此容器的流统计信docker stats
stop(**kwargs)停止容器docker stop
top(**kwargs)显示容器正在运行的进程
update(**kwargs)更新容器的资源配置
wait(**kwargs)等容器停止,然后返回其退出代码docker wait

4. 使用示例

# -*- coding:utf-8 -*-
# Author:   liyanpeng
# Email:    youran.xia@foxmail.com
# Datetime: 2023/2/3 17:16
# Filename: python_docker·py
import docker
from packaging.version import Version


class PythonDocker(object):
    image_file = '/home/liyanpeng/ubuntu-20.04.tar.gz'
    repository = 'ubuntu'
    tag = 'latest'
    localhost_dir = '/home/liyanpeng/docker-test'
    container_dir = '/home/liyanpeng/docker-test'
    container = None
    container_name = 'python_docker'

    client = docker.from_env()
    # client = docker.Dockerclient("ssh://username@yourip", use_ssh_client=True)

    @classmethod
    def load_image(cls):
        with open(cls.image_file, 'rb') as f:
            img = cls.client.images.load(f)
            img_obj = img[0]
            print('Image Info: ', img_obj.short_id, img_obj.tags)

    @classmethod
    def check_version(cls):
        docker_version = cls.client.version()['Components'][0]['Version']
        assert Version(docker_version) >= Version('19.03'), 'Docker version requires: docker >= 19.03.'

    @classmethod
    def run_container(cls):
        try:
            cls.container = cls.client.containers.run('{}:{}'.format(cls.repository, cls.tag),
                                                      command='/bin/bash',
                                                      user='root',
                                                      name=cls.container_name,
                                                      volumes=['{}:{}'.format(cls.localhost_dir, cls.container_dir)],
                                                      working_dir='/home',
                                                      tty=True,
                                                      detach=True,
                                                      stdin_open=True,
                                                      environment=['PYTHONPATH=xxxxx:$PYTHONPATH'])
        except Exception as err:
            cls.container = cls.client.containers.get(cls.container_name)

    @classmethod
    def run_docker(cls):
        result = cls.container.exec_run('python3 x.py')     # 在容器内执行代码
        print(result.exit_code, result.output.decode('utf-8'))

    @classmethod
    def close_docker(cls):
        cls.container.stop()    # 停止容器 ==> docker stop id
        cls.container.remove()  # 删除容器 ==> docker rm id
        cls.client.close()      # 关闭连接

结束语

  由于业务场景的需要,根据官方文档进行了学习,本篇是对python-docker探索过程中的总结。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐