docker-py(Docker SDK for Python)使用指南

2023-11-12

前言

  本篇博客围绕官方提供的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探索过程中的总结。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

docker-py(Docker SDK for Python)使用指南 的相关文章

  • 如何将 ARG 值传递给 ENTRYPOINT?

    Docker 1 9 允许将参数传递给 dockerfile 参见链接 https docs docker com engine reference builder arg https docs docker com engine refe
  • Python:多处理和请求

    以下是我正在运行的使用多处理并行触发 HTTP 请求的代码片段 在控制台上运行后 它挂在 requests get url 处 既不继续前进也不抛出错误 def echo 100 q print before r requests get
  • 刷新访问令牌时出现“invalid_grant”错误的情况?

    最近我一直在为这个问题揪心 一些背景 使用oauth2客户端 https code google com p google api python client 库来管理用户的令牌 这些令牌用于定期并发执行各种后台任务 每次要为用户运行其中一
  • Python中#和"""注释的区别

    开始用 Python 编程 我看到一些带有注释的脚本 and comments 这两种评论方式有什么区别 最好的事情就是阅读PEP 8 Python 代码风格指南 https www python org dev peps pep 0008
  • 同情因子简单关系

    我在 sympy 中有一个简单的因式分解问题 无法解决 我在 sympy 处理相当复杂的积分方面取得了巨大成功 但我对一些简单的事情感到困惑 如何得到 phi 2 2 phi phi 0 phi 0 2 8 因式分解 phi phi 0 2
  • 运行源代码中包含 Unicode 字符的 Python 2.7 代码

    我想运行一个在源代码中包含 unicode utf 8 字符的 Python 源文件 我知道这可以通过添加评论来完成 coding utf 8 在一开始的时候 但是 我希望不使用这种方法来做到这一点 我能想到的一种方法是以转义形式编写 un
  • 在一张图中同时绘制两个截面强度

    我有一个形状数组 512 512 看起来像 行 x 列 y 密度 z 数组的数量 0 012825 0 020408 0 022976 0 015938 0 02165 0 024357 0 036332 0 031904 0 025462
  • Python 中的二进制相移键控

    我目前正在编写一些代码 以使用音频转换通过激光传输消息 文件 和其他数据 我当前的代码使用 python 中 binascii 模块中的 hexlify 函数将数据转换为二进制 然后为 1 发出一个音调 为 0 发出不同的音调 这在理论上是
  • 用于打印 C/C++ 文件的所有函数定义的 Python 脚本

    我想要一个 python 脚本来打印 C C 文件中定义的所有函数的列表 e g abc c定义两个函数为 void func1 int func2 int i printf d i return 1 我只想搜索文件 abc c 并打印其中
  • 代理阻止网络套接字?如何绕行

    我有一个用 Python 编写的正在运行的 websocket 服务器 来自https github com opiate SimpleWebSocketServer https github com opiate SimpleWebSoc
  • 如何通过facebook-sdk python api获取用户帖子?

    我使用 facebook jssdk 授权我的应用程序读取用户个人资料和用户帖子 FB login function response scope user status user likes user photos user videos
  • 检查对象数组中的多个属性匹配

    我有一个对象数组 它们都是相同的对象类型 并且它们有多个属性 有没有办法返回一个较小的对象数组 其中所有属性都与测试用例 字符串匹配 无论该属性类型是什么 使用列表理解all http docs python org 3 library f
  • Selenium 网页抓取与动态内容和隐藏数据表上的美丽汤

    真的需要这个社区的帮助 我正在使用 Selenium 和 Beautiful Soup 对 Python 中的动态内容进行网页抓取 问题是定价数据表无法解析为 Python 即使使用以下代码 html browser execute scr
  • 来自不安全注册表的 Dockerfile

    有没有办法从 Dockerfile 构建 docker 映像 该 Dockerfile 使用来自 Gitlab 托管的本地不安全注册表的基础映像 例如 如果我的 Dockerfile 是 FROM insecure registry loc
  • 从文档字符串生成 sphinx 文档不起作用

    我有一个具有以下结构的项目 我想保留 my project build here is where sphinx should dump into requirements txt make bat Makefile more config
  • Scrapy - 不会爬行

    我正在尝试运行递归爬行 由于我编写的爬行不能正常工作 因此我从网络上提取了一个示例并进行了尝试 我真的不知道问题出在哪里 但是爬行没有显示任何错误 谁能帮我这个 另外 是否有任何逐步调试工具可以帮助理解蜘蛛的爬行流程 非常感谢任何与此相关的
  • Pandas - 分割大的Excel文件

    我有一个大约有 500 000 行的 Excel 文件 我想将其拆分为多个 Excel 文件 每个文件有 50 000 行 我想用熊猫来做 这样它会是最快和最简单的 有什么想法如何制作吗 感谢您的帮助 假设您的 Excel 文件只有一个 第
  • python 的 fcntl.flock 函数是否提供文件访问的线程级锁定?

    Python 的 fcnt 模块提供了一种名为 flock 1 的方法来证明文件锁定 其描述如下 对文件执行锁定操作op 描述符 fd 文件对象提供 fileno 方法被接受为 出色地 请参阅 Unix 手册集群 2 了解详情 在某些系统上
  • 在 pip 中为 Flask 应用程序构建 docker 映像失败

    from alpine latest RUN apk add no cache python3 dev pip3 install upgrade pip WORKDIR backend COPY backend RUN pip no cac
  • 获取长度为 n 的所有(n-选择-k)组合

    我怎样才能获得长度的所有组合 按顺序 n从数字列表中 例如 给定列表 1 2 3 4 并设置n 3 我怎样才能得到这些结果 1 2 3 1 2 4 1 3 4 2 3 4 For combinations of all possible l

