JupyterHub 看上去只是把 Jupyter Notebook 的登录入口统一起来,服务器租用时却不能按普通网站或单体应用的办法估容量。普通应用通常由一组固定进程承接全部请求,访问量变化主要反映在请求队列和响应时间上;JupyterHub 则会为每个在线用户拉起独立的单用户服务,并在用户执行代码时创建一个或多个内核进程。用户读入的数据、导入的库、生成的中间变量都留在自己的内核地址空间里,彼此不会自动共享。平台上多一个活跃内核,就可能多出一整份数据副本和一段突发计算负载。
因此,采购人员问“有多少账号”只能知道容量上界,回答不了机器该租多大。真正有用的问题是:高峰时同时有多少人执行单元格,每个活跃内核的峰值常驻内存是多少,一分钟内会不会集中启动几十个环境,用户目录放在哪里,空闲服务多久回收,以及某个用户超限后会影响自己还是拖慢整台机器。把这些问题逐项量化,才有资格比较本机多进程、Docker 容器和 Kubernetes 三种部署形态。
本篇讨论的是多用户交互式数据科学平台的资源模型,不是泛泛的 Python 安装教程。文中出现的并发比例、内存倍率、余量和阈值均属于工程经验值或演算示例,并非某台服务器的实测成绩,正式下单前需要拿真实 Notebook、真实镜像和真实数据做压测验证。服务器价格统一按“需询价 / 公开渠道差异较大,以实时报价为准”处理,不用未经核对的金额替代商务报价。
JupyterHub 容量表里至少要同时记录三个数量:注册账号数、正在运行的单用户服务数、正在执行或持有大对象的内核数。注册账号只是身份库中的记录,用户没有登录时几乎不消耗计算资源。单用户服务是用户进入工作区后拉起的网页服务,它即使没有执行代码,也会常驻一份 Python 运行时、扩展和 Web 连接。内核才是执行单元格的进程,一个用户可以没有内核,也可以同时打开多个不同语言或不同项目的内核。
这三个数在一天内的变化并不同步。五十个账号可能只有十个单用户服务正在运行,而十个单用户服务中真正执行代码的只有五个;也可能出现一名分析人员打开六份 Notebook,每份都保留一个装着大表的内核。后者的资源压力可能高于十个人各开一个轻量工作区。这里的“五十、十、五、六”只是便于理解的场景演算,不是实测比例,真实并发必须从平台指标和业务作息中采集。
容量采样也要区分“页面在线”和“内核活跃”。浏览器标签页保持连接,不代表 CPU 正在工作;内核没有执行代码,也不代表内存已经释放。建议以单用户服务启动时间、最后一次活动时间、内核状态、进程常驻内存、CPU 使用时间和工作目录读写量组成一条会话记录,再按半小时或课程节次汇总峰值。只看整机平均 CPU,会把交互式负载最关键的短时尖峰抹平;只看当前内存,又无法判断哪些占用来自仍有价值的会话,哪些只是忘记关闭的环境。
注册人数仍然有用,但它解决的是容量边界、磁盘配额和身份管理问题。每人都可能生成代码、模型、图表和缓存,所以持久化存储容量可以按账号规划;CPU 与内存则应按峰值活跃内核规划。把两套口径混成“人均多少核、人均多少内存”,通常会得到一个看似整齐、实际既贵又不稳的采购单。
估算内存时,可以先写出一个可复核的式子:可分配给用户的内存,约等于峰值活跃内核数乘以单内核峰值常驻内存,再加正在运行但尚未执行的单用户服务基础占用;整机内存还要留给操作系统、文件页缓存、Hub、代理、容器运行时或集群系统组件。这个式子不应该直接填行业平均值,而应从团队最常跑的几类 Notebook 取样。
以“五十个账号里高峰有五到十人同时跑单元格”为例,这个比例只能当工程经验场景,需压测验证,不能写进验收指标。办公室里的探索性分析往往是稀疏并发:有人读结果,有人写说明,有人开会,真正按下运行键的时刻互相错开。课堂则相反,教师发出指令后,学生会在很短时间内执行同一段代码,峰值活跃内核可能接近到课人数。两个系统即使账号总数相同,核数、磁盘和启动队列也应完全不同。
正确的调查方法不是发一张“每人需要多少资源”的问卷,而是选取典型任务分组。轻量组可能只做数据库查询和绘图,中量组会把整理后的数据加载到内存,重量组则会做特征工程、模型训练或大文件转换。对每组采集单内核峰值内存、一次执行持续时间、临时文件写入量,再将用户排班和课程安排叠加到时间轴上。最终采用的不是三组平均值,而是业务可以接受的高分位峰值;具体分位点属于容量策略,需要结合预算和容错目标决定并压测验证。
还有一个容易漏掉的量:每名用户同时保留的内核数。平台允许自由新建 Notebook 时,用户常会把昨天的实验保留着,今天再开一份,旧内核虽然不再计算,却继续占内存。若统计口径只按活跃用户数乘单内核峰值,会低估这批“挂着变量的旧内核”。上线前应明确每人可运行的服务数、内核数和资源上限,并让监控能从用户追溯到具体内核,否则发生内存紧张时只能看到整机告警,找不到该回收哪一份。
数据文件大小、DataFrame 报告的逻辑大小和操作系统看到的进程常驻内存不是同一个数字。压缩格式文件解码后会展开,字符串列会产生对象与索引开销,合并、排序、分组和类型转换还可能同时保留输入、输出与临时数组。一份标称八吉字节的数据表,进入 pandas 工作流后按一点五到三倍内存预留,是常见的工程经验值而非实测结论,具体倍率受列类型、缺失值、字符串基数、算法和库版本影响,必须用目标代码压测验证。
倍率最大的阶段常常不是文件刚读完,而是执行 join、groupby、sort_values、astype 或复制切片的瞬间。例如原表仍被变量引用,新表已经分配,中间哈希结构和排序缓冲区又同时存在,进程峰值会明显高于最终结果。Notebook 的交互性还会放大问题:分析人员为了比较结果,把多个版本都留在不同变量里,旧对象没有释放;即便执行删除和垃圾回收,内存分配器也不一定立即把全部地址空间归还操作系统。
所以服务器租用前的内存测评不能只打开一个代表性文件,等加载完成后读一次数值。应完整执行最重的处理链,记录进程常驻内存曲线和容器工作集曲线,尤其关注数据重排、模型拟合、结果序列化三个阶段。压测还应重复执行,因为第二次运行可能复用缓存,也可能因前一次对象未清理而叠加占用。采用哪条曲线作为单内核预算,应写入容量说明,不能把最低点包装成平台能力。
避免膨胀也不等于无限加内存。能够在数据库或分布式引擎中完成的过滤、聚合,尽量不要把全量明细搬进 Notebook;文本列能转分类类型时先评估转换收益;中间结果可落 Parquet 等列式格式,下一步只读所需列;超大数据任务应交给批处理计算系统,Notebook 保留控制、探索和结果解释。若业务坚持让每个用户把同一份大数据复制进内存,再大的单机也只是延后冲突时间。
Notebook 的 CPU 曲线像开关。用户阅读代码、修改参数和查看图形时,内核可能长时间不占计算;按下执行后,单线程代码可以立即吃满一个核心,使用多线程数学库的操作甚至会占用多个核心。把全天 CPU 使用量除以时间得到平均值,再按平均值配核数,会在上午开课或业务晨会后出现明显排队。容量应按峰值并发乘以单内核突发需求估计,再结合允许的排队时长做取舍。
这里不能简单把“十个并发内核”翻译成“十个核心”。一部分 Python 代码受单线程执行约束,一部分数值库会根据可见核心数自动开线程,另一些任务会把工作交给子进程。若容器没有 CPU 配额,一个用户执行矩阵计算时可能把整机所有核心占满,其他人的网页和内核响应一起变慢。容器或集群场景应为单用户声明请求量和上限,本机多进程场景则至少要用操作系统控制组约束服务进程树;具体配额比例属于工程经验值,需用代表性任务压测验证。
CPU 测评要关注三类时间。第一类是单元格真正获得 CPU 的计算时间,第二类是就绪后等待调度的时间,第三类是用户感知的墙钟时间。只看整机利用率无法区分代码本身慢,还是大量内核在抢时间片。建议在压测脚本中模拟“执行一段、停顿一段、再次执行”的真实节奏,而不是让全部进程持续满载半小时。持续满载适合验证散热和极限吞吐,却不能代表交互体验。
核数不足通常表现为变慢,内存不足则可能触发进程终止,两者的后果不同。预算有限时,先保证最重单内核在资源上限内能完成,再决定允许多少并发排队。课程场景重视同一时刻的响应,核数和本地盘要留较大突发空间;研究团队可以接受少数任务慢一些,但不能接受长时间计算被回收或被其他用户挤掉。没有一种固定核内存比适用于所有 JupyterHub。
稳定运行时的资源曲线无法代表启动时刻。上课、上班或统一培训开始时,几十名用户可能在几分钟内点击启动。Spawner 要创建进程、容器或 Pod,读取用户配置,挂载目录,初始化环境,启动单用户服务并建立代理路由。若节点上没有目标镜像,还要拉取多个镜像层;镜像到达后需要校验与解压,大量小文件写入容器层。这个短时集中动作就是启动洪峰。
启动洪峰往往先暴露磁盘问题。机械盘处理顺序大文件尚可,一旦几十个环境同时扫描 Python 包目录、创建缓存和读取元数据,寻道与小文件 IOPS 会成为瓶颈。用户看到的不是计算慢,而是工作区长时间停在等待页面,随后触发启动超时。SSD 或 NVMe 的价值在这里比单纯提高网络峰值更直接,但具体设备能承载多少并发启动没有通用数字,属于必须压测验证的工程指标。
Docker 和 Kubernetes 还会受到镜像是否命中的影响。节点已有镜像时,启动主要消耗容器创建和文件系统初始化;镜像缺失时,外部下载、校验和解压叠加在一起,延迟可能跨越一个数量级。测试报告必须分别记录热启动与冷启动,不能用预热后的结果冒充首次启动成绩。正式变更镜像前,可以在课程开始或班次切换之前将镜像预拉到候选节点,并控制镜像体积,避免把开发工具、编译缓存和无关模型全部塞进基础镜像。
控制并发启动数也很重要。一次放行全部请求,可能让每个请求都在争盘,最终全部超时;设置启动队列,让节点按可承受的并发逐批拉起,单个用户虽然要等待,但整体成功率和可预期性通常更好。队列上限、启动超时和重试次数都是工程参数,需依据冷启动压测调整。若平台要求所有学员在教师开课后立即可用,更稳妥的做法是提前启动并预热环境,而不是指望高峰时临时创造全部工作区。
本机多进程形态让 Hub 在同一台服务器上为用户启动单用户进程,常见做法包括直接使用本地进程或通过 SystemdSpawner 把每个用户服务放进独立的 systemd 单元。它的优点是路径短、组件少、日志直观:进程、端口、文件和系统用户都能在宿主机上直接查看,适合人数少、成员互相信任、环境版本比较统一的团队。服务器租好后不必先维护镜像仓库和集群控制面,初期运维负担最低。
它的主要问题不是“能不能设置限制”,而是限制是否完整、是否容易持续维护。systemd 可以用控制组为服务单元设置内存和 CPU 约束,但要确认内核及其子进程都被收进同一控制组,并验证超限时的行为。若直接启动普通进程而没有进程树级限制,一个用户的多进程任务可能绕开单进程观察,消耗整机内存。正式上线前要故意触发一次资源上限,确认被终止的是该用户服务而非其他进程,并检查用户能否得到可理解的错误提示。
环境隔离也是取舍。所有人共享系统 Python 时,升级一个包可能改变全部 Notebook;每人使用虚拟环境虽能隔开依赖,却会在 home 中复制大量小文件,给共享存储增加元数据压力。系统用户边界能隔开文件权限,但不会天然提供容器文件系统那样的运行环境封装。若团队频繁要求不同 CUDA、不同系统库或互相冲突的依赖,本机多进程很快会把运维人员拖进版本协调。
故障半径方面,宿主机内存耗尽、根目录写满、内核升级重启都会影响全部用户。这个形态适合把一台机器当作一个完整租户池,而不是追求跨节点调度。出现容量瓶颈后,最简单的扩展通常是再建一个独立 Hub 按团队分流,而不是把同一 Hub 的用户进程随意散到多台机器。若业务无法接受一名用户误操作拖慢所有人,就不要只因为部署简单而选择它。
DockerSpawner 为每名用户创建独立容器,CPU、内存、进程数、挂载点和环境镜像都能以容器为边界管理。单个用户发生内存溢出时,理想结果是该容器内的内核或服务被终止,其他容器继续运行。对于几十人的固定团队,一台或少量几台大内存服务器配 DockerSpawner,通常能在隔离性和运维复杂度之间取得平衡。这里的“几十人”只是形态描述,不是容量承诺,具体上限仍取决于并发内核和任务重量。
容器不会消除资源争用,只会让边界更清楚。所有容器仍然共享宿主机 CPU、内存带宽、磁盘和网络。如果只设置内存上限而不设置 CPU 配额,一个重计算用户仍可能占满核心;如果用户把临时数据写入共享卷,存储争用仍会影响全体。资源声明必须同时覆盖 CPU、内存、进程数和临时空间,并通过压测确认上限触发后不会造成容器运行时或宿主机异常。
镜像治理是 Docker 形态新增的成本。统一镜像能保证环境一致,但镜像越大,冷启动和更新越慢;让每个团队维护自己的镜像,又会带来版本、漏洞修补和存储清理问题。建议把稳定的系统层和常用科学计算库放进基础镜像,把变化快的项目依赖通过受控方式叠加,并为镜像设置可追溯版本。不要只用“最新”标签覆盖旧版本,否则用户昨天能复现的 Notebook,今天可能因包升级而改变结果。
持久化数据必须挂载到容器外部。容器可写层只适合短期缓存,重建后不应指望它保留用户成果。home、共享数据集和项目成果分别挂载,权限边界也要分别设计。若所有路径都落到同一个网络文件系统,容器虽然隔离了进程,却没有隔离 IOPS;若全部落本地盘,容器迁移到另一台宿主机后又找不到原文件。最实用的做法是持久化资料走共享存储,临时计算走本地高速盘,并明确哪些目录会随容器销毁。
KubeSpawner 或 Zero-to-JupyterHub 把每个单用户服务放进 Kubernetes Pod,通过资源请求、资源上限、节点选择、污点容忍和存储声明完成调度。它适合节点数量会增长、CPU 与 GPU 节点混合、不同团队需要不同镜像,或者平台已经具备 Kubernetes 运维能力的场景。资源不足时,新 Pod 可以等待合适节点,而不是强行挤进一台已经接近上限的机器,故障也能被限制在节点或工作负载范围。
调度器只能根据声明做决定,声明失真就会得到失真的调度。请求量写得过低,调度器会把过多 Pod 塞进同一节点,运行时大家同时冲高导致争用;请求量写得过高,节点明明有空闲资源却无法接纳新用户。上限过松会扩大突发冲突,上限过紧又会让合法任务频繁终止。请求量可以参考常态工作集,上限参考可接受峰值,但这两者都属于需压测验证的工程参数,不能照搬示例配置。
集群形态还引入一组固定开销:控制面、网络插件、入口、证书、监控、日志、镜像仓库、存储驱动和升级维护。用户只有个位数,或者只有一台工作节点时,这些组件不会因为规模小而自动消失,反而可能比 Notebook 本身更难维护。已有成熟集群时,JupyterHub 只是新增一种工作负载;从零搭集群时,则要把整套平台值班和升级能力算进成本。
节点故障时,Pod 可以在其他节点重建,但“重建”不等于“计算无损迁移”。内核里的变量和正在进行的计算通常会消失,只有写入持久化存储的 Notebook 与结果能保留。平台说明必须把这一点说清楚,不能把调度层的重建能力写成会话连续性承诺。需要长时间训练的任务,应从交互内核提交到可恢复的批处理队列,记录检查点,而不是依赖一个浏览器会话持续数天。
| 部署形态 | 用户隔离边界 | 资源上限控制 | 扩展方式 | 主要故障半径 | 适用前提 |
|---|---|---|---|---|---|
| 本机多进程或 SystemdSpawner | 系统用户与进程树 | 依赖控制组,需自行验证子进程归属 | 换大机器或按团队拆分 Hub | 整台宿主机 | 规模较小、环境统一、成员互信 |
| DockerSpawner | 单用户容器 | 容器配额直观,仍共享宿主机磁盘 | 纵向扩容或增加独立容器节点 | 单容器与所在宿主机 | 能维护镜像、卷与容器运行时 |
| KubeSpawner 或 Zero-to-JupyterHub | Pod、命名空间与节点池 | 请求量和上限可调度,策略最完整 | 增加节点并由调度器分配 | Pod、节点及集群公共组件 | 已有集群能力或确有多节点需求 |
多节点或可迁移容器通常要求用户 home 位于共享存储,NFS 是常见选择。采购时如果只问容量和顺序读写速度,会漏掉 Jupyter 工作负载最敏感的部分:目录遍历、文件属性查询、包导入、自动保存、检查点和大量小文件创建。几十个用户同时启动 Python 环境时,存储端会收到密集的元数据请求,即使传输的数据量不大,延迟也可能明显上升。
Python 虚拟环境尤其容易放大小文件问题。每名用户在 home 下各装一份环境,会生成成千上万的包文件和元数据;启动内核时,解释器沿搜索路径反复查询这些文件。把全部环境都放共享盘,节点本地盘再快也帮不上忙。更合适的分工是:稳定环境做成镜像或放在节点本地只读层,用户代码、Notebook、最终结果与需要跨节点访问的数据放共享盘,解压缓存、排序溢出、模型临时检查点和可重建中间结果放本地高速盘。
“数据放共享盘、临时计算结果放本地盘”还需要目录和生命周期配合。共享数据集按只读方式挂载,避免每人复制一份;用户 home 持久化并设置配额;本地 scratch 明确标注不备份、节点重建会丢失,并设置自动清理周期。代码不能默认临时目录永久存在,重要输出在任务结束前要显式写回共享盘或对象存储。若团队不接受临时文件丢失,就应把任务改造成有检查点的批处理,而不是把本地盘假装成持久层。
存储测评应同时跑三组负载:大文件顺序读写、海量小文件创建与查询、多人启动环境后的混合读写。报告要分别列出延迟分布和错误情况,不能只给一个聚合吞吐数字。并发数、文件大小和目录深度要来自实际项目;任何每秒操作数或吞吐结论都必须标为实测或工程经验并注明测试条件,本文不提供可直接套用的数值。
还要检查故障行为。NFS 短暂不可用时,Notebook 保存是报错、卡住还是导致整个内核不可响应;共享盘空间耗尽时,是单个用户配额先触发,还是所有人一起写失败;节点本地盘写满时,容器运行时是否也使用同一分区。把系统盘、容器数据目录和用户临时盘分开,能缩小一次写满事故的影响范围,这个分区设计往往比盲目扩大总容量更有效。
JupyterHub 的单用户服务和内核不会因为用户关掉浏览器就必然退出。浏览器断开、网络切换、用户忘记登出,都会留下仍占内存的工作区。idle culling 的作用是依据空闲时间停止服务或内核,把“曾经登录过的人数”重新压回“当前真正需要资源的人数”。在按节点或按实例付费的租用环境中,回收率会直接决定夜间是否能缩容,也决定白天需要保留多少常驻余量。
回收策略至少区分三种状态:页面有活动且内核在执行、页面无活动但内核仍在执行、页面和内核都长期无活动。第一种不能回收;第二种可能是用户关闭浏览器后留下的长任务,不能仅凭网页心跳判断;第三种才是最明确的回收对象。若平台允许长任务在交互会话里运行,就要给这些任务显式标签、延长时限或引导到批处理队列,否则激进回收会中断合法计算。
阈值不存在行业统一答案。办公团队午休和夜间是天然回收窗口,课堂可以在下课后批量停止,研究任务则可能跨越多个工作日。任何“多少分钟自动关停”的数字都只是工程经验值,需结合会话日志、用户作息和任务时长验证。上线初期可以先只记录拟回收对象而不真正停止,观察误判;随后对明确空闲的服务执行回收,并在界面提前提示。策略变更要保留审计记录,发生争议时能说明为何停止某个工作区。
成本核算应把回收前后两条曲线画出来:运行中的单用户服务数、活跃内核数、已分配内存、节点数量和本地临时盘占用。若回收了大量服务,节点数却无法下降,问题可能在于资源请求写得过高、Pod 分布太碎或共享存储不允许迁移。回收只释放工作负载,能否退租节点还取决于调度和节点池策略。单机租用无法按小时缩小物理内存,但回收仍能降低内存争用,使同一台机器承载更多真实活跃用户。
回收前保存 Notebook 只能保住代码和已写入文件的输出,不能保住内存变量。产品说明要明确区分“文件已保存”和“运行状态可恢复”。如果用户依赖内存里的临时对象,回收后必须重新执行;若重新执行代价很高,就应让流程定期落盘或生成检查点。把回收的不便转化成可恢复的工作流,比无限延长空闲时间更稳。
GPU 版 JupyterHub 容易出现一个误解:多个用户在不同容器里,就能公平分时使用同一张卡。容器可以控制设备是否可见,却不一定能把显存和计算能力做成严格边界。两个用户同时把模型和批数据放入同一张卡,其中一人的显存申请可能让另一人的下一次分配失败;即使都没有溢出,计算核、显存带宽和数据传输也会互相争用,交互延迟很难预测。
适合共享的场景是短时推理、轻量教学和能容忍排队的实验,前提是任务显存峰值已经测清,并有队列或设备分配机制。显存峰值不能只看模型权重,还要加激活、优化器状态、梯度、框架缓存和数据批次。各种“模型大小乘某个固定倍数”的说法都只能作为工程经验值,需用目标框架、精度和批大小压测验证。容器内看到尚有空闲显存,也不代表下一次峰值一定能容纳。
应整卡独占的情形包括:长时间训练、显存接近卡容量、任务失败重跑代价高、需要稳定时延、多个框架对显存管理方式不同,或者业务无法接受他人任务导致自己溢出。整卡独占的调度单位清楚,问题用户只影响自己的卡,代价是空闲碎片可能增加。与其让三名用户同时争一张卡并反复失败,不如排队串行执行,整体完成时间反而更可控。
某些硬件和平台支持把一张卡划分为更明确的实例,也有时间片共享方案,但隔离能力、支持型号、驱动版本和可分配粒度必须以实际产品文档与服务器配置为准。不要在采购方案中把“设备可见性隔离”写成“显存硬隔离”。验收时应让多个用户同时运行峰值任务,观察显存分配失败、性能抖动和故障影响范围,再决定共享、切分还是整卡分配。
GPU 节点的 CPU、系统内存和本地盘同样不能省。数据预处理、解码与批次装载发生在 CPU 侧,显存前的数据需要经过系统内存,本地缓存会影响训练启动。只按卡型比较服务器,忽略主机内存与盘,最后常见的结果是 GPU 等数据。任何吞吐提升比例都需要在目标数据管道上实测,本文不提供脱离条件的性能数字。
服务器租用清单应从工作负载证据反推,而不是先挑一款机器再往里塞 JupyterHub。第一项是可用内存:按峰值活跃内核、单内核峰值、空闲单用户服务、系统和文件缓存分别列账。第二项是 CPU:按峰值同时执行数、单任务线程行为和可接受排队时长估算。第三项是本地 NVMe:承担镜像解压、容器层、临时文件和可重建缓存。第四项是共享存储:承担 home、公共数据与最终结果。第五项才是公网访问和内部数据流量,带宽要区分用户浏览器传输、镜像拉取、存储访问和节点间调度。
验收不能只跑一个满负载基准。至少应覆盖冷启动洪峰、热启动洪峰、多用户同时读共享数据、多个内核执行 CPU 任务、单用户触发内存上限、共享盘短时高延迟、空闲回收和节点重启后的恢复。每项都要定义成功条件,例如启动成功率、可接受等待时间、超限时的故障范围和文件是否保留。具体阈值由业务方确定,测试得到的吞吐和时延必须注明服务器型号、存储介质、镜像大小、并发数和数据集,不能脱离条件宣传。
扩容边界也要提前写。单机形态增加内存或更换机型会涉及停机和迁移;DockerSpawner 若跨多台宿主机,需要额外解决调度、路由和共享存储;Kubernetes 可以加节点,但控制面、网络和存储也有容量上限。若预期一年内从一个小组扩展到多个院系或多个项目,直接采购超大单机未必划算,分阶段验证后再扩节点更容易校正模型。若规模长期稳定,简单形态反而能省下集群维护成本。
商务报价不能代替技术验收。JupyterHub 服务器、GPU、共享存储和流量价格均应写“需询价 / 公开渠道差异较大,以实时报价为准”,并在询价单中把内存容量、磁盘介质、盘的归属、GPU 是否独占、共享存储性能口径、备份范围、可扩容方式列清。只比较一个月度总价,会把不同存储和隔离条件的方案放进同一行,得出错误结论。
迁移路径同样属于租用边界。用户 home 如何导出,镜像如何保存,Hub 数据库和配置如何备份,外部身份系统如何切换,临时盘上哪些内容本来就不迁移,都应在上线前演练。真正降低锁定成本的不是一句“随时可迁”,而是用户数据、环境定义和平台配置都有可验证的导出物。
第一种是不超过少数人的低频探索,大家偶尔才开一次 Notebook,也没有内部数据合规要求。为了这点使用量维护 Hub、代理、证书、身份接入、镜像、监控和备份,固定成本往往高于计算本身。直接使用托管 Notebook 或按需启动的开发环境更省事,用户不工作时也不必保留整套平台。这里的“少数人”不是固定人数门槛,判断依据是使用频率和维护投入。
第二种是一次性培训或短期竞赛。若课程结束后平台就不再使用,自建集群的学习、测试和收尾工作很难摊薄。除非课程必须访问内部数据、必须控制网络和镜像,或者需要长期保留学习成果,否则临时托管环境更合适。课程组织者真正需要的是统一环境、集中发放和结束后可导出,而不是拥有一套长期运行的基础设施。
第三种是团队没有人负责 Linux、容器、存储和身份系统,却选择 Kubernetes 只因为听起来更先进。KubeSpawner 能解决调度问题,不能替代值班、升级、容量管理和故障处置。没有运维责任人时,组件越多,问题恢复越慢。此时要么选托管 Notebook,要么把形态降到一台可控的 Docker 或本机多进程服务器,并把用户范围限制在可信团队。
第四种是所有任务都可以批处理,用户并不需要在浏览器里逐步探索。固定脚本、定时特征处理、长训练和报表生成更适合工作流调度系统,Jupyter 只作为开发入口。把长任务强行留在交互内核,会让空闲判断、资源回收和故障恢复变得困难。先确认用户真的需要“边看边改边运行”,再决定是否自建多用户交互平台。
自建成立的理由通常来自内部数据不能离开受控环境、需要统一身份与审计、需要定制镜像、需要共享昂贵 GPU,或者用户规模和使用频率足以摊薄维护成本。若这些理由一条都不占,托管方案减少的不是服务器费用,而是镜像更新、存储故障和账号治理这些长期工作。
最后把容量账收成一张可执行的计算单。先从平台日志取得注册账号数、峰值运行服务数、峰值活跃内核数和每名用户的内核数量分布;再从代表性任务取得单内核常态内存、峰值内存、执行持续时间、临时盘写入和共享盘访问。所有数据按办公日、课程日和特殊发布日分组,不用一个月平均值抹平启动洪峰。
内存账按“活跃内核峰值工作集加空闲服务基础工作集加系统与缓存余量”核算;CPU 账按“峰值同时执行内核乘单内核线程需求”核算,并把超出核数后的排队时间列出来;本地盘按冷启动镜像、容器层、临时计算和清理窗口核算;共享盘按持久化容量、小文件 IOPS 和多人读写延迟核算。式子里的每个系数都应标注来源:平台采样、压测实测或工程经验。工程经验项在签约前必须安排验证,不能与实测项混写。
再把回收策略套进去,分别演算不回收、下班后回收、按内核与服务分层回收三条曲线。观察释放的内存能否让单机承载更多真实并发,或能否让集群退掉空闲节点;同时统计误回收、重启等待和用户重算成本。回收收益如果只体现在指标变好,却没有减少争用或节点数量,说明调度请求、节点布局或使用习惯还需调整。
部署形态的选择由故障半径决定。可信小团队且环境统一,可以用本机多进程换简单;需要清楚的单用户资源上限,选 DockerSpawner;需要多节点、异构 GPU、弹性节点池和统一调度,才进入 KubeSpawner。形态越复杂,越要证明复杂度解决了真实问题。把 Kubernetes 用在一台节点上,通常只是把单机故障外面再包一层控制面,并没有得到真正的横向能力。
可落地的服务器租用测评结论不应该写成“某配置支持多少用户”,而应写成“在某镜像、某数据集、某并发执行节奏和某回收策略下,观察到哪些资源峰值,故障被限制在哪一层,还剩多少余量”。只有条件完整,下一次镜像变大、数据翻倍或课程人数变化时,团队才知道该改哪个参数,而不是重新猜一遍机器规格。
本文的软件架构口径依据 JupyterHub 的 Hub、代理、Spawner、单用户服务与内核职责划分,并结合 SystemdSpawner、DockerSpawner、KubeSpawner 和 Zero-to-JupyterHub 的公开配置思路整理。具体参数名、默认行为和支持范围会随版本变化,部署时应以所选版本的官方文档、发行说明和实际配置验证为准。本文没有引用某台服务器的跑分,也没有把演算示例写成客户实测。
文中的“五十个账号里五到十人同时运行”、DataFrame 一点五到三倍内存、启动并发、回收时长和资源余量均属于工程经验值或示意场景,不是性能承诺,必须用自身数据、代码、镜像和用户节奏压测验证。核对时应保留测试脚本、镜像摘要、数据集规模、并发模型、机器规格、存储介质、监控曲线和异常日志,任何吞吐与时延结论都要能回到这些条件。
价格口径为“需询价 / 公开渠道差异较大,以实时报价为准”。正式询价时,把 CPU、内存、本地盘、共享存储、GPU 分配方式、网络、备份、迁移和支持边界拆项确认,不以本文作为报价或服务承诺。最终配置应由业务峰值、压测结果、故障容忍度和预算共同决定。
一万网络深耕 19 年(成立于 2007 年),可提供上述形态的服务器租用以及带宽、IP、共享存储与 GPU 分配方案;具体机型、计费周期与支持边界需询价,公开渠道差异较大,以实时报价为准。
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品