背景
写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();
}
}