Appearance
通过 TCP 端口访问容器内服务
本文档将指导你如何在 Mythra Agent Studio 沙箱中运行 HTTP 或 WebSocket 服务,并通过 TCP 端口直接访问容器内的服务。
概述
在 Mythra Agent Studio 平台上,你可以在沙箱的 Docker 容器中运行自己的 HTTP 服务。由于平台是多租户架构,所有用户的 HTTP 服务都共享同一个域名(https://agent.wiseverds.com),平台通过 URL 路径来进行路由转发,将请求正确地发送到你的服务。
两种访问方式的选择:
- TCP 端口 (本文档):通过容器名 + 端口号访问,适用于大多数现成的 Docker 镜像和服务(如数据库、缓存、Web 框架等),无需修改应用代码。
- UNIX Domain Socket (另见文档):需要你的应用直接监听 Unix socket 文件,适用于新建服务或愿意改造服务的场景;平台通过 socket 文件路径进行路由,安全高效。
平台的路由机制
当你启动一个容器并在其中运行 HTTP/WebSocket 服务后,外部客户端可以通过以下 URL 访问:
https://agent.wiseverds.com/WebIDE/api/v1/projects/{ProjectID}/sandboxes/{SandboxID}/tcp-socket/{ServiceAddress}/http/{RequestPath}其中:
ProjectID:你的项目 IDSandboxID:你的沙箱 IDServiceAddress:容器名:端口号(例如mythra-svc-12345678-projabcd-sandbox1-AbCdEfGh:8000)RequestPath:你的 HTTP 服务的接口路径(例如/api/users)
平台会将这个 URL 的请求经过身份验证和容器归属校验后,通过 TCP 连接转发到沙箱容器中的指定端口。
容器命名规则
当前平台创建的容器命名格式为:
mythra-{purpose}-{userId[:8]}-{projectId[:8]}-{sandboxId[:8]}-{nonce}例如:mythra-svc-12345678-projabcd-sandbox1-AbCdEfGh
你可以通过沙箱管理界面或 API 获取你当前沙箱中运行的容器名称。
两种方式对比
| 特性 | TCP 端口 | UNIX Domain Socket |
|---|---|---|
| URL 路径 | .../tcp-socket/{容器名}:{端口}/http/... | .../unix-socket/{Base64路径}/http/... |
| 寻址方式 | 容器名 + 端口号 | Socket 文件路径(Base64 编码) |
| 适用场景 | 现成软件、数据库、缓存、Web 框架 | 新建服务、需要 socket 权限控制的场景 |
| 代码改造 | 不需要 | 需要监听 Unix socket |
| TCP 端口管理 | 需要监听指定端口 | 不需要 |
| Socket 文件管理 | 不需要 | 需要创建、清理、设置权限 |
| 性能 | 略低(需经网络栈) | 更高(直接内核通信) |
| 跨容器访问 | 支持(同一项目内) | 支持(同一沙箱内) |
快速开始
让我们从一个最简单的例子开始:创建一个 "Hello World" HTTP 服务。
Node.js 示例
创建一个名为 server.js 的文件:
javascript
const http = require('http');
const PORT = 3000;
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from TCP service!\n');
});
// 正常监听 TCP 端口
server.listen(PORT, '0.0.0.0', () => {
console.log(`HTTP server listening on port ${PORT}`);
});Python 示例
创建一个名为 server.py 的文件:
python
from http.server import HTTPServer, BaseHTTPRequestHandler
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
self.wfile.write(b'Hello from TCP service!\n')
PORT = 8000
server = HTTPServer(('0.0.0.0', PORT), Handler)
print(f'HTTP server listening on port {PORT}')
server.serve_forever()启动服务
在容器中运行你的服务(在容器启动时由 command 和 args 参数指定):
bash
# Node.js
node server.js
# Python
python3 server.py服务启动后,你的 HTTP 服务就在容器内的 TCP 端口上监听了。
访问你的 HTTP 服务
获取容器名称
首先,你需要知道容器的名称。可以通过沙箱管理界面查看,或通过 API 获取容器列表。
假设容器的名称为:
mythra-svc-abcd1234-projabcd-sandbox1-XyZ12345服务监听在容器的 3000 端口。
构造访问 URL
假设:
- 项目 ID:
2231 - 沙箱 ID:
j4urFhRG - 容器名:
mythra-svc-abcd1234-projabcd-sandbox1-XyZ12345 - 端口:
3000 - 接口路径:
/user/login
完整的 URL 为:
https://agent.wiseverds.com/WebIDE/api/v1/projects/2231/sandboxes/j4urFhRG/tcp-socket/mythra-svc-abcd1234-projabcd-sandbox1-XyZ12345:3000/http/user/login提示:URL 中的
:可以使用 URL 编码%3A替代,两种写法均支持:.../tcp-socket/mythra-svc-abcd1234-projabcd-sandbox1-XyZ12345%3A3000/http/...
为了保证接口安全,平台要求在请求头中包含有效的 API 密钥。请确保在 HTTP 请求的 Authorization 头中添加正确的 API 密钥,使用 Bearer 前缀。
使用 curl 测试
bash
#!/bin/bash
WDS_BASE_URL="https://agent.wiseverds.com"
WDS_API_KEY="WDS_**************"
ProjectID=2231
SandboxID=j4urFhRG
ContainerName="mythra-svc-abcd1234-projabcd-sandbox1-XyZ12345"
Port=3000
ServiceAddress="${ContainerName}:${Port}"
curl -i \
-H "Authorization: Bearer $WDS_API_KEY" \
"$WDS_BASE_URL/WebIDE/api/v1/projects/$ProjectID/sandboxes/$SandboxID/tcp-socket/$ServiceAddress/http/"如果一切正常,你会看到:
HTTP/1.1 200 OK
Content-Type: text/plain
Hello from TCP service!使用 JavaScript (fetch) 测试
javascript
const baseUrl = 'https://agent.wiseverds.com';
const apiKey = 'WDS_**************';
const projectId = 2231;
const sandboxId = 'j4urFhRG';
const containerName = 'mythra-svc-abcd1234-projabcd-sandbox1-XyZ12345';
const port = 3000;
const serviceAddress = `${containerName}:${port}`;
const url = `${baseUrl}/WebIDE/api/v1/projects/${projectId}/sandboxes/${sandboxId}/tcp-socket/${serviceAddress}/http/api/users`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
const data = await response.json();
console.log(data);进阶用法
使用 Express 框架(Node.js)
使用 Express 框架的方式与标准 TCP 监听完全一致:
javascript
const express = require('express');
const app = express();
app.get('/api/users', (req, res) => {
res.json({ users: ['Alice', 'Bob'] });
});
app.post('/api/users', (req, res) => {
res.status(201).json({ message: 'User created' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, '0.0.0.0', () => {
console.log(`Express server listening on port ${PORT}`);
});使用 Flask 框架(Python)
python
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/users', methods=['GET'])
def get_users():
return jsonify({'users': ['Alice', 'Bob']})
@app.route('/api/users', methods=['POST'])
def create_user():
return jsonify({'message': 'User created'}), 201
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=False)处理路径和查询参数
平台在转发请求时,会自动进行 URL rewrite,将请求路径中的 .../http/ 前缀去掉,仅保留自定义接口路径。
因此,你的 HTTP 服务可以按照正常逻辑处理接口路径和查询参数:
javascript
const http = require('http');
const url = require('url');
const PORT = 3000;
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
console.log('Path:', parsedUrl.pathname);
console.log('Query:', parsedUrl.query);
if (parsedUrl.pathname === '/api/users') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ users: ['Alice', 'Bob'] }));
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
});
server.listen(PORT, '0.0.0.0', () => {
console.log(`HTTP server listening on port ${PORT}`);
});访问示例:
bash
curl -H "Authorization: Bearer $WDS_API_KEY" \
"$WDS_BASE_URL/WebIDE/api/v1/projects/$ProjectID/sandboxes/$SandboxID/tcp-socket/$ServiceAddress/http/api/users?page=1"WebSocket 协议
平台对 TCP 端口的 WebSocket 支持与 HTTP 完全相同,平台会自动判断请求是 HTTP 还是 WebSocket,并进行相应的转发。
Node.js WebSocket 示例
javascript
const http = require('http');
const WebSocket = require('ws'); // npm install ws
const PORT = 3000;
const server = http.createServer();
server.listen(PORT, '0.0.0.0', () => {
console.log('HTTP server listening on port', PORT);
});
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws) => {
console.log('Client connected');
ws.on('message', (message) => {
console.log('Received:', message);
ws.send(`Echo: ${message}`);
});
ws.on('close', () => {
console.log('Client disconnected');
});
});Python WebSocket 示例
python
import asyncio
import websockets # pip install websockets
async def echo(websocket):
print('Client connected')
try:
async for message in websocket:
print(f'Received: {message}')
await websocket.send(f'Echo: {message}')
except Exception as e:
print('Exception:', e)
print('Client disconnected')
async def main():
async with websockets.serve(echo, '0.0.0.0', 8765):
print('WebSocket server listening on port 8765')
await asyncio.Future()
if __name__ == '__main__':
asyncio.run(main())使用 wscat 测试
首先安装 wscat:
bash
npm install -g wscat然后连接到你的 WebSocket 服务:
bash
#!/bin/bash
WDS_BASE_URL="agent.wiseverds.com"
WDS_API_KEY="WDS_**************"
ProjectID=2231
SandboxID=j4urFhRG
ContainerName="mythra-svc-abcd1234-projabcd-sandbox1-XyZ12345"
Port=8765
ServiceAddress="${ContainerName}:${Port}"
wscat -c "wss://$WDS_BASE_URL/WebIDE/api/v1/projects/$ProjectID/sandboxes/$SandboxID/tcp-socket/$ServiceAddress/http/" \
-H "Authorization: Bearer $WDS_API_KEY"注意:
- WebSocket 使用
wss://协议(加密的 WebSocket) - URL 路径格式与 HTTP 完全相同
- 同样需要提供
Authorization头部进行身份验证
安全机制
容器归属验证
平台会对每次请求进行容器归属验证,确保请求的容器确实属于指定的项目和沙箱。验证逻辑:
- 从容器名中提取项目 ID 和沙箱 ID 的缩写(前 8 位)
- 与 URL 中的
project_id和sandbox_id进行匹配 - 匹配失败返回
403 Forbidden,防止跨项目/跨沙箱访问
网络隔离
每个项目拥有独立的 Docker 网络(mythra-project-{ProjectID}),不同项目的容器之间网络隔离,无法互相通信。
常见问题
如何获取容器名称
你可以通过以下方式获取容器名称:
- 通过沙箱管理界面:在 Mythra Agent Studio 的沙箱管理页面查看正在运行的容器列表
- 通过平台API:调用沙箱容器列表 API 获取
连接被拒绝 / 502 错误
如果返回 502 错误,可能原因:
- 容器内的服务未启动或已崩溃——请在容器内检查服务状态
- 服务监听的端口不正确——请确认服务监听的端口与 URL 中的端口一致
- 服务只监听
localhost(127.0.0.1)而非0.0.0.0——请确保服务监听在0.0.0.0上,以便 Docker 网络内的其他容器可以访问
容器名无效 / 400 错误
如果返回 400 错误,可能原因:
service_address格式不正确——确保格式为容器名:端口号- 端口号不在有效范围(1-65535)
- 容器名包含不合法字符
容器不属于当前项目 / 403 错误
如果返回 403 错误,说明容器不属于当前项目或沙箱。请确认:
- 容器名称是否正确
- 该容器是否确实是在当前项目和沙箱中创建的
服务没有响应 / 504 错误
如果返回 504 错误,说明请求超时。请检查:
- 容器是否正在运行
- 服务是否响应正常
- 网络连接是否正常
最佳实践
- 监听所有网络接口:确保服务监听在
0.0.0.0而非127.0.0.1,否则 Docker 网络中的其他容器(包括 Agent Server)无法访问 - 使用环境变量配置端口:使用
PORT等环境变量配置服务端口,提高灵活性 - 日志记录:记录请求日志,便于调试和监控
- 错误处理:妥善处理各种错误情况,返回有意义的错误信息
- 健康检查:为服务添加健康检查端点(如
/health),便于排查问题 - 选择合适的访问方式:如果是新建服务且对性能有更高要求,可考虑使用 UNIX Domain Socket 方式
相关文档
- 沙箱 - 了解沙箱的基本概念和使用方法
- 通过 UNIX Domain Socket 访问容器内服务 - 另一种访问容器内服务的方式,性能更高
- 开发外部 AI Agent - 学习如何开发外部 Agent 服务