通过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();
    }
}
相关推荐
忆雾屿5 分钟前
云原生时代 Kafka 深度实践:06原理剖析与源码解读
java·后端·云原生·kafka
武昌库里写JAVA18 分钟前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
gaoliheng00626 分钟前
Redis看门狗机制
java·数据库·redis
我是唐青枫29 分钟前
.NET AOT 详解
java·服务器·.net
Su米苏1 小时前
Axios请求超时重发机制
java
本郡主是喵2 小时前
并发编程 - go版
java·服务器·开发语言
南风lof2 小时前
源码赏析:Java线程池中的那些细节
java·源码阅读
pengyu2 小时前
【Java设计原则与模式之系统化精讲:零】 | 编程世界的道与术(理论篇)
java·后端·设计模式
程序员岳焱2 小时前
16.Java Annotation注解:元数据与代码增强
java·后端·编程语言