feat(code-runner): add Go and Rust language support
为 Code Runner 新增 Go 和 Rust 两种编译型语言,沿用现有 base 镜像分层结构。 Go: - run_cmd 直接用 go run(单条命令,内部编译+运行) - 镜像把 GOCACHE/GOTMPDIR/GOPATH 重定向到 /tmp tmpfs (只读根文件系统下 /Users/issuser/.cache 不可写) - default_limits: 1 核 / 256MB / 10s(编译冷启动比解释型慢) Rust: - rustc 编译 + 运行是两步,但 docker.rs 注入脚本用 exec 执行 run_cmd, exec 替换 shell 进程后 'A && B' 后半段不执行, 因此镜像内置 /usr/local/bin/run-rust.sh wrapper 封装两步 - default_limits: 1 核 / 512MB / 15s(rustc 内存大、编译慢) 新增镜像与白名单后,默认 CODE_RUNNER_LANGUAGES=python,node,go,rust。
This commit is contained in:
parent
d690910efa
commit
c624997c04
@ -140,7 +140,7 @@ CODE_RUNNER_MAX_CPU_CORES=2.0
|
||||
CODE_RUNNER_MAX_MEMORY_MB=1024
|
||||
CODE_RUNNER_MAX_TIMEOUT_SECS=30
|
||||
CODE_RUNNER_MAX_OUTPUT_BYTES=1048576
|
||||
CODE_RUNNER_LANGUAGES=python,node
|
||||
CODE_RUNNER_LANGUAGES=python,node,go,rust
|
||||
DOCKER_SOCKET_PATH=/var/run/docker.sock
|
||||
|
||||
# Rate limits (governor keyed limiters)
|
||||
|
||||
@ -185,7 +185,7 @@ Readers can execute fenced code blocks in isolated Docker containers; authors ge
|
||||
|
||||
**Governor 0.8 caveat**: `Quota::per_day` does not exist; `CODE_EXEC_DAILY_LIMITER` uses `Quota::with_period(24h).allow_burst(daily)`. `RATE_LIMIT_CODE_EXEC_PER_SEC` must be an integer (governor's `per_second` takes `NonZeroU32`; decimals in `.env` fall back to the default 1).
|
||||
|
||||
**Runner images** (`docker/`): `build-runners.sh` builds `yggdrasil-runner-base` → `yggdrasil-runner-python` → `yggdrasil-runner-node`; tags must match `LANGUAGES` image fields. Python image symlinks `python`→`python3` to match `run_cmd`. `runner.toml` files are image self-descriptions only — runtime config is the Rust `LANGUAGES` registry + `CODE_RUNNER_*` env, not parsed from toml.
|
||||
**Runner images** (`docker/`): `build-runners.sh` builds `yggdrasil-runner-base` → `yggdrasil-runner-python` → `yggdrasil-runner-node` → `yggdrasil-runner-go` → `yggdrasil-runner-rust`; tags must match `LANGUAGES` image fields. Python image symlinks `python`→`python3` to match `run_cmd`. Go image redirects `GOCACHE`/`GOTMPDIR`/`GOPATH` to `/tmp` (read-only rootfs makes `$HOME/.cache` unwritable). Rust image ships a `/usr/local/bin/run-rust.sh` wrapper because `rustc` compile + run is two steps and `docker.rs` injects `run_cmd` via `exec` — `exec A && B` would replace the shell and never reach `B`, so the two steps are encapsulated in the wrapper. `runner.toml` files are image self-descriptions only — runtime config is the Rust `LANGUAGES` registry + `CODE_RUNNER_*` env, not parsed from toml.
|
||||
|
||||
## Frontend Lib Subprojects
|
||||
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
#!/bin/sh
|
||||
# 构建代码运行器所需的沙箱镜像:base → python / node。
|
||||
# 构建代码运行器所需的沙箱镜像:base → python / node / go / rust。
|
||||
#
|
||||
# 依赖:本机已安装 docker。构建顺序固定(python/node FROM base)。
|
||||
# 依赖:本机已安装 docker。构建顺序固定(python/node/go/rust 均为 FROM base)。
|
||||
# 镜像 tag 与 src/api/code_runner/languages.rs 的注册项严格对应:
|
||||
# yggdrasil-runner-base:latest
|
||||
# yggdrasil-runner-python:latest
|
||||
# yggdrasil-runner-node:latest
|
||||
# yggdrasil-runner-go:latest
|
||||
# yggdrasil-runner-rust:latest
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
@ -19,6 +21,12 @@ docker build -t yggdrasil-runner-python:latest "$SCRIPT_DIR/runner-python"
|
||||
echo "==> Building yggdrasil-runner-node:latest"
|
||||
docker build -t yggdrasil-runner-node:latest "$SCRIPT_DIR/runner-node"
|
||||
|
||||
echo "==> Building yggdrasil-runner-go:latest"
|
||||
docker build -t yggdrasil-runner-go:latest "$SCRIPT_DIR/runner-go"
|
||||
|
||||
echo "==> Building yggdrasil-runner-rust:latest"
|
||||
docker build -t yggdrasil-runner-rust:latest "$SCRIPT_DIR/runner-rust"
|
||||
|
||||
echo "==> Done. Images:"
|
||||
docker images --filter "reference=yggdrasil-runner-*" \
|
||||
--format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
|
||||
|
||||
21
docker/runner-go/Dockerfile
Normal file
21
docker/runner-go/Dockerfile
Normal file
@ -0,0 +1,21 @@
|
||||
# Go 运行沙箱镜像。
|
||||
# 与 src/api/code_runner/languages.rs 的 go 注册项对齐:
|
||||
# image: yggdrasil-runner-go:latest
|
||||
# run_cmd: go run /code/main.go
|
||||
#
|
||||
# go run 是单条命令(内部编译 + 运行),可直接作为 run_cmd(无需 wrapper)。
|
||||
# 注意:运行时只读根文件系统 + tmpfs /code /tmp,$HOME/.cache 不可写,
|
||||
# 必须把 GOCACHE / GOTMPDIR / GOPATH 重定向到可写的 /tmp tmpfs。
|
||||
FROM yggdrasil-runner-base:latest
|
||||
|
||||
RUN apk add --no-cache go \
|
||||
&& mkdir -p /tmp/go-cache /tmp/gopath \
|
||||
&& chown -R runner:runner /tmp/go-cache /tmp/gopath
|
||||
|
||||
ENV GOCACHE=/tmp/go-cache \
|
||||
GOTMPDIR=/tmp \
|
||||
GOPATH=/tmp/gopath
|
||||
|
||||
COPY runner.toml /runner.toml
|
||||
|
||||
USER runner
|
||||
15
docker/runner-go/runner.toml
Normal file
15
docker/runner-go/runner.toml
Normal file
@ -0,0 +1,15 @@
|
||||
# Go runner 镜像的运行时默认值(参考用)。
|
||||
# 实际生效配置由 Rust 侧 LANGUAGES 注册表 + CODE_RUNNER_* 环境变量决定(见
|
||||
# src/api/code_runner/languages.rs、src/infra/runner_config.rs),本文件仅作为
|
||||
# 镜像构建产物的自描述,便于运维核对镜像与代码的契约一致。
|
||||
language = "go"
|
||||
extension = "go"
|
||||
run_command = "go run /code/main.go"
|
||||
allow_network = false
|
||||
|
||||
[default_limits]
|
||||
cpu_cores = 1.0
|
||||
memory_mb = 256
|
||||
timeout_secs = 10
|
||||
output_bytes = 1048576
|
||||
allow_network = false
|
||||
18
docker/runner-rust/Dockerfile
Normal file
18
docker/runner-rust/Dockerfile
Normal file
@ -0,0 +1,18 @@
|
||||
# Rust 运行沙箱镜像。
|
||||
# 与 src/api/code_runner/languages.rs 的 rust 注册项对齐:
|
||||
# image: yggdrasil-runner-rust:latest
|
||||
# run_cmd: /usr/local/bin/run-rust.sh
|
||||
#
|
||||
# rustc 编译 + 运行是两步,但 docker.rs 的注入脚本用 exec 执行 run_cmd,
|
||||
# exec 会替换 shell 进程,导致 "A && B" 的后半段不执行。
|
||||
# 因此镜像内置 run-rust.sh wrapper 封装「编译并运行」逻辑。
|
||||
FROM yggdrasil-runner-base:latest
|
||||
|
||||
RUN apk add --no-cache rust
|
||||
|
||||
COPY run-rust.sh /usr/local/bin/run-rust.sh
|
||||
RUN chmod +x /usr/local/bin/run-rust.sh
|
||||
|
||||
COPY runner.toml /runner.toml
|
||||
|
||||
USER runner
|
||||
13
docker/runner-rust/run-rust.sh
Normal file
13
docker/runner-rust/run-rust.sh
Normal file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Rust 源码编译并运行的 wrapper。
|
||||
#
|
||||
# 背景:src/infra/docker.rs 的源码注入脚本为
|
||||
# sh -c "cat > /code/main.rs && exec {run_cmd}"
|
||||
# 其中 exec 会用 run_cmd 进程替换 sh 进程。若直接写
|
||||
# "rustc -o /tmp/main /code/main.rs && /tmp/main"
|
||||
# exec 替换后 && 后半段永远不会执行(exec 成功即不再返回 shell)。
|
||||
# 因此必须把「编译 + 运行」封装进本脚本,由 sh 解释执行:
|
||||
# rustc 编译成功后,再用 exec 替换为编译产物,避免多一层常驻进程。
|
||||
#
|
||||
# 编译产物写到 /tmp/main(tmpfs 64m 可写;只读根文件系统下 / 不可写)。
|
||||
rustc -o /tmp/main /code/main.rs && exec /tmp/main
|
||||
19
docker/runner-rust/runner.toml
Normal file
19
docker/runner-rust/runner.toml
Normal file
@ -0,0 +1,19 @@
|
||||
# Rust runner 镜像的运行时默认值(参考用)。
|
||||
# 实际生效配置由 Rust 侧 LANGUAGES 注册表 + CODE_RUNNER_* 环境变量决定(见
|
||||
# src/api/code_runner/languages.rs、src/infra/runner_config.rs),本文件仅作为
|
||||
# 镜像构建产物的自描述,便于运维核对镜像与代码的契约一致。
|
||||
#
|
||||
# run_command 指向镜像内置的 wrapper 脚本(run-rust.sh),而非裸 rustc 命令:
|
||||
# rustc 编译 + 运行是两步,docker.rs 注入脚本用 exec 执行 run_cmd,
|
||||
# exec 替换 shell 进程后 "A && B" 后半段不执行,故封装进脚本。
|
||||
language = "rust"
|
||||
extension = "rs"
|
||||
run_command = "/usr/local/bin/run-rust.sh"
|
||||
allow_network = false
|
||||
|
||||
[default_limits]
|
||||
cpu_cores = 1.0
|
||||
memory_mb = 512
|
||||
timeout_secs = 15
|
||||
output_bytes = 1048576
|
||||
allow_network = false
|
||||
@ -63,6 +63,46 @@ pub static LANGUAGES: LazyLock<HashMap<String, LanguageDef>> = LazyLock::new(||
|
||||
},
|
||||
);
|
||||
|
||||
// 编译型语言:go run 是单条命令(内部编译 + 运行),可直接作为 run_cmd。
|
||||
// 只读根文件系统下 $HOME/.cache 不可写,镜像已把 GOCACHE/GOTMPDIR/GOPATH
|
||||
// 重定向到可写的 /tmp tmpfs。编译冷启动比解释型慢,timeout 提到 10s。
|
||||
m.insert(
|
||||
"go".to_string(),
|
||||
LanguageDef {
|
||||
image: "yggdrasil-runner-go:latest".to_string(),
|
||||
run_cmd: "go run /code/main.go".to_string(),
|
||||
extension: "go".to_string(),
|
||||
default_limits: ResourceLimits {
|
||||
cpu_cores: 1.0,
|
||||
memory_mb: 256,
|
||||
timeout_secs: 10,
|
||||
output_bytes: 1_048_576,
|
||||
allow_network: false,
|
||||
},
|
||||
allow_network: false,
|
||||
},
|
||||
);
|
||||
|
||||
// rustc 编译 + 运行是两步,但 docker.rs 注入脚本用 exec 执行 run_cmd,
|
||||
// exec 替换 shell 进程后 "A && B" 后半段不执行,故镜像内置 run-rust.sh wrapper。
|
||||
// rustc 内存开销大、编译慢,memory 提到 512MB、timeout 提到 15s。
|
||||
m.insert(
|
||||
"rust".to_string(),
|
||||
LanguageDef {
|
||||
image: "yggdrasil-runner-rust:latest".to_string(),
|
||||
run_cmd: "/usr/local/bin/run-rust.sh".to_string(),
|
||||
extension: "rs".to_string(),
|
||||
default_limits: ResourceLimits {
|
||||
cpu_cores: 1.0,
|
||||
memory_mb: 512,
|
||||
timeout_secs: 15,
|
||||
output_bytes: 1_048_576,
|
||||
allow_network: false,
|
||||
},
|
||||
allow_network: false,
|
||||
},
|
||||
);
|
||||
|
||||
m
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user