|
|
@@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit;
|
|
|
* 等价命令:
|
|
|
* docker run -d --name oj-sandbox -w /code --user nobody --read-only \
|
|
|
* --network none --cap-drop ALL -m 64m --cpus 1 --pids-limit 64 \
|
|
|
- * --tmpfs /code --tmpfs /tmp amazoncorretto:17 sh -c "sleep 300"
|
|
|
+ * --tmpfs /code --tmpfs /tmp eclipse-temurin:17-jdk-alpine sh -c "sleep 300"
|
|
|
* docker exec <cid> sh -c "printf %s '<BASE64>' | base64 -d > /code/Main.java"
|
|
|
* docker exec <cid> javac -encoding UTF-8 -d /code /code/Main.java
|
|
|
* docker exec -i <cid> java -Xmx48m -cp /code Main < case1.in
|
|
|
@@ -40,8 +40,8 @@ import java.util.concurrent.TimeUnit;
|
|
|
public class OjSandboxAPI {
|
|
|
private static final Logger log = LoggerFactory.getLogger(OjSandboxAPI.class);
|
|
|
|
|
|
- /** 判题镜像:本机已存在的 amazoncorretto JDK 镜像,自带 javac/java */
|
|
|
- private static final String JUDGE_IMAGE = "amazoncorretto:17";
|
|
|
+ /** 判题镜像:Alpine 版最小 JDK(自带 javac/java),压缩体积比普通 corretto 小约 30% */
|
|
|
+ private static final String JUDGE_IMAGE = "eclipse-temurin:17-jdk-alpine";
|
|
|
/** 容器内代码目录(挂在可写 tmpfs 上,与只读根文件系统配合) */
|
|
|
private static final String CODE_DIR = "/code";
|
|
|
/** 源码文件名(选手主类必须叫 Main) */
|
|
|
@@ -110,6 +110,8 @@ public class OjSandboxAPI {
|
|
|
* @return 判题结果(编译失败时 results 为空)
|
|
|
*/
|
|
|
public JudgeOutcome judge(String source, JudgeConfig config, List<TestCase> cases) {
|
|
|
+ // 判断镜像是否可用,缺失则自动拉取
|
|
|
+ ensureJudgeImageAvailable();
|
|
|
String containerId = null;
|
|
|
try {
|
|
|
containerId = createSandboxContainer(config);
|
|
|
@@ -155,6 +157,31 @@ public class OjSandboxAPI {
|
|
|
|
|
|
// ==================== 容器创建 ====================
|
|
|
|
|
|
+ /**
|
|
|
+ * 检查判题镜像是否存在,不存在则拉取(等价 docker pull)
|
|
|
+ * 由于"编译+运行共用一个容器",容器内必须同时有 javac 和 java,
|
|
|
+ * 因此不能换用纯 JRE 镜像(JRE 只含 java 不含 javac)
|
|
|
+ */
|
|
|
+ private void ensureJudgeImageAvailable() {
|
|
|
+ boolean present = dockerClient.listImagesCmd().exec().stream()
|
|
|
+ .anyMatch(img -> img.getRepoTags() != null
|
|
|
+ && java.util.Arrays.asList(img.getRepoTags()).contains(JUDGE_IMAGE));
|
|
|
+ if (present) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ log.info("判题镜像 {} 不存在,开始拉取...", JUDGE_IMAGE);
|
|
|
+ try {
|
|
|
+ // 等价命令: docker pull eclipse-temurin:17-jdk-alpine
|
|
|
+ dockerClient.pullImageCmd(JUDGE_IMAGE)
|
|
|
+ .exec(new ResultCallback.Adapter<>() {
|
|
|
+ }).awaitCompletion();
|
|
|
+ log.info("判题镜像 {} 拉取完成", JUDGE_IMAGE);
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ log.error("判题镜像拉取被中断: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 创建并启动沙箱容器:只读根 + tmpfs 可写代码区 + 资源/权限全部收紧
|
|
|
* @return 容器 ID
|
|
|
@@ -163,7 +190,7 @@ public class OjSandboxAPI {
|
|
|
// 等价命令:
|
|
|
// docker run -d -w /code --user nobody --read-only --network none \
|
|
|
// --cap-drop ALL -m 64m --cpus 1 --pids-limit 64 \
|
|
|
- // --tmpfs /code --tmpfs /tmp amazoncorretto:17 sh -c "sleep 300"
|
|
|
+ // --tmpfs /code --tmpfs /tmp eclipse-temurin:17-jdk-alpine sh -c "sleep 300"
|
|
|
HostConfig hostConfig = buildSandboxHostConfig(config);
|
|
|
CreateContainerResponse container = dockerClient.createContainerCmd(JUDGE_IMAGE)
|
|
|
.withName("oj-sandbox-" + System.nanoTime())
|