Docker Java API 简介
1. 概述
在本文中,我们将了解另一个成熟的平台特定 API — Java API Client for Docker 。
在整篇文章中,我们理解了如何连接正在运行的 Docker 守护程序以及 API 为 Java 开发人员提供了哪些类型的重要功能。
2. Maven依赖
首先,我们需要将主要依赖添加到我们的pom.xml文件中:
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.0.14</version>
</dependency>
在撰写本文时,API 的最新版本是 3.0.14。每个版本都可以从GitHub 发布页面 或Maven 存储库 中查看。
3. 使用 Docker 客户端
DockerClient是我们可以在 Docker 引擎/守护进程和我们的应用程序之间建立连接的地方。
默认情况下,只能通过 unix:///var/run/docker.sock 文件访问 Docker 守护进程。除非另有配置,否则我们可以在本地与侦听 Unix 套接字的 Docker 引擎进行通信。
在这里,我们通过接受默认设置来应用DockerClientBuilder类来创建连接:
DockerClient dockerClient = DockerClientBuilder.getInstance().build();
同样,我们可以分两步打开连接:
DefaultDockerClientConfig.Builder config
= DefaultDockerClientConfig.createDefaultConfigBuilder();
DockerClient dockerClient = DockerClientBuilder
.getInstance(config)
.build();
由于引擎可以依赖其他特性,因此客户端也可以根据不同的条件进行配置。
例如,构建器接受服务器 URL,也就是说,如果引擎在端口 2375 上可用,我们可以更新连接值:
DockerClient dockerClient
= DockerClientBuilder.getInstance("tcp://docker.blogdemo.com:2375").build();
请注意,我们需要根据连接类型在连接字符串前面加上unix://或tcp:// 。
如果我们更进一步,我们可以使用DefaultDockerClientConfig类获得更高级的配置:
DefaultDockerClientConfig config
= DefaultDockerClientConfig.createDefaultConfigBuilder()
.withRegistryEmail("[[email protected]](/cdn_cgi/l/email_protection)")
.withRegistryPassword("blogdemo")
.withRegistryUsername("blogdemo")
.withDockerCertPath("/home/blogdemo/.docker/certs")
.withDockerConfig("/home/blogdemo/.docker/")
.withDockerTlsVerify("1")
.withDockerHost("tcp://docker.blogdemo.com:2376").build();
DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
同样,我们可以使用Properties执行相同的方法:
Properties properties = new Properties();
properties.setProperty("registry.email", "[[email protected]](/cdn_cgi/l/email_protection)");
properties.setProperty("registry.password", "blogdemo");
properties.setProperty("registry.username", "baaldung");
properties.setProperty("DOCKER_CERT_PATH", "/home/blogdemo/.docker/certs");
properties.setProperty("DOCKER_CONFIG", "/home/blogdemo/.docker/");
properties.setProperty("DOCKER_TLS_VERIFY", "1");
properties.setProperty("DOCKER_HOST", "tcp://docker.blogdemo.com:2376");
DefaultDockerClientConfig config
= DefaultDockerClientConfig.createDefaultConfigBuilder()
.withProperties(properties).build();
DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
除非我们在源代码中配置引擎的设置,否则另一种选择是设置相应的环境变量,这样我们就只能考虑在项目中默认实例化DockerClient:
export DOCKER_CERT_PATH=/home/blogdemo/.docker/certs
export DOCKER_CONFIG=/home/blogdemo/.docker/
export DOCKER_TLS_VERIFY=1
export DOCKER_HOST=tcp://docker.blogdemo.com:2376
4. 容器管理
API 允许我们在容器管理方面有多种选择。让我们来看看他们中的每一个。
4.1.列出容器
现在我们已经建立了连接,我们可以列出位于 Docker 主机上的所有正在运行的容器:
List<Container> containers = dockerClient.listContainersCmd().exec();
如果显示正在运行的容器不满足需要,我们可以利用提供的选项来查询容器。
在这种情况下,我们显示具有“exited”状态的容器:
List<Container> containers = dockerClient.listContainersCmd()
.withShowSize(true)
.withShowAll(true)
.withStatusFilter("exited").exec()
它相当于:
$ docker ps -a -s -f status=exited
# or
$ docker container ls -a -s -f status=exited
4.2. 创建一个容器
使用createContainerCmd方法创建容器。我们可以使用以“with”*前缀开头的可用方法*声明更复杂的声明。
假设我们有一个docker create命令,它定义了一个依赖于主机的 MongoDB 容器,该容器在内部侦听端口 27017:
$ docker create --name mongo \
--hostname=blogdemo \
-e MONGO_LATEST_VERSION=3.6 \
-p 9999:27017 \
-v /Users/blogdemo/mongo/data/db:/data/db \
mongo:3.6 --bind_ip_all
我们能够以编程方式引导同一个容器及其配置:
CreateContainerResponse container
= dockerClient.createContainerCmd("mongo:3.6")
.withCmd("--bind_ip_all")
.withName("mongo")
.withHostName("blogdemo")
.withEnv("MONGO_LATEST_VERSION=3.6")
.withPortBindings(PortBinding.parse("9999:27017"))
.withBinds(Bind.parse("/Users/blogdemo/mongo/data/db:/data/db")).exec();
4.3. 启动、停止和终止容器
一旦我们创建了容器,我们就可以分别通过名称或 id 来启动、停止和终止它:
dockerClient.startContainerCmd(container.getId()).exec();
dockerClient.stopContainerCmd(container.getId()).exec();
dockerClient.killContainerCmd(container.getId()).exec();
4.4. 检查容器
inspectContainerCmd方法接受一个String参数,该参数指示容器的名称或 id。使用这种方法,我们可以直接观察容器的元数据:
InspectContainerResponse container
= dockerClient.inspectContainerCmd(container.getId()).exec();
4.5. 快照容器
与docker commit命令类似,我们可以使用commitCmd方法创建一个新镜像。
在我们的示例中,场景是**,我们之前运行了一个 alpine:3.6 容器,其 id 为*“3464bb547f88”,并在其上安装了git*。**
现在,我们要从容器中创建一个新的镜像快照:
String snapshotId = dockerClient.commitCmd("3464bb547f88")
.withAuthor("Blogdemo <[[email protected]](/cdn_cgi/l/email_protection)>")
.withEnv("SNAPSHOT_YEAR=2018")
.withMessage("add git support")
.withCmd("git", "version")
.withRepository("alpine")
.withTag("3.6.git").exec();
由于我们与git捆绑的新镜像保留在主机上,我们可以在 Docker 主机上搜索它:
$ docker image ls alpine --format "table {{.Repository}} {{.Tag}}"
REPOSITORY TAG
alpine 3.6.git
5. 镜像管理
我们提供了一些适用的命令来管理镜像操作。
5.1.列出镜像
要列出 Docker 主机上所有可用的镜像,包括悬空镜像,我们需要应用listImagesCmd方法:
List<Image> images = dockerClient.listImagesCmd().exec();
如果我们的 Docker 主机上有两个镜像,我们应该在运行时获取它们的Image对象。我们寻找的镜像是:
$ docker image ls --format "table {{.Repository}} {{.Tag}}"
REPOSITORY TAG
alpine 3.6
mongo 3.6
接下来,要查看中间镜像,我们需要明确地请求它:
List<Image> images = dockerClient.listImagesCmd()
.withShowAll(true).exec();
如果只显示悬空镜像,则必须考虑withDanglingFilter方法:
List<Image> images = dockerClient.listImagesCmd()
.withDanglingFilter(true).exec();
5.2. 构建镜像
让我们专注于使用 API 构建镜像的方式。buildImageCmd方法从Dockerfile构建 Docker 映像。在我们的项目中,我们已经有一个 Dockerfile,它提供了一个安装了 git 的 Alpine 镜像:
FROM alpine:3.6
RUN apk --update add git openssh && \
rm -rf /var/lib/apt/lists/* && \
rm /var/cache/apk/*
ENTRYPOINT ["git"]
CMD ["--help"]
新镜像将在不使用缓存的情况下构建,并且在开始构建过程之前,无论如何,Docker 引擎都会尝试拉取更新版本的alpine:3.6 。如果一切顺利,我们最终应该会看到具有给定名称 alpine:git 的镜像:
String imageId = dockerClient.buildImageCmd()
.withDockerfile(new File("path/to/Dockerfile"))
.withPull(true)
.withNoCache(true)
.withTag("alpine:git")
.exec(new BuildImageResultCallback())
.awaitImageId();
5.3. 检查镜像
借助 inspectImageCmd方法,我们可以检查有关镜像的低级信息:
InspectImageResponse image
= dockerClient.inspectImageCmd("161714540c41").exec();
5.4. 标记镜像
使用docker tag命令向我们的镜像添加标签非常简单,因此 API 也不例外。我们也可以使用tagImageCmd方法实现相同的目的。使用 git 将 id 为161714540c41的 Docker 映像标记到 blogdemo/alpine 存储库中:
String imageId = "161714540c41";
String repository = "blogdemo/alpine";
String tag = "git";
dockerClient.tagImageCmd(imageId, repository, tag).exec();
我们将列出新创建的镜像,它是:
$ docker image ls --format "table {{.Repository}} {{.Tag}}"
REPOSITORY TAG
blogdemo/alpine git
5.5. 推送镜像
在将镜像发送到注册表服务之前,必须将 docker 客户端配置为与该服务协作,因为使用注册表需要提前进行身份验证。
由于我们假设客户端配置了 Docker Hub,我们可以将blogdemo/alpine镜像推送到 blogdemo DockerHub 账户:
dockerClient.pushImageCmd("blogdemo/alpine")
.withTag("git")
.exec(new PushImageResultCallback())
.awaitCompletion(90, TimeUnit.SECONDS);
我们必须遵守这个过程的持续时间。在示例中,我们等待 90 秒。
5.6. 拉取镜像
要从注册表服务下载镜像,我们使用pullImageCmd方法。此外,如果从私有注册表中提取镜像,客户端必须知道我们的凭据,否则该过程会以失败告终。和拉图一样,我们指定一个回调和一个固定的时间来拉取镜像:
dockerClient.pullImageCmd("blogdemo/alpine")
.withTag("git")
.exec(new PullImageResultCallback())
.awaitCompletion(30, TimeUnit.SECONDS);
拉取镜像后检查 Docker 主机上是否存在上述镜像:
$ docker images blogdemo/alpine --format "table {{.Repository}} {{.Tag}}"
REPOSITORY TAG
blogdemo/alpine git
5.7. 删除镜像
其余的另一个简单函数是removeImageCmd方法。我们可以删除具有短 ID 或长 ID 的镜像:
dockerClient.removeImageCmd("beaccc8687ae").exec();
5.8. 在注册表中搜索
为了从 Docker Hub 中搜索镜像,客户端附带了searchImagesCmd方法,该方法采用一个表示术语的字符串值。在这里,我们探索与Docker Hub 中包含“ Java”的名称相关的镜像:
List<SearchItem> items = dockerClient.searchImagesCmd("Java").exec();
输出返回SearchItem对象列表中的前 25 个相关镜像。
6. 卷管理
如果 Java 项目需要与 Docker 交互以获取卷,我们还应该考虑这一节。简而言之,我们看一下 Docker Java API 提供的卷的基本技术。
6.1.列出卷
包括命名和未命名的所有可用卷都列出:
ListVolumesResponse volumesResponse = dockerClient.listVolumesCmd().exec();
List<InspectVolumeResponse> volumes = volumesResponse.getVolumes();
6.2. 检查卷
inspectVolumeCmd方法是显示卷详细信息的形式。我们通过指定它的短 id 来检查卷:
InspectVolumeResponse volume
= dockerClient.inspectVolumeCmd("0220b87330af5").exec();
6.3. 创建卷
API 提供两种不同的选项来创建卷。非参数createVolumeCmd方法创建一个卷,其名称由 Docker 给出:
CreateVolumeResponse unnamedVolume = dockerClient.createVolumeCmd().exec();
与使用默认行为不同,名为withName的辅助方法允许我们为卷设置名称:
CreateVolumeResponse namedVolume
= dockerClient.createVolumeCmd().withName("myNamedVolume").exec();
6.4. 删除卷
我们可以使用removeVolumeCmd方法直观地从 Docker 主机中删除一个卷。**需要注意的是,如果某个卷正在从容器中使用,我们将无法删除它。**我们从卷列表中删除卷myNamedVolume:
dockerClient.removeVolumeCmd("myNamedVolume").exec();
7. 网络管理
最后一节是关于使用 API 管理网络任务的。
7.1. 列出网络
我们可以使用以 list 开头的传统 API 方法之一显示网络单元列表:
List<Network> networks = dockerClient.listNetworksCmd().exec();
7.2. 创建网络
使用createNetworkCmd方法执行相当于docker network create命令。如果我们有三十方或自定义网络驱动程序,除了内置驱动程序之外, withDriver方法可以接受它们。在我们的例子中,让我们创建一个名为blogdemo的桥接网络:
CreateNetworkResponse networkResponse
= dockerClient.createNetworkCmd()
.withName("blogdemo")
.withDriver("bridge").exec();
此外,使用默认设置创建网络单元并不能解决问题,我们可以申请其他辅助方法来构建高级网络。因此,要使用自定义值覆盖默认子网:
CreateNetworkResponse networkResponse = dockerClient.createNetworkCmd()
.withName("blogdemo")
.withIpam(new Ipam()
.withConfig(new Config()
.withSubnet("172.36.0.0/16")
.withIpRange("172.36.5.0/24")))
.withDriver("bridge").exec();
我们可以使用docker命令运行的相同命令是:
$ docker network create \
--subnet=172.36.0.0/16 \
--ip-range=172.36.5.0/24 \
blogdemo
7.3. 检查网络
API 中还介绍了显示网络的低级详细信息:
Network network
= dockerClient.inspectNetworkCmd().withNetworkId("blogdemo").exec();
7.4. 删除网络
我们可以使用removeNetworkCmd方法安全地删除具有名称或 id 的网络单元:
dockerClient.removeNetworkCmd("blogdemo").exec();