Skip to content

通过 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:你的项目 ID
  • SandboxID:你的沙箱 ID
  • ServiceAddress:容器名:端口号(例如 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}),不同项目的容器之间网络隔离,无法互相通信。

常见问题 ​

如何获取容器名称 ​

你可以通过以下方式获取容器名称:

  1. 通过沙箱管理界面:在 Mythra Agent Studio 的沙箱管理页面查看正在运行的容器列表
  2. 通过平台API:调用沙箱容器列表 API 获取

连接被拒绝 / 502 错误 ​

如果返回 502 错误,可能原因:

  1. 容器内的服务未启动或已崩溃——请在容器内检查服务状态
  2. 服务监听的端口不正确——请确认服务监听的端口与 URL 中的端口一致
  3. 服务只监听 localhost(127.0.0.1)而非 0.0.0.0——请确保服务监听在 0.0.0.0 上,以便 Docker 网络内的其他容器可以访问

容器名无效 / 400 错误 ​

如果返回 400 错误,可能原因:

  1. service_address 格式不正确——确保格式为 容器名:端口号
  2. 端口号不在有效范围(1-65535)
  3. 容器名包含不合法字符

容器不属于当前项目 / 403 错误 ​

如果返回 403 错误,说明容器不属于当前项目或沙箱。请确认:

  1. 容器名称是否正确
  2. 该容器是否确实是在当前项目和沙箱中创建的

服务没有响应 / 504 错误 ​

如果返回 504 错误,说明请求超时。请检查:

  1. 容器是否正在运行
  2. 服务是否响应正常
  3. 网络连接是否正常

最佳实践 ​

  1. 监听所有网络接口:确保服务监听在 0.0.0.0 而非 127.0.0.1,否则 Docker 网络中的其他容器(包括 Agent Server)无法访问
  2. 使用环境变量配置端口:使用 PORT 等环境变量配置服务端口,提高灵活性
  3. 日志记录:记录请求日志,便于调试和监控
  4. 错误处理:妥善处理各种错误情况,返回有意义的错误信息
  5. 健康检查:为服务添加健康检查端点(如 /health),便于排查问题
  6. 选择合适的访问方式:如果是新建服务且对性能有更高要求,可考虑使用 UNIX Domain Socket 方式

相关文档 ​

Happy Coding!