diff --git a/README.md b/README.md index 5e6f65d2..b2f8cd5f 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,12 @@ Astron-xmod-shim 也提供了 Helm Chart 部署方式,适用于 Kubernetes 环 # 进入 Helm chart 目录 cd deploy/helm -# 安装或升级应用 -helm upgrade --install astron-xmod-shim astron-xmod-shim/ -f astron-xmod-shim/values.yaml - +# 安装应用 + helm install astron-xmod-shim ./astron-xmod-shim --kubeconfig ../../conf/shimlets/kubeconfig +# 升级应用 + helm upgrade astron-xmod-shim ./astron-xmod-shim --kubeconfig ../../conf/shimlets/kubeconfig +# 卸载应用 + helm uninstall astron-xmod-shim --kubeconfig ../../conf/shimlets/kubeconfig # 验证部署 kubectl get pods -l app.kubernetes.io/name=astron-xmod-shim ``` @@ -118,37 +121,8 @@ kubectl get pods -l app.kubernetes.io/name=astron-xmod-shim helm uninstall astron-xmod-shim ``` -## API 参考 - -### 部署模型服务 - -```bash -curl -X POST http://localhost:8080/api/v1/modserv/deploy \ - -H "Content-Type: application/json" \ - -d '{ - "modelName": "example-model", - "modelFile": "/path/to/model", - "resourceRequirements": { - "acceleratorType": "NVIDIA GPU", - "acceleratorCount": 1, - "cpu": "4", - "memory": "16Gi" - }, - "replicaCount": 1 - }' -``` - -### 查询服务状态 - -```bash -curl http://localhost:8080/api/v1/modserv/{serviceId} -``` - -### 列出已加载插件 - -```bash -curl http://localhost:8080/api/v1/plugins -``` +## API 参考文档 +[API参考文档](deploy/docs/api.md) ## 插件开发指南 @@ -162,108 +136,7 @@ Astron-xmod-shim 原生内置了 Kubernetes Shimlet,用于在 Kubernetes 环 Kubernetes 的资源操作(如创建 Deployment 和 Service 等)。 #### 扩展示例:Docker Shimlet 实现 - -以下是实现 Docker 环境适配插件的示例代码,用于将模型服务部署到 Docker 容器中: - -#### 步骤 1:实现 Shimlet 接口 - -```go -package dockershimlet - -import ( - "astron-xmod-shim/internal/core/shimlet" - dto "astron-xmod-shim/internal/dto/deploy" - "astron-xmod-shim/pkg/log" - // 导入 Docker SDK 相关包 - // "github.com/docker/docker/client" -) - -// DockerShimlet 实现 Docker 环境适配插件 -type DockerShimlet struct { - // 这里可以添加 Docker 客户端等成员变量 - // client *client.Client -} - -// 确保 DockerShimlet 实现了 Shimlet 接口(编译期检查) -var _ shimlet.Shimlet = (*DockerShimlet)(nil) - -// InitWithConfig 初始化 Docker 客户端和配置 -func (d *DockerShimlet) InitWithConfig(confPath string) error { - // 实现 Docker 客户端初始化逻辑 - // 1. 解析配置文件 - // 2. 创建 Docker 客户端连接 - // 3. 验证连接是否成功 - log.Info("Initializing Docker shimlet with config: %s", confPath) - return nil -} - -// Apply 应用部署规范,创建或更新 Docker 容器 -func (d *DockerShimlet) Apply(spec *dto.RequirementSpec) error { - // 实现 Docker 容器创建/更新逻辑 - // 1. 根据 spec 构建容器配置(镜像、端口映射、卷挂载等) - // 2. 检查容器是否已存在 - // 3. 存在则更新,不存在则创建新容器 - log.Info("Applying deployment spec to Docker: %s", spec.ModelName) - return nil -} - -// Delete 删除指定的 Docker 容器资源 -func (d *DockerShimlet) Delete(resourceID string) error { - // 实现 Docker 容器删除逻辑 - // 1. 根据 resourceID 查找对应的容器 - // 2. 停止并删除容器 - log.Info("Deleting Docker resource: %s", resourceID) - return nil -} - -// Status 查询 Docker 容器的运行状态 -func (d *DockerShimlet) Status(resourceID string) (*dto.RuntimeStatus, error) { - // 实现容器状态查询逻辑 - // 1. 根据 resourceID 查找容器 - // 2. 获取容器状态信息 - // 3. 构建并返回 RuntimeStatus 对象 - log.Info("Getting status for Docker resource: %s", resourceID) - return &dto.RuntimeStatus{}, nil -} - -// ID 返回当前 Shimlet 的唯一标识符 -func (d *DockerShimlet) ID() string { - // 返回固定的标识符 - return "docker" -} - -// Description 返回当前 Shimlet 的描述信息 -func (d *DockerShimlet) Description() string { - // 返回描述文本 - return "Docker环境适配插件,用于在Docker容器中部署模型服务" -} - -// ListDeployedServices 列出所有已部署的服务 -func (d *DockerShimlet) ListDeployedServices() ([]string, error) { - // 实现服务列表查询逻辑 - // 1. 列出所有与模型服务相关的容器 - // 2. 提取并返回服务 ID 列表 - log.Info("Listing all deployed services in Docker") - return []string{}, nil -} -``` - -#### 步骤 2:注册 Docker Shimlet - -```go -package dockershimlet - -import ( - "astron-xmod-shim/internal/core/shimlet" -) - -// init 函数在插件加载时自动调用 -func init() { - // 注册自定义的 Docker shimlet - shimlet.Registry.AutoRegister(&DockerShimlet{}) -} -``` - +[自定义Shimlet示例](deploy/docs/shimlet-demo.md) ### 预定义收敛目标集合(GoalSet) @@ -275,80 +148,6 @@ GoalSet 定义了模型部署的具体目标和执行逻辑。Astron-xmod-shim Astron-xmod-shim 原生内置了 OpenSourceLLM GoalSet,用于开源大模型的部署流程。它采用 Builder 模式实现,包含模型路径映射、部署完成验证、规格一致性检查和服务暴露等关键目标,使用户能够快速部署开源大模型服务。 -#### 步骤 1:定义 Goal - -```go -package mygoalset - -import ( - "astron-xmod-shim/internal/core/goal" - "astron-xmod-shim/pkg/log" -) - -// 定义一个验证模型的 Goal -var validateModel = goal.Goal{ - Name: "validate-model", - IsAchieved: func(ctx *goal.Context) bool { - // 检查目标是否已达成 - return ctx.DeploySpec.ModelName != "" - }, - Ensure: func(ctx *goal.Context) error { - log.Info("开始验证模型: %s", ctx.DeploySpec.ModelName) - // 实现模型验证逻辑 - return nil - }, -} - -// 定义一个准备资源的 Goal -var prepareResources = goal.Goal{ - Name: "prepare-resources", - IsAchieved: func(ctx *goal.Context) bool { - // 检查资源是否已准备好 - // 这里可以检查模型文件、配置等是否存在 - resourceReady, exists := ctx.Get("resourceReady").(bool) - return exists && resourceReady - }, - Ensure: func(ctx *goal.Context) error { - log.Info("准备部署资源") - // 实现资源准备逻辑 - // 准备完成后设置标记 - ctx.Set("resourceReady", true) - return nil - }, -} -``` - -#### 步骤 2:创建并注册 GoalSet - -```go -package mygoalset - -import ( - "astron-xmod-shim/internal/core/goal" - "time" -) - -// init 函数在插件加载时自动调用 -func init() { - // 使用 Builder 模式创建并注册自定义 GoalSet - newMyGoalSet() -} - -// newMyGoalSet 创建自定义 GoalSet 实例 -func newMyGoalSet() { - goal.NewGoalSetBuilder("my-goalset"). - AddGoal(validateModel). - AddGoal(prepareResources). - WithMaxRetries(3). // 失败最多重试 3 次 - WithTimeout(2 * time.Minute). // 整体超时 2 分钟 - BuildAndRegister() -} -``` - -### 扩展示例:Docker Shimlet - -除了内置的Kubernetes Shimlet外,开发者还可以实现Docker环境适配插件,将模型服务部署到Docker容器中。Docker Shimlet通过Docker -API创建和管理容器,支持模型服务的完整生命周期管理。 ### 扩展示例:业务场景 GoalSet @@ -358,85 +157,14 @@ API创建和管理容器,支持模型服务的完整生命周期管理。 - **边缘部署GoalSet**:添加资源限制检查、模型量化优化、离线推理支持等特殊目标 - **企业级安全GoalSet**:集成身份验证、加密传输、访问控制等安全增强目标 -### 插件集成方式 - -Astron-xmod-shim 使用 Go 语言的初始化注册机制实现插件集成,而不是通过共享库编译和热加载。 - -#### 内置插件集成 - -内置插件(如 Kubernetes Shimlet)通过在 `init()` 函数中自动注册到框架中: - -```go -// K8sShimlet 的注册方式示例 -func init() { -shimlet.Registry.AutoRegister(&K8sShimlet{}) -} -``` - -#### 自定义插件集成 - -自定义插件可以通过以下方式集成到 Astron-xmod-shim 中: +[自定义GoalSet示例](deploy/docs/goalset-demo.md) -1. **实现标准接口**:按照文档中示例实现 `Shimlet` 或创建 `GoalSet` -2. **自动注册**:在 `init()` 函数中使用注册表完成自动注册 -3. **重新编译**:将自定义插件代码放在正确的包路径下,然后重新编译整个应用程序 -#### 插件选择与配置 - -通过命令行参数或配置文件指定要使用的插件: - -```bash -# 通过命令行指定插件 -./model-serve-shim --shimlet=k8s --goalset=opensource-llm-deploy - -# 通过配置文件指定插件 -# config.yaml 中设置 -defaultShimlet: k8s -defaultGoalSet: opensource-llm-deploy -``` - -## 配置说明 +## 配置文件结构说明 Astron-xmod-shim 支持通过命令行参数和配置文件进行配置: +[配置文件整体说明](deploy/docs/conf.md) -### 命令行参数 - -```bash -./model-serve-shim --help - -Usage of model-serve-shim: - --port int 服务监听端口 (默认: 8080) - --config string 配置文件路径 - --shimlet string 默认加载的 shimlet 插件 - --goalset string 默认加载的 goalset 插件 - --plugin-dir string 插件目录路径 - --log-level string 日志级别 (debug, info, warn, error) (默认: "info") -``` - -### 配置文件 - -配置文件采用 YAML 格式: - -```yaml -# config.yaml -service: - port: 8080 - readTimeout: 30s - writeTimeout: 30s - -plugins: - defaultShimlet: k8s - defaultGoalSet: opensource-llm-deploy - pluginDir: ./plugins - preload: - - type: shimlet - path: ./plugins/myshimlet.so - -logging: - level: info - format: text - output: stdout -``` ## 贡献指南 diff --git a/api/route/route_test.go b/api/route/route_test.go new file mode 100644 index 00000000..8be0d87b --- /dev/null +++ b/api/route/route_test.go @@ -0,0 +1,108 @@ +package route + +import ( + // 为自定义 http 包添加别名以避免命名冲突 + xmodhttp "astron-xmod-shim/pkg/http" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +// MockServer 模拟 HTTP 服务器 +// 注意:这个结构体现在只用于模拟,不会直接传递给 RegisterRoutes +type MockServer struct { + mock.Mock + engine *gin.Engine +} + +func (m *MockServer) GetEngine() *gin.Engine { + args := m.Called() + return args.Get(0).(*gin.Engine) +} + +func (m *MockServer) Run() error { + args := m.Called() + return args.Error(0) +} + +func TestRegisterRoutes(t *testing.T) { + // 设置测试环境 + gin.SetMode(gin.TestMode) + + // 创建一个符合 RegisterRoutes 参数要求的服务器 + // 使用 NewServer 函数而不是直接初始化结构体 + testServer := xmodhttp.NewServer(":8080") + // 获取引擎用于测试 + engine := testServer.GetEngine() + + // 创建一个模拟的 HTTP 服务器(仅用于验证调用) + mockServer := new(MockServer) + mockServer.On("GetEngine").Return(engine) + + // 注册路由 + RegisterRoutes(testServer) + + // 验证路由是否被正确注册 + // 创建测试请求来验证各个路由 + + // 测试部署路由 + r1, _ := http.NewRequest("POST", "/api/v1/modserv/deploy", nil) + w1 := httptest.NewRecorder() + engine.ServeHTTP(w1, r1) + assert.NotEqual(t, http.StatusNotFound, w1.Code) + + // 测试模型列表路由 + r2, _ := http.NewRequest("GET", "/api/v1/modserv/list", nil) + w2 := httptest.NewRecorder() + engine.ServeHTTP(w2, r2) + assert.NotEqual(t, http.StatusNotFound, w2.Code) + + // 测试获取服务状态路由 + r3, _ := http.NewRequest("GET", "/api/v1/modserv/test-service-id", nil) + w3 := httptest.NewRecorder() + engine.ServeHTTP(w3, r3) + assert.NotEqual(t, http.StatusNotFound, w3.Code) + + // 测试删除服务路由 + r4, _ := http.NewRequest("DELETE", "/api/v1/modserv/test-service-id", nil) + w4 := httptest.NewRecorder() + engine.ServeHTTP(w4, r4) + assert.NotEqual(t, http.StatusNotFound, w4.Code) + + // 测试更新服务路由 + r5, _ := http.NewRequest("PUT", "/api/v1/modserv/test-service-id", nil) + w5 := httptest.NewRecorder() + engine.ServeHTTP(w5, r5) + assert.NotEqual(t, http.StatusNotFound, w5.Code) + + // 测试指标路由 + r6, _ := http.NewRequest("GET", "/api/v1/modserv/metrics", nil) + w6 := httptest.NewRecorder() + engine.ServeHTTP(w6, r6) + assert.NotEqual(t, http.StatusNotFound, w6.Code) +} + +func TestRegisterRoutes_NonExistentRoute(t *testing.T) { + // 设置测试环境 + gin.SetMode(gin.TestMode) + + // 创建一个符合 RegisterRoutes 参数要求的服务器 + testServer := xmodhttp.NewServer(":8080") + // 获取引擎用于测试 + engine := testServer.GetEngine() + + // 注册路由 + RegisterRoutes(testServer) + + // 测试不存在的路由 + r, _ := http.NewRequest("GET", "/api/v1/non-existent-route", nil) + w := httptest.NewRecorder() + engine.ServeHTTP(w, r) + + // 验证返回 404 状态码 + assert.Equal(t, http.StatusNotFound, w.Code) +} \ No newline at end of file diff --git a/deploy/docs/README.md b/deploy/docs/README.md deleted file mode 100644 index 038df215..00000000 --- a/deploy/docs/README.md +++ /dev/null @@ -1,172 +0,0 @@ -# ModelServeShim - -## 一句话介绍 -极致轻量AI服务管控中间件,基于双插件化架构(`shimlet` 基础环境适配 + `pipeline` 部署流程扩展),原生内置集成`k8s-shimlet`(k8s环境支持) 与 `opensourcellm-pipeline`(开源模型部署流程)),支持跨环境部署与自定义流程,简化全生命周期管控。 - -## 命名释义 -ModelServeShim(简称 astron-xmod-shim)由两部分构成: - -ModelServe:体现核心能力 —— 聚焦大模型(Model)的服务化部署与运维(Serve); -Shim:源自技术术语 “适配层”,指通过抽象接口屏蔽底层环境差异,是实现跨环境统一管控的核心设计。 - -## 核心特性 -| 特性分类 | 核心能力说明 | 关键价值 | -|-------------------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------| -| 插件化跨环境适配架构 | 1. 基于 `shim` 抽象层定义标准化接口,通过 `shimlet` 插件实现环境适配
2. 已实现 `k8sshimlet`(深度对接 K8s Deployment/StatefulSet 资源)
3. 新增环境仅需开发专属 `shimlet`,核心逻辑与配置完全复用 | 一套管控逻辑覆盖多部署场景,环境扩展无需修改核心代码,开发成本显著降低 | -| 可定制化部署流水线 | 1. 基于 `pipeline` 插件化设计,默认提供 `opensourcellm pipeline`(开源大模型部署流程)
2. 支持自定义 `pipeline` 替换默认流程,可新增量化、权限校验等个性化步骤
3. 流水线步骤支持配置重试策略与超时控制 | 兼顾开源模型快速部署与业务场景定制化需求,流程扩展灵活无侵入 | -| 轻量架构与便捷部署 | 1. 单二进制文件交付(≈15MB),无外部依赖,解压即可启动
2. 支持插件热加载,新增 `shimlet`/`pipeline` 无需重启服务
3. 配置简洁,仅需指定环境插件与流程插件即可运行 | 部署门槛低,运维成本小,适配中小团队快速落地需求 | -| 全链路状态可视与监控 | 1. FSM 状态机驱动,实时展示「初始化→流水线执行→环境部署→健康检查→运行中」全流转
2. 每个状态与步骤关联详细日志(操作人、耗时、参数)
3. 暴露 Prometheus 指标:部署成功率、资源使用率、流水线耗时等 | 状态可追溯,故障可快速定位,服务可用性全程可控 | - - -## 技术架构 -采用“核心逻辑+双插件”解耦设计,兼顾稳定性与扩展性: - -![img.png](../../img1.png) - -## 快速上手(K8s + 开源LLM默认流水线) -### 1. 环境准备 -- K8s 集群(v1.20+),已配置 GPU 调度(nvidia-device-plugin) -- 本地 `kubectl` 可访问集群(配置 KUBECONFIG) - -### 2. 下载安装 -```bash -# 下载单文件二进制(Linux x86_64) -wget https://github.com/your-org/ModelServeShim/releases/latest/download/model-serve-shim -chmod +x model-serve-shim - -# 验证安装(无依赖检查) -./model-serve-shim --version -# 输出示例:ModelServeShim v1.0.0 (commit: abc123) -``` - -### 3. 启动服务(加载默认插件) -```bash -# 启用 K8s shimlet 与开源LLM默认 pipeline -./model-serve-shim --port=8080 \ - --shimlet=k8s \ - --pipeline=opensourcellm -``` - -### 4. 部署开源大模型(Llama-3-8B 示例) -```bash -curl -X POST http://localhost:8080/api/v1/modserv/deploy \ - -H "Content-Type: application/json" \ - -d '{ - "modelName": "llama-3-8b", - "modelFile": "/models/llama-3-8b", - "resourceRequirements": { - "acceleratorType": "NVIDIA H20", - "acceleratorCount": 1, - "cpu": "4", - "memory": "16Gi" - }, - "replicaCount": 1 - }' - -# 响应示例(获取 serviceId 跟踪进度) -{ - "code": 0, - "data": { - "serviceId": "llama-3-8b-123e4567-e89b-12d3-a456-426614174000", - "currentStep": "model-validation", - "totalSteps": ["model-validation", "config-rendering", "resource-deployment", "health-check"] - } -} -``` - -### 5. 查看部署状态与流水线进度 -```bash -# 查看整体状态 -curl http://localhost:8080/api/v1/modserv/llama-3-8b-123e4567-e89b-12d3-a456-426614174000 - -# 查看流水线详细执行日志 -curl http://localhost:8080/api/v1/modserv/llama-3-8b-123e4567-e89b-12d3-a456-426614174000/pipeline/logs -``` - - -## 插件扩展示例 -### 1. 新增 shimlet(适配 Docker 环境) -只需实现 `Shim` 抽象接口,即可接入新环境: -``` go -// dockershimlet 核心实现 -type DockerShimlet struct{} - -// 实现创建资源接口 -func (d *DockerShimlet) Create(ctx *deploy.Context) (string, error) { - // 调用 Docker SDK 创建容器 - container, err := dockerClient.ContainerCreate( - context.Background(), - &container.Config{Image: ctx.ModelImage}, - &container.HostConfig{Resources: getDockerResources(ctx.Requirements)}, - nil, nil, ctx.ModelName) - if err != nil { - return "", fmt.Errorf("create docker container failed: %v", err) - } - // 启动容器 - return container.ID, dockerClient.ContainerStart(context.Background(), container.ID, types.ContainerStartOptions{}) -} - -// 实现查询状态接口 -func (d *DockerShimlet) Status(resourceID string) (deploy.Status, error) { - // 查询 Docker 容器状态 - inspect, err := dockerClient.ContainerInspect(context.Background(), resourceID) - if err != nil { - return deploy.StatusFailed, err - } - if inspect.State.Running { - return deploy.StatusRunning, nil - } - return deploy.StatusStopped, nil -} -``` - -### 2. 自定义 pipeline(增加模型量化步骤) -实现 `Pipeline` 接口,替换默认部署流程: -``` go -// 带量化步骤的自定义流水线 -type QuantLLMpipeline struct{} - -// 定义流水线步骤 -func (p *QuantLLMpipeline) Steps() []string { - return []string{"model-validation", "model-quantization", "config-rendering", "resource-deployment", "health-check"} -} - -// 实现步骤执行逻辑 -func (p *QuantLLMpipeline) RunStep(step string, ctx *pipeline.Context) error { - switch step { - case "model-quantization": - // 调用量化工具(如 GGUF)处理模型 - return quantizeModel(ctx.ModelFile, ctx.QuantConfig) - case "model-validation": - return validateModel(ctx.ModelFile) - // 实现其他步骤... - default: - return fmt.Errorf("unsupported step: %s", step) - } -} -``` - - -## 常用 API 参考 -| 操作类型 | HTTP 方法 | 接口路径 | 说明 | -|------------------|-----------|-----------------------------------|---------------------------------------| -| 部署服务 | POST | `/api/v1/modserv/deploy` | 使用指定插件创建模型服务实例 | -| 查询服务状态 | GET | `/api/v1/modserv/{serviceId}` | 查看服务整体状态与资源信息 | -| 查询流水线进度 | GET | `/api/v1/modserv/{id}/pipeline` | 查看流水线步骤执行情况 | -| 列出可用插件 | GET | `/api/v1/plugins` | 查看已加载的shimlet与pipeline | -| 加载新插件 | POST | `/api/v1/plugins/load` | 热加载自定义插件(无需重启服务) | - - -## 插件生态路线图 -| 插件类型 | 现有实现 | 规划扩展 | -|----------------|-------------------------|---------------------------| -| shimlet | k8sshimlet | dockershimlet、edgelet | -| pipeline | opensourcellm pipeline | privatellm pipeline、quant-pipeline | - -## 🛠️ 代码规范 - -本项目使用 [pre-commit](https://pre-commit.com) 自动检查代码风格,确保提交的代码格式统一。 - -[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://pre-commit.com/) -## 许可证 -Apache License 2.0 \ No newline at end of file diff --git a/deploy/docs/api.md b/deploy/docs/api.md new file mode 100644 index 00000000..ccfb2dbc --- /dev/null +++ b/deploy/docs/api.md @@ -0,0 +1,161 @@ +# 模型服务管理 API 文档 + +## 1. 获取模型路径列表 + +**请求方法**:`GET` +**请求 URL**:`/api/v1/modserv/list` + +**响应体**: +```json +{ + "code": 0, + "message": "success", + "data": [ + { + "modelName": "Llama-3-8B", + "modelPath": "/models/llama-3-8b" + }, + { + "modelName": "bge-Large", + "modelPath": "/models/bge-large" + }, + { + "modelName": "xdeepseekv3", + "modelPath": "/models/xdeepseekv3" + } + ] +} +``` + +> **说明**: +> - `code`: `0` 表示成功,`1` 表示失败。 +> - `modelPath` 字段仅供调试使用,前端不应展示。 + +--- + +## 2. 部署新服务(创建服务) + +**请求方法**:`POST` +**请求 URL**:`/api/v1/modserv/deploy` +**请求头**: +```http +Content-Type: application/json +``` + +**请求体**: +```json +{ + "modelName": "xdeepseekv3", + "resourceRequirements": { + "acceleratorCount": 8 + }, + "replicaCount": 1, + "contextLength": 1234 +} +``` + +**响应体**: +```json +{ + "code": 0, + "message": "deploy submit success", + "data": { + "serviceId": "xdeepseekv3-" + } +} +``` + +> **说明**: +> - `replicaCount` 当前固定为 `1`,前端可预留字段。 +> - 成功后返回唯一 `serviceId`,用于后续操作。 + +--- + +## 3. 更新服务配置 + +**请求方法**:`PUT` +**请求 URL**:`/api/v1/modserv/{serviceId}`(例如:`/api/v1/modserv/xdeepseekv3-a1b2c3d4`) +**请求头**: +```http +Content-Type: application/json +``` + +**请求体**: +```json +{ + "modelName": "xdeepseekv3", + "resourceRequirements": { + "acceleratorCount": 2 + }, + "replicaCount": 1, + "contextLength": 1234 +} +``` + +**响应体**: +```json +{ + "code": 0, + "message": "update submit success", + "data": { + "serviceId": "xdeepseekv3-" + } +} +``` + +> **说明**: +> - 通过 URL 中的 `serviceId` 定位要更新的服务。 +> - 更新操作为异步提交,实际生效可能有延迟。 + +--- + +## 4. 查看服务状态 + +**请求方法**:`GET` +**请求 URL**:`/api/v1/modserv/{serviceId}` + +**响应体**: +```json +{ + "code": 0, + "message": "success", + "data": { + "serviceId": "xdeepseekv3-", + "modelName": "xdeepseekv3", + "status": "running", + "endpoint": "http://xxxx:xxxx/xx", + "updateTime": "2025-09-01 14:30" + } +} +``` + +> **状态说明**: +> - `running`:运行中 +> - `pending`:阻塞中(资源不足等) +> - `failed`:部署失败 +> - `initializing`:初始化中 +> - `notExist`:服务不存在 +> - `terminating`:正在删除中 + +--- + +## 5. 删除服务 + +**请求方法**:`DELETE` +**请求 URL**:`/api/v1/modserv/{serviceId}` +**请求体**:无 + +**响应体**: +```json +{ + "code": 0, + "message": "delete submit success", + "data": { + "serviceId": "xdeepseekv3-" + } +} +``` + +> **说明**: +> - 删除为异步操作,服务状态将变为 `terminating`。 +> - 资源释放可能需要一定时间。 diff --git a/deploy/docs/conf.md b/deploy/docs/conf.md new file mode 100644 index 00000000..684ff09c --- /dev/null +++ b/deploy/docs/conf.md @@ -0,0 +1,9 @@ +## 项目文件结构 + + +conf/ +├── base/ +│ └── conf.yaml # 核心配置文件 +└── shimlets/ + ├── k8s-shimlet.yaml # Kubernetes shimlet 配置 + └── kubeconfig # Kubernetes 集群连接配置 \ No newline at end of file diff --git a/deploy/docs/goalset-demo.md b/deploy/docs/goalset-demo.md new file mode 100644 index 00000000..b704f1c4 --- /dev/null +++ b/deploy/docs/goalset-demo.md @@ -0,0 +1,69 @@ +#### 步骤 1:定义 Goal + +```go +package mygoalset + +import ( + "astron-xmod-shim/internal/core/goal" + "astron-xmod-shim/pkg/log" +) + +// 定义一个验证模型的 Goal +var validateModel = goal.Goal{ + Name: "validate-model", + IsAchieved: func(ctx *goal.Context) bool { + // 检查目标是否已达成 + return ctx.DeploySpec.ModelName != "" + }, + Ensure: func(ctx *goal.Context) error { + log.Info("开始验证模型: %s", ctx.DeploySpec.ModelName) + // 实现模型验证逻辑 + return nil + }, +} + +// 定义一个准备资源的 Goal +var prepareResources = goal.Goal{ + Name: "prepare-resources", + IsAchieved: func(ctx *goal.Context) bool { + // 检查资源是否已准备好 + // 这里可以检查模型文件、配置等是否存在 + resourceReady, exists := ctx.Get("resourceReady").(bool) + return exists && resourceReady + }, + Ensure: func(ctx *goal.Context) error { + log.Info("准备部署资源") + // 实现资源准备逻辑 + // 准备完成后设置标记 + ctx.Set("resourceReady", true) + return nil + }, +} +``` + +#### 步骤 2:创建并注册 GoalSet + +```go +package mygoalset + +import ( + "astron-xmod-shim/internal/core/goal" + "time" +) + +// init 函数在插件加载时自动调用 +func init() { + // 使用 Builder 模式创建并注册自定义 GoalSet + newMyGoalSet() +} + +// newMyGoalSet 创建自定义 GoalSet 实例 +func newMyGoalSet() { + goal.NewGoalSetBuilder("my-goalset"). + AddGoal(validateModel). + AddGoal(prepareResources). + WithMaxRetries(3). // 失败最多重试 3 次 + WithTimeout(2 * time.Minute). // 整体超时 2 分钟 + BuildAndRegister() +} +``` \ No newline at end of file diff --git "a/deploy/docs/modserv-shim \346\212\200\346\234\257\350\256\276\350\256\241\346\226\207\346\241\243.docx" "b/deploy/docs/modserv-shim \346\212\200\346\234\257\350\256\276\350\256\241\346\226\207\346\241\243.docx" deleted file mode 100644 index dfd11c0a..00000000 Binary files "a/deploy/docs/modserv-shim \346\212\200\346\234\257\350\256\276\350\256\241\346\226\207\346\241\243.docx" and /dev/null differ diff --git a/deploy/docs/shimlet-demo.md b/deploy/docs/shimlet-demo.md new file mode 100644 index 00000000..424620c6 --- /dev/null +++ b/deploy/docs/shimlet-demo.md @@ -0,0 +1,102 @@ +除了内置的Kubernetes Shimlet外,开发者还可以实现Docker环境适配插件,将模型服务部署到Docker容器中。Docker Shimlet通过Docker +API创建和管理容器,支持模型服务的完整生命周期管理。 +以下是实现 Docker 环境适配插件的示例代码,用于将模型服务部署到 Docker 容器中: + +#### 步骤 1:实现 Shimlet 接口 + +```go +package dockershimlet + +import ( + "astron-xmod-shim/internal/core/shimlet" + dto "astron-xmod-shim/internal/dto/deploy" + "astron-xmod-shim/pkg/log" + // 导入 Docker SDK 相关包 + // "github.com/docker/docker/client" +) + +// DockerShimlet 实现 Docker 环境适配插件 +type DockerShimlet struct { + // 这里可以添加 Docker 客户端等成员变量 + // client *client.Client +} + +// 确保 DockerShimlet 实现了 Shimlet 接口(编译期检查) +var _ shimlet.Shimlet = (*DockerShimlet)(nil) + +// InitWithConfig 初始化 Docker 客户端和配置 +func (d *DockerShimlet) InitWithConfig(confPath string) error { + // 实现 Docker 客户端初始化逻辑 + // 1. 解析配置文件 + // 2. 创建 Docker 客户端连接 + // 3. 验证连接是否成功 + log.Info("Initializing Docker shimlet with config: %s", confPath) + return nil +} + +// Apply 应用部署规范,创建或更新 Docker 容器 +func (d *DockerShimlet) Apply(spec *dto.RequirementSpec) error { + // 实现 Docker 容器创建/更新逻辑 + // 1. 根据 spec 构建容器配置(镜像、端口映射、卷挂载等) + // 2. 检查容器是否已存在 + // 3. 存在则更新,不存在则创建新容器 + log.Info("Applying deployment spec to Docker: %s", spec.ModelName) + return nil +} + +// Delete 删除指定的 Docker 容器资源 +func (d *DockerShimlet) Delete(resourceID string) error { + // 实现 Docker 容器删除逻辑 + // 1. 根据 resourceID 查找对应的容器 + // 2. 停止并删除容器 + log.Info("Deleting Docker resource: %s", resourceID) + return nil +} + +// Status 查询 Docker 容器的运行状态 +func (d *DockerShimlet) Status(resourceID string) (*dto.RuntimeStatus, error) { + // 实现容器状态查询逻辑 + // 1. 根据 resourceID 查找容器 + // 2. 获取容器状态信息 + // 3. 构建并返回 RuntimeStatus 对象 + log.Info("Getting status for Docker resource: %s", resourceID) + return &dto.RuntimeStatus{}, nil +} + +// ID 返回当前 Shimlet 的唯一标识符 +func (d *DockerShimlet) ID() string { + // 返回固定的标识符 + return "docker" +} + +// Description 返回当前 Shimlet 的描述信息 +func (d *DockerShimlet) Description() string { + // 返回描述文本 + return "Docker环境适配插件,用于在Docker容器中部署模型服务" +} + +// ListDeployedServices 列出所有已部署的服务 +func (d *DockerShimlet) ListDeployedServices() ([]string, error) { + // 实现服务列表查询逻辑 + // 1. 列出所有与模型服务相关的容器 + // 2. 提取并返回服务 ID 列表 + log.Info("Listing all deployed services in Docker") + return []string{}, nil +} +``` + +#### 步骤 2:注册 Docker Shimlet + +```go +package dockershimlet + +import ( + "astron-xmod-shim/internal/core/shimlet" +) + +// init 函数在插件加载时自动调用 +func init() { + // 注册自定义的 Docker shimlet + shimlet.Registry.AutoRegister(&DockerShimlet{}) +} +``` diff --git a/internal/dto/config/config_test.go b/internal/dto/config/config_test.go new file mode 100644 index 00000000..928ce989 --- /dev/null +++ b/internal/dto/config/config_test.go @@ -0,0 +1,137 @@ +package dto + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestGlobalConfig 测试 GlobalConfig 结构体的基本功能 +func TestGlobalConfig(t *testing.T) { + // 准备测试数据 + globalConfig := &GlobalConfig{ + K8s: K8sConfig{ + Kubeconfig: "/path/to/kubeconfig", + Context: "test-context", + QPS: 10.0, + Burst: 20, + Timeout: 30, + }, + Server: Server{ + Port: "8080", + }, + Log: LogConfig{ + Level: "info", + Path: "/var/log/app.log", + MaxSize: 100, + MaxAge: 7, + Compress: true, + ShowLine: true, + EnableConsole: true, + }, + CurrentShimlet: "kubernetes", + Shimlets: map[string]ShimletConfig{ + "kubernetes": { + ConfigPath: "/etc/kubernetes/config.yaml", + }, + }, + ModelManage: ModelManageConfig{ + ModelRoot: "/models", + }, + } + + // 验证结构体字段值 + assert.Equal(t, "/path/to/kubeconfig", globalConfig.K8s.Kubeconfig) + assert.Equal(t, "test-context", globalConfig.K8s.Context) + assert.Equal(t, float32(10.0), globalConfig.K8s.QPS) + assert.Equal(t, 20, globalConfig.K8s.Burst) + assert.Equal(t, int64(30), globalConfig.K8s.Timeout) + assert.Equal(t, "8080", globalConfig.Server.Port) + assert.Equal(t, "info", globalConfig.Log.Level) + assert.Equal(t, "/var/log/app.log", globalConfig.Log.Path) + assert.Equal(t, 100, globalConfig.Log.MaxSize) + assert.Equal(t, 7, globalConfig.Log.MaxAge) + assert.Equal(t, true, globalConfig.Log.Compress) + assert.Equal(t, true, globalConfig.Log.ShowLine) + assert.Equal(t, true, globalConfig.Log.EnableConsole) + assert.Equal(t, "kubernetes", globalConfig.CurrentShimlet) + assert.Equal(t, "/etc/kubernetes/config.yaml", globalConfig.Shimlets["kubernetes"].ConfigPath) + assert.Equal(t, "/models", globalConfig.ModelManage.ModelRoot) + + // 测试 JSON 序列化和反序列化 + jsonData, err := json.Marshal(globalConfig) + assert.NoError(t, err) + assert.NotEmpty(t, jsonData) + + var unmarshaled GlobalConfig + err = json.Unmarshal(jsonData, &unmarshaled) + assert.NoError(t, err) + assert.Equal(t, globalConfig.K8s.Kubeconfig, unmarshaled.K8s.Kubeconfig) + assert.Equal(t, globalConfig.Server.Port, unmarshaled.Server.Port) +} + +// TestK8sConfig 测试 K8sConfig 结构体的基本功能 +func TestK8sConfig(t *testing.T) { + k8sConfig := &K8sConfig{ + Kubeconfig: "/another/path/to/kubeconfig", + Context: "another-context", + QPS: 5.0, + Burst: 10, + Timeout: 15, + } + + assert.Equal(t, "/another/path/to/kubeconfig", k8sConfig.Kubeconfig) + assert.Equal(t, "another-context", k8sConfig.Context) + assert.Equal(t, float32(5.0), k8sConfig.QPS) + assert.Equal(t, 10, k8sConfig.Burst) + assert.Equal(t, int64(15), k8sConfig.Timeout) +} + +// TestServerConfig 测试 Server 结构体的基本功能 +func TestServerConfig(t *testing.T) { + server := &Server{ + Port: "9090", + } + + assert.Equal(t, "9090", server.Port) +} + +// TestLogConfig 测试 LogConfig 结构体的基本功能 +func TestLogConfig(t *testing.T) { + logConfig := &LogConfig{ + Level: "debug", + Path: "/var/log/test.log", + MaxSize: 50, + MaxAge: 3, + Compress: false, + ShowLine: false, + EnableConsole: false, + } + + assert.Equal(t, "debug", logConfig.Level) + assert.Equal(t, "/var/log/test.log", logConfig.Path) + assert.Equal(t, 50, logConfig.MaxSize) + assert.Equal(t, 3, logConfig.MaxAge) + assert.Equal(t, false, logConfig.Compress) + assert.Equal(t, false, logConfig.ShowLine) + assert.Equal(t, false, logConfig.EnableConsole) +} + +// TestShimletConfig 测试 ShimletConfig 结构体的基本功能 +func TestShimletConfig(t *testing.T) { + shimletConfig := &ShimletConfig{ + ConfigPath: "/etc/shimlet/config.yaml", + } + + assert.Equal(t, "/etc/shimlet/config.yaml", shimletConfig.ConfigPath) +} + +// TestModelManageConfig 测试 ModelManageConfig 结构体的基本功能 +func TestModelManageConfig(t *testing.T) { + modelManageConfig := &ModelManageConfig{ + ModelRoot: "/test/models", + } + + assert.Equal(t, "/test/models", modelManageConfig.ModelRoot) +}