内容:
● 在容器中的Slurm
● Slurm容器支持
○ sbatch/salloc/srun --container
● scrun
○ 容器运行时可以通过scrun向Slurm提交作业
■ 用户无法直接看到Slurm
○ 使用Docker的示例
○ 使用Podman的示例
Slurmd守护进程可以在容器中运行
● slurmctld、slurmdbd和mysql已经能够在容器中运行
● slurmd现在可以在容器中运行(23.02)
○ 需要cgroup v2
○ 在特权模式下对cgroup v1的支持有限(并不理想)
Slurm如何运行容器:sbatch/salloc/srun --container
Slurm容器支持
● 添加了‘--container’(21.08)支持,用于以下命令:
○ srun
○ salloc
○ sbatch
● 添加了查看作业容器[包路径](21.08)和容器ID(23.02)功能,适用于以下命令:
○ scontrol show jobs
○ scontrol show steps
○ sacct
■ 如果作为“--format”参数的一部分传递,使用“Container”
○ slurmd、slurmstepd、slurmdbd和slurmctld的日志(太多地方无法一一列举)
srun示例
$ srun --container=/tmp/centos grep ^NAME /etc/os-release
NAME="CentOS Linux"
salloc示例
$ salloc --container=/tmp/centos grep ^NAME /etc/os-release
salloc: Granted job allocation 65
NAME="CentOS Linux"
salloc: Relinquishing job allocation 65
sbatch示例
$ sbatch --container=/tmp/centos --wrap 'grep ^NAME /etc/os-release'
Submitted batch job 24419
$ cat slurm-24419.out
NAME="CentOS Linux"
$srun --container ~/oci_images/alpine/ uptime
srun: job 13772 queued and waiting for resources
srun: job 13772 has been allocated resources
17:12:33 up 2 days, 20:27, 0 users, load average: 1.78, 3.42, 3.44
$sacct --format=jobid,container%40
13772 /home/scott/oci_images/alpine/
13772.extern
13772.0 /home/scott/oci_images/alpine/
$scontrol show jobs | grep -i -E 'container|jobid'
JobId=13772 JobName=uptime
Container=/home/scott/oci_images/alpine/ ContainerID=(null)
● 如果在容器外部能够正常运行,则在容器内部也应该能够正常运行
○ Slurm的cgroups功能适用于容器
○ 目前Slurm只支持非特权容器
○ 容器必须能够在现有主机网络中正常运行
● 通过/etc/slurm/中的‘oci.conf’进行每个主机的配置
○ slurm.schedmd.com/oci.conf.html
● 环境变量SLURM_CONTAINER(22.05)和SLURM_CONTAINER_ID(23.02)将始终设置一个值(如果存在)
容器运行时如何向Slurm提交作业:scrun
使用Slurm用户命令前端
OCI运行时代理 - scrun
● scrun的目标是使Slurm对运行Docker/Podman的用户透明
○ 用户直接与OCI运行时客户端(Docker、Podman、Podman-HPC等)接口
○ 更容易让已经熟悉Docker/Podman的用户上手
○ 站点管理员需要对配置进行设置和维护
● 利用Slurm现有基础设施在计算节点上运行容器
● 允许用户使用他们想要的工具,同时在Slurm集群上运行工作
● scrun是一个新的CLI命令,加入了srun、sbatch和salloc,但用户应该永远不需要直接调用它,甚至不需要真正意识到它的存在
○ scrun仍然相对较新,我们欢迎提交关于增强功能的请求和特别是错误报告的工单
● 结束了用户在计算节点上手动准备镜像的要求
○ 我们建议使用网络文件系统
○ 我们还可以选择实现容器的自动装入和卸载
■ Lua
■ SPANK
● scrun并不打算直接由用户调用
通过无根(Rootless)Docker(23.02)的scrun
例如:
$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
$ export DOCKER_SECURITY=”--security-opt label:disable --security-opt
seccomp=unconfined --security-opt apparmor=unconfined --net=none”
$ docker run $DOCKER_SECURITY -i ubuntu /bin/sh -c 'grep ^NAME /etc/os-release'
NAME="Ubuntu"
$ docker run $DOCKER_SECURITY -i centos /bin/sh -c 'grep ^NAME /etc/os-release'
NAME="CentOS Linux"
无根(Rootless)Docker进程树
无根(Rootless)Docker配置(23.02)
~/.config/docker/daemon.json
{
"default-runtime": "slurm",
"runtimes": {
"slurm": {
"path": "/usr/local/slurm/sbin/scrun"
}
},
"experimental": true,
"iptables": false,
"bridge": "none",
"no-new-privileges": true,
"rootless": true,
"selinux-enabled": false
}
通过无根(Rootless)Podman的scrun(23.02)
例如:
$ podman run ubuntu /bin/sh -c 'grep ^NAME /etc/os-release'
NAME="Ubuntu"
$ podman run centos /bin/sh -c 'grep ^NAME /etc/os-release'
NAME="CentOS Linux"
$ podman run centos /bin/sh -c 'printenv SLURM_JOB_ID'
77
$ podman run centos /bin/sh -c 'printenv SLURM_JOB_ID'
78
Podman进程树
用于scrun的Podman配置(23.02)
~/.config/containers/containers.conf:
[containers]
apparmor_profile = "unconfined"
cgroupns = "host"
cgroups = "enabled"
default_sysctls = []
label = false
netns = "host"
no_hosts = true
pidns = "host"
utsns = "host"
userns = "host"
[engine]
runtime = "slurm"
runtime_supports_nocgroups = [ "slurm" ]
runtime_supports_json = [ "slurm" ]
remote = false
[engine.runtimes]
slurm = [ "/usr/local/slurm/sbin/scrun" ]
Questions?
● Documentation: ○ https://slurm.schedmd.com/containers.html
附录1:scrun的限制
scrun - 限制(23.02)
● 完全支持在 scrun 中使用注释 ○ 注释不会记录在 Slurm 的会计中 - 在 sacct/sreport 中不会显示 ○ 注释不被 slurmctld 识别 - 在使用 scontrol 显示作业/步骤时不会显示
● slurm-23.02 中不支持网络命名空间 ○ 所有容器必须在主机网络下运行 ○ Slurm 没有与网络命名空间相关的设计限制。这只是尚未实施的功能。
■ 欢迎用户提交 RFE(功能请求)以开始关于添加此功能的讨论。
● Kubernetes 操作员和 Kubernetes CNI 支持也未实施。 ○ 这只是尚未实施的功能。
■ 欢迎用户提交 RFE 以开始关于添加此功能的讨论。
● 登录节点上的无根 Docker/Podman 不应启用 cgroup/apparmor/selinux 支持
● 在大多数情况下,这些限制/系统在 Docker/Podman 的无根模式下是无效的,无论是否使用 Slurm。它们在使用用户命名空间时不适用。
● 所有容器都是远程执行的,因此本地系统的安全系统对容器来说是无关紧要的。Slurm 将对在 scrun 下运行的容器强制执行为 Slurm 集群定义的现有限制。
● 如果 Docker/Podman 在任何 selinux 标记下运行,则该标记将在阶段操作期间由 scrun 自动继承。结果作业需要由 Slurm 中现有的 selinux 支持应用这些标签。
● Podman 允许轻松配置禁用,而 Docker 需要命令行参数(在撰写本文时)。
● 目前尚未实现自动资源选择
○ 使用 Slurm 环境变量可以控制作业属性
○ scrun 当前将以请求的默认资源运行默认作业
● 容器失败可能需要检查 slurmd 日志和/或系统日志以确定根本原因
○ 这似乎是容器编排系统的一个常见问题,将来版本中可能有几种潜在的处理方法
● Lua 必须与 JSON 支持一起编译,或者必须安装库。
○ 在 Lua 安装 JSON 库后,可能需要重新编译 Slurm,以便能够使用它。
● 当前 scrun 不会在 Lua 脚本运行时终止或停止该脚本。
○ 如果 Lua 阶段脚本挂起,则作业时间限制可能会被触发并终止作业。
● scrun 具有相关的 SPANK 和 clifilter 支持。
○ 这些钩子不是安全设备,任何用户都可以像使用 srun/sbatch/salloc 一样覆盖它们。
○ scrun 使用标准 Slurm RPC 和用户权限。任何用户都可以修改或 ptrace 自己的进程。任何安全性必须在控制器上应用。
● 每个用户每个主机一个 podman/docker 实例
○ scrun 不提供其自身以外作业的信息
■ 作业将在 squeue/sacct/slurmrestd 中可见
○ docker/podman 对任何外部启动的容器将是盲目的
● MUNGE 身份验证 ○ scrun 目前仅通过 MUNGE 工作
○ 作业提交主机必须安装 Slurm 并位于 MUNGE 安全范围内
● JWT 身份验证
○ 当前未实施
● 容器 ID 必须对每个用户唯一
○ Docker 或 Podman 将逐字传递容器 ID 给 scrun。
○ 如果本地锚定进程已死,scrun 将尝试按 ID 搜索容器。
● 在 Slurm 中运行容器的所有现有限制仍然适用:
○ 容器必须安装兼容版本的 Slurm 以调用 Slurm 命令
○ 为使用基于 MUNGE 的身份验证,MUNGE 的套接字必须挂载在容器中
○ 从容器进行 JWT 身份验证是可能的,但目前没有可用的秘密功能。
■ 目前,Slurm 不支持通过 JWT 进行的步骤控制/命令。
● 用户环境必须显式设置
○ 在调用 docker/podman 时的环境不会被容器继承,除非 Docker/Podman 支持环境变量。
○ 任何环境变量必须进行设置:
■ 这可以通过在容器镜像中的 config.json 中设置环境变量来完成。
■ Docker 还支持在运行时设置环境变量的 ‘--env’,‘-e’ 和 ‘--env-file’。
● scrun 将创建一个本地进程,该进程必须在作业持续时间内保持活动
○ 如果本地进程被终止,则 Slurm 将终止作业。这与通过 srun 运行的任何作业是相同的要求。
○ scrun 可以从批处理作业启动,以避免提交主机的正常运行时间要求。
● Docker 当前使用基于事件和轮询的系统来确定容器是否存活
○ 这可能会导致运行 Docker 的主机上 CPU 使用率高于仅通过 srun 直接运行容器的情况。
○ 这属于 Docker 的设计,与 Slurm 无关。
○ 选择无根模式的登录节点将需要注意即使用户并不总是直接与 Docker 交互,也要考虑额外的 CPU 和内存使用。
● scrun 支持内置的 Docker/Podman 日志记录 ○ scrun 增加了对 Docker JSON 格式的日志文件输出的支持
■ Docker JSON 格式的日志包括元数据,以确定日志条目的来源
■ 作业输出到 stdout 和 stderr 将被注释(因此可以在 Docker 中轻松查询)
○ scrun 的日志将被标记为来自 stdout 源
■ 正常的 slurm 命令将继续根据日志的严重性将日志打印到 stdout 和 stderr。
● scrun 需要完全配置 oci.conf
○ 如果 oci.conf 未配置,则 scrun 将拒绝运行,并且将不支持容器。
● 提交主机的 I/O 限制和其他限制将影响容器的阶段进出
● Slurm 无法控制 Docker/Podman ○ Docker 和 Podman 需要独立于 Slurm 配置
○ 仅支持无根 Docker/Podman
■ 无根 Docker 在旧内核上支持程度不一
■ 建议用户运行其发行版和 Docker 的最新版本,以避免问题 ○ Slurm(scrun)作为启动 Docker/Podman 中容器的最后一步之一来运行
● 目前尚未实现 Docker/Podman 的所有功能
● 在线镜像库独立于 Slurm 存在,并可能会适用带宽或使用限制 ○ 这些限制可能会错误地暗示 scrun(和 Slurm)运行缓慢
○ 建议用户在可能的情况下设置本地缓存代理
○ scrun 不缓存镜像
● scrun 不是安全解决方案、反病毒软件或新的安全层
○ 它不扫描或推理容器镜像的内容,仅执行对基本 OCI 镜像格式的强制
○ 它将图像推送到执行主机,在那里配置的和在 oci.conf 中的 OCI 运行时将被执行以启动容器
○ 用户有责任确保容器镜像遵循站点政策和程序,并且没有恶意代码
● scrun 仅在 POSIX 用户/组下运行,既不添加也不移除用户和容器进程的能力/权限
● 网站必须配置阶段进出 Lua 脚本,以清理缓存的镜像
○ 未能清理镜像可能会导致文件系统的巨大浪费。
● 网站必须配置 Docker/Podman 以独立于 Slurm 清理缓存的镜像
○ Docker 的构建缓存可能会变得非常大!
● 为了支持 docker build
或 podman build
,容器的阶段进出必须完成,以将最终更改应用回容器的私有挂载空间
○ 这只会在单个计算节点上发生,因此不应出现跨节点同步更改的问题。
○ 残留的容器镜像文件树将减少同步调用,但最终清理必须在 scrun 外部进行。
● scrun exec 尚未实现
附录 2:通过 Scrun 的工作生命周期
容器创建流程 - Scrun
附录:3 Scrun 的容器暂存
Scrun - 容器暂存
● Scrun 在启动时需要将镜像暂存到远程宿主机
● Scrun 在作业结束时需要将镜像从远程宿主机中移除
● 由于每个站点具有不同的共享文件系统配置和数据进出规则,因此需要灵活性。
○ Scrun 尽量避免对请求主机与在 Slurm 中的执行主机之间的假设
○ 站点管理员必须配置镜像的存放位置和方式。
Scrun - 通过 Lua 进行容器暂存
● Scrun 的 Lua 暂存插件允许站点编写自定义和简单的脚本,以便将镜像移动到远程存储并返回。
● Scrun 的暂存 Lua 脚本位于:
○ /etc/slurm/scrun.lua
● Lua 脚本以用户身份运行,避免任何额外的提升权限风险
● Lua 通过库已经支持 JSON
● 如果需要,站点可以编写本地的 Slurm 插件,而不是使用 Lua 插件。
Scrun - Lua 容器暂存示例
简化的暂存到(常见共享文件系统)的钩子:
function slurm_scrun_stage_in(id, bundle, spool_dir, config_file, job_id, user_id,
group_id, job_env)
os.execute(string.format("/usr/bin/env rsync --numeric-ids --delete-after
--ignore-errors -a -- %s/ %s/", rootfs, dstfs))
slurm.set_bundle_path(p)
slurm.set_root_path(p.."rootfs")
write_file(jc, json.encode(c))
return slurm.SUCCESS
end
Scrun - Lua 容器移除示例
简化的移除钩子:(此示例仅删除容器)
function slurm_scrun_stage_out(id, bundle, orig_bundle, root_path, orig_root_path,
spool_dir, config_file, jobid, user_id, group_id)
os.execute("rm --one-file-system --preserve-root=all -rf "..bundle)
return slurm.SUCCESS
end