详解 Daytona 的代码执行
在上一篇文章中,我们学习了 Daytona 的核心特性和基本用法,然后通过 Python SDK 创建沙箱并执行代码。回顾下之前学过的内容,可以将 Daytona 执行代码分成四步:
- 初始化 Daytona 客户端;
- 通过客户端创建沙箱,指定沙箱环境,Daytona 支持执行 Python 和 JS 代码;
- 在沙箱执行代码,返回执行结果;
- 销毁沙箱;
其中第三步执行代码是 Daytona 最核心的功能,我们今天继续深入学习它。
执行代码和运行命令
Daytona 的 Sandbox
类含有不少属性,如下:
其中 process
属性是我们关注的焦点,通过它的 code_run()
方法可以执行代码:
response = sandbox.process.code_run('print("Hello")')
这个方法支持传入一些额外参数,比如环境变量或命令行参数等:
params = CodeRunParams(
argv=[
"-h", "-i"
],
env={
'DEMO_ENV': 'xxx'
}
)
response = sandbox.process.code_run('''
import os
import sys
print("环境变量:DEMO_ENV = ", os.getenv("DEMO_ENV"))
print("命令行参数:", *[f"{arg}" for _, arg in enumerate(sys.argv)])
''', params=params)
在代码中可以通过 os.getenv()
或 sys.argv
来获取,调用结果如下:
环境变量:DEMO_ENV = xxx
命令行参数: -c -h -i
这里的命令行参数比较有意思,可以看到我们只传入了 -h -i
两个参数,但是输出却多了一个 -c
,这说明 Daytona 很有可能是通过 python -c
去执行我们的 Python 代码的:
python -c "print('Hello')"
process
属性还有一个 exec()
方法,可以运行 Shell 命令:
response = sandbox.process.exec('echo "Hello"')
这个方法同样支持传入一些额外参数,比如设置当前工作目录:
response = sandbox.process.exec('ls', cwd="/")
设置环境变量:
response = sandbox.process.exec('echo $DEMO_ENV', env={"DEMO_ENV": "xxx"})
运行长耗时任务
对于一些长耗时任务,为避免超时,可以在运行时加上 timeout
参数:
response = sandbox.process.exec("sleep 5", timeout=10)
除此之外,Daytona 还提供了一种推荐做法:通过后台会话运行长耗时任务。
sandbox.process
属性提供了一系列和会话相关的方法:
create_session()
- 创建会话get_session()
- 获取会话list_sessions()
列出所有会话delete_session()
- 删除会话execute_session_command()
- 在会话中执行命令get_session_command()
- 获取会话中的命令get_session_command_logs()
- 获取会话中的命令日志get_session_command_logs_async()
- 异步获取会话中的命令日志
我们首先选择一个唯一标志作为会话 ID,然后通过它创建一个会话:
session_id = "my-session"
sandbox.process.create_session(session_id)
接着就可以使用 execute_session_command()
在会话中执行命令,要执行的命令需要放在一个 SessionExecuteRequest
对象中:
req = SessionExecuteRequest(
run_async=True,
command="for ((i=0; i<10; i++)); do echo $i; sleep 1; done"
)
response = sandbox.process.execute_session_command(session_id, req)
注意这里的 run_async=True
表示异步执行,调用完 execute_session_command()
方法后会立即返回,不会阻塞后面的代码。返回的 response
有一个 cmd_id
字段,通过 get_session_command()
方法可以检查命令的实时状态以及命令输出的日志。下面的代码示例中每隔 1 秒检查一次命令状态:
while(True):
time.sleep(1)
command = sandbox.process.get_session_command(session_id, response.cmd_id)
if command.exit_code is None:
print(f"Command is running ...")
continue
if command.exit_code == 0:
print(f"Command {command.command} completed successfully")
else:
print(f"Command {command.command} failed")
logs = sandbox.process.get_session_command_logs(session_id, response.cmd_id)
print(f"{logs}")
break
当命令处于运行中状态时,exit_code
一直等于 None
,当 exit_code
为 0 时表示命令运行成功,否则运行失败,再通过 get_session_command_logs()
方法获取命令的完整日志。
有时候我们不想等命令运行结束才打印日志,而是希望实时地输出日志,这对调试和监控都非常有意义。这时我们可以使用 get_session_command_logs_async()
方法:
await sandbox.process.get_session_command_logs_async(
session_id,
response.cmd_id,
lambda chunk: print(f"Log chunk: {chunk}")
)
该方法接受一个 callback 函数,当有日志输出时就回调该函数。
小结
在本文中,我们深入探讨了 Daytona 的代码执行功能,特别是如何在沙箱中有效地执行代码和长耗时任务。我们学习了 Daytona 的 process
属性以及其便捷的 code_run()
和 exec()
方法,这些方法支持环境变量、命令行参数等设置,让我们能够灵活地执行 Python 和 Shell 代码。
此外,对于长耗时任务,我们介绍了如何通过后台会话进行处理,实现非阻塞的命令执行。我们可以创建会话、执行异步命令,并实时获取日志输出,这为任务的监控和调试提供了极大的便利。通过 get_session_command_logs_async()
方法,我们甚至能够实现实时日志的流式处理,进一步增强了我们对代码执行过程的可控性。
以上就是今天的全部内容了,基本上涉及到了和 Daytona 代码执行相关的各个细节,不过还有一点非常重要,那就是代码执行的环境,我们下一篇继续学习。