Skip to content

Files

Latest commit

781b2cb · Dec 27, 2021

History

History
610 lines (373 loc) · 27.3 KB

File metadata and controls

610 lines (373 loc) · 27.3 KB

六、Docker API 和 SDK

在本章中,我们将介绍以下食谱:

  • 使用应用编程接口处理映像
  • 使用 API 构建映像
  • 使用 API 启动容器
  • 使用 API 执行容器操作
  • 探索 Docker 远程应用编程接口客户端库
  • 为远程连接配置 Docker 守护程序
  • 保护 Docker 守护进程的远程连接

介绍

在前面的章节中,我们已经使用了很多 Docker 命令来处理 Docker 映像、容器、卷和网络。Docker 的标志之一是它通过易于记忆和结构良好的命令提供了惊人的用户体验。通过一个 Docker 命令,我们可以旋转一个非常有用的微服务或实用程序容器。然而,在幕后,Docker 客户端将我们的请求翻译成多个 API 调用来实现它。这些 API 被称为 Docker 引擎 API,它们是使用 REST 范式设计的。

Note: REST (aka RESTful) stands for REpresentational State Transfer, which is a web standard for data communication over the HTTP protocol.

Docker 引擎 API 使用 OpenAPI(以前称为swaggle)规范进行记录。因此,我们可以通过任何标准的 OpenAPI 编辑器访问 API 帮助。在这本书里,我们使用了一个名为 Swagger Editor 的编辑器,它可以在http://Editor . swaggy . io上找到;但是,您可以使用自己选择的任何 OpenAPI 编辑器。Swagger Editor 有“试用”和“执行”等选项,可用于生成带有适当选项的curl命令。下面的截图显示了带有 Docker 引擎 API 文档的 Swagger 编辑器:

在这里,我们从https://docs.docker.com/engine/api/v1.35/swagger.yaml.提供的swagger.yaml文件中生成了 Docker Engine API 文档,当然,您也可以从https://raw . githubusercontent . com/Moby/Moby/master/API/swag . YAML .中参考当前正在进行的 API 版本

默认情况下,Docker 引擎监听/var/run/docker.sock,一个 Unix 域套接字,与其客户端通信。Unix 域套接字,也称为进程间通信套接字,支持主机内的可靠通信。除此之外,/var/run/docker.sock对用户root和组docker有读写权限;因此,客户端应用必须拥有root权限或者是docker组的成员。除了 Unix 套接字之外,Docker 守护程序还支持两种套接字类型:

  • fd : Systemd 的套接字激活。在基于 Systemd 的系统上,例如 Ubuntu 16.04,Docker 守护进程监听fd套接字,该套接字使用 systemd 的套接字激活特性在内部映射到 Unix 套接字/var/run/docker.sock
  • tcp:用于远程连接。在配方中,配置 Docker 守护程序进行远程连接,我们将配置 Docker 守护程序接受来自客户端的未加密通信,在配方中,保护 Docker 守护程序的远程连接,我们将配置 Docker 守护程序使用安全通信。

Docker 还为 Python 和 Go 字母组合语言提供了软件开发工具包。这些 SDK 在内部使用 Docker 引擎 REST APIs。除了这些标准的 SDK 之外,还有许多其他编程语言的社区支持的 API 绑定。这些应用编程接口绑定中的一些列在https://docs.docker.com/develop/sdk/#unofficial-libraries上。然而,Docker 并没有测试这些库。

在本章中,我们将使用两个工具:curljq:

  • curl是传输数据的命令行工具。我们使用curl连接到我们的 Docker 守护进程。请确保您运行的是curlT3 或更高版本,因为 curl 从版本7.40开始支持 Unix 套接字。你可以在官方网站https://curl.haxx.se/.找到更多关于curl的详细信息
  • jq是处理 JSON 数据的命令行工具。我们使用jq来处理从 Docker 守护进程接收的数据。您可以在https://stedolan.github.io/jq/.T5 官方网站上找到更多关于jq的详细信息

