SpringBoot 实现 Api 操作 Docker

Docker Remote Api

docker 常用端口

  • 2375:未加密的 docker socket, 远程 root 无密码访问主机
  • 2376:tls 加密套接字,很可能这是您的 CI 服务器 4243 端口作为 https 443 端口的修改
  • 2377:群集模式套接字,适用于群集管理器,不适用于 docker 客户端
  • 5000:docker 注册服务
  • 4789 和 7946:覆盖网络

开启配置

此步骤只适合开发环境使用,生产环境务必使用安全访问 2376 端口并配置 CA 认证

在 /usr/lib/systemd/system/docker.service,配置远程访问。

主要是在 [Service] 这个部分,加上下面两个参数 -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock

1
2
3
4
5
vim /usr/lib/systemd/system/docker.service


[Service]
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock -H fd:// --containerd=/run/containerd/containerd.sock

image-20221213094411941

重启 Docker 服务

1
2
3
systemctl daemon-reload
systemctl restart docker
systemctl status docker

image-20221213094640169

简单使用

-H 为连接目标主机 docker 服务
查看 docker 版本

1
docker -H tcp://192.168.147.100:2375 version

image-20221213094719444

docker-java

https://github.com/docker-java/docker-java

  1. maven pom.xml
1
2
3
4
5
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.2.14</version>
</dependency>
  1. dockerConfig 配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.DockerClientConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* @Auther: Likfees
* @Date: 2022/12/13 09:22
* @Description:
*/

@Configuration
public class DockerConfig {

@Value("${docker-config.host}")
private String dockerHost;
@Value("${docker-config.port}")
private int dockerPort;

/**
* 创建 DockerClient 对象
*
* @param:
* @return: DockerClient
*/
@Bean
public DockerClient dockerClient() {
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withDockerHost(String.format("tcp://%s:%d", dockerHost, dockerPort))
.build();
return DockerClientBuilder.getInstance(config).build();
}

}
  1. 工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package cn.likfees.common.utils.docker;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.async.ResultCallbackTemplate;
import com.github.dockerjava.api.command.*;
import com.github.dockerjava.api.exception.NotFoundException;
import com.github.dockerjava.api.model.*;

import com.google.common.collect.ImmutableSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sound.sampled.Port;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* @Auther: Likfees
* @Date: 2022/12/9 17:25
* @Description: Docker Java Api
*/
@Component
public class DockerClientUtils {


@Autowired
private DockerClient dockerClient;



/**
* 通过 Dockerfile 文件编译镜像文件
* List<String> dockerfile = Arrays.asList(
* "FROM alpine:3.2",
* "RUN echo 'hello from Docker build process'",
* "CMD yes"
* );
* @param: 镜像名称, Dockerfile 文件路径
* @return: 镜像Id
*/
public String buildImage(String imageName, File dockerfile) {
String imageTag = "v1";
ImmutableSet<String> tagSet = ImmutableSet.of(imageName + ":" + imageTag);
String imageId = this.dockerClient.buildImageCmd(dockerfile)
.withTags(tagSet)
.start()
.awaitImageId();
return imageId;
}

public DockerClient getDockerClient() {
return dockerClient;
}

/**
* 创建容器
*
* @param: 镜像名称
* @return: CreateContainerResponse
*/
public CreateContainerResponse createContainer(String imageName) {
try {
dockerClient.inspectImageCmd(imageName).exec();
} catch (NotFoundException e) {
System.out.printf("%s 镜像不存在,正在拉取... \n", imageName);
if (pullImage(imageName)) {
throw new RuntimeException(String.format("%s 拉取镜像失败, err_msg: %s", imageName, e.getMessage()));
}
}
// 创建容器需要使用的命令
return dockerClient.createContainerCmd(imageName).exec();

}


/**
* 创建容器-并映射端口
*
* @param: 镜像名称,端口映射列表
* @return: CreateContainerResponse
*/
public CreateContainerResponse createContainer(String imageName, Map<String, Port> portMap) {
try {
dockerClient.inspectImageCmd(imageName).exec();
} catch (NotFoundException e) {
System.out.printf("%s 镜像不存在,正在拉取... \n", imageName);
if (pullImage(imageName)) {
throw new RuntimeException(String.format("%s 拉取镜像失败, err_msg: %s", imageName, e.getMessage()));
}
}
// 创建容器需要使用的命令
CreateContainerCmd ccm = dockerClient.createContainerCmd(imageName);
// 封装端口映射
List<PortBinding> list = new ArrayList<>();
for (String hostPort : portMap.keySet()) {
// 暴露端口
ccm = ccm.withExposedPorts(ExposedPort.parse(portMap.get(hostPort) + "/tcp"));
// 绑定主机端⼝ -> docker容器端⼝
list.add(PortBinding.parse(hostPort + ":" + portMap.get(hostPort)));
}
HostConfig hostConfig = HostConfig.newHostConfig()
.withPortBindings(list);
// 执行创建命令

return ccm
.withHostConfig(hostConfig)
.exec();
}

/**
* 拉取镜像
*
* @param: 镜像名称
* @return:
*/
public Boolean pullImage(String imageName) {
try {
return dockerClient.pullImageCmd(imageName)
.start()
.awaitCompletion(40, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println(imageName + "拉取镜像失败:" + e.getMessage());
return false;
}

}

/**
* 获取容器日志
*
* @param: 容器Id
* @return:
*/
public String getContainerLogs(String id) {
StringBuffer stringBuffer = new StringBuffer();
try {
this.dockerClient.logContainerCmd(id)
.withStdOut(Boolean.TRUE)
.withStdOut(Boolean.TRUE)
.exec(new ResultCallbackTemplate<ResultCallback<Frame>, Frame>() {
@Override
public void onNext(Frame frame) {
stringBuffer.append(new String(frame.getPayload()));
}
}).awaitCompletion();
} catch (InterruptedException e) {
throw new RuntimeException(id + " ,获取日志失败:" + e.getMessage());
}
return stringBuffer.toString();
}

/**
* 获取所有容器列表
*
* @return: List<Container>
*/
public List<Container> getDockerContaineList() {
return this.dockerClient.listContainersCmd().exec();
}


/**
* 启动容器
*
* @param: 容器Id
*/
public void startContainer(String id) {
this.dockerClient.startContainerCmd(id).exec();
}

/**
* 停止容器
*
* @param: 容器Id
*/
public void stopContainer(String id) {
this.dockerClient.stopContainerCmd(id).exec();
}

/**
* 重启容器
*
* @param: 容器Id
*/
public void restartContainer(String id) {
this.dockerClient.restartContainerCmd(id).exec();
}

/**
* 删除容器
*
* @param: 容器Id
*/
public void deleteContainer(String id) {
// 先关闭在删除
this.stopContainer(id);
this.dockerClient.removeContainerCmd(id).exec();
}


}

  1. 简单使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    class TestDockerApi {


    @Autowired
    private DockerClientUtils dockerClientUtils;


    public void createTest(long id) {
    // 查看运行的容器
    List<Container> dockerContaineList = dockerClientUtils.getDockerContaineList();
    System.out.println(dockerContaineList);

    // 创建容器
    CreateContainerResponse container = dockerClientUtils.createContainer("hello-world");
    // 启动容器
    dockerClientUtils.startContainer(container.getId());
    // 查看容器启动日志
    String containerLogs = dockerClientUtils.getContainerLogs(container.getId());
    System.out.println(containerLogs);

    }

    }

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/