基于docker-java封装的工具类

基于docker-java封装的工具类

背景

写OJ系统时需要用docker作为代码沙箱使用,顺手封装了一个工具类,给自己做个笔记,如果可以的话也希望帮助到其他人。

环境

  • docker 26.1.4
  • docker-java 3.4.2
  • docker-java-transport-httpclient5 3.4.2

工具类


import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.PullImageResultCallback;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.api.model.PullResponseItem;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author 
 * @Description dockerClient工具类
 * @create 2025-04-19 0:30
 */
@Slf4j
public class DockerClientUtil {

    /**
     * 获取镜像
     * @return
     */
    public static void getImage(DockerClient dockerClient, String imageName, String imageTag) throws InterruptedException {
        // 判断镜像是否存在
        imageTag = ObjectUtils.isEmpty(imageTag)? "latest" : imageTag;
        // 获取所有本地镜像
        List<Image> images = dockerClient.listImagesCmd().exec();
        // 构造完整镜像名
        String fullImageName = imageName + ":" + imageTag;

        // 假设镜像不辞你在
        boolean isImageExists = false;
        // 遍历镜像列表,检查是否存在匹配的镜像
        for (Image image : images) {
            if (image.getRepoTags() != null) {
                // 确保 RepoTags 不为空
                for (String repoTag : image.getRepoTags()) {
                    if (repoTag.equals(fullImageName)) {
                        // 存在
                        isImageExists = true;
                        break;
                    }
                }
            }
        }

        if (!isImageExists) {
            System.out.println("拉取镜像");
            // 不存在,拉取镜像
            dockerClient.pullImageCmd(fullImageName).exec(new PullImageResultCallback() {
                @Override
                public void onNext(PullResponseItem item) {
                    super.onNext(item);
                }
            }).awaitCompletion();
        }
    }

    /**
     * 创建可交互控制台容器
     */
    public static CreateContainerResponse createContainer(DockerClient dockerClient, String imageName, String imageTag) {
        return dockerClient.createContainerCmd(String.format("%s:%s", imageName, imageTag))
                // 启用伪终端
                .withTty(true)
                // 保持标准输入打开
                .withStdinOpen(true)
                // 附加标准输入
                .withAttachStdin(true)
                // 附加标准输出
                .withAttachStdout(true)
                // 附加标准错误
                .withAttachStderr(true)
                // 使用登录 shell
                .withCmd("sh", "-l")
                .exec();
    }


    /**
     * 字符串作为文件保存到容器
     * @param dockerClient docker客户端
     * @param containerId 容器id
     * @param filePath 文件目标路径
     * @param fileContent 文件内容
     * @return 是否保存成功
     * @throws InterruptedException
     */
    public static boolean saveFileToContainer(DockerClient dockerClient, String containerId, String filePath, String fileContent) throws InterruptedException {
        // 准备编译命令
        String command = String.format("mkdir -p %s && printf \"%s\" > %s",filePath.replaceAll("([^/]+)/[^/]*$", "$1"), fileContent.replace("\"", "\\\""), filePath);

        // 创建exec配置
        String execId = dockerClient.execCreateCmd(containerId)
                .withAttachStdout(true)
                .withAttachStderr(true)
                // 通过sh -c执行
                .withCmd("sh", "-c", command)
                .exec()
                .getId();

        AtomicBoolean rs = new AtomicBoolean(true);

        // 执行并获取输出
        dockerClient.execStartCmd(execId)
                .exec(new ResultCallback.Adapter<Frame>() {
                    @Override
                    public void onNext(Frame object) {
                        rs.set(ObjectUtils.isEmpty(object.getPayload()));
                        if (!ObjectUtils.isEmpty(object.getPayload())) {
                            rs.set(false);
                        }
                        super.onNext(object);
                    }
                })
                .awaitCompletion();
        return rs.get();
    }

    /**
     * 运行命令
     * @param dockerClient
     * @param containerId
     * @param command
     * @return
     * @throws InterruptedException
     */
    public static String runCommand(DockerClient dockerClient, String containerId, String command) throws InterruptedException {

        // 创建exec配置
        String execId = dockerClient.execCreateCmd(containerId)
                .withAttachStdout(true)
                .withAttachStderr(true)
                // 通过sh -c执行
                .withCmd("sh", "-c", command)
                .exec()
                .getId();

        StringBuilder rs = new StringBuilder();
        // 执行并获取输出
        dockerClient.execStartCmd(execId)
                .exec(new ResultCallback.Adapter<Frame>() {
                    @Override
                    public void onNext(Frame object) {
                        rs.append(new String(object.getPayload()));
                        super.onNext(object);
                    }
                })
                .awaitCompletion();
        return rs.toString();
    }

    /**
     * 运行代码并批量输入
     * @param dockerClient
     * @param containerId
     * @param runCommand
     * @param inputStr
     * @return 运行结果
     * @throws InterruptedException
     */
    public static String runCommandWithInputBatch(DockerClient dockerClient, String containerId, String runCommand, String inputStr) throws InterruptedException {
        return runCommand(dockerClient, containerId, String.format("echo -e \"%s\" | %s", inputStr.trim().replaceAll(" ", "\n").replaceAll("\"", "\\\""), runCommand)).replaceAll("\\n$", "");
    }

    /**
     * 清理容器
     * @param dockerClient
     * @param containerId
     */
    public static void destroyContainer(DockerClient dockerClient, String containerId) throws InterruptedException {
        // 停止容器
        dockerClient.stopContainerCmd(containerId).exec();
        // 删除容器
        dockerClient.removeContainerCmd(containerId).exec();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值