1. Overview
1.概述
In this article, we take a look at another well-established platform specific API — Java API Client for Docker.
在这篇文章中,我们看一下另一个成熟的特定平台API – Java API Client for Docker。
Throughout the article, we comprehend the way of how to connect with a running Docker daemon and what type of important functionality the API offers to Java developers.
在整个文章中,我们理解了如何与正在运行的Docker守护程序连接的方式,以及该API为Java开发者提供了哪些类型的重要功能。
2. Maven Dependency
2.Maven的依赖性
First, we need to add the main dependency into our pom.xml file:
首先,我们需要在pom.xml文件中添加主要依赖关系。
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.0.14</version>
</dependency>
At the time of writing the article, the latest version of the API is 3.0.14. Each release can be viewed either from the GitHub release page or from the Maven repository.
在撰写本文时,API的最新版本是3.0.14。每个版本都可以从GitHub发布页面或Maven资源库中查看。
3. Using the Docker Client
3.使用Docker客户端
DockerClient is where we can establish a connection between a Docker engine/daemon and our application.
DockerClient是我们可以在Docker引擎/守护进程和我们的应用程序之间建立连接的地方。
By default, the Docker daemon can only be accessible at the unix:///var/run/docker.sock file. We can locally communicate with the Docker engine listening on the Unix socket unless otherwise configured.
默认情况下,Docker守护进程只能在unix://var/run/docker.sock文件中访问。我们可以在本地与在Unix套接字上监听的Docker引擎通信,除非另有配置。
Here, we apply to the DockerClientBuilder class to create a connection by accepting the default settings:
在这里,我们适用于DockerClientBuilder类,通过接受默认设置来创建一个连接。
DockerClient dockerClient = DockerClientBuilder.getInstance().build();
Similarly, we can open a connection in two steps:
同样地,我们可以通过两个步骤打开一个连接。
DefaultDockerClientConfig.Builder config
= DefaultDockerClientConfig.createDefaultConfigBuilder();
DockerClient dockerClient = DockerClientBuilder
.getInstance(config)
.build();
Since engines could rely on other characteristics, the client is also configurable with different conditions.
由于引擎可以依靠其他特性,因此客户也可以配置不同的条件。
For example, the builder accepts a server URL, that is, we can update the connection value if the engine is available on port 2375:
例如,构建器接受一个服务器URL,也就是说,如果引擎在2375端口可用,我们可以更新连接值。
DockerClient dockerClient
= DockerClientBuilder.getInstance("tcp://docker.baeldung.com:2375").build();
Note that we need to prepend the connection string with unix:// or tcp:// depending on the connection type.
注意,我们需要在连接字符串前加上unix://或tcp://,这取决于连接类型。
If we go one step further, we can end up with a more advanced configuration using the DefaultDockerClientConfig class:
如果我们更进一步,我们可以使用DefaultDockerClientConfig类,最终获得更高级的配置。
DefaultDockerClientConfig config
= DefaultDockerClientConfig.createDefaultConfigBuilder()
.withRegistryEmail("info@baeldung.com")
.withRegistryPassword("baeldung")
.withRegistryUsername("baeldung")
.withDockerCertPath("/home/baeldung/.docker/certs")
.withDockerConfig("/home/baeldung/.docker/")
.withDockerTlsVerify("1")
.withDockerHost("tcp://docker.baeldung.com:2376").build();
DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
Likewise, we can carry out the same approach using Properties:
同样,我们可以使用Properties进行同样的方法。
Properties properties = new Properties();
properties.setProperty("registry.email", "info@baeldung.com");
properties.setProperty("registry.password", "baeldung");
properties.setProperty("registry.username", "baaldung");
properties.setProperty("DOCKER_CERT_PATH", "/home/baeldung/.docker/certs");
properties.setProperty("DOCKER_CONFIG", "/home/baeldung/.docker/");
properties.setProperty("DOCKER_TLS_VERIFY", "1");
properties.setProperty("DOCKER_HOST", "tcp://docker.baeldung.com:2376");
DefaultDockerClientConfig config
= DefaultDockerClientConfig.createDefaultConfigBuilder()
.withProperties(properties).build();
DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
Another choice unless we configure the engine’s settings in the source code is to set corresponding environment variables so that we can only consider the default instantiation of DockerClient in the project:
除非我们在源代码中配置引擎的设置,否则另一个选择是设置相应的环境变量,这样我们就只能考虑项目中DockerClient的默认实例化。
export DOCKER_CERT_PATH=/home/baeldung/.docker/certs
export DOCKER_CONFIG=/home/baeldung/.docker/
export DOCKER_TLS_VERIFY=1
export DOCKER_HOST=tcp://docker.baeldung.com:2376
4. Container Management
4.容器管理
The API allows us a variety of choices about container management. Let’s look at each one of them.
API允许我们对容器管理有多种选择。让我们来看看它们中的每一个。
4.1. List Containers
4.1.列表容器
Now that we have an established connection, we can list all the running containers located on the Docker host:
现在我们已经建立了一个连接,我们可以列出位于Docker主机上的所有正在运行的容器。
List<Container> containers = dockerClient.listContainersCmd().exec();
Provided that showing the running containers doesn’t appeal the need, we can make use of the offered options to query containers.
如果显示运行中的容器并不吸引人,我们可以利用提供的选项来查询容器。
In this case, we display containers with the “exited” status:
在这种情况下,我们显示具有 “已退出 “状态的容器。
List<Container> containers = dockerClient.listContainersCmd()
.withShowSize(true)
.withShowAll(true)
.withStatusFilter("exited").exec()
It’s an equivalent of:
这是一个相当于。
$ docker ps -a -s -f status=exited
# or
$ docker container ls -a -s -f status=exited
4.2. Create a Container
4.2.创建一个容器
Creating a container is served with the createContainerCmd method. We can declare more complex declaration using the available methods starting with the “with” prefix.
创建一个容器是通过createContainerCmd方法提供的。我们可以使用以”with”前缀开头的可用方法声明更复杂的声明。
Let’s assume that we have a docker create command defining a host-dependent MongoDB container listening internally on port 27017:
让我们假设我们有一个docker create命令,定义了一个依赖主机的MongoDB容器,在内部监听27017端口。
$ docker create --name mongo \
--hostname=baeldung \
-e MONGO_LATEST_VERSION=3.6 \
-p 9999:27017 \
-v /Users/baeldung/mongo/data/db:/data/db \
mongo:3.6 --bind_ip_all
We’re able to bootstrap the same container along with its configurations programmatically:
我们能够以编程方式引导同一容器及其配置。
CreateContainerResponse container
= dockerClient.createContainerCmd("mongo:3.6")
.withCmd("--bind_ip_all")
.withName("mongo")
.withHostName("baeldung")
.withEnv("MONGO_LATEST_VERSION=3.6")
.withPortBindings(PortBinding.parse("9999:27017"))
.withBinds(Bind.parse("/Users/baeldung/mongo/data/db:/data/db")).exec();
4.3. Start, Stop, and Kill a Container
4.3.启动、停止和杀死一个容器
Once we create the container, we can start, stop and kill it by name or id respectively:
一旦我们创建了容器,我们可以分别通过名称或ID来启动、停止和杀死它。
dockerClient.startContainerCmd(container.getId()).exec();
dockerClient.stopContainerCmd(container.getId()).exec();
dockerClient.killContainerCmd(container.getId()).exec();
4.4. Inspect a Container
4.4.检查集装箱
The inspectContainerCmd method takes a String argument which indicates the name or id of a container. Using this method, we can observe the metadata of a container directly:
inspectContainerCmd方法接受一个String参数,该参数表示一个容器的名称或id。使用这个方法,我们可以直接观察一个容器的元数据。
InspectContainerResponse container
= dockerClient.inspectContainerCmd(container.getId()).exec();
4.5. Snapshot a Container
4.5.对一个容器进行快照
Similar to the docker commit command, we can create a new image using the commitCmd method.
与docker commit命令类似,我们可以使用 commitCmd方法创建一个新的镜像。
In our example, the scenario is, we previously run an alpine:3.6 container whose id is “3464bb547f88” and installed git on top of it.
在我们的例子中,情况是,我们之前运行的alpine:3.6容器的id是“3464bb547f88”,并在它上面安装了git。
Now, we want to create a new image snapshot from the container:
现在,我们要从容器中创建一个新的图像快照。
String snapshotId = dockerClient.commitCmd("3464bb547f88")
.withAuthor("Baeldung <info@baeldung.com>")
.withEnv("SNAPSHOT_YEAR=2018")
.withMessage("add git support")
.withCmd("git", "version")
.withRepository("alpine")
.withTag("3.6.git").exec();
Since our new image bundled with git remains on the host, we can search it on the Docker host:
由于我们与git捆绑的新镜像仍然在主机上,我们可以在Docker主机上搜索它。
$ docker image ls alpine --format "table {{.Repository}} {{.Tag}}"
REPOSITORY TAG
alpine 3.6.git
5. Image Management
5.图像管理
There are a few applicable commands we are given to manage image operations.
有几个适用的命令,我们被赋予管理图像操作。
5.1. List Images
5.1.列表图像
To list all the available images including dangling images on the Docker host, we need to apply to the listImagesCmd method:
为了列出所有可用的图像,包括Docker主机上的悬空图像,我们需要应用到listImagesCmd方法。
List<Image> images = dockerClient.listImagesCmd().exec();
If we have two images on our Docker Host, we should obtain the Image objects of them at run-time. The images we look for are:
如果我们的Docker Host上有两个镜像,我们应该在运行时获得它们的Image对象。我们寻找的镜像是。
$ docker image ls --format "table {{.Repository}} {{.Tag}}"
REPOSITORY TAG
alpine 3.6
mongo 3.6
Next to this, to see the intermediate images, we need to request it explicitly:
在这旁边,要看到中间图像,我们需要明确请求。
List<Image> images = dockerClient.listImagesCmd()
.withShowAll(true).exec();
If only displaying the dangling images is the case, the withDanglingFilter method must be considered:
如果只显示悬空的图像,必须考虑withDanglingFilter方法。
List<Image> images = dockerClient.listImagesCmd()
.withDanglingFilter(true).exec();
5.2. Build an Image
5.2.建立一个图像
Let’s focus on the way of building an image using the API. The buildImageCmd method builds Docker images from a Dockerfile. In our project, we already have one Dockerfile which gives an Alpine image with git installed:
让我们关注一下使用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"]
The new image will be built without using cache and before starting the building process, in any case, Docker engine will attempt to pull the newer version of alpine:3.6. If everything goes well, we should eventually see the image with the given name, alpine:git:
新的镜像将在不使用缓存的情况下被构建,在任何情况下,在开始构建过程之前,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. Inspect an Image
5.3.检查一个图像
We can inspect the low-level information about an image thanks to the inspectImageCmd method:
我们可以通过inspectImageCmd方法来检查图像的低层次信息。
InspectImageResponse image
= dockerClient.inspectImageCmd("161714540c41").exec();
5.4. Tag an Image
5.4.标签到图像
Adding a tag to our image is quite simple using the docker tag command, so the API is no exception. We can carry out the same intention with the tagImageCmd method as well. To tag a Docker image with id 161714540c41 into the baeldung/alpine repository with git:
使用docker tag命令向我们的图像添加标签是非常简单的,所以API也不例外。我们也可以用tagImageCmd方法进行同样的意图。用git将id为161714540c41的Docker镜像标记到baeldung/alpine仓库中:
String imageId = "161714540c41";
String repository = "baeldung/alpine";
String tag = "git";
dockerClient.tagImageCmd(imageId, repository, tag).exec();
We would list the newly created image, and there it is:
我们会列出新创建的图像,就在那里。
$ docker image ls --format "table {{.Repository}} {{.Tag}}"
REPOSITORY TAG
baeldung/alpine git
5.5. Push an Image
5.5.推送一个图像
Before sending out an image to a registry service, the docker client must be configured to cooperate with the service because working with registries need to be authenticated in advance.
在向注册表服务发送镜像之前,docker客户端必须被配置为与服务合作,因为与注册表合作需要事先进行认证。
Since we assume that the client was configured with Docker Hub, we can push the baeldung/alpine image to the baeldung DockerHub account:
由于我们假设客户端已经配置了Docker Hub,我们可以将baeldung/alpine镜像推送到baeldung DockerHub账户。
dockerClient.pushImageCmd("baeldung/alpine")
.withTag("git")
.exec(new PushImageResultCallback())
.awaitCompletion(90, TimeUnit.SECONDS);
We must abide by the duration of the process. In the example, we are waiting 90 seconds.
我们必须遵守这个过程的持续时间。在这个例子中,我们正在等待90秒。
5.6. Pull an Image
5.6.拉出一个图像
To download images from registry services, we make use of the pullImageCmd method. In addition, if the image being pulled from a private registry, the client must know our credential otherwise the process ends up with a failure. Same as the pulling an image, we specify a callback along with a fixed period to pull an image:
为了从注册表服务中下载图像,我们使用了pullImageCmd方法。此外,如果图像是从一个私有的注册表中提取的,客户必须知道我们的凭证,否则该过程将以失败告终。与拉取图像一样,我们指定一个回调以及一个固定的周期来拉取图像:。
dockerClient.pullImageCmd("baeldung/alpine")
.withTag("git")
.exec(new PullImageResultCallback())
.awaitCompletion(30, TimeUnit.SECONDS);
To check out whether the mentioned image exists on the Docker host after pulling it:
要检查拉取后的Docker主机上是否存在提到的镜像。
$ docker images baeldung/alpine --format "table {{.Repository}} {{.Tag}}"
REPOSITORY TAG
baeldung/alpine git
5.7. Remove an Image
5.7.删除一个图像
Another simple function among the rest is the removeImageCmd method. We can remove an image with its short or long ID:
其余的另一个简单功能是removeImageCmd方法。我们可以用它的短或长的ID来删除一个图像。
dockerClient.removeImageCmd("beaccc8687ae").exec();
5.8. Search in Registry
5.8.在注册表中搜索
To search an image from Docker Hub, the client comes with the searchImagesCmd method taking a String value which indicates a term. Here, we explore images related to a name containing ‘Java’ in Docker Hub:
为了从Docker Hub中搜索图像,客户端提供了searchImagesCmd方法,该方法使用一个表示术语的String值。在这里,我们探索与Docker Hub中包含’Java’的名称相关的图像。
List<SearchItem> items = dockerClient.searchImagesCmd("Java").exec();
The output returns first 25 related images in a list of SearchItem objects.
输出结果在SearchItem对象的列表中返回前25张相关图片。
6. Volume Management
<6.体积管理
If Java projects need to interact with Docker for volumes, we should also take into account this section. Briefly, we look at the fundamental techniques of volumes provided by the Docker Java API.
如果Java项目需要与Docker进行卷的交互,我们也应该考虑到这一部分。简而言之,我们看看Docker Java API所提供的卷的基本技术。
6.1. List Volumes
6.1.清单卷数
All of the available volumes including named and unnamed are listed with:
所有可用的卷,包括已命名和未命名的卷都列在一起。
ListVolumesResponse volumesResponse = dockerClient.listVolumesCmd().exec();
List<InspectVolumeResponse> volumes = volumesResponse.getVolumes();
6.2. Inspect a Volume
6.2.检查卷宗
The inspectVolumeCmd method is the form to show the detailed information of a volume. We inspect the volume by specifying its short id:
inspectVolumeCmd方法是显示一个卷的详细信息的形式。我们通过指定卷的简短id来检查它。
InspectVolumeResponse volume
= dockerClient.inspectVolumeCmd("0220b87330af5").exec();
6.3. Create a Volume
6.3.创建一个卷轴
The API serves two different options to create a volume. The non-arg createVolumeCmd method creates a volume where the name is given by Docker:
API提供了两个不同的选项来创建一个卷。非参数createVolumeCmd方法创建一个卷,名称由Docker给出。
CreateVolumeResponse unnamedVolume = dockerClient.createVolumeCmd().exec();
Rather than using the default behavior, the helper method called withName lets us set a name to a volume:
不使用默认行为,而是使用名为withName的辅助方法让我们为卷设置一个名称。
CreateVolumeResponse namedVolume
= dockerClient.createVolumeCmd().withName("myNamedVolume").exec();
6.4. Remove a Volume
6.4.移除一个卷轴
We can intuitively delete a volume from the Docker host using the removeVolumeCmd method. What is important to note that we cannot delete a volume if it is in use from a container. We remove the volume, myNamedVolume, from the volume list:
我们可以使用removeVolumeCmd方法直观地从Docker主机中删除一个卷。需要注意的是,如果一个卷正在被容器使用,我们不能删除它。我们从卷列表中删除卷,myNamedVolume。
dockerClient.removeVolumeCmd("myNamedVolume").exec();
7. Network Management
7.网络管理
Our last section is about managing network tasks with the API.
我们的最后一节是关于用API管理网络任务。
7.1. List Networks
7.1.列表网络
We can display the list of network units with one of the conventional API methods starting with list:
我们可以用以list开头的常规API方法之一来显示网络单元的列表。
List<Network> networks = dockerClient.listNetworksCmd().exec();
7.2. Create a Network
7.2.创建一个网络
The equivalent of the docker network create command is conducted with the createNetworkCmd method. If we have a thirty party or a custom network driver, the withDriver method can accept them besides the built-in drivers. In our case, let’s create a bridge network whose name is baeldung:
相当于docker网络创建命令是通过createNetworkCmd方法进行的。如果我们有一个第三方或自定义的网络驱动程序,withDriver方法可以接受他们,除了内置的驱动程序。在我们的例子中,让我们创建一个桥接网络,其名称为baeldung。
CreateNetworkResponse networkResponse
= dockerClient.createNetworkCmd()
.withName("baeldung")
.withDriver("bridge").exec();
Furthermore, creating a network unit with the default settings doesn’t solve the problem, we can apply for other helper methods to construct an advanced network. Thus, to override the default subnetwork with a custom value:
此外,用默认设置创建一个网络单元并不能解决问题,我们可以申请其他帮助方法来构建一个高级网络。因此,用一个自定义值覆盖默认的子网络。
CreateNetworkResponse networkResponse = dockerClient.createNetworkCmd()
.withName("baeldung")
.withIpam(new Ipam()
.withConfig(new Config()
.withSubnet("172.36.0.0/16")
.withIpRange("172.36.5.0/24")))
.withDriver("bridge").exec();
The same command we can run with the docker command is:
我们可以用docker命令运行的相同命令是。
$ docker network create \
--subnet=172.36.0.0/16 \
--ip-range=172.36.5.0/24 \
baeldung
7.3. Inspect a Network
7.3.检查网络
Displaying the low-level details of a network is also covered in the API:
显示网络的低级细节也包括在API中。
Network network
= dockerClient.inspectNetworkCmd().withNetworkId("baeldung").exec();
7.4. Remove a Network
7.4.删除一个网络
We can safely remove a network unit with its name or id using the removeNetworkCmd method:
我们可以使用removeNetworkCmd方法安全地删除一个网络单元的名称或id。
dockerClient.removeNetworkCmd("baeldung").exec();
8. Conclusion
8.结论
In this extensive tutorial, we explored the various diverse functionality of the Java Docker API Client, along with several implementation approaches for deployment and management scenarios.
在这个广泛的教程中,我们探索了Java Docker API Client的各种不同功能,以及部署和管理场景的几种实现方法。
All the examples illustrated in this article can be found over on GitHub.
本文中说明的所有例子都可以在GitHub上找到over。