Categorygithub.com/criyle/go-judge
repository
1.9.8
Repository: https://github.com/criyle/go-judge.git
Documentation: pkg.go.dev

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# README

go-judge

Go Reference Go Report Card Release Build

English | 文档

快速,简单,安全

快速上手

安装和运行

下载对应平台预编译二进制文件 go-judge Release 并在终端开启

或者使用 docker

docker run -it --rm --privileged --shm-size=256m -p 5050:5050 --name=go-judge criyle/go-judge

REST API 接口

沙箱服务提供 REST API 接口来在受限制的环境中运行程序(默认监听于 localhost:5050)。

  • POST /run 在受限制的环境中运行程序
  • GET /file 得到所有在文件存储中的文件 ID 到原始命名映射
    • POST /file 上传一个文件到文件存储,返回一个文件 ID 用于提供给 /run 接口
    • GET /file/:fileId 下载文件 ID 指定的文件
    • DELETE /file/:fileId 删除文件 ID 指定的文件
  • /ws /run 接口的 WebSocket 版
  • /stream 运行交互式命令。支持流式 api
  • /version 得到本程序编译版本和 go 语言运行时版本
    • /config 得到本程序部分运行参数,包括沙箱详细参数

REST API 接口定义

接口数据类型定义

示例

请使用 postman 或其他 REST API 调试工具向 http://localhost:5050/run 发送请求

请求实例

进阶设置

运行要求

  • Linux 内核版本 >= 3.10
  • 系统 Cgroup 文件系统挂载于 /sys/fs/cgroup(Systemd 默认)

系统架构

+----------------------------------------------------------------------------+
| 传输层 (HTTP / WebSocket / FFI / ...)                                      |
+----------------------------------------------------------------------------+
| 工作协程 (运行环境池 和 运行环境生产者 )                                     |
+-----------------------------------------------------------+----------------+
| 运行环境                                                   | 文件存储       |
+--------------------+----------------+---------------------+----------+-----+
| Linux (go-sandbox) | Windows (winc) | macOS (app sandbox) | 共享内存 | 磁盘 |
+--------------------+----------------+---------------------+----------+-----+

配置

服务相关:

  • 默认监听地址是 localhost:5050,使用 -http-addr 指定
  • 默认 gRPC 接口处于关闭状态,使用 -enable-grpc 开启
    • 默认 gRPC 监听地址是 localhost:5051 ,使用 -grpc-addr 指定
  • 默认日志等级是 info ,使用 -silent 关闭 或 使用 -release 开启 release 级别日志
  • 默认没有开启鉴权,使用 -auth-token 指定令牌鉴权
  • 默认没有开启 go 语言调试接口(localhost:5052/debug),使用 -enable-debug 开启,同时将日志层级设为 Debug
  • 默认没有开启 prometheus 监控接口,使用 -enable-metrics 开启 localhost:5052/metrics
  • 在启用 go 语言调试接口或者 prometheus 监控接口的情况下,默认监控接口为 localhost:5052,使用 -monitor-addr 指定

沙箱相关:

  • 默认同时运行任务数为和 CPU 数量相同,使用 -parallelism 指定
  • 使用 -mount-conf 指定沙箱文件系统挂载细节,详细请参见 文件系统挂载 (仅 Linux)
  • 使用 -file-timeout 指定文件存储文件最大时间。超出时间的文件将会删除。(例如指定 30m 时,缓存文件将在创建后 30 分钟删除)
  • 默认文件存储在共享内存文件系统中(/dev/shm/),可以使用 -dir 指定另外的本地目录为文件存储
  • 默认最大输出限制为 256MiB,使用 -output-limit 指定
  • 默认最大 copyOut 文件大小为 64MiB ,使用 -copy-out-limit 指定

可以在此查看更多配置文档

指标监控

Prometheus 指标监控接口

在沙箱中运行终端

Release 下载 go-judge-shell 。运行将连接本地 go-judge 沙箱服务并开启一个容器内的终端用于调试。

