Apache Toree 0.5.0-incubating 常用命令与 magic 详解
系统讲解 Apache Toree 0.5.0-incubating 的 jupyter toree install 全部参数、kernel.json 与 run.sh 启动链路、内核管理与 nbconvert 调试命令,以及 %AddJar/%AddDeps/%ShowTypes/%%SQL/%%HTML 等 magic 的逐个实测行为与异常结论,全部已在 Ubuntu 22.04 实测。
Apache Toree 0.5.0-incubating 常用命令与 magic 详解
目标:系统掌握 Apache Toree 0.5.0-incubating 的命令面与 magic 面——
jupyter toree install全参数、kernel.json 与 run.sh 的启动链路、kernelspec 管理与 nbconvert 调试命令,以及 %AddJar、%AddDeps、%ShowTypes、%%SQL、%%HTML 等 magic 的逐个实测行为(含 %Truncate 不存在、%ShowOutput 不生效两项实测异常结论)。以下全部命令与回显均已在 Ubuntu 22.04(x86_64)、OpenJDK 1.8.0_504、Spark 3.0.3 实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- JDK:OpenJDK 1.8.0_504(Toree 0.5.0 + Spark 3.0.3 的实测可用组合)
- Python:3.10.12,Jupyter notebook 7.6.3、jupyter_core 5.9.1
- 内核:apache-toree 0.5.0(pip 包,内核 jar toree-assembly-0.5.0-incubating.jar)
- Spark:spark-3.0.3-bin-hadoop2.7(
/usr/local/spark) - 内核目录:
/usr/local/share/jupyter/kernels/(系统级注册) - 验证用工作目录:
/root/toree-test
确认已注册内核(实时回显):
jupyter kernelspec list
Available kernels:
apache_toree_scala /usr/local/share/jupyter/kernels/apache_toree_scala
python3 /usr/local/share/jupyter/kernels/python3
二、安装与注册命令:jupyter toree install 全参数
jupyter toree install 是 Toree 唯一的注册入口,本质是把 pip 包内的 toree 目录复制为 kernelspec 并生成 kernel.json。全部参数如下(前六项为 Toree 专有,后四项为 Jupyter 通用;依据官方 Installation 文档与实测):
| 参数 | 默认值 | 作用 | 实测要点 |
|---|---|---|---|
--spark_home | /usr/local/spark(或环境变量 SPARK_HOME) | 写入 kernel.json 的 env.SPARK_HOME | 必须指向解压后的 Spark 目录(含 bin/spark-submit) |
--kernel_name | Apache Toree | 内核注册名前缀与显示名 | 生成 toree_local2_scala 形式的目录名(空格转下划线、转小写) |
--interpreters | Scala | 要注册的解释器列表,逗号分隔 | 合法值只有 Scala、SQL(区分大小写) |
--spark_opts | 空 | 写入 env.__TOREE_SPARK_OPTS__ | 如 --master=local[2],运行时会被同名外部变量覆盖 |
--toree_opts | 空 | 写入 env.__TOREE_OPTS__ | 如 --spark-context-initialization-mode none |
--python_exec | python | SQL 解释器回调 Python 时的可执行文件 | el8/py3 场景建议显式 python3 |
--replace | 关 | 覆盖同名已注册内核 | 改配置重注册时必加,否则报已存在 |
--user | 关 | 注册到 ~/.local/share/jupyter/kernels | 无 root 权限时用 |
--sys-prefix | 关 | 注册到 sys.prefix 下 | conda/虚拟环境专用 |
--debug | 关 | 日志级别 DEBUG | 排查注册问题时加 |
四个实测锚点:
--kernel_name 决定目录名(实测注册名为 Toree Local2 时目录名是小写下划线形式):
jupyter toree install --spark_home=/usr/local/spark --kernel_name='Toree Local2' --spark_opts='--master=local[2]' --replace
[ToreeInstall] Creating kernel Scala
[ToreeInstall] Installed kernelspec toree_local2_scala in /usr/local/share/jupyter/kernels/toree_local2_scala
--interpreters=Scala,SQL 一次注册两个内核:
jupyter toree install --spark_home=/usr/local/spark --kernel_name='Toree Dual' --interpreters=Scala,SQL --replace
jupyter kernelspec list | grep toree_dual
[ToreeInstall] Creating kernel Scala
[ToreeInstall] Installed kernelspec toree_dual_scala in /usr/local/share/jupyter/kernels/toree_dual_scala
[ToreeInstall] Creating kernel SQL
[ToreeInstall] Installed kernelspec toree_dual_sql in /usr/local/share/jupyter/kernels/toree_dual_sql
toree_dual_scala /usr/local/share/jupyter/kernels/toree_dual_scala
toree_dual_sql /usr/local/share/jupyter/kernels/toree_dual_sql
--interpreters 传非法值时跳过并告警(实测传 PySpark):
[ToreeInstall] Creating kernel PySpark
[ToreeInstall] ERROR] Unknown interpreter PySpark. Skipping installation of PySpark interpreter
⚠️ 注意:
--toree_opts与--spark_opts中的引号会原样写入 kernel.json 的 env,含空格的参数值用单引号包裹整个 opts 值;运行时如需临时改参,设置外部环境变量TOREE_OPTS/SPARK_OPTS优先级高于 kernel.json 内的占位值(run.sh 逻辑见第三章)。
三、内核启动机制解析
理解 kernel.json → run.sh → spark-submit 的三层链路,是定位「内核起不来」的基础。
第一层,kernel.json(Jupyter 读取的入口):
cat /usr/local/share/jupyter/kernels/apache_toree_scala/kernel.json
{
"argv": [
"/usr/local/share/jupyter/kernels/apache_toree_scala/bin/run.sh",
"--profile",
"{connection_file}"
],
"env": {
"DEFAULT_INTERPRETER": "Scala",
"__TOREE_SPARK_OPTS__": "",
"__TOREE_OPTS__": "",
"SPARK_HOME": "/usr/local/spark"
},
"display_name": "Apache Toree - Scala",
"language": "scala",
"interrupt_mode": "signal",
"metadata": {},
"kernel_protocol_version": ""
}
第二层,run.sh 的关键逻辑(节选自 /usr/local/share/jupyter/kernels/apache_toree_scala/bin/run.sh,与文件逐行一致):
PROG_HOME="$(cd "`dirname "$0"`"/..; pwd)"
if [ -z "$SPARK_HOME" ]; then
echo "SPARK_HOME must be set to the location of a Spark distribution!"
exit 1
fi
echo "Starting Spark Kernel with SPARK_HOME=$SPARK_HOME"
KERNEL_ASSEMBLY=`(cd ${PROG_HOME}/lib; ls -1 toree-assembly-*.jar;)`
# disable randomized hash for string in Python 3.3+
export PYTHONHASHSEED=0
TOREE_ASSEMBLY=${PROG_HOME}/lib/${KERNEL_ASSEMBLY}
# The SPARK_OPTS values during installation are stored in __TOREE_SPARK_OPTS__. This allows values to be specified during
# install, but also during runtime. The runtime options take precedence over the install options.
if [ "${SPARK_OPTS}" = "" ]
then
SPARK_OPTS=${__TOREE_SPARK_OPTS__}
fi
if [ "${TOREE_OPTS}" = "" ]
then
TOREE_OPTS=${__TOREE_OPTS__}
fi
eval exec \
"${SPARK_HOME}/bin/spark-submit" \
--name "'Apache Toree'" \
"${SPARK_OPTS}" \
--class org.apache.toree.Main \
"${TOREE_ASSEMBLY}" \
"${TOREE_OPTS}" \
"$@"
第三层,最终执行的等价命令(手动复现用,也是最快的报错定位法):
/usr/local/spark/bin/spark-submit \
--name 'Apache Toree' \
--class org.apache.toree.Main \
/usr/local/share/jupyter/kernels/apache_toree_scala/lib/toree-assembly-0.5.0-incubating.jar \
--profile {connection_file}
逐项说明:
SPARK_HOME缺失时 run.sh 直接退出并打印提示,这是「内核秒死」的最常见原因之一。SPARK_OPTS/TOREE_OPTS的优先级:外部环境变量 > kernel.json 内的__TOREE_SPARK_OPTS__/__TOREE_OPTS__占位值(脚本里先取空判断再回退)。- 内核以
spark-submit --class org.apache.toree.Main方式运行,因此内核进程的 JVM 版本由 Spark 的启动脚本决定——这就是 JDK 版本配对必须匹配 Spark 的根因。 --profile {connection_file}是 Jupyter 传入的连接文件(运行时替换为实际路径),内核据此建立 5 个 ZeroMQ 通道;手动复现时不传该参数内核会在启动后等待连接,用于只看启动报错时加--spark-context-initialization-mode none并限时执行。
四、内核管理命令
注册/列出/删除/覆盖四个高频操作(实时回显):
jupyter toree install --spark_home=/usr/local/spark --replace
[ToreeInstall] Creating kernel Scala
[ToreeInstall] Installed kernelspec apache_toree_scala in /usr/local/share/jupyter/kernels/apache_toree_scala
jupyter kernelspec list
Available kernels:
apache_toree_scala /usr/local/share/jupyter/kernels/apache_toree_scala
python3 /usr/local/share/jupyter/kernels/python3
jupyter kernelspec remove toree_local2_scala -y
Kernel specs to remove:
toree_local2_scala /usr/local/share/jupyter/kernels/toree_local2_scala
Removed 1 kernelspecs
查看指定内核详情:
jupyter kernelspec list --json | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['kernelspecs']['apache_toree_scala']['spec']['display_name'])"
Apache Toree - Scala
逐项说明:
--replace:不加时重复注册同名内核会报已存在错误;改--spark_home/--spark_opts/--toree_opts后必须带--replace重注册。jupyter kernelspec remove 内核名 -y:直接删除 kernelspec 目录(如上文实测的toree_local2_scala),pip 卸载 apache-toree 不会连带删除已注册内核。- 权限隔离:
--user注册到~/.local/share/jupyter/kernels/,--sys-prefix注册到sys.prefix;jupyter kernelspec list会按搜索路径全部列出,同名时用户级覆盖系统级。
五、执行与调试命令
5.1 nbconvert 批量执行(本文实测的主路径)
cd /root/toree-test && timeout 300 jupyter nbconvert --to notebook --execute \
--ExecutePreprocessor.timeout=240 first.ipynb --output first-out.ipynb
[NbConvertApp] Writing 1302 bytes to first-out.ipynb
三个关键开关:
--ExecutePreprocessor.timeout=240:单单元格超时(秒)。Toree 内核冷启动含 Spark 初始化约 40~60 秒,给足超时。--allow-errors:单元格出错时不中断整个 notebook,错误保留在输出里继续执行(本文各实测 notebook 均开启,便于收集全部行为)。--output:指定输出文件名(如上文的first-out.ipynb);不加--output会覆盖原文件。
5.2 jupyter console 交互执行
printf 'val a = 3\na * 7\nexit\n' | timeout 240 jupyter console --kernel apache_toree_scala
Jupyter console 6.6.3
Apache ToreeIn [1]:
In [1]: val a = 3
...
Do you really want to exit ([y]/n)?
Shutting down kernel
实测要点:console 能启动内核并逐行求值(回显里可见内核加载的 (Scala,...) 与 (SQL,...) 解释器清单),但非终端管道模式下结果渲染受限(实测 Out[2] 行的值未正常回显,日志出现 WARN KernelOutputStream: Suppressing empty output: '');交互验证请优先用 JupyterLab/notebook 界面或 nbconvert。
5.3 手动 spark-submit 定位法
内核秒死时,绕开 Jupyter 直接复现启动报错(实测 JDK 17 下的输出):
timeout 60 /usr/local/spark/bin/spark-submit --class org.apache.toree.Main \
/usr/local/share/jupyter/kernels/apache_toree_scala/lib/toree-assembly-0.5.0-incubating.jar \
--spark-context-initialization-mode none 2>&1 | grep -m2 -E 'Exception|Caused'
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make private java.nio.DirectByteBuffer(long,int) accessible: module java.base does not "opens java.nio" to unnamed module @4f51ab48
验证点:Jupyter 侧只报 Kernel died before replying to kernel_info,而此法直接给出根因(JDK 17 不被 Spark 3.0 支持)。版本配对结论与两种失败模式详见第 1 篇第三章。
六、magic 详解
Toree 的 magic 分行 magic(%Name,作用于声明)与单元格 magic(%%Name,作用于整格)。以下全部为 0.5.0 内核实测行为,按「实测可用 / 实测异常 / 未覆盖」三档归类。
6.1 实测可用 magic
%AddJar:行 magic,向运行时 classpath 添加 jar,支持远程 URL 与 file:// 本地路径。实测(Maven Central 直链):
[stream] Starting download from https://repo1.maven.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
[stream] Finished download of commons-math3-3.6.1.jar
同一 URL 第二次执行输出 Using cached version of commons-math3-3.6.1.jar(内置缓存)。import 必须写在 AddJar 之后的单元格。
%AddDeps:行 magic,按 Maven/Ivy 坐标解析并加载依赖。实测 org.apache.commons:commons-math3:3.6.1:
[stream] Marking org.apache.commons:commons-math3:3.6.1 for download
[stream] Obtained 2 files
随后单元格 import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics 并计算,实测输出 mean=4.0(数据 {2.0,4.0,6.0})。要点:坐标按 groupId artifactId version 空格分隔;实测输出流中 Marking.../Obtained... 各出现两次,属 0.5.0 的重复日志行为,不影响加载结果。
%ShowTypes:行 magic,开关式(on/off),开启后求值结果带类型标注。实测元组:
[stream] Types will be printed.
[display_data] {'text/plain': 't: (Int, String) = (1,a)'}
[execute_result] {'text/plain': '(Int, String) = (1,a)'}
%%SQL:单元格 magic,整格按 SQL 解释执行,execute_result 直接返回结果表(实测 Spark 3.0.3):
[execute_result] {'text/plain': '+---+-----+\n|two| name|\n+---+-----+\n| 2|toree|\n+---+-----+'}
%%HTML:单元格 magic,整格按 HTML 输出(实测返回 text/html MIME):
[execute_result] {'text/html': '<h1>Toree HTML Magic</h1>'}
6.2 实测异常 magic(0.5.0 如实披露)
%Truncate 不存在。实测 %Truncate on 直接报错(尽管 0.3.0 的 RELEASE_NOTES 提及过 %Truncate 修复):
[error] Error parsing magics! : Magics [Truncate] do not exist!
%ShowOutput off 不生效。实测 %ShowOutput off 后,虽回显确认声明 Console output will NOT be shown.,但同单元格内 println 的输出仍然照常出现在 stream 中:
[stream] Console output will NOT be shown.
Console output will NOT be shown.
this line should be hidden
结论:0.5.0 上不要依赖 %Truncate 与 %ShowOutput 控制输出,长结果截断在 notebook 前端自行处理,隐藏输出用单元格注释或不调用 println。
未知 magic 的报错形态:任何未注册的 magic 统一返回 error 输出 Error parsing magics! : Magics [<名字>] do not exist!,内核不崩溃、后续单元格正常执行(实测 %FooBar)。
6.3 未覆盖项
官方文档体系还提及 %AddRsrc、%JavaScript 等资源/脚本类 magic,本文未实测,行为以官方 Magics 文档为准;--interpreters=SQL 注册的 SQL 专属内核在实测中执行挂起(InterpreterActor 的 AskTimeoutException),建议生产统一使用 Scala 内核 + %%SQL 组合(详见第 5 篇)。
💡 技术拓展:Jupyter 内核生态里 magic 能力的三档实现对比——
内核 magic 体系 代表能力 IPython(Python) 最丰富 %%timeit、%%bash、%%capture 等数十个 Apache Toree(Scala/SQL) 精简 依赖加载(%AddJar/%AddDeps)、输出控制(%ShowTypes)、SQL/HTML 单元格 sparkmagic(经 Livy) 面向远程 %%spark 会话管理、SQL 取数转 pandas 本文实测采用 Apache Toree:其 magic 覆盖了「带依赖跑 Spark + SQL 探索」的最小闭环,与 IPython 生态互补。
七、常见问题(FAQ)
Q1:jupyter toree install 报 Unknown interpreter PySpark. Skipping installation?
合法值只有 Scala 与 SQL(区分大小写)。PySpark/R 内置解释器自 0.3.0 移除(官方 RELEASE_NOTES 明示),需要 PySpark 时另用 pyspark 内核方案。
Q2:改了 --spark_opts 重注册但没生效?
检查三点:重注册是否带 --replace;notebook 元数据里的 kernelspec.name 是否指向新内核名;是否有同名外部环境变量 SPARK_OPTS 覆盖了 kernel.json 的占位值(run.sh 的优先级逻辑见第三章)。
Q3:怎么确认内核实际使用的 Spark 版本与 master?
在单元格执行 println(spark.version + " @ " + sc.master)。实测 --spark_opts='--master=local[2]' 注册的内核输出 master=local[2]、appName=Apache Toree。
Q4:%AddDeps 能否加载带传递依赖的库?
可以,坐标解析含传递依赖(实测 commons-math3 这种零依赖库取回 jar+pom 共 2 个文件)。公司内网可指向自建 Nexus(%AddDeps 支持自定义 resolver,官方 0.3.0 引入);下载地址固定时用 %AddJar 更直接。
Q5:单元格超时被杀但不知道卡在哪?
分两步:nbconvert 侧看 --ExecutePreprocessor.timeout 到期信息;然后手动 spark-submit 复现(5.3 节)确认是内核启动问题还是代码问题。SQL 类单元格挂起优先怀疑 SQL 专属内核(见 6.3 节),换 Scala 内核 + %%SQL 重试。
八、参考链接
- 官方 Installation(jupyter toree install 参数原文):https://toree.apache.org/docs/current/user/installation
- 官方 Magics Overview:https://toree.apache.org/docs/current/user/magics
- 官方 RELEASE_NOTES(各版本 magic 演进):https://github.com/apache/incubator-toree/blob/master/RELEASE_NOTES.md
- Apache Toree 安装(本文配套第 1 篇):/docs/Apache Toree/Ubuntu 22.04 安装 Apache Toree 指南.md
- 快速入门与集成示例(本文配套第 3 篇):/docs/Apache Toree/Apache Toree 0.5.0-incubating 快速入门与集成示例.md