跳转到主要内容

使用systemd进行资源隔离的JupyterHub Spawner

项目描述

功能 | 要求 | 安装 | 配置 | 获取帮助 | 许可证

systemdspawner

Latest PyPI version Latest conda-forge version GitHub Workflow Status - Test Test coverage of code GitHub Discourse

systemdspawner 允许 JupyterHub 使用 systemd 启动单用户笔记本服务器。

功能

如果您想使用 Linux 容器(Docker、rkt 等)以实现隔离和安全性,但又不想处理容器镜像管理的头痛和复杂性,那么您应该使用 SystemdSpawner。

使用 systemdspawner,您可以使用熟悉的传统系统管理工具,无论您喜欢还是不喜欢,而不需要学习额外的容器相关工具。

以下功能目前可用:

  1. 限制每个用户允许的最大内存。

    如果他们请求超过此内存,则不会授予(malloc 将失败,具体表现取决于您使用的编程语言)。

  2. 限制每个用户可用的最大 CPU。

  3. 为用户提供公平的调度,独立于他们运行的进程数量。

    例如,如果用户 A 正在运行 100 个占用 CPU 的进程,通常意味着用户 B 的 2 个占用 CPU 的进程将永远得不到足够的 CPU 时间,因为传统的调度是按进程进行的。使用 Systemd Spawner,这两个用户的进程将作为一个整体获得相同数量的 CPU 时间,无论运行了多少个进程。如果用户是 B,这是一个好消息。

  4. 精确统计内存和 CPU 使用情况(通过 cgroups,systemd 内部使用)。

    您可以使用 systemd-cgtop 检查此功能。

  5. /tmp 隔离。

    每个用户都获得自己的 /tmp,以防止意外信息泄露。

  6. 在系统上以特定本地用户身份启动笔记本服务器。

    这可以替代使用 SudoSpawner 的需要。

  7. 限制用户在笔记本内sudo到root(或作为其他用户)的能力。

    这是一项额外的安全措施,以确保 jupyterhub 笔记本实例的妥协不会允许root访问。

  8. 限制用户可以写入的路径。

    这允许将 / 设置为只读,并且只授予特定路径的写入权限,以提高安全性。

  9. 自动将每个用户的笔记本日志收集到 journald 中,它还处理日志轮换。

  10. 使用 Systemd 的 动态用户 功能动态分配用户。与 tmpauthenticator 结合使用非常有用。

要求

Systemd 和 Linux 发行版

建议使用 SystemdSpawner 1 与 systemd 版本 245 或更高版本一起使用,但 可能 也适用于 systemd 版本 243-244。以下是使用 systemd 并有推荐版本的 Linux 发行版的示例。

  • Ubuntu 20.04+
  • Debian 11+
  • Rocky 9+ / CentOS 9+

可以使用 systemctl --version 命令来验证是否使用 systemd,以及使用的是哪个版本。

内核配置

某些内核选项需要启用才能使 CPU / 内存限制功能正常工作。如果没有启用这些选项,CPU / 内存限制将静默失败。您可以通过运行 check-kernel.bash 脚本来检查您的内核是否支持这些功能。

root 权限

目前,JupyterHub 必须以 root 用户身份运行才能使用 Systemd Spawner。需要以 root 用户身份运行 systemd-run 才能设置内存和 CPU 限制。简单的 sudo 规则不起作用,因为对 systemd-run 的无限制访问等同于 root。我们将很快探索加固方法。

本地用户

如果以c.SystemdSpawner.dynamic_users = False(默认值)运行,则每个用户的服务器都会以本地Unix用户账户的身份启动。因此,此启动器要求所有进行身份验证的用户已在机器上拥有本地账户。

如果以c.SystemdSpawner.dynamic_users = True运行,则不需要本地用户账户。Systemd将自动根据需要创建动态用户。有关详细信息,请参阅这篇博客文章

安装

您可以使用以下命令从PyPI安装它:

pip install jupyterhub-systemdspawner

您可以在jupyterhub_config.py文件中使用以下行为您自己的jupyterhub启用它:

