Преглед на файлове

feat: use minimal Alpine JDK image for OJ sandbox with auto-pull

yangyi преди 1 месец
родител
ревизия
97fbc4afc9
променени са 2 файла, в които са добавени 32 реда и са изтрити 5 реда
  1. 31 4
      src/main/java/space/anyi/docker/OjSandboxAPI.java
  2. 1 1
      src/test/java/space/anyi/docker/OjSandboxAPITest.java

+ 31 - 4
src/main/java/space/anyi/docker/OjSandboxAPI.java

@@ -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())

+ 1 - 1
src/test/java/space/anyi/docker/OjSandboxAPITest.java

@@ -13,7 +13,7 @@ import static org.junit.jupiter.api.Assertions.*;
 
 /**
  * OJ 代码沙箱(第 12 章)测试
- * 注意:需要本机 Docker daemon 运行中,且本地存在 amazoncorretto:17 镜像
+ * 注意:需要本机 Docker daemon 运行中;判题镜像 eclipse-temurin:17-jdk-alpine 缺失时会自动拉取
  */
 class OjSandboxAPITest {