/run 接口返回状态

  • Accepted: 程序在资源限制内正常退出
  • Memory Limit Exceeded: 超出内存限制
  • Time Limit Exceeded: (通常 exitStatus9(超时时被 SIGKILL 结束))
    • 超出 timeLimit 时间限制
    • 或者超过 clockLimit 等待时间限制
  • Output Limit Exceeded:
    • 超出 pipeCollector 限制
    • 或者超出 -output-limit 最大输出限制
  • File Error:
    • copyIn 指定文件不存在
    • 或者 copyIn 指定文件大小超出沙箱文件系统限制
    • 或者 copyOut 指定文件不存在
  • Non Zero Exit Status: 程序用非 0 返回值退出
  • Signalled: 程序收到结束信号而退出(例如 SIGSEGV
  • Dangerous Syscall: 程序被 seccomp 过滤器结束(默认不启用)
  • Internal Error:
    • 指定程序路径不存在
    • 或者容器创建失败
      • 比如使用非特权 docker
      • 或者在个人目录下以 root 权限运行
    • 或者其他错误

容器的文件系统

在 Linux 平台,默认只读挂载点包括主机的 /lib, /lib64, /usr, /bin, /etc/ld.so.cache, /etc/alternatives, /etc/fpc.cfg, /dev/null, /dev/urandom, /dev/random, /dev/zero, /dev/full 和临时文件系统 /w, /tmp 以及 /proc

使用 mount.yaml 定制容器文件系统

不使用 mount.yaml 时,/w/tmp 挂载 tmpfs 大小通过 -tmp-fs-param 指定,默认值为 size=128m,nr_inodes=4k

如果在容器的根目录存在 /.env 文件,那么这个文件会在容器创建时被载入。文件的每一行会作为环境变量的初始值加入到运行程序当中。

如果之后指定的挂载点目标在之前的挂载点之下,那么需要保证之前的挂载点存在目标文件或者文件夹。

注意

[!WARNING]
Window 和 macOS 平台为实验性支持,请不要在生产环境使用

使用 cgroup

在 cgroup v1 系统上 go-judge 需要 root 权限创建 cgroup。请使用 sudoroot 用户运行或者确保运行用户拥有以下目录的读写权限 /sys/fs/cgroup/cpuacct/gojudge, /sys/fs/cgroup/memory/gojudge, /sys/fs/cgroup/pids/gojudge

在 cgroup v2 系统上,go-judge 会和 system dbus 沟通,创建一个临时 scope。如果 systemd 不存在,并且拥有 root 权限那么将尝试进行嵌套初始化。

如果没有 cgroup 的权限,那么 cgroup 相关的资源配置将不会生效。

cgroup v2

go-judge 目前已经支持 cgroup v2 鉴于越来越多的 Linux 发行版默认启用 cgroup v2 而不是 v1 (比如 Ubuntu 21.10+,Fedora 31+)。然而,对于内核版本小于 5.19 的版本,因为 cgroup v2 在内存控制器里面缺少 memory.max_usage_in_bytes,内存使用量计数会转而采用 maxrss 指标。这项指标会显示的比使用 cgroup v1 时候要稍多,在运行使用内存较少的程序时比较明显。对于内核版本大于或等于 5.19 的版本,memory.peak 会被采用。

同时,如果本程序在容器中运行,容器中的进程会被移到 /api cgroup v2 控制器中来开启 cgroup v2 嵌套支持。

systemdinit 的发行版中运行时,go-judge 会使用 dbus 通知 systemd 来创建一个临时 scope 作为 cgroup 的根。

在高于 5.7 的内核中运行时,go-judge 会尝试更快的 clone3(CLONE_INTO_CGROUP)vfork 方法.

内存使用

控制进程通常会使用 20M 内存,每个容器进程最大会使用 20M 内存,每个请求最大会使用 2 * 16M + 总 copyOutMax 限制 * 2 内存。请注意,缓存文件会存储在宿主机的共享内存中 (/dev/shm),请保证其大小足够存储运行时最大可能文件。

比方说当同时请求数最大为 4 的时候,本程序最大会占用 60 + (20+32) * 4M = 268M + 总 copy out max 限制 * 8 内存 + 总运行程序最大内存限制。

因为 go 语言 runtime 垃圾收集算法实现,有时并不会主动归还占用内存。这种情况可能会引发 OOM Killer 杀死进程,因此引入了一个后台检查线程用于在堆内存占用高时强制垃圾收集和归还内存。

  • -force-gc-target 默认 20m, 堆内存使用超过该值是强制垃圾收集和归还内存
  • -force-gc-interval 默认 5s, 为后台线程检查的频繁程度