c.JupyterHub.spawner_class = "systemd"

请注意,为了确认systemdspawner已正确安装在jupyterhub环境中,新生成的配置文件应在配置行上方列出systemdspawner作为可用的启动器类之一(在注释中)。

配置

有许多配置选项供您选择!您应将这些选项全部放入您的jupyterhub_config.py文件中

mem_limit

指定每个用户可以使用的最大内存。它可以指定为绝对字节值。您可以使用后缀KMGT分别表示千字节、兆字节、吉字节或太字节。将其设置为None将禁用内存限制。

即使您希望单个用户使用尽可能多的内存,仍然建议将内存限制设置为总物理内存的80-90%。这可以防止一个用户意外地通过OOM(内存不足)而使整个机器崩溃。

c.SystemdSpawner.mem_limit = '4G'

默认值为None,不提供内存限制。

此信息作为整数字节的环境变量MEM_LIMIT暴露给单用户服务器。

cpu_limit

表示每个用户可以使用的总CPU核心数的浮点数。1代表一个完整的CPU,4代表4个完整的CPU,0.5代表半个CPU,等等。此值最终转换为百分比并四舍五入到最接近的整数百分比,即1.5转换为150%,0.125转换为12%,等等。

c.SystemdSpawner.cpu_limit = 4.0

默认值为None,不提供CPU限制。

此信息作为浮点数环境变量CPU_LIMIT暴露给单用户服务器。

注意:systemd v231中存在一个错误,该错误阻止CPU限制设置为大于100%的值。

CPU公平性

cpu_limit完全无关的是CPU公平性的概念——在没有限制的情况下,每个用户应平等地访问所有CPU。在正常情况下,对于Jupyter笔记本,这并不完全适用,因为CPU调度是在进程级别而不是用户级别进行的。这意味着运行100个进程的用户比运行一个进程的用户有100倍多的CPU访问权限。这远非理想情况。

由于每个用户的笔记本服务器都运行在其自己的Systemd服务中,因此这个问题得到了缓解——来自用户笔记本服务器的所有进程都在一个cgroup中运行,并且cgroups在CPU调度中被平等对待。因此,独立于每个用户正在运行的进程数量,他们都会平等地访问CPU。这对于大多数情况来说非常理想,因为它允许用户在没有人使用CPU时爆发式地使用所有CPU,并强制他们在其他用户想要使用CPU时自动释放。

user_workingdir

为每个用户的笔记本服务器生成目录。当用户打开他们的笔记本服务器时,他们会看到这个目录。通常,这是用户的家目录。

在此配置值中的{USERNAME}{USERID}将被扩展为被生成用户的适当值。

c.SystemdSpawner.user_workingdir = '/home/{USERNAME}'

默认为用户的家目录。如果dynamic_users为真,则不予以尊重。

username_template

每个被生成用户应该以Unix用户名模板来生成。

在此配置值中的{USERNAME}{USERID}将被扩展为被生成用户的适当值。

此用户应该已经在系统中存在。

c.SystemdSpawner.username_template = 'jupyter-{USERNAME}'

如果dynamic_users设置为True,则不予以尊重。

default_shell

笔记本终端默认要使用的shell。将SHELL环境变量设置为这个值。

c.SystemdSpawner.default_shell = '/bin/bash'

默认为JupyterHub进程中的SHELL环境变量的值,如果没有设置SHELL,则为/bin/bash

extra_paths

应添加到生成笔记本服务器的PATH环境变量的路径列表。这比设置env属性更容易,因为您想要添加到PATH,而不是完全替换它。当您想默认将virtualenv或conda安装添加到用户的PATH中时,这非常有用。

c.SystemdSpawner.extra_paths = ['/home/{USERNAME}/conda/bin']

在此配置值中的{USERNAME}{USERID}将被扩展为被生成用户的适当值。

默认为[],不会向PATH添加任何额外路径。

unit_name_template

用于为每个用户笔记本服务器形成Systemd服务单元名的模板。这允许在相同机器上使用Systemd Spawner区分多个jupyterhubs。应只包含[a-zA-Z0-9_-]。

