前提需要docker开启通信功能,有多种开启方式,我使用的是修改注册服务时的配置文件docker.service:
bash
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
[Service]
Type=notify
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=infinity
LimitNPROC=infinity
TimeoutStartSec=0
Delegate=yes
KillMode=process
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s
[Install]
WantedBy=multi-user.target
1、依赖
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java-transport-httpclient5</artifactId>
<version>3.3.4</version>
</dependency>
2、DockerManager.class
bash
package com.ctrip.crawler.container;
import com.ctrip.fx.octopus.client.constant.Constants;
import com.ctrip.fx.octopus.client.dto.EnvironmentSettingDTO;
import com.ctrip.hotel.octopus.commons.base.utils.JsonUtils;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.BuildImageResultCallback;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.BuildResponseItem;
import com.github.dockerjava.api.model.Container;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.api.model.PruneType;
import com.github.dockerjava.api.model.RestartPolicy;
import com.github.dockerjava.api.model.Ulimit;
import com.github.dockerjava.api.model.Volume;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.lang3.ObjectUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* @author ZhaoXu
* @date 2024/1/12 17:47
*/
@Slf4j
public class DockerManager {
private final DockerClient dockerClient;
public DockerManager(String host) {
// "tcp://192.168.56.112:2375"
DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withDockerTlsVerify(false)
.withDockerHost("tcp://" + host + ":2375")
.withRegistryUrl(Constants.GATEWAY_HOST)
.withApiVersion("1.43")
.build();
// 创建 HTTP 客户端
ApacheDockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(config.getDockerHost())
.sslConfig(config.getSSLConfig())
.maxConnections(100)
.connectionTimeout(Duration.ofSeconds(Integer.MAX_VALUE))
.responseTimeout(Duration.ofSeconds(Integer.MAX_VALUE))
.build();
dockerClient = DockerClientImpl.getInstance(config, httpClient);
}
/**
* 将文本内容转为输入流
* @param fileName
* @param textContent
* @return
*/
private static ByteArrayInputStream createTarStreamFromText(String fileName, String textContent) {
try {
// 创建 ByteArrayOutputStream 以捕获 tar 流
ByteArrayOutputStream tarData = new ByteArrayOutputStream();
// 使用 TarArchiveOutputStream 创建 tar 流
try (TarArchiveOutputStream tarStream = new TarArchiveOutputStream(tarData)) {
// 创建 TarArchiveEntry
TarArchiveEntry entry = new TarArchiveEntry(fileName);
entry.setSize(textContent.length());
tarStream.putArchiveEntry(entry);
tarStream.write(textContent.getBytes());
tarStream.closeArchiveEntry();
}
// 将 ByteArrayOutputStream 转换为 ByteArrayInputStream
return new ByteArrayInputStream(tarData.toByteArray());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 构建镜像
* @param imageName
* @param imageTag
* @param dockerFileText
* @return
*/
public void buildImage(String imageName, String imageTag, String dockerFileText) {
long start = System.currentTimeMillis();
// 构建镜像
try {
String imageId = dockerClient.buildImageCmd(createTarStreamFromText("Dockerfile", dockerFileText))
.withForcerm(Boolean.TRUE)
.withTags(new HashSet<>(Collections.singletonList(imageName + ":" + imageTag)))
.exec(new BuildImageResultCallback() {
@Override
public void onNext(BuildResponseItem item) {
// 处理构建响应
log.info("构建镜像,imageName:{},imageTag:{} {}", imageName, imageTag, JsonUtils.toJson(item));
super.onNext(item);
}
}).awaitImageId();
dockerClient.tagImageCmd(imageId, imageName, imageTag).exec();
} catch (Exception e) {
log.info("构建镜像出错,请检查,imageName:{},imageTag:{} dockerFileText:{}", imageName, imageTag, dockerFileText, e);
throw new RuntimeException(e);
}
log.info("构建镜像 {} 成功,耗时:{}", imageName, System.currentTimeMillis() - start);
}
/**
* 拉取镜像
* @param imageName
* @param imageTag
*/
public void pullImage(String imageName, String imageTag) {
try {
dockerClient.pullImageCmd(imageName)
.withTag(imageTag)
.start()
.awaitCompletion();
} catch (InterruptedException e) {
log.error("镜像拉取失败,imageName:{},imageTag:{}", imageName, imageTag);
throw new RuntimeException(e);
}
}
/**
* 创建并启动容器
*
* @param imageName
* @param tag
* @param environmentSettingDTO
*/
public void createContainer(String imageName, String tag, EnvironmentSettingDTO environmentSettingDTO) {
HostConfig hostConfig = HostConfig.newHostConfig();
// 自启动
hostConfig.withRestartPolicy(RestartPolicy.alwaysRestart());
// 资源分配
hostConfig.withPrivileged(Boolean.TRUE);
List<Ulimit> ulimitList = Arrays.asList(new Ulimit("nofile", 1024L, 1024L),
new Ulimit("nproc", 1024L, 1024L));
hostConfig.withUlimits(ulimitList);
// 环境设置
List<Volume> volumeList = new ArrayList<>();
List<String> envList = new ArrayList<>();
if (ObjectUtils.isNotEmpty(environmentSettingDTO)) {
// env环境变量设置
List<EnvironmentSettingDTO.EnvironmentMap> envVariables = environmentSettingDTO.getEnvVariables();
for (EnvironmentSettingDTO.EnvironmentMap envVariable : envVariables) {
String key = envVariable.getKey();
String value = envVariable.getValue();
envList.add(key + "=" + value);
}
// 目录挂载配置
List<EnvironmentSettingDTO.EnvironmentMap> mountDirectoryList = environmentSettingDTO.getMountDirectory();
for (EnvironmentSettingDTO.EnvironmentMap mountDirectory : mountDirectoryList) {
String localLogDirectory = mountDirectory.getKey();
String containerLogDirectory = mountDirectory.getValue();
Volume volume = new Volume(containerLogDirectory);
hostConfig.withBinds(new Bind(localLogDirectory, volume));
volumeList.add(volume);
}
}
// 网络
hostConfig.withNetworkMode("host");
CreateContainerResponse exec = dockerClient.createContainerCmd(imageName + ":" + tag)
.withHostConfig(hostConfig)
.withVolumes(volumeList)
.withEnv(envList)
.withLabels(new HashMap<String, String>(2) {{
put("imageName", imageName);
put("tag", tag);
}})
.exec();
dockerClient.startContainerCmd(exec.getId()).exec();
}
/**
* 通过镜像名称查询容器
*
* @param imageName
* @return
*/
public List<Container> queryContainerByImageName(String imageName) {
log.info("通过镜像名称查询容器, imageName:{}", imageName);
List<Container> containerList = dockerClient.listContainersCmd()
.withShowAll(Boolean.TRUE)
.exec();
return Optional.ofNullable(containerList).orElse(Collections.emptyList())
.stream()
.filter(container -> {
Map<String, String> labels = container.labels;
String imageNameLabel = labels.get("imageName");
return ObjectUtils.isNotEmpty(labels) && Objects.equals(imageNameLabel, imageName);
})
.collect(Collectors.toList());
}
/**
* 停止并删除容器
*
* @param containerIdList
* @return
*/
public void stopContainer(List<String> containerIdList) {
Optional.ofNullable(containerIdList)
.orElse(Collections.emptyList())
.forEach(containerId -> {
log.info("停止目标容器,containerId:{}", containerId);
try {
dockerClient.stopContainerCmd(containerId).exec();
} catch (Exception e) {
log.warn("容器未停止,containerId:{}", containerId);
}
});
}
/**
* 清理docker
*/
public void dockerGc() {
// 清理停止了的容器
dockerClient.pruneCmd(PruneType.CONTAINERS).exec();
List<Image> imageList = dockerClient.listImagesCmd()
.withShowAll(Boolean.TRUE)
.exec();
Map<String, List<Image>> repoTag2ImageList = imageList.stream()
.filter(item -> ObjectUtils.isNotEmpty(item.getRepoTags()) && item.getRepoTags()[0].contains(":"))
.collect(Collectors.groupingBy(item -> {
String[] repoTags = item.getRepoTags();
return repoTags[0].split(":")[0];
}));
repoTag2ImageList.forEach((tag, images) -> {
// 每个镜像保留最新的两个副本
images.stream()
.sorted(Comparator.comparing(Image::getCreated).reversed())
.skip(2)
.forEach(image -> {
try {
dockerClient.removeImageCmd(image.getId()).withForce(Boolean.TRUE).exec();
} catch (Exception e) {
log.warn("清理镜像出错", e);
}
});
});
dockerClient.pruneCmd(PruneType.IMAGES).exec();
dockerClient.pruneCmd(PruneType.BUILD).exec();
}
}