已合并
[bugfix]accelerator-type自动获取bugfix & docker only文档优化 #481
ganglv创建于 7月11日
[bugfix]accelerator-type自动获取bugfix & docker only文档优化 #481
已合并
共 8 个文件变更+397-223
| @@ -1,6 +1,66 @@ | |||
| 1 | -# 镜像制作 | 1 | +# 基于vllm-ascend/sglang镜像安装Motor |
| 2 | 2 | ||
| 3 | -## 获取vLLM-Ascend发布的镜像版本 | 3 | +## 依赖下载(可选) |
| 4 | + | ||
| 5 | +>[!NOTE]说明 | ||
| 6 | +>如果制作镜像的机器不能联网,先下载依赖。 | ||
| 7 | + | ||
| 8 | +在有网的环境执行如下步骤: | ||
| 9 | + | ||
| 10 | +### 1. 下载pciutils | ||
| 11 | + | ||
| 12 | +```sh | ||
| 13 | +mkdir -p /mnt/pciutils-offline | ||
| 14 | +cd /mnt/pciutils-offline | ||
| 15 | + | ||
| 16 | +apt-get install -y apt-rdepends | ||
| 17 | +apt-get download $(apt-rdepends pciutils | grep -v "^ ") | ||
| 18 | + | ||
| 19 | +cd /mnt/ | ||
| 20 | +tar -czvf pciutils-offline.tar.gz pciutils-offline | ||
| 21 | +``` | ||
| 22 | + | ||
| 23 | +将`/mnt/pciutils-offline.tar.gz`拷贝到制作镜像机器的`/mnt/`路径下 | ||
| 24 | + | ||
| 25 | +### 2. 下载whl依赖 | ||
| 26 | + | ||
| 27 | +下载Motor代码到`/mnt/`路径下 | ||
| 28 | + | ||
| 29 | +```bash | ||
| 30 | +cd /mnt/ | ||
| 31 | +git clone <motor的git链接> | ||
| 32 | + | ||
| 33 | +mkdir -p /mnt/packages-offline | ||
| 34 | + | ||
| 35 | +# 镜像已自带 transformers,下载前删除该依赖,避免版本冲突 | ||
| 36 | +sed -i '/^transformers/d' /mnt/MindIE-PyMotor/requirements.txt | ||
| 37 | + | ||
| 38 | +pip download -r /mnt/MindIE-PyMotor/requirements.txt -d /mnt/packages-offline -i https://pypi.tuna.tsinghua.edu.cn/simple | ||
| 39 | + | ||
| 40 | +cd /mnt/ | ||
| 41 | +tar -czvf packages-offline.tar.gz packages-offline | ||
| 42 | +``` | ||
| 43 | + | ||
| 44 | +将`/mnt/packages-offline.tar.gz`拷贝到制作镜像机器的`/mnt/`路径下 | ||
| 45 | + | ||
| 46 | +### 3. 构建motor的whl包 | ||
| 47 | + | ||
| 48 | +```bash | ||
| 49 | +cd /mnt/MindIE-PyMotor | ||
| 50 | + | ||
| 51 | +# 构建好的whl包在/mnt/MindIE-PyMotor/dist/路径下 | ||
| 52 | +bash build.sh | ||
| 53 | + | ||
| 54 | +cd /mnt/ | ||
| 55 | +tar -czvf MindIE-PyMotor.tar.gz MindIE-PyMotor | ||
| 56 | +``` | ||
| 57 | + | ||
| 58 | +将`/mnt/MindIE-PyMotor.tar.gz`拷贝到制作镜像机器的`/mnt/`路径下 | ||
| 59 | + | ||
| 60 | +## 获取基础镜像,以vLLM-Ascend为例 | ||
| 61 | + | ||
| 62 | +>[!NOTE]说明 | ||
| 63 | +>为提高下载速度,可将`quay.io`替换为`quay.nju.edu.cn`。 | ||
| 4 | 64 | ||
| 5 | 获取方法:打开[RED HAT](https://quay.io/repository/ascend/vllm-ascend?tab=tags),点击需要下载的版本。 | 65 | 获取方法:打开[RED HAT](https://quay.io/repository/ascend/vllm-ascend?tab=tags),点击需要下载的版本。 |
| 6 | 以v0.13.0版本为例,下载命令为: | 66 | 以v0.13.0版本为例,下载命令为: |
| @@ -9,145 +69,115 @@ | |||
| 9 | docker pull quay.io/ascend/vllm-ascend:v0.13.0 | 69 | docker pull quay.io/ascend/vllm-ascend:v0.13.0 |
| 10 | ``` | 70 | ``` |
| 11 | 71 | ||
| 12 | ->[!NOTE]说明 | 72 | +## 安装PyMotor |
| 13 | ->为提高下载速度,可将`quay.io`替换为`quay.nju.edu.cn`。 | ||
| 14 | 73 | ||
| 15 | -## 在镜像中安装PyMotor | 74 | +### 1. 查看镜像 |
| 16 | 75 | ||
| 17 | ->[!NOTE]说明 | 76 | +```bash |
| 18 | ->如果制作镜像的机器不能联网,需要在步骤4中下载依赖。 | 77 | +docker images |
| 78 | +``` | ||
| 19 | 79 | ||
| 20 | -1. 准备好目标motor代码,执行以下命令,git命令根据需要下载的分支或tag进行修改。 | 80 | +### 2. 创建容器,并挂载mnt目录 |
| 81 | + | ||
| 82 | +```bash | ||
| 83 | +docker run -d --name docker-vllm-ascend -v /mnt/:/mnt/ <镜像名称> | ||
| 84 | +``` | ||
| 85 | + | ||
| 86 | +### 3. 启动容器 | ||
| 87 | + | ||
| 88 | +```bash | ||
| 89 | +docker start docker-vllm-ascend | ||
| 90 | +``` | ||
| 91 | + | ||
| 92 | +### 4. 进入容器 | ||
| 93 | + | ||
| 94 | +```bash | ||
| 95 | +docker exec -it docker-vllm-ascend bash | ||
| 96 | +``` | ||
| 97 | + | ||
| 98 | +### 5. 安装motor及其依赖 | ||
| 99 | + | ||
| 100 | +#### 5.1 安装 pciutils | ||
| 101 | + | ||
| 102 | +- 在线安装: | ||
| 103 | + | ||
| 104 | +```bash | ||
| 105 | +apt-get update && apt-get install pciutils -y | ||
| 106 | +``` | ||
| 107 | + | ||
| 108 | +- 离线安装: | ||
| 109 | + | ||
| 110 | +```sh | ||
| 111 | +cd /mnt/ | ||
| 112 | +tar -xzvf pciutils-offline.tar.gz | ||
| 113 | +cd pciutils-offline | ||
| 114 | + | ||
| 115 | +dpkg -i *.deb | ||
| 116 | +``` | ||
| 117 | + | ||
| 118 | +#### 5.2 安装whl依赖 | ||
| 119 | + | ||
| 120 | +- 在线安装: | ||
| 21 | 121 | ||
| 22 | ```bash | 122 | ```bash |
| 123 | + # 下载motor代码,执行以下命令,git命令根据需要下载的分支或tag进行修改 | ||
| 23 | cd /mnt/ | 124 | cd /mnt/ |
| 24 | git clone <motor的git链接> | 125 | git clone <motor的git链接> |
| 126 | + | ||
| 127 | + cd /mnt/MindIE-PyMotor | ||
| 128 | + | ||
| 129 | + # 镜像已自带 transformers,安装前删除该依赖,避免版本冲突 | ||
| 130 | + sed -i '/^transformers/d' requirements.txt | ||
| 131 | + | ||
| 132 | + pip install -r requirements.txt | ||
| 133 | + | ||
| 134 | + bash build.sh | ||
| 135 | + pip install --force-reinstall ./dist/motor-0.1.0-py3-none-any.whl | ||
| 136 | + | ||
| 137 | + mkdir -p /tmp/motor/ | ||
| 138 | + cp -r ./examples/ /tmp/motor/ | ||
| 139 | + | ||
| 140 | + # 退出容器 | ||
| 141 | + exit | ||
| 25 | ``` | 142 | ``` |
| 26 | 143 | ||
| 27 | -2. 执行以下命令查看第一步下载下来的镜像。 | 144 | +- 离线安装 |
| 28 | 145 | ||
| 29 | ```bash | 146 | ```bash |
| 30 | - docker images | 147 | + # 安装whl依赖 |
| 148 | + cd /mnt/ | ||
| 149 | + tar -xzvf packages-offline.tar.gz | ||
| 150 | + pip install /mnt/packages-offline/*.whl --force-reinstall --no-index -v | ||
| 151 | + | ||
| 152 | + # 安装motor | ||
| 153 | + pip install --force-reinstall /mnt/MindIE-PyMotor/dist/motor-0.1.0-py3-none-any.whl --force-reinstall --no-index -v | ||
| 154 | + | ||
| 155 | + # 拷贝examples | ||
| 156 | + mkdir -p /tmp/motor/ | ||
| 157 | + cp -r /mnt/MindIE-PyMotor/examples/ /tmp/motor/ | ||
| 158 | + | ||
| 159 | + # 退出容器 | ||
| 160 | + exit | ||
| 31 | ``` | 161 | ``` |
| 32 | 162 | ||
| 33 | -3. 执行以下命令运行容器并挂载mnt目录。 | 163 | +### 6. 保存镜像 |
| 34 | 164 | ||
| 35 | - ```bash | 165 | +```bash |
| 36 | - docker run -d --name docker-vllm-ascend -v /mnt/:/mnt/ <镜像名称> | 166 | +docker commit -m "add motor" docker-vllm-ascend mindie-motor-vllm:dev-800I-A3-py311-lts-aarch64 |
| 37 | - ``` | 167 | +``` |
| 38 | 168 | ||
| 39 | -4. 执行以下命令启动容器。 | 169 | +保存后,`mindie-motor-vllm:dev-800I-A3-py311-lts-aarch64`镜像就是制作好之后带motor的镜像。 |
| 40 | 170 | ||
| 41 | - ```bash | 171 | +### 7. 打包镜像 |
| 42 | - docker start docker-vllm-ascend | ||
| 43 | - ``` | ||
| 44 | 172 | ||
| 45 | -5. 依赖下载,**如果制作镜像的机器能联网,可在线安装,忽略此步骤** | 173 | +```bash |
| 174 | +docker save -o /mnt/motor-vllm-ascend.tar mindie-motor-vllm:dev-800I-A3-py311-lts-aarch64 | ||
| 175 | +``` | ||
| 46 | 176 | ||
| 47 | - 1. 下载`pciutils` | 177 | +### 8. 导入带有motor的镜像 |
| 48 | 178 | ||
| 49 | - ```sh | 179 | +在非制作镜像的节点导入镜像 |
| 50 | - mkdir -p /mnt/pciutils-offline | ||
| 51 | - cd /mnt/pciutils-offline | ||
| 52 | 180 | ||
| 53 | - apt-get install -y apt-rdepends | 181 | +```bash |
| 54 | - apt-get download $(apt-rdepends pciutils | grep -v "^ ") | 182 | +docker load -i /mnt/motor-vllm-ascend.tar |
| 55 | - | 183 | +``` |
| 56 | - cd /mnt/ | ||
| 57 | - tar -czvf pciutils-offline.tar.gz pciutils-offline | ||
| 58 | - ``` | ||
| 59 | - | ||
| 60 | - 将`/mnt/pciutils-offline.tar.gz`拷贝到制作镜像机器的`/mnt/`路径下 | ||
| 61 | - | ||
| 62 | - 2. 下载`whl`依赖 | ||
| 63 | - | ||
| 64 | - ```sh | ||
| 65 | - mkdir -p /mnt/packages-offline | ||
| 66 | - pip download -r MindIE-PyMotor/requirements.txt -d /mnt/packages-offline -i https://pypi.tuna.tsinghua.edu.cn/simple | ||
| 67 | - | ||
| 68 | - cd /mnt/ | ||
| 69 | - tar -czvf packages-offline.tar.gz packages-offline | ||
| 70 | - ``` | ||
| 71 | - | ||
| 72 | - 将`/mnt/packages-offline.tar.gz`拷贝到制作镜像机器的`/mnt/`路径下 | ||
| 73 | - | ||
| 74 | -6. 执行以下命令制作镜像。 | ||
| 75 | - | ||
| 76 | - **进入容器** | ||
| 77 | - | ||
| 78 | - ```bash | ||
| 79 | - docker exec -it docker-vllm-ascend bash | ||
| 80 | - ``` | ||
| 81 | - | ||
| 82 | - **安装 `pciutils`** | ||
| 83 | - | ||
| 84 | - - 在线安装: | ||
| 85 | - | ||
| 86 | - ```bash | ||
| 87 | - apt-get update && apt-get install pciutils -y | ||
| 88 | - ``` | ||
| 89 | - | ||
| 90 | - - 离线安装: | ||
| 91 | - | ||
| 92 | - ```sh | ||
| 93 | - cd /mnt/ | ||
| 94 | - tar -xzvf pciutils-offline.tar.gz | ||
| 95 | - cd pciutils-offline | ||
| 96 | - | ||
| 97 | - dpkg -i *.deb | ||
| 98 | - ``` | ||
| 99 | - | ||
| 100 | - **安装whl依赖** | ||
| 101 | - | ||
| 102 | - - 在线安装: | ||
| 103 | - | ||
| 104 | - ```bash | ||
| 105 | - cd /mnt/MindIE-PyMotor | ||
| 106 | - pip install -r requirements.txt | ||
| 107 | - bash build.sh | ||
| 108 | - pip install --force-reinstall ./dist/motor-0.1.0-py3-none-any.whl | ||
| 109 | - | ||
| 110 | - mkdir -p /tmp/motor/ | ||
| 111 | - cp -r ./examples/ /tmp/motor/ | ||
| 112 | - | ||
| 113 | - exit | ||
| 114 | - ``` | ||
| 115 | - | ||
| 116 | - - 离线安装 | ||
| 117 | - | ||
| 118 | - ```bash | ||
| 119 | - cd /mnt/ | ||
| 120 | - tar -xzvf packages-offline.tar.gz | ||
| 121 | - | ||
| 122 | - cd /mnt/MindIE-PyMotor | ||
| 123 | - pip install --no-index --find-links=/mnt/packages-offline -r requirements.txt | ||
| 124 | - bash build.sh | ||
| 125 | - pip install --force-reinstall ./dist/motor-0.1.0-py3-none-any.whl | ||
| 126 | - | ||
| 127 | - mkdir -p /tmp/motor/ | ||
| 128 | - cp -r ./examples/ /tmp/motor/ | ||
| 129 | - | ||
| 130 | - exit | ||
| 131 | - ``` | ||
| 132 | - | ||
| 133 | -7. 执行以下命令保存镜像。 | ||
| 134 | - | ||
| 135 | - ```bash | ||
| 136 | - docker commit -m "add motor" docker-vllm-ascend mindie-motor-vllm:dev-800I-A3-py311-lts-aarch64 | ||
| 137 | - ``` | ||
| 138 | - | ||
| 139 | - 保存后,`mindie-motor-vllm:dev-800I-A3-py311-lts-aarch64`镜像就是制作好之后带motor的镜像。 | ||
| 140 | - | ||
| 141 | -8. 打包镜像 | ||
| 142 | - | ||
| 143 | - ```bash | ||
| 144 | - docker save -o /mnt/motor-vllm-ascend.tar mindie-motor-vllm:dev-800I-A3-py311-lts-aarch64 | ||
| 145 | - ``` | ||
| 146 | - | ||
| 147 | -9. 导入带有motor的镜像 | ||
| 148 | - | ||
| 149 | - 在非制作镜像的节点导入镜像 | ||
| 150 | - | ||
| 151 | - ```bash | ||
| 152 | - docker load -i /mnt/motor-vllm-ascend.tar | ||
| 153 | - ``` | ||
| @@ -6,6 +6,36 @@ | |||
| 6 | 6 | ||
| 7 | ## 部署流程 | 7 | ## 部署流程 |
| 8 | 8 | ||
| 9 | +以`/mnt/motor`作为根路径,目录结构如下: | ||
| 10 | + | ||
| 11 | +```text | ||
| 12 | +/mnt/motor/ | ||
| 13 | +├── prepare.sh | ||
| 14 | +├── start_motor.sh | ||
| 15 | +├── start_docker.sh | ||
| 16 | +├── user_config.json | ||
| 17 | +├── env.json | ||
| 18 | +├── examples/ | ||
| 19 | +└── configmap/ # 该目录下的文件都是自动生成的 | ||
| 20 | + ├── boot.sh | ||
| 21 | + ├── common.sh | ||
| 22 | + ├── hccl_tools.py | ||
| 23 | + ├── mooncake_config.py | ||
| 24 | + ├── all_combine_in_single_container.sh | ||
| 25 | + ├── controller.sh | ||
| 26 | + ├── coordinator.sh | ||
| 27 | + ├── engine.sh | ||
| 28 | + ├── kv_conductor.sh | ||
| 29 | + ├── kv_pool.sh | ||
| 30 | + ├── mf_store.sh | ||
| 31 | + ├── user_config.json | ||
| 32 | + └── env.json | ||
| 33 | +``` | ||
| 34 | + | ||
| 35 | +### 准备examples | ||
| 36 | + | ||
| 37 | +`examples` 获取方式见[quick_start](../../quick_start.md#服务部署) | ||
| 38 | + | ||
| 9 | ### 准备user_config.json和env.json配置文件 | 39 | ### 准备user_config.json和env.json配置文件 |
| 10 | 40 | ||
| 11 | 可从如下路径获取[user_config.json](https://gitcode.com/Ascend/MindIE-PyMotor/blob/master/examples/infer_engines/vllm/user_config.json)和[env.json](https://gitcode.com/Ascend/MindIE-PyMotor/blob/master/examples/infer_engines/vllm/env.json)模板,本文主要介绍docker-only部署方式相关适配点,其他特性请参考[quick_start](../../quick_start.md)。 | 41 | 可从如下路径获取[user_config.json](https://gitcode.com/Ascend/MindIE-PyMotor/blob/master/examples/infer_engines/vllm/user_config.json)和[env.json](https://gitcode.com/Ascend/MindIE-PyMotor/blob/master/examples/infer_engines/vllm/env.json)模板,本文主要介绍docker-only部署方式相关适配点,其他特性请参考[quick_start](../../quick_start.md)。 |
| @@ -61,17 +91,20 @@ | |||
| 61 | } | 91 | } |
| 62 | ``` | 92 | ``` |
| 63 | 93 | ||
| 64 | -### 准备CONFIGMAP_PATH | 94 | +### 准备configmap |
| 65 | 95 | ||
| 66 | 准备阶段需将配置文件、启动脚本拷贝到环境变量**CONFIGMAP_PATH**对应目录下,并通过set_env_docker.py加载环境变量。准备阶段脚本**prepare.sh**示例(**EXAMPLES_PATH**、**CONFIGMAP_PATH**、**USER_CONFIG_PATH**、**ENV_PATH**需修改为实际路径): | 96 | 准备阶段需将配置文件、启动脚本拷贝到环境变量**CONFIGMAP_PATH**对应目录下,并通过set_env_docker.py加载环境变量。准备阶段脚本**prepare.sh**示例(**EXAMPLES_PATH**、**CONFIGMAP_PATH**、**USER_CONFIG_PATH**、**ENV_PATH**需修改为实际路径): |
| 67 | 97 | ||
| 98 | +以下以`/mnt/motor`作为根路径为例 | ||
| 99 | + | ||
| 68 | ```shell | 100 | ```shell |
| 69 | -EXAMPLES_PATH="xxx" # 主机examples部署脚本路径 | 101 | +EXAMPLES_PATH="/mnt/motor/examples/" |
| 70 | -CONFIGMAP_PATH="xxx" # 服务启动脚本路径,需挂载到容器内 | 102 | +CONFIGMAP_PATH="/mnt/motor/configmap/" |
| 71 | -USER_CONFIG_PATH="xxx" # user_config.json路径 | 103 | +USER_CONFIG_PATH="/mnt/motor/user_config.json" |
| 72 | -ENV_PATH="xxx" # env.json路径 | 104 | +ENV_PATH="/mnt/motor/env.json" |
| 73 | 105 | ||
| 74 | mkdir -p $CONFIGMAP_PATH | 106 | mkdir -p $CONFIGMAP_PATH |
| 107 | + | ||
| 75 | # 容器启动脚本boot.sh,其运行时会调用startup目录下其他脚本,需要将其统一拷贝到$CONFIGMAP_PATH目录下。 | 108 | # 容器启动脚本boot.sh,其运行时会调用startup目录下其他脚本,需要将其统一拷贝到$CONFIGMAP_PATH目录下。 |
| 76 | cp -f $EXAMPLES_PATH/deployer/startup/boot.sh $CONFIGMAP_PATH/boot.sh | 109 | cp -f $EXAMPLES_PATH/deployer/startup/boot.sh $CONFIGMAP_PATH/boot.sh |
| 77 | cp -f $EXAMPLES_PATH/deployer/startup/common.sh $CONFIGMAP_PATH/common.sh | 110 | cp -f $EXAMPLES_PATH/deployer/startup/common.sh $CONFIGMAP_PATH/common.sh |
| @@ -109,38 +142,52 @@ python $EXAMPLES_PATH/deployer/startup/set_env_docker.py --configmap_path $CONFI | |||
| 109 | sh prepare.sh | 142 | sh prepare.sh |
| 110 | ``` | 143 | ``` |
| 111 | 144 | ||
| 112 | -### Docker启动服务 | 145 | +执行完成后,在`/mnt/motor/configmap/`目录下会生成一些脚本 |
| 113 | 146 | ||
| 114 | -准备启动脚本start_docker.sh,脚本示例(**CONFIGMAP_PATH**需修改为实际路径,**IMAGE_NAME**需修改为实际镜像名): | 147 | +### 准备Motor启动脚本 |
| 148 | + | ||
| 149 | +准备`start_motor.sh`脚本 | ||
| 150 | + | ||
| 151 | +```sh | ||
| 152 | +CONFIGMAP_PATH="/mnt/motor/configmap" # CONFIGMAP_PATH需与prepare.sh保持一致,且必须使用绝对路径 | ||
| 153 | +CONFIG_PATH=/usr/local/Ascend/pyMotor/conf | ||
| 154 | + | ||
| 155 | +ROLE=SINGLE_CONTAINER | ||
| 156 | + | ||
| 157 | +# mooncake池化配置 | ||
| 158 | +# 若开启池化,KVS_MASTER_SERVICE设置为任意非空字符串,如kvp_master,不开启池化设置为空。 | ||
| 159 | +KVS_MASTER_SERVICE="" | ||
| 160 | +KV_STORE_PORT=50088 | ||
| 161 | +KV_STORE_EVICTION_HIGH_WATERMARK_RATIO=0.9 | ||
| 162 | +KV_STORE_EVICTION_RATIO=0.1 | ||
| 163 | +DEFAULT_KV_LEASE_TTL=11000 | ||
| 164 | + | ||
| 165 | +source $CONFIGMAP_PATH/boot.sh | ||
| 166 | +``` | ||
| 167 | + | ||
| 168 | +环境变量说明: | ||
| 169 | + | ||
| 170 | +| 变量名 | 含义 | 取值 | | ||
| 171 | +| :--- | :--- | :--- | | ||
| 172 | +| KVS_MASTER_SERVICE | mooncake_master部署域名 | 若开启kv_pool,设置为任意非空字符串,如kvp_master,boot.sh会自动适配为容器ip;若不开启则设置为空 | | ||
| 173 | +| KV_STORE_PORT | mooncake_master部署端口 | 若开启kv_pool,设置任意有效端口,如50088;若不开启则设置为空 | | ||
| 174 | +| KV_STORE_EVICTION_HIGH_WATERMARK_RATIO | mooncake_master进程高水位比例 | 若开启kv_pool,取值0~1;若不开启则设置为空 | | ||
| 175 | +| KV_STORE_EVICTION_RATIO | mooncake_master进程逐出比例 | 若开启kv_pool,取值0~1;若不开启则设置为空 | | ||
| 176 | +| DEFAULT_KV_LEASE_TTL | 控制 KV 对象的默认租约 TTL(毫秒) | 配置值需大于env.json中vllm实例的环境变量`ASCEND_CONNECT_TIMEOUT`和`ASCEND_TRANSFER_TIMEOUT`。默认值11000;若不开启kv_pool则设置为空 | | ||
| 177 | + | ||
| 178 | +### 准备Docker启动脚本 | ||
| 179 | + | ||
| 180 | +准备启动脚本`start_docker.sh`,脚本示例(**CONFIGMAP_PATH**需修改为实际路径,**IMAGE_NAME**需修改为实际镜像名): | ||
| 115 | 181 | ||
| 116 | ```shell | 182 | ```shell |
| 117 | # 默认不开启特权容器,如需开启,将--privileged=false改为--privileged=true | 183 | # 默认不开启特权容器,如需开启,将--privileged=false改为--privileged=true |
| 118 | -CONFIGMAP_PATH="xxx" # CONFIGMAP_PATH需与prepare.sh保持一致,且必须使用绝对路径 | 184 | +CONFIGMAP_PATH="/mnt/motor/configmap" # CONFIGMAP_PATH需与prepare.sh保持一致,且必须使用绝对路径 |
| 119 | IMAGE_NAME="xxx" # 镜像名 | 185 | IMAGE_NAME="xxx" # 镜像名 |
| 120 | 186 | ||
| 121 | -# 从环境变量读取可见卡,默认自动检测主机昇腾卡,用逗号拼接,如"0,1,2,3" | ||
| 122 | -if [ -z "$ASCEND_VISIBLE_DEVICES" ]; then | ||
| 123 | - ASCEND_VISIBLE_DEVICES=$(ls /dev/davinci[0-9]* 2>/dev/null | sed 's/[^0-9]//g' | paste -sd "," -) | ||
| 124 | -fi | ||
| 125 | ASCEND_DEVICES="--device=/dev/davinci_manager --device=/dev/devmm_svm --device=/dev/hisi_hdc" | 187 | ASCEND_DEVICES="--device=/dev/davinci_manager --device=/dev/devmm_svm --device=/dev/hisi_hdc" |
| 126 | -# 循环挂载ASCEND_VISIBLE_DEVICES指定卡 | ||
| 127 | -IFS=',' read -ra ADDR <<< "$ASCEND_VISIBLE_DEVICES" | ||
| 128 | -for i in "${ADDR[@]}"; do | ||
| 129 | - ASCEND_DEVICES="$ASCEND_DEVICES --device=/dev/davinci$i" | ||
| 130 | -done | ||
| 131 | 188 | ||
| 132 | docker run -u root --rm --name single_container \ | 189 | docker run -u root --rm --name single_container \ |
| 133 | -e ASCEND_RUNTIME_OPTIONS=NODRV --privileged=false \ | 190 | -e ASCEND_RUNTIME_OPTIONS=NODRV --privileged=false \ |
| 134 | --e CONFIGMAP_PATH=$CONFIGMAP_PATH \ | ||
| 135 | --e CONFIG_PATH=/usr/local/Ascend/pyMotor/conf \ | ||
| 136 | --e ROLE=SINGLE_CONTAINER \ | ||
| 137 | --e KVS_MASTER_SERVICE=$KVS_MASTER_SERVICE \ | ||
| 138 | --e KV_STORE_PORT=$KV_STORE_PORT \ | ||
| 139 | --e KV_STORE_EVICTION_HIGH_WATERMARK_RATIO=$KV_STORE_EVICTION_HIGH_WATERMARK_RATIO \ | ||
| 140 | --e KV_STORE_EVICTION_RATIO=$KV_STORE_EVICTION_RATIO \ | ||
| 141 | --e DEFAULT_KV_LEASE_TTL=$DEFAULT_KV_LEASE_TTL \ | ||
| 142 | --p $ENDPOINT_PORT_RANGE:$ENDPOINT_PORT_RANGE \ | ||
| 143 | --p $KV_PORT_RANGE:$KV_PORT_RANGE \ | ||
| 144 | $ASCEND_DEVICES \ | 191 | $ASCEND_DEVICES \ |
| 145 | -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ | 192 | -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ |
| 146 | -v /usr/local/Ascend/add-ons/:/usr/local/Ascend/add-ons/ \ | 193 | -v /usr/local/Ascend/add-ons/:/usr/local/Ascend/add-ons/ \ |
| @@ -148,30 +195,20 @@ $ASCEND_DEVICES \ | |||
| 148 | -v /usr/local/sbin:/usr/local/sbin \ | 195 | -v /usr/local/sbin:/usr/local/sbin \ |
| 149 | -v /var/log/npu/:/usr/slog \ | 196 | -v /var/log/npu/:/usr/slog \ |
| 150 | -v /mnt:/mnt \ | 197 | -v /mnt:/mnt \ |
| 198 | +-p 31015:1025 \ | ||
| 199 | +-p 31017:1027 \ | ||
| 151 | $IMAGE_NAME \ | 200 | $IMAGE_NAME \ |
| 152 | -bash -c "export POD_IP=\$(grep \$(hostname) /etc/hosts | cut -f1) && source \$CONFIGMAP_PATH/boot.sh" | 201 | +bash -c "export POD_IP=\$(grep \$(hostname) /etc/hosts | cut -f1) && source /mnt/motor/start_motor.sh" |
🟠 High Priority 变更行:第 190–200 行, 旧版有:
-p ENDPOINT_PORT_RANGE 新版完全移除了这些端口映射,同时 影响:新版 建议:恢复 ![]() ![]() 不准确? | |||
| 153 | ``` | 202 | ``` |
| 154 | 203 | ||
| 155 | -环境变量说明: | 204 | +**注意:挂载路径要包含/mnt** |
| 156 | 205 | ||
| 157 | -| 变量名 | 含义 | 取值 | | 206 | +### 启动Docker |
| 158 | -| :--- | :--- | :--- | | ||
| 159 | -| CONFIGMAP_PATH | 启动脚本路径 | 与2.2小节保持一致,需挂载到容器中 | | ||
| 160 | -| IMAGE_NAME | 镜像名 | 版本镜像,确保docker images能查询到 | | ||
| 161 | -| ASCEND_VISIBLE_DEVICES | 可见卡 | 指定挂载卡,如"0,1,2,3",默认自动检测主机昇腾卡 | | ||
| 162 | -| ENDPOINT_PORT_RANGE | endpoint端口映射区间 | 非host网络部署设置endpoint端口映射,起始端口默认值10000,先P后D,每dp端口偏移2,分别对应推理端口和管理端口 | | ||
| 163 | -| KV_PORT_RANGE | kv_port映射端口区间 | 非host网络部署设置kv_port映射端口,起始端口user-config.json中motor_engine_prefill_config下kv_port值,先P后D,每实例端口偏移1 | | ||
| 164 | -| KVS_MASTER_SERVICE | mooncake_master部署域名 | 若开启kv_pool,设置为任意非空字符串,如kvp_master,boot.sh会自动适配为容器ip;若不开启则设置为空 | | ||
| 165 | -| KV_STORE_PORT | mooncake_master部署端口 | 若开启kv_pool,设置任意有效端口,如50088;若不开启则设置为空 | | ||
| 166 | -| KV_STORE_EVICTION_HIGH_WATERMARK_RATIO | mooncake_master进程高水位比例 | 若开启kv_pool,取值0~1;若不开启则设置为空 | | ||
| 167 | -| KV_STORE_EVICTION_RATIO | mooncake_master进程逐出比例 | 若开启kv_pool,取值0~1;若不开启则设置为空 | | ||
| 168 | -| DEFAULT_KV_LEASE_TTL | 控制 KV 对象的默认租约 TTL(毫秒) | 配置值需大于env.json中vllm实例的环境变量`ASCEND_CONNECT_TIMEOUT`和`ASCEND_TRANSFER_TIMEOUT`。默认值11000;若不开启kv_pool则设置为空 | | ||
| 169 | 207 | ||
| 170 | 启动服务示例(1P1D): | 208 | 启动服务示例(1P1D): |
| 171 | 209 | ||
| 172 | ```shell | 210 | ```shell |
| 173 | -# 若开启池化,KVS_MASTER_SERVICE设置为任意非空字符串,如kvp_master,不开启池化设置为空。 | 211 | +sh start_docker.sh |
| 174 | -ASCEND_VISIBLE_DEVICES=0,1 KVS_MASTER_SERVICE="" KV_STORE_PORT=50088 KV_STORE_EVICTION_HIGH_WATERMARK_RATIO=0.9 KV_STORE_EVICTION_RATIO=0.1 DEFAULT_KV_LEASE_TTL=11000 sh start_docker.sh | ||
| 175 | ``` | 212 | ``` |
| 176 | 213 | ||
| 177 | ### A5 环境额外修改内容 | 214 | ### A5 环境额外修改内容 |
| @@ -196,7 +233,6 @@ A5 启动示例片段(基于上述实例基础修改): | |||
| 196 | 233 | ||
| 197 | ```shell | 234 | ```shell |
| 198 | ASCEND_DEVICES="--device=/dev/davinci_manager --device=/dev/hisi_hdc" | 235 | ASCEND_DEVICES="--device=/dev/davinci_manager --device=/dev/hisi_hdc" |
| 199 | -# 按 ASCEND_VISIBLE_DEVICES 循环追加 --device=/dev/davinci$i | ||
| 200 | 236 | ||
| 201 | docker run -u root --rm --name single_container \ | 237 | docker run -u root --rm --name single_container \ |
| 202 | --network host \ | 238 | --network host \ |
| @@ -182,12 +182,12 @@ def _get_hardware_node_labels(hardware_type): | |||
| 182 | if hardware_type in C.HARDWARE_TYPE_A2 or hardware_type in C.HARDWARE_TYPE_A3: | 182 | if hardware_type in C.HARDWARE_TYPE_A2 or hardware_type in C.HARDWARE_TYPE_A3: |
| 183 | return { | 183 | return { |
| 184 | C.ACCELERATOR: C.ACCELERATOR_910, | 184 | C.ACCELERATOR: C.ACCELERATOR_910, |
| 185 | - C.ACCELERATOR_TYPE: get_accelerator_type_from_cluster(), | 185 | + C.ACCELERATOR_TYPE: get_accelerator_type_from_cluster(hardware_type), |
| 186 | } | 186 | } |
| 187 | if hardware_type in C.HARDWARE_TYPE_950I_A5: | 187 | if hardware_type in C.HARDWARE_TYPE_950I_A5: |
| 188 | return { | 188 | return { |
| 189 | C.ACCELERATOR: C.ACCELERATOR_A5, | 189 | C.ACCELERATOR: C.ACCELERATOR_A5, |
| 190 | - C.ACCELERATOR_TYPE: get_accelerator_type_from_cluster(), | 190 | + C.ACCELERATOR_TYPE: get_accelerator_type_from_cluster(hardware_type), |
| 191 | } | 191 | } |
| 192 | known = [*sorted(C.HARDWARE_TYPE_A2), *sorted(C.HARDWARE_TYPE_A3), *C.HARDWARE_TYPE_950I_A5] | 192 | known = [*sorted(C.HARDWARE_TYPE_A2), *sorted(C.HARDWARE_TYPE_A3), *C.HARDWARE_TYPE_950I_A5] |
| 193 | raise ValueError(f"Unknown hardware_type '{hardware_type}'. Supported values: {known}") | 193 | raise ValueError(f"Unknown hardware_type '{hardware_type}'. Supported values: {known}") |
| @@ -197,9 +197,11 @@ def set_engine_npu(container, deploy_config, node_type): | |||
| 197 | 197 | ||
| 198 | 198 | ||
| 199 | def apply_node_selector_by_hardware(pod_spec, hardware_type): | 199 | def apply_node_selector_by_hardware(pod_spec, hardware_type): |
| 200 | - pod_spec[C.NODE_SELECTOR][C.ACCELERATOR_TYPE] = k8s_utils.get_accelerator_type_from_cluster() | 200 | + if hardware_type in C.HARDWARE_TYPE_A2 or hardware_type in C.HARDWARE_TYPE_A3: |
| 201 | + pod_spec[C.NODE_SELECTOR][C.ACCELERATOR] = C.ACCELERATOR_910 | ||
| 201 | if hardware_type in C.HARDWARE_TYPE_950I_A5: | 202 | if hardware_type in C.HARDWARE_TYPE_950I_A5: |
| 202 | pod_spec[C.NODE_SELECTOR][C.ACCELERATOR] = C.ACCELERATOR_A5 | 203 | pod_spec[C.NODE_SELECTOR][C.ACCELERATOR] = C.ACCELERATOR_A5 |
| 204 | + pod_spec[C.NODE_SELECTOR][C.ACCELERATOR_TYPE] = k8s_utils.get_accelerator_type_from_cluster(hardware_type) | ||
| 203 | 205 | ||
| 204 | 206 | ||
| 205 | def apply_pd_heterogeneous_node_selector(pod_spec, deploy_config, node_type): | 207 | def apply_pd_heterogeneous_node_selector(pod_spec, deploy_config, node_type): |
| @@ -220,29 +220,79 @@ def _get_kubectl_path(): | |||
| 220 | return kubectl | 220 | return kubectl |
| 221 | 221 | ||
| 222 | 222 | ||
| 223 | -def get_accelerator_type_from_cluster(): | 223 | +def _get_cluster_nodes(label_selector): |
| 224 | - """Resolve accelerator-type from the first cluster node that has the label via kubectl.""" | ||
| 225 | - if C.ACCELERATOR_TYPE in _g_accelerator_type_cache: | ||
| 226 | - return _g_accelerator_type_cache[C.ACCELERATOR_TYPE] | ||
| 227 | - | ||
| 228 | kubectl = _get_kubectl_path() | 224 | kubectl = _get_kubectl_path() |
| 229 | - out = run_cmd_get_output([kubectl, "get", "nodes", "-o", "json"]) | 225 | + out = run_cmd_get_output([kubectl, "get", "nodes", "-l", label_selector, "-o", "json"]) |
| 230 | - nodes = json.loads(out).get("items", []) | 226 | + return json.loads(out).get("items", []) |
| 231 | 227 | ||
| 228 | + | ||
| 229 | +def _collect_node_accelerator_types(nodes): | ||
| 230 | + accelerator_types = set() | ||
| 232 | for node in nodes: | 231 | for node in nodes: |
| 233 | labels = node.get("metadata", {}).get("labels", {}) | 232 | labels = node.get("metadata", {}).get("labels", {}) |
| 234 | accelerator_type = labels.get(C.ACCELERATOR_TYPE) | 233 | accelerator_type = labels.get(C.ACCELERATOR_TYPE) |
| 235 | if accelerator_type: | 234 | if accelerator_type: |
| 236 | - logger.info( | 235 | + accelerator_types.add(accelerator_type) |
| 237 | - "Resolved %s=%s from node %s", | 236 | + return accelerator_types |
| 238 | - C.ACCELERATOR_TYPE, | ||
| 239 | - accelerator_type, | ||
| 240 | - node.get("metadata", {}).get("name", "<unknown>"), | ||
| 241 | - ) | ||
| 242 | - _g_accelerator_type_cache[C.ACCELERATOR_TYPE] = accelerator_type | ||
| 243 | - return accelerator_type | ||
| 244 | 237 | ||
| 245 | - raise RuntimeError(f"No node in cluster has label {C.ACCELERATOR_TYPE}") | 238 | + |
| 239 | +def _matches_hardware_generation(accelerator_type, hardware_type): | ||
| 240 | + if hardware_type in C.HARDWARE_TYPE_A2: | ||
| 241 | + return "910b" in accelerator_type.lower() | ||
| 242 | + if hardware_type in C.HARDWARE_TYPE_A3: | ||
| 243 | + return "a3" in accelerator_type.lower() | ||
| 244 | + return True | ||
| 245 | + | ||
| 246 | + | ||
| 247 | +def _resolve_accelerator_type_from_nodes(nodes, hardware_type): | ||
| 248 | + accelerator_types = _collect_node_accelerator_types(nodes) | ||
| 249 | + if not accelerator_types: | ||
| 250 | + raise RuntimeError(f"Matched nodes for hardware_type={hardware_type} do not have label {C.ACCELERATOR_TYPE}") | ||
| 251 | + | ||
| 252 | + matched_types = {value for value in accelerator_types if _matches_hardware_generation(value, hardware_type)} | ||
| 253 | + if not matched_types: | ||
| 254 | + raise RuntimeError( | ||
| 255 | + f"No {C.ACCELERATOR_TYPE} on cluster matches hardware_type={hardware_type}. " | ||
| 256 | + f"Found values: {sorted(accelerator_types)}" | ||
| 257 | + ) | ||
| 258 | + if len(matched_types) == 1: | ||
| 259 | + return next(iter(matched_types)) | ||
| 260 | + | ||
| 261 | + raise RuntimeError( | ||
| 262 | + f"Multiple {C.ACCELERATOR_TYPE} values match hardware_type={hardware_type}: {sorted(matched_types)}" | ||
| 263 | + ) | ||
| 264 | + | ||
| 265 | + | ||
| 266 | +def get_accelerator_type_from_cluster(hardware_type): | ||
| 267 | + """Resolve accelerator-type node label value from cluster nodes via kubectl.""" | ||
| 268 | + if hardware_type in _g_accelerator_type_cache: | ||
| 269 | + return _g_accelerator_type_cache[hardware_type] | ||
| 270 | + | ||
| 271 | + if hardware_type in C.HARDWARE_TYPE_950I_A5: | ||
| 272 | + label_selector = f"{C.ACCELERATOR}={C.ACCELERATOR_A5},{C.ACCELERATOR_TYPE}={hardware_type}" | ||
| 273 | + nodes = _get_cluster_nodes(label_selector) | ||
| 274 | + if not nodes: | ||
| 275 | + raise RuntimeError(f"No node in cluster matches {label_selector} for hardware_type={hardware_type}") | ||
| 276 | + accelerator_type = hardware_type | ||
| 277 | + elif hardware_type in C.HARDWARE_TYPE_A2 or hardware_type in C.HARDWARE_TYPE_A3: | ||
| 278 | + nodes = _get_cluster_nodes(f"{C.ACCELERATOR}={C.ACCELERATOR_910}") | ||
| 279 | + if not nodes: | ||
| 280 | + raise RuntimeError( | ||
| 281 | + f"No node in cluster with label {C.ACCELERATOR}={C.ACCELERATOR_910} for hardware_type={hardware_type}" | ||
| 282 | + ) | ||
| 283 | + accelerator_type = _resolve_accelerator_type_from_nodes(nodes, hardware_type) | ||
| 284 | + else: | ||
| 285 | + known = [*sorted(C.HARDWARE_TYPE_A2), *sorted(C.HARDWARE_TYPE_A3), *C.HARDWARE_TYPE_950I_A5] | ||
| 286 | + raise ValueError(f"Unknown hardware_type '{hardware_type}'. Supported values: {known}") | ||
| 287 | + | ||
| 288 | + logger.info( | ||
| 289 | + "Resolved %s=%s from cluster for hardware_type=%s", | ||
| 290 | + C.ACCELERATOR_TYPE, | ||
| 291 | + accelerator_type, | ||
| 292 | + hardware_type, | ||
| 293 | + ) | ||
| 294 | + _g_accelerator_type_cache[hardware_type] = accelerator_type | ||
| 295 | + return accelerator_type | ||
| 246 | 296 | ||
| 247 | 297 | ||
| 248 | def get_baseline_config_from_configmap(job_id): | 298 | def get_baseline_config_from_configmap(job_id): |
| @@ -1,3 +1,13 @@ | |||
| 1 | +# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved. | ||
| 2 | +# MindIE is licensed under Mulan PSL v2. | ||
| 3 | +# You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 4 | +# You may obtain a copy of Mulan PSL v2 at: | ||
| 5 | +# http://license.coscl.org.cn/MulanPSL2 | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 7 | +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 8 | +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 9 | +# See the Mulan PSL v2 for more details. | ||
| 10 | + | ||
| 1 | import sys | 11 | import sys |
| 2 | import types | 12 | import types |
| 3 | from pathlib import Path | 13 | from pathlib import Path |
| @@ -37,7 +47,7 @@ def mock_accelerator_type_from_cluster(request, monkeypatch): | |||
| 37 | monkeypatch.setattr( | 47 | monkeypatch.setattr( |
| 38 | k8s_utils, | 48 | k8s_utils, |
| 39 | "get_accelerator_type_from_cluster", | 49 | "get_accelerator_type_from_cluster", |
| 40 | - lambda: C.ACCELERATOR_TYPE_A3, | 50 | + lambda _hardware_type: C.ACCELERATOR_TYPE_A3, |
| 41 | ) | 51 | ) |
| 42 | yield | 52 | yield |
| 43 | k8s_utils._g_accelerator_type_cache.clear() | 53 | k8s_utils._g_accelerator_type_cache.clear() |
| @@ -1,3 +1,13 @@ | |||
| 1 | +# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved. | ||
| 2 | +# MindIE is licensed under Mulan PSL v2. | ||
| 3 | +# You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 4 | +# You may obtain a copy of Mulan PSL v2 at: | ||
| 5 | +# http://license.coscl.org.cn/MulanPSL2 | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 7 | +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 8 | +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 9 | +# See the Mulan PSL v2 for more details. | ||
| 10 | + | ||
| 1 | import json | 11 | import json |
| 2 | 12 | ||
| 3 | import pytest | 13 | import pytest |
| @@ -11,55 +21,67 @@ def mock_kubectl_path(monkeypatch): | |||
| 11 | monkeypatch.setattr(k8s_utils, "_get_kubectl_path", lambda: "kubectl") | 21 | monkeypatch.setattr(k8s_utils, "_get_kubectl_path", lambda: "kubectl") |
| 12 | 22 | ||
| 13 | 23 | ||
| 14 | -def test_get_accelerator_type_from_cluster_returns_first_node_label(monkeypatch): | 24 | +def _nodes_json(*label_maps): |
| 15 | - k8s_utils._g_accelerator_type_cache.clear() | 25 | + return { |
| 16 | - nodes_json = { | ||
| 17 | "items": [ | 26 | "items": [ |
| 18 | - {"metadata": {"name": "node-0", "labels": {"host-arch": "huawei-arm"}}}, | ||
| 19 | { | 27 | { |
| 20 | "metadata": { | 28 | "metadata": { |
| 21 | - "name": "node-1", | 29 | + "name": f"node-{index}", |
| 22 | - "labels": {C.ACCELERATOR_TYPE: C.ACCELERATOR_TYPE_A3}, | 30 | + "labels": labels, |
| 23 | } | 31 | } |
| 24 | - }, | 32 | + } |
| 25 | - { | 33 | + for index, labels in enumerate(label_maps) |
| 26 | - "metadata": { | ||
| 27 | - "name": "node-2", | ||
| 28 | - "labels": {C.ACCELERATOR_TYPE: C.ACCELERATOR_TYPE_910B}, | ||
| 29 | - } | ||
| 30 | - }, | ||
| 31 | ] | 34 | ] |
| 32 | } | 35 | } |
| 33 | - monkeypatch.setattr(k8s_utils, "run_cmd_get_output", lambda _args: json.dumps(nodes_json)) | ||
| 34 | - | ||
| 35 | - assert k8s_utils.get_accelerator_type_from_cluster() == C.ACCELERATOR_TYPE_A3 | ||
| 36 | 36 | ||
| 37 | 37 | ||
| 38 | -def test_get_accelerator_type_from_cluster_uses_accelerator_type_cache_key(monkeypatch): | 38 | +def test_get_accelerator_type_from_cluster_returns_matching_a3_label(monkeypatch): |
| 39 | + k8s_utils._g_accelerator_type_cache.clear() | ||
| 40 | + monkeypatch.setattr( | ||
| 41 | + k8s_utils, | ||
| 42 | + "run_cmd_get_output", | ||
| 43 | + lambda _args: json.dumps( | ||
| 44 | + _nodes_json( | ||
| 45 | + {"host-arch": "huawei-arm"}, | ||
| 46 | + {C.ACCELERATOR_TYPE: C.ACCELERATOR_TYPE_A3}, | ||
| 47 | + {C.ACCELERATOR_TYPE: C.ACCELERATOR_TYPE_910B}, | ||
| 48 | + ) | ||
| 49 | + ), | ||
| 50 | + ) | ||
| 51 | + | ||
| 52 | + assert k8s_utils.get_accelerator_type_from_cluster(C.HARDWARE_TYPE_800I_A3) == C.ACCELERATOR_TYPE_A3 | ||
| 53 | + | ||
| 54 | + | ||
| 55 | +def test_get_accelerator_type_from_cluster_returns_matching_a2_label(monkeypatch): | ||
| 56 | + k8s_utils._g_accelerator_type_cache.clear() | ||
| 57 | + monkeypatch.setattr( | ||
| 58 | + k8s_utils, | ||
| 59 | + "run_cmd_get_output", | ||
| 60 | + lambda _args: json.dumps( | ||
| 61 | + _nodes_json( | ||
| 62 | + {C.ACCELERATOR_TYPE: C.ACCELERATOR_TYPE_A3}, | ||
| 63 | + {C.ACCELERATOR_TYPE: C.ACCELERATOR_TYPE_910B}, | ||
| 64 | + ) | ||
| 65 | + ), | ||
| 66 | + ) | ||
| 67 | + | ||
| 68 | + assert k8s_utils.get_accelerator_type_from_cluster(C.HARDWARE_TYPE_800I_A2) == C.ACCELERATOR_TYPE_910B | ||
| 69 | + | ||
| 70 | + | ||
| 71 | +def test_get_accelerator_type_from_cluster_uses_hardware_type_cache_key(monkeypatch): | ||
| 39 | k8s_utils._g_accelerator_type_cache.clear() | 72 | k8s_utils._g_accelerator_type_cache.clear() |
| 40 | call_count = {"n": 0} | 73 | call_count = {"n": 0} |
| 41 | 74 | ||
| 42 | def fake_run(_args): | 75 | def fake_run(_args): |
| 43 | call_count["n"] += 1 | 76 | call_count["n"] += 1 |
| 44 | - return json.dumps( | 77 | + return json.dumps(_nodes_json({C.ACCELERATOR_TYPE: C.ACCELERATOR_TYPE_910B})) |
| 45 | - { | ||
| 46 | - "items": [ | ||
| 47 | - { | ||
| 48 | - "metadata": { | ||
| 49 | - "name": "node-0", | ||
| 50 | - "labels": {C.ACCELERATOR_TYPE: C.ACCELERATOR_TYPE_910B}, | ||
| 51 | - } | ||
| 52 | - } | ||
| 53 | - ] | ||
| 54 | - } | ||
| 55 | - ) | ||
| 56 | 78 | ||
| 57 | monkeypatch.setattr(k8s_utils, "run_cmd_get_output", fake_run) | 79 | monkeypatch.setattr(k8s_utils, "run_cmd_get_output", fake_run) |
| 58 | 80 | ||
| 59 | - assert k8s_utils.get_accelerator_type_from_cluster() == C.ACCELERATOR_TYPE_910B | 81 | + assert k8s_utils.get_accelerator_type_from_cluster(C.HARDWARE_TYPE_800I_A2) == C.ACCELERATOR_TYPE_910B |
| 60 | - assert k8s_utils.get_accelerator_type_from_cluster() == C.ACCELERATOR_TYPE_910B | 82 | + assert k8s_utils.get_accelerator_type_from_cluster(C.HARDWARE_TYPE_800I_A2) == C.ACCELERATOR_TYPE_910B |
| 61 | assert call_count["n"] == 1 | 83 | assert call_count["n"] == 1 |
| 62 | - assert k8s_utils._g_accelerator_type_cache[C.ACCELERATOR_TYPE] == C.ACCELERATOR_TYPE_910B | 84 | + assert k8s_utils._g_accelerator_type_cache[C.HARDWARE_TYPE_800I_A2] == C.ACCELERATOR_TYPE_910B |
| 63 | 85 | ||
| 64 | 86 | ||
| 65 | def test_get_accelerator_type_from_cluster_raises_when_label_missing(monkeypatch): | 87 | def test_get_accelerator_type_from_cluster_raises_when_label_missing(monkeypatch): |
| @@ -67,8 +89,32 @@ def test_get_accelerator_type_from_cluster_raises_when_label_missing(monkeypatch | |||
| 67 | monkeypatch.setattr( | 89 | monkeypatch.setattr( |
| 68 | k8s_utils, | 90 | k8s_utils, |
| 69 | "run_cmd_get_output", | 91 | "run_cmd_get_output", |
| 70 | - lambda _args: json.dumps({"items": [{"metadata": {"name": "node-0", "labels": {}}}]}), | 92 | + lambda _args: json.dumps(_nodes_json({})), |
| 71 | ) | 93 | ) |
| 72 | 94 | ||
| 73 | with pytest.raises(RuntimeError, match=C.ACCELERATOR_TYPE): | 95 | with pytest.raises(RuntimeError, match=C.ACCELERATOR_TYPE): |
| 74 | - k8s_utils.get_accelerator_type_from_cluster() | 96 | + k8s_utils.get_accelerator_type_from_cluster(C.HARDWARE_TYPE_800I_A3) |
| 97 | + | ||
| 98 | + | ||
| 99 | +def test_get_accelerator_type_from_cluster_raises_when_no_matching_generation(monkeypatch): | ||
| 100 | + k8s_utils._g_accelerator_type_cache.clear() | ||
| 101 | + monkeypatch.setattr( | ||
| 102 | + k8s_utils, | ||
| 103 | + "run_cmd_get_output", | ||
| 104 | + lambda _args: json.dumps(_nodes_json({C.ACCELERATOR_TYPE: C.ACCELERATOR_TYPE_910B})), | ||
| 105 | + ) | ||
| 106 | + | ||
| 107 | + with pytest.raises(RuntimeError, match=C.HARDWARE_TYPE_800I_A3): | ||
| 108 | + k8s_utils.get_accelerator_type_from_cluster(C.HARDWARE_TYPE_800I_A3) | ||
| 109 | + | ||
| 110 | + | ||
| 111 | +def test_get_accelerator_type_from_cluster_a5_uses_hardware_type(monkeypatch): | ||
| 112 | + k8s_utils._g_accelerator_type_cache.clear() | ||
| 113 | + a5_type = C.HARDWARE_TYPE_950I_A5[0] | ||
| 114 | + monkeypatch.setattr( | ||
| 115 | + k8s_utils, | ||
| 116 | + "run_cmd_get_output", | ||
| 117 | + lambda _args: json.dumps(_nodes_json({C.ACCELERATOR_TYPE: a5_type})), | ||
| 118 | + ) | ||
| 119 | + | ||
| 120 | + assert k8s_utils.get_accelerator_type_from_cluster(a5_type) == a5_type | ||
| @@ -134,7 +134,7 @@ def test_generate_yaml_engine_creates_hybrid_workload(tmp_path, monkeypatch): | |||
| 134 | monkeypatch.setattr( | 134 | monkeypatch.setattr( |
| 135 | k8s_utils, | 135 | k8s_utils, |
| 136 | "get_accelerator_type_from_cluster", | 136 | "get_accelerator_type_from_cluster", |
| 137 | - lambda: cluster_accelerator_type, | 137 | + lambda _hardware_type: cluster_accelerator_type, |
| 138 | ) | 138 | ) |
| 139 | 139 | ||
| 140 | user_config = make_pd_hybrid_user_config() | 140 | user_config = make_pd_hybrid_user_config() |


🟠 High Priority
变更行:第 188 行,
start_docker.sh示例中ASCEND_DEVICES仅保留了管理设备(davinci_manager、devmm_svm、hisi_hdc),删除了旧版中通过ASCEND_VISIBLE_DEVICES循环挂载各个/dev/davinci$i计算设备的逻辑。影响:昇腾 NPU 架构中,
davinci_manager仅为管理设备,实际 AI 计算需要各个/dev/davinci0、/dev/davinci1等计算设备。容器内缺少这些设备将导致服务无法执行推理任务,启动即失败。证据:同一文档 A5 环境章节(第 235 行)仍保留了
# 按 ASCEND_VISIBLE_DEVICES 循环追加 --device=/dev/davinci$i的注释,说明该循环逻辑是必需的,此处删除是遗漏。建议:恢复
ASCEND_VISIBLE_DEVICES的检测与循环挂载逻辑,将计算设备/dev/davinci$i加入ASCEND_DEVICES变量中