In this chapter we are using Ubuntu 16.04 and Docker 17.10, because of an issue with the default curl command that comes along with the Ubuntu 18.04 installation.  If you choose to continue with Ubuntu 18.04 and Docker 18.03, you can do so by prefixing any text in the Docker API endpoint (http://bug/version) as a workaround.

本章中的所有配方都假设 Docker 已经安装并运行。

使用应用编程接口处理映像

如前所述,Docker 在内部使用 Docker 引擎 API 来满足我们所有的容器化需求

南在本食谱中,我们将使用curl命令和 Docker 引擎 API 对 Docker 映像执行各种操作。

怎么做...

在本食谱中,我们将看一些映像操作,如下所示:

  1. 要列出映像,请使用以下应用编程接口:

下面是前面语法的一个例子:

  1. 您可以使用以下应用编程接口从任何注册表或 tar 文件中提取映像来创建映像:

/img/create应用编程接口支持几个选项来处理映像,如下所列:

现在让我们看几个例子:

  1. 从 Docker Hub 获取cookbook/apache2映像:
 $ curl -X POST \ --unix-socket /var/run/docker.sock \ 
 httimg/create?fromImage=cookbook/apache2
  1. 获取带有latest标签的 WordPress 映像:

  1. tar文件创建映像:

在本例中,我们选择使用curl命令的--data-binary选项从 Docker 主机上传映像作为 tar 包。这里,myimage.tar的内容通过 HTTP 消息体发送给 Docker 守护进程。如果你仔细观察curl命令调用,你会注意到-i选项。我们正在使用curl命令的-i选项来获取 HTTP 头信息。

要删除映像,请使用以下应用编程接口:

下面是前面语法的一个例子:

    $ curl -X DELETE \
           --unix-socket /var/run/docker.sock \
           httimg/wordpress:latest 

它是如何工作的...

在本食谱中,我们使用curl命令对 Docker 映像执行了各种操作。curl命令通过 HTTP 将我们的应用编程接口请求作为 REST 应用编程接口请求发送给 Docker 守护程序。接下来,Docker 守护程序将对映像执行请求的操作,并回复操作状态。

还有更多...

在这个食谱中,我们只介绍了与 Docker 映像处理相关的三个 API,但还有更多。下面的截图列出了所有可用的/imagesAPI:

请参见

使用 API 构建映像

在前面的食谱中,我们探索了使用 API 对 Docker 映像执行的一些操作。在这个食谱中,我们将使用/build API 构建一个 Docker 映像。以下是斯瓦格编辑器的/build应用编程接口片段:

怎么做...

  1. 首先克隆https://github.com/docker-cookbook/apache2存储库,如下所示:
    $ git clone https://github.com/docker-cookbook/apache2  

该存储库包含用于捆绑apache2服务的Dockerfile;这里列出的是Dockerfile的内容:

  1. 让我们通过将克隆的apache2存储库的内容绑定为 tar 文件来创建构建上下文,如下所示:
        $ cd apache2
        $ tar cvf /tmp/apache2.tar *  
  1. 继续使用/build应用编程接口构建 Docker 映像:
        $ curl -X POST \
           -H "Content-Type:application/tar" \
           --data-binary '@/tmp/apache2.tar' \
    --unix-socket /var/run/docker.sock \ 
           http:/build

在构建过程中,您会收到一系列 JSON 消息形式的构建日志。一旦构建成功完成,您将获得类似如下的 JSON 消息:

    {"stream":"Successfully built 3c6f5044386d\n"} 

在前面的 JSON 消息中,3c6f5044386d是我们刚刚使用/build API 构建的映像的 ID。

它是如何工作的...

在这个方法中,我们将构建上下文捆绑为一个 tar 文件,并将其作为/build API 调用的一部分发送给 Docker 引擎。Docker 引擎使用构建上下文和构建上下文中的Dockerfile来构建 Docker 映像。

还有更多...

  1. 在这个配方中,我们没有指定任何存储库或标签名,因此创建的映像没有任何存储库或标签名,如下所示:
        $ curl -s --unix-socket /var/run/docker.sock \
                 httimg/json | jq ".[0].RepoTags"
    [
      "<none>:<none>"
    ]  

当然,您现在可以使 img/{name}/tag`应用编程接口用适当的存储库和标签名称来标记映像。以下是从斯瓦格编辑器中截取的帮助文档:

或者,您可以在构建期间使用t参数将映像与存储库名称捆绑在一起,如下所示:

        $ curl -X POST \
           -H "Content-Type:application/tar" \
           --data-binary '@/tmp/apache2.tar' \
    --unix-socket /var/run/docker.sock \ 
           http:/build?t=apache2:usingapi  

标签名是可选的,如果没有指定标签名,Docker 构建引擎将采用标签名latest

  1. 您还可以使用以下应用编程接口从容器创建映像:

以下是从容器标识4aaec8980c43提交映像的示例:

请参见

每个应用编程接口端点可以有不同的输入来控制操作。有关更多详细信息,请访问 Docker 网站上的文档,网址为https://docs.docker.com/engine/api/latest/.

使用 API 启动容器

第二章与 Docker Containers 合作、配方启动容器中,我们有条不紊、一丝不苟地探索了运行容器的不同方式。在所有这些场景中,我们使用了带有几个选项的docker container run命令,并且我们所有的容器都已启动并运行。然而,在幕后,Docker CLI 通过首先通过/create API 创建容器层,然后通过/start API 启动应用(cmd)来实现,如下图所示:

除了/create/start API 之外,Docker CLI 还使用了/attach/wait等 API 来满足我们的要求。在本食谱中,我们将创建一个alpine容器,并运行一个简单的ls命令来演示通过 Docker 引擎 API 启动容器所涉及的步骤。

怎么做...

  1. 首先从alpine映像创建一个容器,如下图所示:

这里,我们使用curl命令的-d选项将我们的容器配置作为 JSON 数据传递给 Docker 引擎。显然,映像是alpine,我们选择在容器启动时运行的命令是ls。此外,我们还请求 Docker 引擎将STDERRSTDOUT附加到该容器,以便检索ls命令的输出。

HTTP 头响应代码201 Created表示我们的容器创建成功。显然,来自 Docker 引擎的响应也是一个 JSON 负载。有效载荷的标识字段包含容器标识,即f9fd4b2e2040d4dea32deb527889bf2fb95b351d8316a4c74bfb6e2e38c9b499。我们将使用容器标识的简称f9fd4b2e2040,对该容器执行进一步的操作。

  1. 由于我们想要在屏幕上捕获ls命令的输出,让我们使用/attach应用编程接口连接到容器:

如果使用stderrstdoutstream参数调用/attach应用编程接口,将会阻塞客户端(即curl,所以我们在后台运行curl命令。

  1. 现在,继续使用/start API 启动容器,如下所示:

很酷,不是吗?我们用 Docker 引擎 API 模拟了docker container run命令。

它是如何工作的...

在这个配方中,我们使用了三个 Docker 引擎 API 来成功启动一个容器。在后端,Docker 引擎接收来自客户端的 API 调用,并代表客户端为/container/create API 调用创建容器,然后为/containers/attach API 调用阻塞 HTTP 流,最后为/containers/start API 调用在容器的名称空间内运行ls命令。

还有更多...

以下是处理容器生命周期的 API 列表:

请参见

每个应用编程接口端点可以有不同的输入来控制操作。有关更多详细信息,请访问 Docker 网站上的文档,网址为https://docs.docker.com/engine/api/latest/.

使用 API 执行容器操作

在前面的配方中,我们启动了一个容器,并使用/create/attach/start应用编程接口在容器中执行了一个命令。在这个配方中,我们对容器执行了一些操作。

怎么做...

在本食谱中,我们将了解一些容器操作:

  1. 要列出容器,请使用以下应用编程接口:

这里有几个例子:

  • 以下是如何获得所有运行的容器:
        $ curl --unix-socket /var/run/docker.sock \
           http:/containers/json 
  • 以下是如何获取所有正在运行的容器,包括停止的容器:
        $ curl --unix-socket /var/run/docker.sock \
           http:/containers/json?all=1
  1. 要检查容器,请使用以下应用编程接口:

这里有一个检查容器的例子591ab8ac2650:

    $ curl --unix-socket /var/run/docker.sock \ 
        http:/containers/591ab8ac2650/json  
  1. 要获取容器内运行的进程列表,请使用以下应用编程接口:

下面是一个在591ab8ac2650容器中运行的进程的例子:

        $ curl --unix-socket /var/run/docker.sock \ 
        http:/containers/591ab8ac2650/top
  1. 要获取容器的资源使用统计数据,请使用以下应用编程接口:

下面是获取591ab8ac2650容器的资源使用统计的一个例子:

        $ curl --unix-socket /var/run/docker.sock \ 
        http:/containers/591ab8ac2650/stats  

默认情况下,该应用编程接口流式传输资源使用统计数据。但是,您可以使用stream参数禁用流,如下所示:

        $ curl --unix-socket /var/run/docker.sock \ 
        http:/containers/591ab8ac2650/stats?stream=0  

它是如何工作的...

当我们使用这个配方中描述的 API 与 Docker 引擎连接时,Docker 引擎会反过来从它的数据源收集相关信息并将其发送回客户端。

请参见

每个应用编程接口端点可以有不同的输入来控制操作。有关更多详细信息,请访问 Docker 网站上的文档,网址为https://docs.docker.com/engine/api/latest/.

探索 Docker 远程应用编程接口客户端库

在前面的食谱中,我们探索了 Docker 提供的 API,以连接和执行 Docker 守护进程上的操作。Docker 还为pythongo语言提供了软件开发工具包。

在这个食谱中,让我们用几个例子来探索python SDK。

准备好

  • 确保安装python3
  • ubuntu 16.04可能没有pip3,所以使用以下命令安装pip3:
        $ sudo apt-get -y install python3-pip  

怎么做...

  1. 让我们首先使用pip3python安装 docker 软件包:
        $ sudo pip3 install docker  
  1. 现在,让我们启动python3并导入dockerjson包,如下所示:

  1. 导入dockerjson包后,让我们使用docker.DockerClient通过 Unix 套接字unix://var/run/docker.sock连接到 Docker 守护程序,如下所示:

这里,base_url是 Docker 守护进程的连接地址。

  1. 继续使用以下代码打印 Docker 守护程序版本:

这里client.version()json格式从 Docker 服务器获取版本详细信息,然后我们使用json.dump()以漂亮的格式打印获取的json数据。

  1. 让我们再编写一点代码,使用client.containers.list()列出所有正在运行的容器,如下所示:

如您所见,我们有两个运行容器:458b8add4a62c814650f

  1. 最后,让我们使用client.containers.run()启动一个容器,如下图所示:

它是如何工作的...

在前面的所有情况下,Docker 模块会将我们的请求翻译到适当的 Docker 引擎 API,将其包装为 RESTful 消息,并将其发送给 Docker 守护程序。

请参见

https://docker-py.readthedocs.io/en/stable/.你会发现一个清晰的 Python SDK 文档

为远程连接配置 Docker 守护程序

在前面的食谱中,我们使用 Unix 套接字(/var/run/docker.sock)与 Docker 引擎进行对话。如前所述,默认情况下dockerd监听 Unix 套接字/var/run/docker.sock。然而,对 Unix 套接字的访问仅限于本地系统。但是在一些用例中,您必须远程访问 Docker 守护程序。您可以通过配置 Docker 守护程序使用tcp套接字监听远程连接来实现这一点。在本食谱中,我们将为远程应用编程接口连接配置我们的 Docker 守护程序。

怎么做...

  1. 让我们首先使用systemctl命令定位 Docker 服务的 Systemd 单元文件,如下所示:
         $ sudo systemctl docker status | grep Loaded
    Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)

显然,/lib/systemd/system/docker.service是 Docker 服务的单元文件。以下是默认 Docker 服务单元文件的内容:

可以看到,单元文件中的ExecStart被配置为以fd://为监听套接字启动dockerd (Docker 守护进程)。

  1. 继续配置dockerd以接受来自远程系统的连接,方法是将-H tcp://0.0.0.0:2375附加到单元文件中的ExecStart行,如下所示:
        ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375

这里,IP 地址0.0.0.0是将服务绑定到 Docker 主机的所有 IPv4 地址的通配符地址。端口2375是用于明文(未加密)通信的约定。

  1. 修改了 Docker 服务的 Docker 服务单元文件后,我们需要手动重新加载更改,如下所示:
        $ sudo systemctl daemon-reload    
  1. 继续重新启动 Docker 服务,以便dockerd守护程序可以监听来自外部世界的通信:
 $ sudo systemctl restart docker
  1. 现在,我们可以从与 Docker 主机有网络连接的任何系统连接到 Docker 引擎 API。为了演示这种远程连接,首先让我们使用我们最喜欢的curl命令来获取 Docker 服务器版本,如下所示:

让我们也使用 Docker 客户端演示远程连接,如下所示:

显然,客户端运行在 windows 机器上,服务器运行在 Linux 上。

它是如何工作的...

在前面的命令中,我们配置了 Docker 守护程序,以监听 Docker 主机中所有可用网络接口上的 TCP 端口2375上的客户端连接。通过这一配置更改,客户端可以使用 Docker 主机上的任何网络接口连接到 Docker 守护程序。

还有更多...

  • 在这个方法中,我们配置 Docker 守护程序使用明文或未加密的传输层进行通信。此外,Docker 守护程序可以从任何具有网络连接的系统接收消息。由于 Docker 以 root 身份运行,因此与守护程序的开放式连接存在巨大的安全风险。因此,仅在受限网络中使用此模型进行远程连接。否则,使用传输层安全性 ( TLS )保护 Docker 守护程序和客户端之间的通信,如本章后面所述。
  • 之前,我们使用了docker命令的-H选项来指定远程 Docker 守护程序的地址。当我们不得不对远程 Docker 引擎运行多个命令时,这是违背直觉的。在这种情况下,我们可以使用环境变量DOCKER_HOST,来设置远程地址,如下所示:
    $ export DOCKER_HOST=tcp://dockerhost.example.com:2375

一旦我们配置了环境变量DOCKER_HOST,Docker 客户端将使用该地址发送我们的请求。因此,默认情况下,该会话中的所有未来docker命令都将转到远程 Docker 主机。

请参见

Docker 网站上的文档可以在https://docs . Docker . com/engine/reference/command line/dockerd/# daemon-socket-option 上找到。

保护 Docker 守护进程的远程连接

在本章的前面,我们看到了如何配置 Docker 守护程序来接受远程连接。然而,使用我们遵循的方法,任何人都可以连接到我们的 Docker 守护程序。我们可以确保与传输层安全性(http://en.wikipedia.org/wiki/Transport_Layer_Security)的连接。

我们可以通过使用现有的证书颁发机构 ( CA )或者通过创建我们自己的来配置 TLS。为简单起见,我们将创建自己的,不建议用于生产。对于这个例子,我们假设运行 Docker 守护程序的主机是dockerhost.example.com

准备好

确保安装了openssl库。

怎么做...

  1. 在您的主机上创建一个目录,在其中放置我们的 CA 和其他相关文件:
         $ mkdir -p /etc/docker/keys
         $ cd /etc/docker/keys
  1. 创建证书颁发机构私钥和公钥:
        $ openssl genrsa -aes256 -out ca-key.pem 4096 
        $ openssl req -new -x509 -days 365 -key ca-key.pem \
    -sha256 -out ca.pem 

  1. 现在,让我们创建服务器密钥和证书签名请求。确保通用名称与 Docker 守护程序系统主机名匹配。在我们的情况下,是dockerhost.example.com:
    $ openssl genrsa -out server-key.pem 4096 
    $ openssl req -subj "/CN=dockerhost.example.com" \
    -new -key server-key.pem -out server.csr 

  1. 客户端可以使用 Docker 主机的域名或 IP 地址连接到 Docker 守护程序。因此,必须将域名和 IP 地址作为扩展名制作到证书中。此外,添加 Docker 守护程序密钥的扩展使用属性,以便仅用于服务器身份验证。这两条信息都在extfile.cnf中捕获,如下所示:

这里,192.168.33.10110.0.2.15是两个网络接口的 IP 地址,127.0.0.1是环回地址。

  1. 继续生成密钥:
        $ openssl x509 -req -days 365 -sha256 -in server.csr \
                    -CA ca.pem -CAkey ca-key.pem -CAcreateserial \
                   -out server-cert.pem -extfile extfile.cnf  

  1. 对于客户端身份验证,创建客户端密钥和证书签名请求:
    $ openssl genrsa -out key.pem 4096 
    $ openssl req -subj '/CN=client' -new -key key.pem \
    -out client.csr 

  1. 要使密钥适合客户端身份验证,请创建一个扩展配置文件并对公钥进行签名:
    $ echo extendedKeyUsage = clientAuth > client-extfile.cnf 
    $ openssl x509 -req -days 365 -sha256 -in client.csr \
                   -CA ca.pem -CAkey ca-key.pem -CAcreateserial \
                   -out cert.pem -extfile client-extfile.cnf

  1. 生成cert.pemserver-cert.pem后,我们可以安全地删除两个证书签名请求:
        $ rm -rf client.csr server.csr 
  1. 为了保护密钥不被意外损坏,让我们删除这些密钥文件的写权限:ca-key.pemkey.pemserver-key.pem。此外,让我们将这些文件的读取权限限制为root:
        $ chmod 0400 ca-key.pem key.pem server-key.pem

证书文件ca.pemserver-cert.pemcert.pem需要更广泛的读访问权限,所以让我们给所有这些证书文件读访问权限,如下所示:

 $ chmod 0444 ca.pem server-cert.pem cert.pem
  1. 如果守护程序正在使用systemctl stop docker命令在dockerhost.example.com上运行,则停止守护程序。然后,从/etc/docker/keys手动启动 Docker 守护程序:
    $ dockerd --tlsverify \ 
              --tlscacert=ca.pem \
              --tlscert=server-cert.pem \
              --tlskey=server-key.pem \
    -H=0.0.0.0:2376 
  1. 从另一个终端,转到/etc/docker/keys。运行以下命令连接到 Docker 守护程序:
        $ cd /etc/docker/keys
        $ docker --tlsverify \
           --tlscacert=ca.pem \
           --tlscert=cert.pem \
           --tlskey=key.pem \
    -H=127.0.0.1:2376 version 

Docker 客户端能够通过 TLS 与 Docker 守护进程无缝连接,并获得服务器版本。

它是如何工作的...

一旦我们将 Docker 守护程序配置为使用 TLS 作为传输,它只接受客户端的安全 TLS 连接,并满足客户端的请求。

还有更多...

在本食谱中,我们使用docker命令的--tlscacert--tlscert--tlskey选项来连接到启用了 TLS 的 Docker 守护程序。使用如此长的选项列表来调用docker命令是相当笨拙的。但是,我们可以通过执行以下操作来解决这个问题:

  1. ca.pemcert.pemkey.pem文件复制到用户的$HOME/.docker目录。
  2. 使用chown命令修改该用户的文件所有权。
  3. DOCKER_HOST设置为守护程序地址,如下所示:
          $ export DOCKER_HOST=tcp://127.0.0.1:2376    
  1. DOCKER_TLS_VERIFY设置为1,如下图:
          $ export DOCKER_TLS_VERIFY=1    

现在,您可以像在 Unix 套接字上运行一样运行docker命令。

在这个食谱中,我们从 shell 提示符启动了 Docker 守护程序,这对测试很有好处。但是,必须将 Docker 守护程序配置为使用 Systemd 启动。您可以通过编辑 Docker 服务的单元文件来实现这一点,如为远程连接配置 Docker 守护程序配方中所述,但ExecStart有以下例外:

ExecStart=/usr/bin/dockerd \
             --tlsverify \
             --tlscacert=/etc/docker/keys/ca.pem \
             --tlscert=/etc/docker/keys/server-cert.pem \
             --tlskey=/etc/docker/keys/server-key.pem \
             -H=0.0.0.0:2376

在这里,我们将命令分成多行,以便于理解。但是,它必须在单位文件中的一行中。

  • curl命令还可以安全地连接到启用 TLS 的 Docker 守护程序,如下所示:
$ curl --cacert ${HOME}/.docker/ca.pem \
--cert ${HOME}/.docker/cert.pem \
--key ${HOME}/.docker/key.pem \
https://127.0.0.1:2376/version

请参见