执行长期异步任务
在 linux 沙箱中使用 systemd 运行长期后台任务
本篇文档适用于linux沙箱。如果你使用的是 Windows 沙箱,请参考 在 Windows 沙箱中运行长期任务。
在 Lybic 沙箱中,你可能需要运行一些长期执行的任务,如模型训练、数据处理、Web 服务器等。由于 execSandboxProcess API 有超时限制且不支持后台运行,我们推荐使用 systemd --user run 来执行这类任务。
提示:对于快速执行的短命令(5 秒内完成),请使用 execSandboxProcess API。
工作原理
systemd --user run 创建一个临时的 systemd 服务单元(transient unit),该服务在任务完成后自动清理。任务在 systemd 的管理下运行,与 API 调用解耦。
使用方法
步骤概览
- 准备脚本或程序:将要运行的脚本/程序传输到沙箱
- 启动任务:使用
systemd --user run启动长期任务 - 查看状态:查询任务运行状态和日志
步骤 1:准备脚本或程序
有两种方式准备要运行的脚本或程序:
方式 A:通过文件拷贝
使用 文件传输 API 将脚本上传到沙箱:
# 首先,将脚本上传到对象存储(获取下载 URL)
# 然后,从 URL 下载到沙箱
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"files": [
{
"id": "upload-training-script",
"src": {
"type": "httpGetLocation",
"url": "https://storage.example.com/scripts/train.py"
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/scripts/train.py"
}
}
]
}'import asyncio
from lybic import LybicClient, LybicAuth
from lybic.dto import (
SandboxFileCopyRequestDto,
FileCopyItem,
SandboxFileLocation,
HttpGetLocation
)
async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# 上传脚本到沙箱
await client.sandbox.copy_files(
"SBX-xxxx",
SandboxFileCopyRequestDto(files=[
FileCopyItem(
id="upload-training-script",
src=HttpGetLocation(
url="https://storage.example.com/scripts/train.py"
),
dest=SandboxFileLocation(
path="/home/agent/scripts/train.py"
)
)
])
)
print("脚本上传成功")
if __name__ == "__main__":
asyncio.run(main())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})
// 上传脚本到沙箱
await lybic.copyFilesWithSandbox('SBX-xxxx', {
files: [
{
id: 'upload-training-script',
src: {
type: 'httpGetLocation',
url: 'https://storage.example.com/scripts/train.py',
},
dest: {
type: 'sandboxFileLocation',
path: '/home/agent/scripts/train.py',
},
},
],
})
console.log('脚本上传成功')package main
import (
"context"
"fmt"
"github.com/lybic/lybic-sdk-go"
)
func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)
copyDto := lybic.SandboxFileCopyRequestDto{
Files: []lybic.SandboxFileCopyRequestDtoFiles{
{
Id: "upload-training-script",
Src: map[string]string{
"type": "httpGetLocation",
"url": "https://storage.example.com/scripts/train.py",
},
Dest: map[string]any{
"type": "sandboxFileLocation",
"path": "/home/agent/scripts/train.py",
},
},
},
}
_, err := client.CopyFilesWithSandbox(ctx, "SBX-xxxx", copyDto)
if err != nil {
fmt.Println("上传脚本出错:", err)
return
}
fmt.Println("脚本上传成功")
}方式 B:通过 stdin 写入
使用 execSandboxProcess 将脚本内容通过管道写入文件:
# Base64 编码脚本内容
SCRIPT_CONTENT=$(cat << 'EOF' | base64 -w0
#!/usr/bin/env python3
import time
import sys
print("开始训练模型...")
for i in range(100):
time.sleep(1)
print(f"进度: {i+1}/100")
sys.stdout.flush()
print("训练完成!")
EOF
)
# 写入文件
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "bash",
"args": ["-c", "cat > /home/agent/scripts/train.py && chmod +x /home/agent/scripts/train.py"],
"stdinBase64": "'$SCRIPT_CONTENT'"
}'import asyncio
import base64
from lybic import LybicClient, LybicAuth
async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# 准备脚本内容
script_content = """#!/usr/bin/env python3
import time
import sys
print("开始训练模型...")
for i in range(100):
time.sleep(1)
print(f"进度: {i+1}/100")
sys.stdout.flush()
print("训练完成!")
"""
# Base64 编码
stdin_data = base64.b64encode(script_content.encode()).decode()
# 写入文件并添加执行权限
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="bash",
args=["-c", "cat > /home/agent/scripts/train.py && chmod +x /home/agent/scripts/train.py"],
stdinBase64=stdin_data
)
if result.exitCode == 0:
print("脚本创建成功")
else:
print(f"创建失败: {base64.b64decode(result.stderrBase64 or '').decode()}")
if __name__ == "__main__":
asyncio.run(main())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})
// 准备脚本内容
const scriptContent = `#!/usr/bin/env python3
import time
import sys
print("开始训练模型...")
for i in range(100):
time.sleep(1)
print(f"进度: {i+1}/100")
sys.stdout.flush()
print("训练完成!")
`
// Base64 编码
const stdinBase64 = btoa(scriptContent)
// 写入文件并添加执行权限
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'bash',
args: ['-c', 'cat > /home/agent/scripts/train.py && chmod +x /home/agent/scripts/train.py'],
stdinBase64: stdinBase64,
})
if (result.data?.exitCode === 0) {
console.log('脚本创建成功')
} else {
console.log('创建失败:', atob(result.data?.stderrBase64 || ''))
}package main
import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)
func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)
// 准备脚本内容
scriptContent := `#!/usr/bin/env python3
import time
import sys
print("开始训练模型...")
for i in range(100):
time.sleep(1)
print(f"进度: {i+1}/100")
sys.stdout.flush()
print("训练完成!")
`
// Base64 编码
stdinBase64 := base64.StdEncoding.EncodeToString([]byte(scriptContent))
// 写入文件并添加执行权限
cmd := "cat > /home/agent/scripts/train.py && chmod +x /home/agent/scripts/train.py"
processDto := lybic.SandboxProcessRequestDto{
Executable: "bash",
Args: []string{"-c", cmd},
StdinBase64: &stdinBase64,
}
result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
if result.ExitCode == 0 {
fmt.Println("脚本创建成功")
} else {
stderr, _ := base64.StdEncoding.DecodeString(result.StderrBase64)
fmt.Printf("创建失败: %s\n", string(stderr))
}
}步骤 2:使用 systemd --user run 启动任务
使用 systemd-run --user 启动长期任务。该命令会立即返回,任务在后台继续运行。
# 启动长期任务
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "systemd-run",
"args": [
"--user",
"--unit=my-training-task",
"--description=Model Training Task",
"python3",
"/home/agent/scripts/train.py"
]
}'import asyncio
import base64
from lybic import LybicClient, LybicAuth
async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# 启动 systemd 任务
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="systemd-run",
args=[
"--user",
"--unit=my-training-task",
"--description=Model Training Task",
"python3",
"/home/agent/scripts/train.py"
]
)
output = base64.b64decode(result.stdoutBase64 or '').decode()
print(f"任务已启动: {output}")
print(f"退出代码: {result.exitCode}")
if __name__ == "__main__":
asyncio.run(main())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})
// 启动 systemd 任务
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'systemd-run',
args: [
'--user',
'--unit=my-training-task',
'--description=Model Training Task',
'python3',
'/home/agent/scripts/train.py',
],
})
const output = atob(result.data?.stdoutBase64 || '')
console.log('任务已启动:', output)
console.log('退出代码:', result.data?.exitCode)package main
import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)
func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)
// 启动 systemd 任务
processDto := lybic.SandboxProcessRequestDto{
Executable: "systemd-run",
Args: []string{
"--user",
"--unit=my-training-task",
"--description=Model Training Task",
"python3",
"/home/agent/scripts/train.py",
},
}
result, err := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
if err != nil {
fmt.Println("启动任务出错:", err)
return
}
stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
fmt.Printf("任务已启动: %s\n", string(stdout))
fmt.Printf("退出代码: %d\n", result.ExitCode)
}重要参数说明:
--user:在用户模式下运行(必需)--unit=<名称>:指定单元名称,用于后续查询状态和日志--description=<描述>:任务描述(可选)- 后续参数是要执行的实际命令
步骤 3:查看任务状态
3.1 查看任务运行状态
使用 systemctl --user status 查看任务运行状态:
# 查看任务状态
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "systemctl",
"args": ["--user", "status", "my-training-task"]
}'import asyncio
import base64
from lybic import LybicClient, LybicAuth
async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# 查看任务状态
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="systemctl",
args=["--user", "status", "my-training-task"]
)
status = base64.b64decode(result.stdoutBase64 or '').decode()
print("任务状态:")
print(status)
if __name__ == "__main__":
asyncio.run(main())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})
// 查看任务状态
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'systemctl',
args: ['--user', 'status', 'my-training-task'],
})
const status = atob(result.data?.stdoutBase64 || '')
console.log('任务状态:')
console.log(status)package main
import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)
func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)
// 查看任务状态
processDto := lybic.SandboxProcessRequestDto{
Executable: "systemctl",
Args: []string{"--user", "status", "my-training-task"},
}
result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
fmt.Println("任务状态:")
fmt.Println(string(stdout))
}状态输出示例:
● my-training-task.service - Model Training Task
Loaded: loaded (/run/user/1000/systemd/transient/my-training-task.service; transient)
Transient: yes
Active: active (running) since Mon 2024-01-15 10:30:00 UTC; 5min ago
Main PID: 12345 (python3)
Tasks: 1 (limit: 4915)
Memory: 45.2M
CPU: 2.5s
CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/my-training-task.service
└─12345 python3 /home/agent/scripts/train.py3.2 查看任务日志
使用 journalctl --user 查看任务的输出日志:
# 查看任务日志(最新 100 行)
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "journalctl",
"args": [
"--user",
"--unit=my-training-task",
"-n", "100",
"--no-pager"
]
}'import asyncio
import base64
from lybic import LybicClient, LybicAuth
async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# 查看任务日志
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="journalctl",
args=[
"--user",
"--unit=my-training-task",
"-n", "100",
"--no-pager"
]
)
logs = base64.b64decode(result.stdoutBase64 or '').decode()
print("任务日志:")
print(logs)
if __name__ == "__main__":
asyncio.run(main())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})
// 查看任务日志
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'journalctl',
args: [
'--user',
'--unit=my-training-task',
'-n', '100',
'--no-pager',
],
})
const logs = atob(result.data?.stdoutBase64 || '')
console.log('任务日志:')
console.log(logs)package main
import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)
func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)
// 查看任务日志
processDto := lybic.SandboxProcessRequestDto{
Executable: "journalctl",
Args: []string{
"--user",
"--unit=my-training-task",
"-n", "100",
"--no-pager",
},
}
result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
fmt.Println("任务日志:")
fmt.Println(string(stdout))
}日志输出示例:
Jan 28 06:20:37 agent-machine systemd[877]: Started my-training-task.service - Model Training Task.
Jan 28 06:20:38 agent-machine python3[17083]: 开始训练模型...
Jan 28 06:20:38 agent-machine python3[17083]: 进度: 1/100
Jan 28 06:20:39 agent-machine python3[17083]: 进度: 2/100
Jan 28 06:20:40 agent-machine python3[17083]: 进度: 3/100
Jan 28 06:20:41 agent-machine python3[17083]: 进度: 4/100
Jan 28 06:20:42 agent-machine python3[17083]: 进度: 5/100
...常用 journalctl 参数:
-n <行数>:显示最新的 N 行日志-f:实时跟踪日志(不推荐,因为 API 有超时限制)--no-pager:直接输出,不使用分页器(必需)--since "5 minutes ago":显示最近 5 分钟的日志-o json:以 JSON 格式输出
3.3 检查任务是否仍在运行
使用 systemctl --user is-active 快速检查任务状态:
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "systemctl",
"args": ["--user", "is-active", "my-training-task"]
}'import asyncio
import base64
from lybic import LybicClient, LybicAuth
async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="systemctl",
args=["--user", "is-active", "my-training-task"]
)
status = base64.b64decode(result.stdoutBase64 or '').decode().strip()
if status == "active":
print("✓ 任务正在运行")
elif status == "inactive":
print("✗ 任务已停止")
else:
print(f"任务状态: {status}")
if __name__ == "__main__":
asyncio.run(main())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'systemctl',
args: ['--user', 'is-active', 'my-training-task'],
})
const status = atob(result.data?.stdoutBase64 || '').trim()
if (status === 'active') {
console.log('✓ 任务正在运行')
} else if (status === 'inactive') {
console.log('✗ 任务已停止')
} else {
console.log('任务状态:', status)
}package main
import (
"context"
"encoding/base64"
"fmt"
"strings"
"github.com/lybic/lybic-sdk-go"
)
func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)
processDto := lybic.SandboxProcessRequestDto{
Executable: "systemctl",
Args: []string{"--user", "is-active", "my-training-task"},
}
result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
status := strings.TrimSpace(string(stdout))
if status == "active" {
fmt.Println("✓ 任务正在运行")
} else if status == "inactive" {
fmt.Println("✗ 任务已停止")
} else {
fmt.Printf("任务状态: %s\n", status)
}
}返回值:
active:任务正在运行inactive:任务已停止failed:任务失败
完整示例:运行 Web 服务器
以下是一个完整的示例,展示如何在沙箱中运行一个长期的 Python Web 服务器:
import asyncio
import base64
from lybic import LybicClient, LybicAuth
async def run_web_server():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
sandbox_id = "SBX-xxxx"
# 1. 创建 Web 服务器脚本
server_code = """#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
response = {'status': 'ok', 'message': 'Hello from Lybic!'}
self.wfile.write(json.dumps(response).encode())
def log_message(self, format, *args):
print(f"{self.address_string()} - {format % args}")
if __name__ == '__main__':
server = HTTPServer(('0.0.0.0', 8080), Handler)
print("服务器启动在 http://0.0.0.0:8080")
server.serve_forever()
"""
stdin_data = base64.b64encode(server_code.encode()).decode()
result = await client.sandbox.execute_process(
sandbox_id,
executable="bash",
args=["-c", "mkdir -p /home/agent/server && cat > /home/agent/server/app.py && chmod +x /home/agent/server/app.py"],
stdinBase64=stdin_data
)
if result.exitCode != 0:
print("创建服务器脚本失败")
return
print("✓ 服务器脚本创建成功")
# 2. 使用 systemd 启动服务器
result = await client.sandbox.execute_process(
sandbox_id,
executable="systemd-run",
args=[
"--user",
"--unit=web-server",
"--description=Python Web Server",
"python3",
"/home/agent/server/app.py"
]
)
output = base64.b64decode(result.stdoutBase64 or '').decode()
print(f"✓ Web 服务器已启动: {output}")
# 3. 等待几秒让服务器启动
await asyncio.sleep(3)
# 4. 检查服务器状态
result = await client.sandbox.execute_process(
sandbox_id,
executable="systemctl",
args=["--user", "is-active", "web-server"]
)
status = base64.b64decode(result.stdoutBase64 or '').decode().strip()
print(f"✓ 服务器状态: {status}")
# 5. 查看日志
result = await client.sandbox.execute_process(
sandbox_id,
executable="journalctl",
args=["--user", "--unit=web-server", "-n", "10", "--no-pager"]
)
logs = base64.b64decode(result.stdoutBase64 or '').decode()
print("✓ 服务器日志:")
print(logs)
# 6. 测试服务器
result = await client.sandbox.execute_process(
sandbox_id,
executable="curl",
args=["http://localhost:8080"]
)
response = base64.b64decode(result.stdoutBase64 or '').decode()
print(f"✓ 服务器响应: {response}")
if __name__ == "__main__":
asyncio.run(run_web_server())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})
async function runWebServer() {
const sandboxId = 'SBX-xxxx'
// 1. 创建 Web 服务器脚本
const serverCode = `#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
response = {'status': 'ok', 'message': 'Hello from Lybic!'}
self.wfile.write(json.dumps(response).encode())
def log_message(self, format, *args):
print(f"{self.address_string()} - {format % args}")
if __name__ == '__main__':
server = HTTPServer(('0.0.0.0', 8080), Handler)
print("服务器启动在 http://0.0.0.0:8080")
server.serve_forever()
`
const stdinBase64 = btoa(serverCode)
let result = await lybic.execSandboxProcess(sandboxId, {
executable: 'bash',
args: ['-c', 'mkdir -p /home/agent/server && cat > /home/agent/server/app.py && chmod +x /home/agent/server/app.py'],
stdinBase64: stdinBase64,
})
if (result.data?.exitCode !== 0) {
console.log('创建服务器脚本失败')
return
}
console.log('✓ 服务器脚本创建成功')
// 2. 使用 systemd 启动服务器
result = await lybic.execSandboxProcess(sandboxId, {
executable: 'systemd-run',
args: [
'--user',
'--unit=web-server',
'--description=Python Web Server',
'python3',
'/home/agent/server/app.py',
],
})
const output = atob(result.data?.stdoutBase64 || '')
console.log('✓ Web 服务器已启动:', output)
// 3. 等待几秒让服务器启动
await new Promise(resolve => setTimeout(resolve, 3000))
// 4. 检查服务器状态
result = await lybic.execSandboxProcess(sandboxId, {
executable: 'systemctl',
args: ['--user', 'is-active', 'web-server'],
})
const status = atob(result.data?.stdoutBase64 || '').trim()
console.log('✓ 服务器状态:', status)
// 5. 查看日志
result = await lybic.execSandboxProcess(sandboxId, {
executable: 'journalctl',
args: ['--user', '--unit=web-server', '-n', '10', '--no-pager'],
})
const logs = atob(result.data?.stdoutBase64 || '')
console.log('✓ 服务器日志:')
console.log(logs)
// 6. 测试服务器
result = await lybic.execSandboxProcess(sandboxId, {
executable: 'curl',
args: ['http://localhost:8080'],
})
const response = atob(result.data?.stdoutBase64 || '')
console.log('✓ 服务器响应:', response)
}
runWebServer()高级用法
设置资源限制
可以为任务设置 CPU 和内存限制:
systemd-run --user \
--unit=resource-limited-task \
--property=CPUQuota=50% \
--property=MemoryMax=512M \
python3 /home/agent/scripts/train.py设置环境变量
systemd-run --user \
--unit=task-with-env \
--setenv=API_KEY=your-key \
--setenv=DEBUG=true \
python3 /home/agent/scripts/app.py设置工作目录
systemd-run --user \
--unit=task-with-workdir \
--working-directory=/home/agent/project \
python3 train.py停止任务
如果需要停止正在运行的任务:
systemctl --user stop my-training-task列出所有用户任务
systemctl --user list-units --type=service最佳实践
- 使用有意义的单元名称:使用描述性的
--unit名称,便于后续管理 - 添加任务描述:使用
--description参数添加任务说明 - 定期检查日志:使用
journalctl监控任务输出,及时发现问题 - 资源限制:为长期任务设置合理的资源限制,避免影响其他任务
- 错误处理:在脚本中添加错误处理和日志记录
- 清理任务:完成后及时停止不需要的任务,释放资源
常见问题
Q: 任务启动后如何知道它何时完成?
定期使用 systemctl --user is-active 检查任务状态。当返回 inactive 时,任务已完成。
Q: 如何查看任务的退出代码?
使用 systemctl --user show my-training-task 查看详细信息,包括 ExecMainStatus 字段。
Q: 沙箱重启后任务还会运行吗?
使用 systemd-run 创建的临时单元在沙箱重启后不会自动启动。如需持久化,需要创建永久的 systemd 服务文件。
Q: 可以同时运行多个任务吗?
可以,只需为每个任务使用不同的 --unit 名称即可。
Q: 如何获取实时日志?
由于 API 超时限制,不建议使用 journalctl -f。建议定期调用 API 获取最新日志。
相关文档
- 运行命令和执行代码 - 短期命令执行
- 文件传输 - 上传脚本和下载结果
- Python SDK 使用指南
- TypeScript SDK 使用指南