c.SystemdSpawner.unit_name_template = 'jupyter-{USERNAME}-singleuser'

在此配置值中的{USERNAME}{USERID}将被扩展为被生成用户的适当值。

默认为jupyter-{USERNAME}-singleuser

unit_extra_properties

用于向生成的Jupyterhub单元添加任意属性的键值对字典。

c.SystemdSpawner.unit_extra_properties = {'LimitNOFILE': '16384'}

有关瞬态单元中可用的每个单元属性,请参阅man systemd-run的详细信息。

每个参数值中的{USERNAME}{USERID}将被扩展为被生成用户的适当值。

默认为{},不会向瞬态范围添加任何额外属性。

isolate_tmp

将此设置为true为每个用户提供单独的、私有的/tmp。这对于防止意外泄露其他信息非常有用 - 可能您使用的库/工具在您不知情的情况下创建了/tmp文件,从而导致信息泄露。

c.SystemdSpawner.isolate_tmp = True

默认为false。

isolate_devices

将此设置为true为每个用户提供单独的、私有的/dev。这防止用户直接访问硬件设备,这可能是潜在的安全问题的来源。/dev/null/dev/zero/dev/random和ttyp伪设备已经挂载,所以当此功能启用时,大多数用户应该看不到任何变化。

c.SystemdSpawner.isolate_devices = True

默认为false。

disable_user_sudo

设置为true,这阻止用户使用sudo(或任何其他方式)成为其他用户(包括root)。这有助于在用户也有机器上的sudo权限的情况下,如果用户的凭证被泄露,限制损坏。现在,基于Web的漏洞只能损坏用户的个人物品,而不是获得完整的root权限。

c.SystemdSpawner.disable_user_sudo = True

默认为True。

readonly_paths

应为用户的笔记本服务器只读挂载的文件系统路径列表。这将覆盖可能存在的任何文件系统权限。可以标记为readwrite的只读路径的子路径可以通过readwrite_paths标记。这有助于将/标记为只读,并仅允许笔记本用户写入的路径。如果此处列出的路径不存在,您将得到一个错误。

c.SystemdSpawner.readonly_paths = ['/']

在此配置值中的{USERNAME}{USERID}将被扩展为被生成用户的适当值。

默认为None,禁用此功能。

readwrite_paths

应为用户的笔记本服务器只读挂载的文件系统路径列表。这只有在使用readonly_paths使某些路径只读时才有意义 - 这可以用来使特定路径可读写。这不会覆盖文件系统权限 - 用户需要具有写入这些路径的适当权限。

c.SystemdSpawner.readwrite_paths = ['/home/{USERNAME}']

在此配置值中的{USERNAME}{USERID}将被扩展为被生成用户的适当值。

默认为None,禁用此功能。

dynamic_users

为每个用户动态分配系统用户。

使用Systemd的DynamicUser=功能为每个hub用户动态创建一个新的系统用户。他们的家目录设置在/var/lib/{USERNAME}下,并且随时间持久化。当用户的服务器不再运行时,系统用户将被释放。

请参阅http://0pointer.net/blog/dynamic-users-with-systemd.html获取更多信息。

切片

在指定的systemd切片中运行生成的笔记本。这允许应用聚合配置,该配置将适用于所有启动的单元。这可以用来控制所有笔记本用户可以使用的总内存量。

请参阅https://samthursfield.wordpress.com/2015/05/07/running-firefox-in-a-cgroup-using-systemd/以了解这可能会是什么样子。

有关详细配置,请参阅manpage

获取帮助

我们鼓励您在Jupyter Discourse论坛中提问。

许可证

我们采用共享版权模式,使所有贡献者都能保留其贡献的版权。

所有代码均在修订版BSD许可证的条款下授权。

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪一个,请了解有关安装包的更多信息。

源分布

jupyterhub-systemdspawner-1.0.1.tar.gz (17.7 kB 查看哈希)

上传时间

构建分布

jupyterhub_systemdspawner-1.0.1-py3-none-any.whl (15.0 kB 查看哈希)

上传时间 Python 3

支持者