随机推荐

  • ug产品摆正高级技巧_UG NX如何摆正产品零件模型

    原标题 UG NX如何摆正产品零件模型 有时 我们拿到一个产品模型 按F8也是一个歪的视图 如图 那么该如何才能将产品摆正呢 其实很简单 我们只需要移动下就好了 按ctrl t移动对象 选中模型 变化选项里面运动选择从csys到csys 指
  • 数据结构——串——王道

    目录 串 定义 串和线性表的联系及不同 串的基本操作 存储结构 顺序存储 链式存储 基本操作的实现 字符串模式匹配算法 朴素模式匹配算法 KMP算法 串 定义 串 即字符串 String 是由零个或多个字符组成的有限序列 一般记为 其中 S
  • 编写cuda代码查看自己机器的显卡信息

    代码如下 cudaDeviceProp 是一个结构体 一些参数就是cudaDeviceProp 结构体中的参数 cudaGetDeviceProperties cudade 0 这个函数就是实例化 第一个参数就是实例化 第二个参数是哪一个显
  • Windows如何开启虚拟化,以安装虚拟机?

    参考 Windows如何开启虚拟化 以安装虚拟机 作者 一只青木呀 发布时间 2020 08 28 20 58 45 网址 https blog csdn net weixin 45309916 article details 108286
  • STM32Cube 中的STM32例程EXAMPLE

    STM32 Cube 其实包含的大量的程序例子 一些模块程序编写不需要再去找其他例子 在这就能找到 在这里插入图片描述 https img blog csdnimg cn 7862d4aba5b54d4da2db87586b4ae859 p
  • Spring源码--Bean的加载

    bean的加载在AbstractBeanFactory的doGetBean中 protected
  • Allegro怎么进行走线

    1 切换到走线模式 点击走线 在options中进行走线参数设置 如下图
  • 2022蓝桥杯省赛——顺子日期

    问题描述 本题为填空题 只需要算出结果后 在代码中使用输出语句将所填结果输出即可 小明特别喜欢顺子 顺子指的就是连续的三个数字 123 456 等 顺子日期指的就是在日期的 yyyymmdd 表示法中 存在任意连续的三位数是一个顺子的日期
  • 并行CRC—Verilog代码实现

    CRC并行Verilog 1 构造系数方阵F 2 计算CRC系数 3 计算DATA系数 3 1 DATA系数真相 4 合并系数 5 验证 6 其他选项 6 1输入翻转 6 2预设初值 6 3输出翻转 取反 7 Matlab生成并行crc代码
  • vue实现文件批量上传及进度条展示

    主要实现功能 1 多文件拖拽上传 2 显示文件上传进度条 3 上传期间 还可以继续拖拽上传 4 可以支持手动修改上传并发任务数 5 上传失败自动重试 最大重试次数3次 6 上传过程中可以手动取消上传 本次使用的是element ui 上传拖
  • CUDA实现多batch基数排序

    CUDA实现多batch基数排序 基数排序是具有固定迭代次数的排序算法 其通过对最低位到最高位的一一比较 对数值排序 GPU版的基数排序将数据分为N个部分并行进行基数排序 随后并行规约得到排序后的数组 这里实现了一版多batch的基数排序实
  • 对stdin,stdout 和STDOUT_FILENO,STDIN_FILENO的学习

    在unix系统调用中 标准输入描述字用stdin 标准输出用stdout 标准出错用stderr表示 但在一些调用函数 引用了STDIN FILENO表示标准输入才 同样 标准出入用STDOUT FILENO 标准出错用STDERR FIL
  • C# 异步编程的三种方式

    一 通过委托实现异步 namespace Test1 class AsyncDemo public void async string i 参数 Console WriteLine 调用异步方法前 PostAsync i Console W
  • Java api中文在线版

    转载自 http blog csdn net qw599186875 article details 52265995ticket ST 213719 sl0mP1mtz9GcbY44tTbE passport csdn net java
  • 在Linux虚拟机上安装QT

    一 安装虚拟机 运行VMware workstation full 11 1 0 2496824 exe 二 在虚拟机上安装Linux 1 下载Ubuntu桌面版 从官网进入 选择 桌面版 按照提示进行下载 国内的一个镜像网址是 http
  • NC65 查询模板参照字段启用参照是否包含下级

    NC65 查询模板参照字段 勾选参照是否包含下级时 选择完参照字段返回的查询条件 参照字段的值是一个sql语句 无法查询到正确数据 当查询模板字段勾选了是否查询条件时 通过getQryDLGDelegator getGeneralCondt
  • 华为有没有公开鸿蒙系统,华为捐赠鸿蒙最核心基础架构该如何看待?

    原标题 华为捐赠鸿蒙最核心基础架构该如何看待 6月2日 华为 鸿蒙操作系统 正式发布 这是一款面向全场景的分布式操作系统 和苹果安卓等手机电脑系统不同 鸿蒙系统可用于物联网各种设备 从目前市场上的操作系统市场比例上看 安卓占据了全球68 6
  • 拉链表设计详解

    拉链表设计原理 将拉链表和每日增量数据做左连接 每日增量没有匹配的数据和拉链表endtime lt 99991231的数据保持不变 其他的endtime变为每日增量的starttime 1 如果数据发生修改 拉链表和增量数据左连接 将拉链表
  • Flask之methods、url_for进行反解析、request的使用、上传文件、abort函数与自定义异常处理、元组、使用jsonify返回json数据、设置和读取cookie

    Flask利用methods限制访问方式 在flask中 可以使用在路由装饰器中使用methods 访问方式 同一视图中可以同时存在多种访问方式 通过methods限定访问方式 app route post only methods POS
  • docker-py(Docker SDK for Python)使用指南

    文章目录 前言 1 docker客户端 2 docker镜像 3 docker容器 4 使用示例 结束语 前言 本篇博客围绕官方提供的python版的docker Engine API Docker SDK for Python 结合具体示