通过docker-java与docker进行通信交互

前提需要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();
    }
}
相关推荐
禁默25 分钟前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Cachel wood31 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Code哈哈笑34 分钟前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
gb421528737 分钟前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶37 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
zfoo-framework1 小时前
【jenkins插件】
java
风_流沙1 小时前
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
java·数据库·elasticsearch
年薪丰厚1 小时前
如何在K8S集群中查看和操作Pod内的文件?
docker·云原生·容器·kubernetes·k8s·container
ProtonBase1 小时前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构