first commit

This commit is contained in:
2026-02-08 14:42:58 +08:00
commit 20e1deae21
8197 changed files with 2264639 additions and 0 deletions

View File

@@ -0,0 +1,962 @@
"""服务编排服务,用于管理算法服务的生命周期"""
import os
import json
import time
import docker
import uuid
from typing import Dict, Any, Optional
from docker.errors import DockerException, NotFound
class ServiceOrchestrator:
"""服务编排服务"""
def __init__(self):
"""初始化服务编排器"""
try:
# 连接Docker客户端
self.client = docker.from_env()
# 测试连接
self.client.ping()
print("Docker连接成功")
except DockerException as e:
print(f"Docker连接失败: {e}")
self.client = None
def deploy_service(self, service_id: str, service_config: Dict[str, Any], project_info: Dict[str, Any]) -> Dict[str, Any]:
"""部署服务
Args:
service_id: 服务ID
service_config: 服务配置
project_info: 项目信息
Returns:
部署结果
"""
try:
if not self.client:
return {
"success": False,
"error": "Docker连接失败",
"service_id": service_id,
"container_id": None,
"status": "error",
"api_url": None
}
# 1. 构建Docker镜像
image_name = self._build_docker_image(service_id, project_info, service_config)
# 2. 启动服务容器
container_id = self._start_service_container(service_id, image_name, service_config)
# 3. 验证服务启动
if not self._verify_service_startup(container_id, service_config):
return {
"success": False,
"error": "服务启动验证失败",
"service_id": service_id,
"container_id": container_id,
"status": "error",
"api_url": None
}
# 4. 构建API URL
api_url = f"http://{service_config.get('host', 'localhost')}:{service_config.get('port', 8000)}"
return {
"success": True,
"service_id": service_id,
"container_id": container_id,
"status": "running",
"api_url": api_url,
"error": None
}
except Exception as e:
return {
"success": False,
"error": str(e),
"service_id": service_id,
"container_id": None,
"status": "error",
"api_url": None
}
def start_service(self, service_id: str, container_id: str) -> Dict[str, Any]:
"""启动服务
Args:
service_id: 服务ID
container_id: 容器ID
Returns:
启动结果
"""
try:
if not self.client:
return {
"success": False,
"error": "Docker连接失败",
"service_id": service_id,
"status": "error"
}
# 获取容器
container = self.client.containers.get(container_id)
# 启动容器
container.start()
# 验证服务启动
if not self._verify_service_health(container_id):
return {
"success": False,
"error": "服务健康检查失败",
"service_id": service_id,
"status": "error"
}
return {
"success": True,
"service_id": service_id,
"status": "running",
"error": None
}
except NotFound:
return {
"success": False,
"error": "容器不存在",
"service_id": service_id,
"status": "error"
}
except Exception as e:
return {
"success": False,
"error": str(e),
"service_id": service_id,
"status": "error"
}
def stop_service(self, service_id: str, container_id: str) -> Dict[str, Any]:
"""停止服务
Args:
service_id: 服务ID
container_id: 容器ID
Returns:
停止结果
"""
try:
if not self.client:
return {
"success": False,
"error": "Docker连接失败",
"service_id": service_id,
"status": "error"
}
# 获取容器
container = self.client.containers.get(container_id)
# 停止容器
container.stop(timeout=30)
return {
"success": True,
"service_id": service_id,
"status": "stopped",
"error": None
}
except NotFound:
return {
"success": False,
"error": "容器不存在",
"service_id": service_id,
"status": "error"
}
except Exception as e:
return {
"success": False,
"error": str(e),
"service_id": service_id,
"status": "error"
}
def restart_service(self, service_id: str, container_id: str) -> Dict[str, Any]:
"""重启服务
Args:
service_id: 服务ID
container_id: 容器ID
Returns:
重启结果
"""
try:
if not self.client:
return {
"success": False,
"error": "Docker连接失败",
"service_id": service_id,
"status": "error"
}
# 获取容器
container = self.client.containers.get(container_id)
# 重启容器
container.restart(timeout=30)
# 验证服务启动
if not self._verify_service_health(container_id):
return {
"success": False,
"error": "服务健康检查失败",
"service_id": service_id,
"status": "error"
}
return {
"success": True,
"service_id": service_id,
"status": "running",
"error": None
}
except NotFound:
return {
"success": False,
"error": "容器不存在",
"service_id": service_id,
"status": "error"
}
except Exception as e:
return {
"success": False,
"error": str(e),
"service_id": service_id,
"status": "error"
}
def delete_service(self, service_id: str, container_id: str, image_name: str) -> Dict[str, Any]:
"""删除服务
Args:
service_id: 服务ID
container_id: 容器ID
image_name: 镜像名称
Returns:
删除结果
"""
try:
if not self.client:
return {
"success": False,
"error": "Docker连接失败",
"service_id": service_id
}
# 停止并删除容器
if container_id:
try:
container = self.client.containers.get(container_id)
container.stop(timeout=10)
container.remove(force=True)
except NotFound:
pass
# 删除镜像
if image_name:
try:
self.client.images.remove(image_name, force=True)
except:
pass
return {
"success": True,
"service_id": service_id,
"error": None
}
except Exception as e:
return {
"success": False,
"error": str(e),
"service_id": service_id
}
def get_service_status(self, container_id: str) -> Dict[str, Any]:
"""获取服务状态
Args:
container_id: 容器ID
Returns:
服务状态
"""
try:
if not self.client:
return {
"success": False,
"error": "Docker连接失败",
"status": "unknown",
"health": "unknown"
}
# 获取容器
container = self.client.containers.get(container_id)
# 获取容器状态
status = container.status
# 检查服务健康状态
health = "unknown"
if status == "running":
if self._verify_service_health(container_id):
health = "healthy"
else:
health = "unhealthy"
return {
"success": True,
"status": status,
"health": health,
"error": None
}
except NotFound:
return {
"success": False,
"error": "容器不存在",
"status": "not_found",
"health": "unknown"
}
except Exception as e:
return {
"success": False,
"error": str(e),
"status": "unknown",
"health": "unknown"
}
def get_service_logs(self, container_id: str, lines: int = 100) -> Dict[str, Any]:
"""获取服务日志
Args:
container_id: 容器ID
lines: 日志行数
Returns:
服务日志
"""
try:
if not self.client:
return {
"success": False,
"error": "Docker连接失败",
"logs": []
}
# 获取容器
container = self.client.containers.get(container_id)
# 获取日志
logs = container.logs(tail=lines).decode('utf-8').split('\n')
return {
"success": True,
"logs": logs,
"error": None
}
except NotFound:
return {
"success": False,
"error": "容器不存在",
"logs": []
}
except Exception as e:
return {
"success": False,
"error": str(e),
"logs": []
}
def _build_docker_image(self, service_id: str, project_info: Dict[str, Any], service_config: Dict[str, Any]) -> str:
"""构建Docker镜像
Args:
service_id: 服务ID
project_info: 项目信息
service_config: 服务配置
Returns:
镜像名称
"""
# 生成镜像名称
image_name = f"algorithm-service-{service_id}:{service_config.get('version', '1.0.0')}"
# 构建上下文目录
build_context = os.path.join("/tmp", f"service-build-{service_id}")
os.makedirs(build_context, exist_ok=True)
try:
# 创建Dockerfile
dockerfile_content = self._generate_dockerfile(project_info, service_config)
with open(os.path.join(build_context, "Dockerfile"), "w") as f:
f.write(dockerfile_content)
# 复制项目文件(这里简化处理,实际应该复制完整项目)
# 注意:在实际实现中,应该从算法仓库复制项目文件到构建上下文
# 创建服务包装器
service_wrapper_content = self._generate_service_wrapper(project_info, service_config)
wrapper_extension = ".py" if project_info["project_type"] == "python" else ".js"
with open(os.path.join(build_context, f"service_wrapper{wrapper_extension}"), "w") as f:
f.write(service_wrapper_content)
# 创建依赖文件
self._create_dependency_file(build_context, project_info)
# 构建镜像
print(f"开始构建Docker镜像: {image_name}")
image, logs = self.client.images.build(
path=build_context,
tag=image_name,
rm=True,
pull=False
)
# 打印构建日志
for log in logs:
if 'stream' in log:
print(log['stream'], end='')
print(f"Docker镜像构建成功: {image_name}")
return image_name
finally:
# 清理构建上下文
import shutil
try:
shutil.rmtree(build_context)
except:
pass
def _start_service_container(self, service_id: str, image_name: str, service_config: Dict[str, Any]) -> str:
"""启动服务容器
Args:
service_id: 服务ID
image_name: 镜像名称
service_config: 服务配置
Returns:
容器ID
"""
# 容器名称
container_name = f"algorithm-service-{service_id}"
# 端口映射
ports = {
f"{service_config.get('port', 8000)}/tcp": service_config.get('port', 8000)
}
# 环境变量
environment = {
"HOST": service_config.get('host', '0.0.0.0'),
"PORT": str(service_config.get('port', 8000)),
"TIMEOUT": str(service_config.get('timeout', 30))
}
# 启动容器
container = self.client.containers.run(
image_name,
name=container_name,
ports=ports,
environment=environment,
detach=True,
restart_policy={"Name": "unless-stopped"}
)
print(f"容器启动成功: {container.id}")
return container.id
def _verify_service_startup(self, container_id: str, service_config: Dict[str, Any]) -> bool:
"""验证服务启动
Args:
container_id: 容器ID
service_config: 服务配置
Returns:
是否启动成功
"""
# 等待服务启动
time.sleep(5)
# 验证服务健康状态
return self._verify_service_health(container_id)
def _verify_service_health(self, container_id: str) -> bool:
"""验证服务健康状态
Args:
container_id: 容器ID
Returns:
是否健康
"""
try:
container = self.client.containers.get(container_id)
# 检查容器状态
if container.status != "running":
return False
# 这里可以添加更详细的健康检查例如发送HTTP请求到/health端点
# 简化处理,只检查容器状态
return True
except:
return False
def _generate_dockerfile(self, project_info: Dict[str, Any], service_config: Dict[str, Any]) -> str:
"""生成Dockerfile
Args:
project_info: 项目信息
service_config: 服务配置
Returns:
Dockerfile内容
"""
dockerfile_templates = {
"python": """
FROM python:3.9-slim
WORKDIR /app
# 复制依赖文件
COPY requirements.txt .
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制项目文件
COPY . .
# 复制生成的服务包装器
COPY service_wrapper.py .
# 设置环境变量
ENV HOST={{host}}
ENV PORT={{port}}
ENV TIMEOUT={{timeout}}
# 暴露端口
EXPOSE {{port}}
# 启动服务
CMD ["python", "service_wrapper.py"]
""",
"nodejs": """
FROM node:16-alpine
WORKDIR /app
# 复制依赖文件
COPY package.json package-lock.json* .
# 安装依赖
RUN npm install --production
# 复制项目文件
COPY . .
# 复制生成的服务包装器
COPY service_wrapper.js .
# 设置环境变量
ENV HOST={{host}}
ENV PORT={{port}}
ENV TIMEOUT={{timeout}}
# 暴露端口
EXPOSE {{port}}
# 启动服务
CMD ["node", "service_wrapper.js"]
"""
}
template = dockerfile_templates.get(project_info["project_type"], dockerfile_templates["python"])
# 替换模板变量
template = template.replace("{{host}}", service_config.get("host", "0.0.0.0"))
template = template.replace("{{port}}", str(service_config.get("port", 8000)))
template = template.replace("{{timeout}}", str(service_config.get("timeout", 30)))
return template
def _generate_service_wrapper(self, project_info: Dict[str, Any], service_config: Dict[str, Any]) -> str:
"""生成服务包装器
Args:
project_info: 项目信息
service_config: 服务配置
Returns:
服务包装器代码
"""
if project_info["project_type"] == "python":
return '''
# Python HTTP服务包装器
import os
import sys
import json
import time
from http.server import HTTPServer, BaseHTTPRequestHandler
# 添加项目路径到Python路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# 尝试导入算法模块
try:
# 尝试导入主要模块
import algorithm
algorithm_module = algorithm
print("算法模块导入成功")
except Exception as e:
print(f"算法模块导入失败: {e}")
algorithm_module = None
# 服务配置
HOST = os.environ.get("HOST", "0.0.0.0")
PORT = int(os.environ.get("PORT", "8000"))
TIMEOUT = int(os.environ.get("TIMEOUT", "30"))
class AlgorithmRequestHandler(BaseHTTPRequestHandler):
"""算法请求处理器"""
def do_POST(self):
"""处理POST请求"""
try:
# 读取请求体
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
# 解析请求数据
request_data = json.loads(post_data.decode('utf-8'))
# 记录请求开始时间
start_time = time.time()
# 调用算法
result = self._call_algorithm(request_data)
# 计算响应时间
response_time = time.time() - start_time
# 构建响应
response = {
"success": True,
"result": result,
"response_time": round(response_time, 4),
"message": "算法执行成功"
}
# 发送响应
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(response).encode('utf-8'))
except Exception as e:
# 构建错误响应
error_response = {
"success": False,
"error": str(e),
"message": "算法执行失败"
}
# 发送错误响应
self.send_response(400)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(error_response).encode('utf-8'))
def do_GET(self):
"""处理GET请求"""
if self.path == "/health":
# 健康检查
self._handle_health_check()
elif self.path == "/info":
# 服务信息
self._handle_info()
else:
# 404响应
self.send_response(404)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({"error": "Not Found"}).encode('utf-8'))
def _call_algorithm(self, request_data):
"""调用算法
Args:
request_data: 请求数据
Returns:
算法执行结果
"""
if algorithm_module is None:
raise Exception("算法模块未加载")
# 尝试调用算法的主要函数
try:
# 检查是否有predict函数
if hasattr(algorithm_module, 'predict'):
return algorithm_module.predict(request_data)
# 检查是否有run函数
elif hasattr(algorithm_module, 'run'):
return algorithm_module.run(request_data)
# 检查是否有main函数
elif hasattr(algorithm_module, 'main'):
return algorithm_module.main(request_data)
else:
raise Exception("未找到算法执行函数")
except Exception as e:
raise Exception(f"算法执行失败: {e}")
def _handle_health_check(self):
"""处理健康检查"""
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({"status": "healthy", "service": "{{service_name}}"}).encode('utf-8'))
def _handle_info(self):
"""处理服务信息请求"""
info = {
"service": "{{service_name}}",
"version": "{{service_version}}",
"host": HOST,
"port": PORT,
"timeout": TIMEOUT,
"algorithm_loaded": algorithm_module is not None
}
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(info).encode('utf-8'))
def run_server():
"""启动服务"""
server = HTTPServer((HOST, PORT), AlgorithmRequestHandler)
print(f"服务启动成功,监听地址: {HOST}:{PORT}")
print(f"健康检查地址: http://{HOST}:{PORT}/health")
print(f"服务信息地址: http://{HOST}:{PORT}/info")
try:
server.serve_forever()
except KeyboardInterrupt:
print("服务停止")
server.shutdown()
if __name__ == "__main__":
run_server()
'''.replace("{{service_name}}", service_config.get("name", "algorithm-service")).replace("{{service_version}}", service_config.get("version", "1.0.0"))
else:
return '''
// Node.js HTTP服务包装器
const http = require('http');
const url = require('url');
const fs = require('fs');
const path = require('path');
// 服务配置
const HOST = process.env.HOST || '0.0.0.0';
const PORT = process.env.PORT || 8000;
const TIMEOUT = process.env.TIMEOUT || 30;
// 尝试导入算法模块
let algorithmModule = null;
try {
// 尝试导入主要模块
algorithmModule = require('./algorithm');
console.log('算法模块导入成功');
} catch (e) {
console.error('算法模块导入失败:', e);
algorithmModule = null;
}
/**
* 调用算法
* @param {Object} requestData 请求数据
* @returns {Object} 算法执行结果
*/
function callAlgorithm(requestData) {
if (!algorithmModule) {
throw new Error('算法模块未加载');
}
// 尝试调用算法的主要函数
try {
if (algorithmModule.predict) {
return algorithmModule.predict(requestData);
} else if (algorithmModule.run) {
return algorithmModule.run(requestData);
} else if (algorithmModule.main) {
return algorithmModule.main(requestData);
} else {
throw new Error('未找到算法执行函数');
}
} catch (e) {
throw new Error(`算法执行失败: ${e.message}`);
}
}
/**
* 处理请求
* @param {http.IncomingMessage} req 请求对象
* @param {http.ServerResponse} res 响应对象
*/
function handleRequest(req, res) {
const parsedUrl = url.parse(req.url, true);
const pathname = parsedUrl.pathname;
// 设置响应头
res.setHeader('Content-Type', 'application/json');
if (req.method === 'GET') {
if (pathname === '/health') {
// 健康检查
res.writeHead(200);
res.end(JSON.stringify({ status: 'healthy', service: '{{service_name}}' }));
} else if (pathname === '/info') {
// 服务信息
const info = {
service: '{{service_name}}',
version: '{{service_version}}',
host: HOST,
port: PORT,
timeout: TIMEOUT,
algorithm_loaded: algorithmModule !== null
};
res.writeHead(200);
res.end(JSON.stringify(info));
} else {
// 404响应
res.writeHead(404);
res.end(JSON.stringify({ error: 'Not Found' }));
}
} else if (req.method === 'POST') {
let body = '';
// 读取请求体
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
try {
// 解析请求数据
const requestData = JSON.parse(body);
// 记录请求开始时间
const startTime = Date.now();
// 调用算法
const result = callAlgorithm(requestData);
// 计算响应时间
const responseTime = (Date.now() - startTime) / 1000;
// 构建响应
const response = {
success: true,
result: result,
response_time: responseTime.toFixed(4),
message: '算法执行成功'
};
// 发送响应
res.writeHead(200);
res.end(JSON.stringify(response));
} catch (e) {
// 构建错误响应
const errorResponse = {
success: false,
error: e.message,
message: '算法执行失败'
};
// 发送错误响应
res.writeHead(400);
res.end(JSON.stringify(errorResponse));
}
});
} else {
// 不支持的方法
res.writeHead(405);
res.end(JSON.stringify({ error: 'Method Not Allowed' }));
}
}
/**
* 启动服务
*/
function startServer() {
const server = http.createServer(handleRequest);
server.listen(PORT, HOST, () => {
console.log(`服务启动成功,监听地址: ${HOST}:${PORT}`);
console.log(`健康检查地址: http://${HOST}:${PORT}/health`);
console.log(`服务信息地址: http://${HOST}:${PORT}/info`);
});
server.on('error', (error) => {
console.error('服务启动失败:', error);
});
}
// 启动服务
startServer();
'''.replace("{{service_name}}", service_config.get("name", "algorithm-service")).replace("{{service_version}}", service_config.get("version", "1.0.0"))
def _create_dependency_file(self, build_context: str, project_info: Dict[str, Any]):
"""创建依赖文件
Args:
build_context: 构建上下文目录
project_info: 项目信息
"""
if project_info["project_type"] == "python":
# 创建requirements.txt
with open(os.path.join(build_context, "requirements.txt"), "w") as f:
f.write("""
# 基础依赖
http.server
json
# 算法依赖
# 注意在实际实现中应该从项目的requirements.txt复制依赖
""")
elif project_info["project_type"] == "nodejs":
# 创建package.json
package_data = {
"name": "algorithm-service",
"version": "1.0.0",
"description": "Algorithm service wrapper",
"main": "service_wrapper.js",
"scripts": {
"start": "node service_wrapper.js"
},
"dependencies": {
# 基础依赖
}
}
with open(os.path.join(build_context, "package.json"), "w") as f:
json.dump(package_data, f, indent=2)