package com.se.docker.utils; import cn.hutool.core.util.ObjectUtil; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.*; import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.PullResponseItem; import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import com.github.dockerjava.transport.DockerHttpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.time.Duration; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @SuppressWarnings("ALL") public class DockerUtils { private static volatile DockerClient dockerClient; private static final Logger log = LoggerFactory.getLogger(DockerUtils.class); /** * vim /lib/systemd/system/docker.service *
* # 配置普通模式,-H参数指定docker应用程序监听方式,或者采用下面的安全连接方式2选1 * ExecStart=/usr/bin/dockerd -H unix://var/run/docker.sock -H tcp://0.0.0.0:2375 *
* # 配置安全连接,注意这里的/home/user/certs/ 根据自己情况替换为实际的的密钥存放路径 * ExecStart=/usr/bin/dockerd -D --tlsverify=true --tlscert=/home/user/certs/server-cert.pem --tlskey=/home/user/certs/server-key.pem --tlscacert=/home/user/certs/ca.pem -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock *
* # 重载配置,重启服务 * systemctl daemon-reload * systemctl restart docker *
* # 查看端口监听 * netstat -nlp |grep 2375 *
* vi /etc/docker/daemon.json
* # 配置insecure-registries
* {
* "insecure-registries": ["122.12.12.12:5000"],
* "registry-mirrors": ["https://xxxxx.mirror.aliyuncs.com"]
* }
* # 重载配置,重启服务
* systemctl daemon-reload
* systemctl restart docker
*/
private DockerUtils() {
}
public DockerUtils(String dockerHost, String dockerApiVersion) {
Objects.requireNonNull(dockerHost, "Docker 主机地址不能为空.");
Objects.requireNonNull(dockerApiVersion, "Docker API 版本不能为空.");
// 使用双重校验锁实现 Docker 客户端单例
if (null == dockerClient) {
synchronized (DockerUtils.class) {
if (null == dockerClient) {
dockerClient = createDockerClient(dockerHost, dockerApiVersion);
}
}
}
}
private DockerClient createDockerClient(String dockerHost, String dockerApiVersion) {
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withApiVersion(dockerApiVersion)
.withDockerHost(dockerHost)
.build();
DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(config.getDockerHost())
.sslConfig(config.getSSLConfig())
.maxConnections(1000)
.connectionTimeout(Duration.ofSeconds(60))
.responseTimeout(Duration.ofMinutes(30))
.build();
return DockerClientImpl.getInstance(config, httpClient);
}
public DockerUtils(String dockerHost, String dockerApiVersion, String dockerCertPath) {
Objects.requireNonNull(dockerHost, "Docker 主机地址不能为空.");
Objects.requireNonNull(dockerApiVersion, "Docker API 版本不能为空.");
// 使用双重校验锁实现 Docker 客户端单例
if (null == dockerClient) {
synchronized (DockerUtils.class) {
if (null == dockerClient) {
dockerClient = createDockerClient(dockerHost, dockerApiVersion, dockerCertPath);
}
}
}
}
private DockerClient createDockerClient(String dockerHost, String dockerApiVersion, String dockerCertPath) {
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withApiVersion(dockerApiVersion)
.withDockerHost(dockerHost)
// 如果开启安全连接,需要配置这行
.withDockerTlsVerify(true).withDockerCertPath(dockerCertPath)
.build();
DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(config.getDockerHost())
.sslConfig(config.getSSLConfig())
.maxConnections(1000)
.connectionTimeout(Duration.ofSeconds(60))
.responseTimeout(Duration.ofMinutes(30))
.build();
return DockerClientImpl.getInstance(config, httpClient);
}
/**
* 登录 Docker 镜像仓库
*
* @param authConfig 登录所需的认证信息
* @throws RuntimeException 登录失败时抛出异常
*/
public void login(AuthConfig authConfig) {
try {
Objects.requireNonNull(authConfig, "认证信息不能为空.");
log.info("开始登录镜像仓库:{};username:{};password:{}", authConfig.getRegistryAddress(), authConfig.getUsername(), authConfig.getPassword());
dockerClient.authCmd()
.withAuthConfig(authConfig)
.exec();
log.info("镜像仓库登录成功:{}", authConfig.getRegistryAddress());
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("镜像仓库登录失败:" + e.getMessage());
}
}
/**
* 从registry拉取Docker镜像
*
* @param tag 镜像名称
* @return true表示拉取成功,false表示拉取失败
*/
public void pullImage(AuthConfig authConfig, String tag) {
Objects.requireNonNull(authConfig, "认证信息不能为空.");
if (ObjectUtil.isEmpty(tag)) {
throw new RuntimeException("镜像信息不能为空");
}
log.info("开始拉取 Docker 镜像: {}", tag);
try {
PullImageResultCallback exec = new PullImageResultCallback() {
@Override
public void onNext(PullResponseItem item) {
System.out.println(item.getStatus());
}
};
PullImageCmd pullImageCmd = dockerClient.pullImageCmd(tag);
pullImageCmd.withAuthConfig(authConfig)
.exec(exec)
.awaitCompletion(30, TimeUnit.MINUTES);
exec.close();
log.info("镜像拉取成功:{};", tag);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("镜像拉取失败:{}" + e.getMessage());
}
}
/**
* 保存Docker镜像
*
* @param imageId 镜像Id
* @param filePath 保存文件名
* @return true表示保存成功,false表示保存失败
*/
public void saveImage(java.lang.String imageId, String filePath) {
if (ObjectUtil.isEmpty(filePath)) {
throw new RuntimeException("参数错误:保存路径不能为空");
}
log.info("开始保存镜像:{}", imageId);
try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(filePath));
InputStream inputStream = dockerClient.saveImageCmd(imageId)
.exec()) {
if (null == inputStream) {
throw new RuntimeException("无法获取镜像");
}
byte[] bytesArray = new byte[4096];
int bytesRead = -1;
while ((bytesRead = inputStream.read(bytesArray)) != -1) {
outputStream.write(bytesArray, 0, bytesRead);
}
log.info("镜像保存成功:{}", imageId);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("保存镜像异常:" + e.getMessage());
}
}
/**
* 删除Docker镜像
*
* @param imageId 镜像标签
* @return true表示删除成功,false表示删除失败
*/
public boolean removeImage(String imageId) {
Objects.requireNonNull(imageId, "镜像 ID 不能为空.");
log.info("开始删除 Docker 镜像: {}", imageId);
try {
// 如果镜像当前有容器在运行,则不进行删除操作
if (isRunContainer(imageId)) {
log.warn("Docker 镜像正在使用中,无法删除: {}", imageId);
return false;
}
RemoveImageCmd removeImageCmd = dockerClient.removeImageCmd(imageId);
removeImageCmd.exec();
log.info("Docker 镜像删除成功: {}", imageId);
return true;
} catch (Exception e) {
log.error("Docker 镜像删除失败: {};{}", imageId, e.getMessage());
return false;
}
}
/**
* 推送镜像
*
* @param authConfig
* @param tag
*/
public static void pushImage(AuthConfig authConfig, String tag) {
Objects.requireNonNull(authConfig, "认证信息不能为空.");
Objects.requireNonNull(tag, "镜像信息不能为空.");
log.info("开始推送 Docker 镜像: {}", tag);
try {
PushImageCmd pushImageCmd = dockerClient.pushImageCmd(tag);
pushImageCmd.withAuthConfig(authConfig)
.start()
.awaitCompletion(30, TimeUnit.SECONDS);
log.info("镜像push成功:{}", tag);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("镜像push失败:{}" + e.getMessage());
}
}
/**
* 获取所有 Docker 容器的信息
*
* @return 所有 Docker 容器的信息列表
*/
public List