563 lines
18 KiB
Bash
563 lines
18 KiB
Bash
#!/usr/bin/env bash
|
||
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
PROJECT_ROOT="${SCRIPT_DIR}"
|
||
|
||
TIMESTAMP="$(date +"%Y%m%d_%H%M%S")"
|
||
BUILD_TYPE=""
|
||
VERSION=""
|
||
OUTPUT_ROOT="${PROJECT_ROOT}/build-file"
|
||
REGISTRY="unis"
|
||
IMAGE_NAME="qwen3-asr"
|
||
INCLUDE_MODELS="true"
|
||
METAX_BASE_IMAGE="${METAX_BASE_IMAGE:-}"
|
||
ILUVATAR_BASE_IMAGE="${ILUVATAR_BASE_IMAGE:-}"
|
||
MTHREADS_BASE_IMAGE="${MTHREADS_BASE_IMAGE:-}"
|
||
METAX_PYTHON_BIN="${METAX_PYTHON_BIN:-/opt/conda/bin/python}"
|
||
ILUVATAR_PYTHON_BIN="${ILUVATAR_PYTHON_BIN:-python3}"
|
||
MTHREADS_PYTHON_BIN="${MTHREADS_PYTHON_BIN:-python3}"
|
||
|
||
info() { echo "[INFO] $1"; }
|
||
die() { echo "[ERROR] $1" >&2; exit 1; }
|
||
|
||
show_help() {
|
||
cat <<EOF
|
||
用法:
|
||
./export_offline_bundle.sh --type cpu|gpu|metax|iluvatar|mthreads|all [options]
|
||
|
||
选项:
|
||
-t, --type TYPE 构建类型: cpu、gpu、metax、iluvatar、mthreads 或 all
|
||
-v, --version VER 构建版本; 默认使用当前时间戳
|
||
-o, --output-root DIR 输出根目录; 默认: ${OUTPUT_ROOT}
|
||
-r, --registry REG 镜像仓库命名空间; 默认: ${REGISTRY}
|
||
--skip-models 不在离线交付目录中打包模型
|
||
--metax-base IMAGE 沐曦官方 vLLM 基础镜像(--type metax 时必填)
|
||
--iluvatar-base IMAGE 天数官方 vLLM 基础镜像(--type iluvatar 时必填)
|
||
--mthreads-base IMAGE 摩尔线程官方 vLLM 基础镜像(--type mthreads 时必填)
|
||
--metax-python BIN 沐曦镜像内 Python 路径; 默认: ${METAX_PYTHON_BIN}
|
||
--iluvatar-python BIN 天数镜像内 Python 路径; 默认: ${ILUVATAR_PYTHON_BIN}
|
||
--mthreads-python BIN 摩尔线程镜像内 Python 路径; 默认: ${MTHREADS_PYTHON_BIN}
|
||
-h, --help 显示帮助
|
||
|
||
示例:
|
||
./export_offline_bundle.sh --type gpu
|
||
./export_offline_bundle.sh --type metax
|
||
./export_offline_bundle.sh --type metax --metax-base cr.metax-tech.com/public-ai-release/maca/vllm-metax:0.17.0-maca.ai3.5.3.307-torch2.8-py312-ubuntu22.04-amd64 --skip-models
|
||
./export_offline_bundle.sh --type iluvatar --iluvatar-base registry.iluvatar.com.cn:10443/customer/sz/vllm0.17.0-4.4.0-x86:v5
|
||
./export_offline_bundle.sh --type mthreads --mthreads-base registry.mthreads.com/presale/devtech/vllm_musa:s4000_4.3.5_d0519
|
||
./export_offline_bundle.sh --type cpu --version 1.0.1
|
||
./export_offline_bundle.sh --type all
|
||
EOF
|
||
}
|
||
|
||
parse_args() {
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
-t|--type) BUILD_TYPE="$2"; shift 2 ;;
|
||
-v|--version) VERSION="$2"; shift 2 ;;
|
||
-o|--output-root) OUTPUT_ROOT="$2"; shift 2 ;;
|
||
-r|--registry) REGISTRY="$2"; shift 2 ;;
|
||
--metax-base) METAX_BASE_IMAGE="$2"; shift 2 ;;
|
||
--iluvatar-base) ILUVATAR_BASE_IMAGE="$2"; shift 2 ;;
|
||
--mthreads-base) MTHREADS_BASE_IMAGE="$2"; shift 2 ;;
|
||
--metax-python) METAX_PYTHON_BIN="$2"; shift 2 ;;
|
||
--iluvatar-python) ILUVATAR_PYTHON_BIN="$2"; shift 2 ;;
|
||
--mthreads-python) MTHREADS_PYTHON_BIN="$2"; shift 2 ;;
|
||
--skip-models) INCLUDE_MODELS="false"; shift ;;
|
||
-h|--help) show_help; exit 0 ;;
|
||
*) die "未知参数: $1" ;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
prepare_offline_models() {
|
||
local bundle_dir="$1"
|
||
local model_export_dir="${bundle_dir}/models"
|
||
local model_archive="${bundle_dir}/qwen3-asr-models-${VERSION}.tar.gz"
|
||
|
||
if [[ "$INCLUDE_MODELS" != "true" ]]; then
|
||
info "跳过模型打包 (--skip-models)"
|
||
return 0
|
||
fi
|
||
|
||
info "下载并导出全部运行所需模型到离线交付目录"
|
||
rm -rf "$model_export_dir"
|
||
(
|
||
cd "$PROJECT_ROOT"
|
||
if command -v uv >/dev/null 2>&1; then
|
||
uv run python -m app.utils.download_models --export-dir "$model_export_dir"
|
||
else
|
||
python -m app.utils.download_models --export-dir "$model_export_dir"
|
||
fi
|
||
)
|
||
|
||
info "压缩模型目录: $(basename "$model_archive")"
|
||
tar -C "$bundle_dir" -czf "$model_archive" models
|
||
rm -rf "$model_export_dir"
|
||
}
|
||
|
||
prompt_build_type() {
|
||
local choice
|
||
echo "请选择离线交付类型:"
|
||
echo " 1) GPU"
|
||
echo " 2) CPU"
|
||
echo " 3) MetaX GPU"
|
||
echo " 4) Iluvatar GPU"
|
||
echo " 5) Moore Threads GPU"
|
||
echo " 6) ALL"
|
||
read -r -p "请输入选项 [6]: " choice
|
||
choice="${choice:-6}"
|
||
case "$choice" in
|
||
1) BUILD_TYPE="gpu" ;;
|
||
2) BUILD_TYPE="cpu" ;;
|
||
3) BUILD_TYPE="metax" ;;
|
||
4) BUILD_TYPE="iluvatar" ;;
|
||
5) BUILD_TYPE="mthreads" ;;
|
||
6) BUILD_TYPE="all" ;;
|
||
*) die "无效选项: $choice" ;;
|
||
esac
|
||
}
|
||
|
||
validate() {
|
||
case "$BUILD_TYPE" in
|
||
cpu|gpu|metax|iluvatar|mthreads|all) ;;
|
||
"") if [[ -t 0 ]]; then prompt_build_type; else BUILD_TYPE="all"; fi ;;
|
||
*) die "不支持的构建类型: ${BUILD_TYPE}" ;;
|
||
esac
|
||
|
||
if [[ "$BUILD_TYPE" == "metax" && -z "$METAX_BASE_IMAGE" ]]; then
|
||
die "--type metax 需要指定 --metax-base,值为已 docker load/pull 的沐曦官方 vLLM 镜像"
|
||
fi
|
||
if [[ "$BUILD_TYPE" == "iluvatar" && -z "$ILUVATAR_BASE_IMAGE" ]]; then
|
||
die "--type iluvatar 需要指定 --iluvatar-base,值为已 docker load/pull 的天数官方 vLLM 镜像"
|
||
fi
|
||
if [[ "$BUILD_TYPE" == "mthreads" && -z "$MTHREADS_BASE_IMAGE" ]]; then
|
||
die "--type mthreads 需要指定 --mthreads-base,值为已 docker load/pull 的摩尔线程官方 vLLM 镜像"
|
||
fi
|
||
|
||
VERSION="${VERSION:-$TIMESTAMP}"
|
||
}
|
||
|
||
export_compressor() {
|
||
command -v pigz >/dev/null 2>&1 && echo "pigz -f" || echo "gzip -f"
|
||
}
|
||
|
||
build_and_export_image() {
|
||
local target="$1"
|
||
local dockerfile="$2"
|
||
local tag="$3"
|
||
local bundle_dir="$4"
|
||
local tar_path="${bundle_dir}/${IMAGE_NAME}-${target}-${VERSION}-amd64.tar"
|
||
|
||
info "构建 ${target} 镜像: ${tag}"
|
||
(
|
||
cd "$PROJECT_ROOT"
|
||
case "$target" in
|
||
metax)
|
||
docker build -f "$dockerfile" -t "$tag" \
|
||
--build-arg "METAX_BASE_IMAGE=${METAX_BASE_IMAGE}" \
|
||
--build-arg "PYTHON_BIN=${METAX_PYTHON_BIN}" .
|
||
;;
|
||
iluvatar)
|
||
docker build -f "$dockerfile" -t "$tag" \
|
||
--build-arg "ILUVATAR_BASE_IMAGE=${ILUVATAR_BASE_IMAGE}" \
|
||
--build-arg "PYTHON_BIN=${ILUVATAR_PYTHON_BIN}" .
|
||
;;
|
||
mthreads)
|
||
docker build -f "$dockerfile" -t "$tag" \
|
||
--build-arg "MTHREADS_BASE_IMAGE=${MTHREADS_BASE_IMAGE}" \
|
||
--build-arg "PYTHON_BIN=${MTHREADS_PYTHON_BIN}" .
|
||
;;
|
||
*)
|
||
docker build -f "$dockerfile" -t "$tag" .
|
||
;;
|
||
esac
|
||
)
|
||
|
||
info "导出 ${target} 镜像归档"
|
||
docker save -o "$tar_path" "$tag"
|
||
|
||
info "压缩 ${target} 镜像归档"
|
||
$(export_compressor) "$tar_path"
|
||
}
|
||
|
||
build_offline_images() {
|
||
local bundle_dir="$1"
|
||
local cpu_tag="${REGISTRY}/${IMAGE_NAME}:cpu-${VERSION}"
|
||
local gpu_tag="${REGISTRY}/${IMAGE_NAME}:gpu-${VERSION}"
|
||
local metax_tag="${REGISTRY}/${IMAGE_NAME}:metax-${VERSION}"
|
||
local iluvatar_tag="${REGISTRY}/${IMAGE_NAME}:iluvatar-${VERSION}"
|
||
local mthreads_tag="${REGISTRY}/${IMAGE_NAME}:mthreads-${VERSION}"
|
||
|
||
case "$BUILD_TYPE" in
|
||
cpu)
|
||
build_and_export_image "cpu" "Dockerfile.cpu" "$cpu_tag" "$bundle_dir"
|
||
;;
|
||
gpu)
|
||
build_and_export_image "gpu" "Dockerfile.gpu" "$gpu_tag" "$bundle_dir"
|
||
;;
|
||
metax)
|
||
build_and_export_image "metax" "Dockerfile.metax" "$metax_tag" "$bundle_dir"
|
||
;;
|
||
iluvatar)
|
||
build_and_export_image "iluvatar" "Dockerfile.iluvatar" "$iluvatar_tag" "$bundle_dir"
|
||
;;
|
||
mthreads)
|
||
build_and_export_image "mthreads" "Dockerfile.mthreads" "$mthreads_tag" "$bundle_dir"
|
||
;;
|
||
all)
|
||
build_and_export_image "cpu" "Dockerfile.cpu" "$cpu_tag" "$bundle_dir"
|
||
build_and_export_image "gpu" "Dockerfile.gpu" "$gpu_tag" "$bundle_dir"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
append_bundle_image_env() {
|
||
local bundle_dir="$1"
|
||
local env_file="${bundle_dir}/.env.example"
|
||
local image_tag=""
|
||
local note=""
|
||
|
||
case "$BUILD_TYPE" in
|
||
gpu)
|
||
image_tag="${REGISTRY}/${IMAGE_NAME}:gpu-${VERSION}"
|
||
note="GPU"
|
||
;;
|
||
metax)
|
||
image_tag="${REGISTRY}/${IMAGE_NAME}:metax-${VERSION}"
|
||
note="MetaX GPU"
|
||
;;
|
||
iluvatar)
|
||
image_tag="${REGISTRY}/${IMAGE_NAME}:iluvatar-${VERSION}"
|
||
note="Iluvatar GPU"
|
||
;;
|
||
mthreads)
|
||
image_tag="${REGISTRY}/${IMAGE_NAME}:mthreads-${VERSION}"
|
||
note="Moore Threads GPU"
|
||
;;
|
||
cpu)
|
||
image_tag="${REGISTRY}/${IMAGE_NAME}:cpu-${VERSION}"
|
||
note="CPU"
|
||
;;
|
||
all)
|
||
image_tag="${REGISTRY}/${IMAGE_NAME}:gpu-${VERSION}"
|
||
note="GPU by default; switch to ${REGISTRY}/${IMAGE_NAME}:cpu-${VERSION} when using docker-compose-cpu.yml"
|
||
;;
|
||
esac
|
||
|
||
cat >> "$env_file" <<EOF
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Offline bundle image tag (${note}).
|
||
# Keep this value aligned with the image loaded by docker load.
|
||
# -----------------------------------------------------------------------------
|
||
ASR_IMAGE=${image_tag}
|
||
EOF
|
||
}
|
||
|
||
create_bundle_env() {
|
||
local bundle_dir="$1"
|
||
cp "${bundle_dir}/.env.example" "${bundle_dir}/.env"
|
||
}
|
||
|
||
bundle_compose_files() {
|
||
case "$BUILD_TYPE" in
|
||
gpu)
|
||
printf '%s\n' "docker-compose.yml"
|
||
;;
|
||
metax)
|
||
printf '%s\n' "docker-compose-metax.yml"
|
||
;;
|
||
iluvatar)
|
||
printf '%s\n' "docker-compose-iluvatar.yml"
|
||
;;
|
||
mthreads)
|
||
printf '%s\n' "docker-compose-mthreads.yml"
|
||
;;
|
||
cpu)
|
||
printf '%s\n' "docker-compose-cpu.yml"
|
||
;;
|
||
all)
|
||
printf '%s\n' "docker-compose.yml" "docker-compose-cpu.yml"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
compose_description() {
|
||
case "$1" in
|
||
docker-compose.yml) echo "NVIDIA GPU 版 compose 文件" ;;
|
||
docker-compose-cpu.yml) echo "CPU 版 compose 文件" ;;
|
||
docker-compose-metax.yml) echo "沐曦 GPU 版 compose 文件" ;;
|
||
docker-compose-iluvatar.yml) echo "天数 GPU 版 compose 文件" ;;
|
||
docker-compose-mthreads.yml) echo "摩尔线程 GPU 版 compose 文件" ;;
|
||
*) echo "compose 文件" ;;
|
||
esac
|
||
}
|
||
|
||
copy_bundle_files() {
|
||
local bundle_dir="$1"
|
||
local compose_file
|
||
|
||
while IFS= read -r compose_file; do
|
||
[[ -n "$compose_file" ]] || continue
|
||
cp "${PROJECT_ROOT}/${compose_file}" "${bundle_dir}/${compose_file}"
|
||
done < <(bundle_compose_files)
|
||
cp "${PROJECT_ROOT}/.env.example" "${bundle_dir}/.env.example"
|
||
cp "${PROJECT_ROOT}/docs/deployment.md" "${bundle_dir}/DEPLOYMENT.md"
|
||
if [[ "$BUILD_TYPE" == "metax" ]]; then
|
||
cp "${PROJECT_ROOT}/docs/metax_offline_deployment.md" "${bundle_dir}/METAX_DEPLOYMENT.md"
|
||
fi
|
||
if [[ "$BUILD_TYPE" == "iluvatar" ]]; then
|
||
cp "${PROJECT_ROOT}/docs/iluvatar_offline_deployment.md" "${bundle_dir}/ILUVATAR_DEPLOYMENT.md"
|
||
fi
|
||
if [[ "$BUILD_TYPE" == "mthreads" ]]; then
|
||
cp "${PROJECT_ROOT}/docs/mthreads_offline_deployment.md" "${bundle_dir}/MTHREADS_DEPLOYMENT.md"
|
||
fi
|
||
cp "${PROJECT_ROOT}/scripts/docker/init_host_dirs.sh" "${bundle_dir}/init_host_dirs.sh"
|
||
cp "${PROJECT_ROOT}/scripts/download-models.sh" "${bundle_dir}/download-models.sh"
|
||
cp "${PROJECT_ROOT}/scripts/download_models_standalone.py" "${bundle_dir}/download_models_standalone.py"
|
||
chmod +x "${bundle_dir}/init_host_dirs.sh"
|
||
chmod +x "${bundle_dir}/download-models.sh"
|
||
append_bundle_image_env "$bundle_dir"
|
||
create_bundle_env "$bundle_dir"
|
||
}
|
||
|
||
generate_bundle_metadata() {
|
||
local bundle_dir="$1"
|
||
shift
|
||
local image_archives=("$@")
|
||
local archive
|
||
local compose_file
|
||
|
||
cat > "${bundle_dir}/BUNDLE_INFO.txt" <<EOF
|
||
Qwen3-ASR Offline Bundle
|
||
========================
|
||
|
||
Bundle Type : ${BUILD_TYPE}
|
||
Version : ${VERSION}
|
||
Timestamp : ${TIMESTAMP}
|
||
Image Files :
|
||
EOF
|
||
for archive in "${image_archives[@]}"; do
|
||
echo " ${archive}" >> "${bundle_dir}/BUNDLE_INFO.txt"
|
||
done
|
||
echo "Compose Files:" >> "${bundle_dir}/BUNDLE_INFO.txt"
|
||
while IFS= read -r compose_file; do
|
||
[[ -n "$compose_file" ]] || continue
|
||
echo " ${compose_file}" >> "${bundle_dir}/BUNDLE_INFO.txt"
|
||
done < <(bundle_compose_files)
|
||
cat >> "${bundle_dir}/BUNDLE_INFO.txt" <<EOF
|
||
Host Paths :
|
||
/opt/dep/asr/models
|
||
/opt/dep/asr/data
|
||
EOF
|
||
if [[ "$INCLUDE_MODELS" == "true" ]]; then
|
||
cat >> "${bundle_dir}/BUNDLE_INFO.txt" <<EOF
|
||
Model Files :
|
||
qwen3-asr-models-${VERSION}.tar.gz
|
||
EOF
|
||
fi
|
||
}
|
||
|
||
generate_bundle_readme() {
|
||
local bundle_dir="$1"
|
||
shift
|
||
local image_archives=("$@")
|
||
local content_lines=""
|
||
local import_lines=""
|
||
local startup_lines=""
|
||
local hint_lines=""
|
||
local status_lines=""
|
||
local compose_lines=""
|
||
local archive
|
||
local compose_file
|
||
|
||
for archive in "${image_archives[@]}"; do
|
||
content_lines="${content_lines}- \`${archive}\`: Docker 镜像归档"$'\n'
|
||
import_lines="${import_lines}gunzip -c ${archive} | docker load"$'\n'
|
||
done
|
||
|
||
if [[ "$BUILD_TYPE" == "gpu" ]]; then
|
||
startup_lines=$'docker compose up -d'
|
||
status_lines=$'docker compose ps\ndocker compose logs -f'
|
||
hint_lines="- GPU 版默认按机器资源自动选择 Qwen3-ASR 模型。"
|
||
elif [[ "$BUILD_TYPE" == "metax" ]]; then
|
||
startup_lines=$'docker compose -f docker-compose-metax.yml up -d'
|
||
status_lines=$'docker compose -f docker-compose-metax.yml ps\ndocker compose -f docker-compose-metax.yml logs -f'
|
||
hint_lines="- 沐曦 GPU 版使用 docker-compose-metax.yml,并要求目标机已安装沐曦驱动/容器运行栈。"
|
||
elif [[ "$BUILD_TYPE" == "iluvatar" ]]; then
|
||
startup_lines=$'docker compose -f docker-compose-iluvatar.yml up -d'
|
||
status_lines=$'docker compose -f docker-compose-iluvatar.yml ps\ndocker compose -f docker-compose-iluvatar.yml logs -f'
|
||
hint_lines="- 天数 GPU 版使用 docker-compose-iluvatar.yml,并沿用官方镜像建议的 host network、host pid/ipc、privileged 与设备挂载。"
|
||
elif [[ "$BUILD_TYPE" == "mthreads" ]]; then
|
||
startup_lines=$'docker compose -f docker-compose-mthreads.yml up -d'
|
||
status_lines=$'docker compose -f docker-compose-mthreads.yml ps\ndocker compose -f docker-compose-mthreads.yml logs -f'
|
||
hint_lines="- 摩尔线程 GPU 版使用 docker-compose-mthreads.yml,并沿用官方 MUSA vLLM 镜像建议的 host network、host pid/ipc、privileged 与设备挂载。"
|
||
elif [[ "$BUILD_TYPE" == "cpu" ]]; then
|
||
startup_lines=$'docker compose -f docker-compose-cpu.yml up -d'
|
||
status_lines=$'docker compose -f docker-compose-cpu.yml ps\ndocker compose -f docker-compose-cpu.yml logs -f'
|
||
hint_lines="- CPU 版默认走 vendored QwenASR Rust backend,通常会使用 qwen3-asr-0.6b。"
|
||
else
|
||
startup_lines=$'docker compose up -d\n# 或仅启动 CPU 版本\n# docker compose -f docker-compose-cpu.yml up -d'
|
||
status_lines=$'docker compose ps\ndocker compose logs -f'
|
||
hint_lines=$'- ALL 模式会同时打包 GPU 与 CPU 镜像,目标机可按需选择加载和启动。\n- GPU 版默认按机器资源自动选择 Qwen3-ASR 模型。\n- CPU 版默认走 vendored QwenASR Rust backend,通常会使用 qwen3-asr-0.6b。'
|
||
fi
|
||
|
||
while IFS= read -r compose_file; do
|
||
[[ -n "$compose_file" ]] || continue
|
||
compose_lines="${compose_lines}- \`${compose_file}\`: $(compose_description "$compose_file")"$'\n'
|
||
done < <(bundle_compose_files)
|
||
|
||
cat > "${bundle_dir}/README.md" <<EOF
|
||
# Qwen3-ASR 离线交付目录
|
||
|
||
这是一个可直接拷贝到目标机的离线交付目录。
|
||
|
||
## 内容说明
|
||
|
||
${compose_lines}
|
||
- \`.env.example\`: 环境变量示例
|
||
- \`init_host_dirs.sh\`: 初始化宿主机挂载目录
|
||
- \`download-models.sh\`: 增量下载缺失模型,不删除现有模型目录
|
||
- \`DEPLOYMENT.md\`: 详细部署文档
|
||
$(if [[ "$BUILD_TYPE" == "metax" ]]; then echo "- \`METAX_DEPLOYMENT.md\`: 沐曦 GPU 国产化离线部署文档"; fi)
|
||
$(if [[ "$BUILD_TYPE" == "iluvatar" ]]; then echo "- \`ILUVATAR_DEPLOYMENT.md\`: 天数 GPU 国产化离线部署文档"; fi)
|
||
$(if [[ "$BUILD_TYPE" == "mthreads" ]]; then echo "- \`MTHREADS_DEPLOYMENT.md\`: 摩尔线程 GPU 国产化离线部署文档"; fi)
|
||
- \`BUNDLE_INFO.txt\`: 本次交付元信息
|
||
${content_lines}
|
||
$(if [[ "$INCLUDE_MODELS" == "true" ]]; then echo "- \`qwen3-asr-models-${VERSION}.tar.gz\`: 全量离线模型包"; fi)
|
||
|
||
## 本次交付
|
||
|
||
- 类型: \`${BUILD_TYPE}\`
|
||
- 版本: \`${VERSION}\`
|
||
- 时间: \`${TIMESTAMP}\`
|
||
|
||
## 使用步骤
|
||
|
||
1. 把整个目录复制到目标机,例如 \`/opt/dep/asr/bundles/${TIMESTAMP}-${BUILD_TYPE}\`
|
||
2. 进入目录并初始化宿主机挂载目录:
|
||
|
||
\`\`\`bash
|
||
chmod +x init_host_dirs.sh
|
||
./init_host_dirs.sh
|
||
\`\`\`
|
||
|
||
3. 准备模型目录内容
|
||
|
||
$(if [[ "$INCLUDE_MODELS" == "true" ]]; then cat <<MODEL_EOF
|
||
\`\`\`bash
|
||
tar -xzf qwen3-asr-models-${VERSION}.tar.gz -C /opt/dep/asr/
|
||
\`\`\`
|
||
|
||
模型会解压到 \`/opt/dep/asr/models\`,容器内默认挂载为 \`/app/models\`。
|
||
MODEL_EOF
|
||
else cat <<MODEL_EOF
|
||
- 本次打包使用了 \`--skip-models\`,需要另外准备模型目录
|
||
- 如果目标机可联网,可运行 \`./download-models.sh --models-dir /opt/dep/asr/models\` 增量补齐模型
|
||
- 如果目标机不能联网,请在联网机器上准备模型目录或模型包,再复制到 \`/opt/dep/asr/models\`
|
||
MODEL_EOF
|
||
fi)
|
||
|
||
4. 导入镜像
|
||
|
||
\`\`\`bash
|
||
${import_lines}\`\`\`
|
||
|
||
5. 准备配置
|
||
|
||
- 离线包已自动生成 \`.env\`,里面包含本次镜像对应的 \`ASR_IMAGE\`,不要改回 \`latest\`
|
||
- 按需修改 \`.env\` 里的 \`API_KEY\`、\`CUDA_VISIBLE_DEVICES\` 等变量
|
||
- 默认宿主机挂载目录:
|
||
- \`/opt/dep/asr/models\`
|
||
- \`/opt/dep/asr/data\`(包含 logs、temp、tasks)
|
||
|
||
6. 启动服务
|
||
|
||
\`\`\`bash
|
||
${startup_lines}
|
||
\`\`\`
|
||
|
||
7. 查看状态
|
||
|
||
\`\`\`bash
|
||
${status_lines}
|
||
\`\`\`
|
||
|
||
## 说明
|
||
|
||
${hint_lines}
|
||
- 如果目标机不能联网,请使用本目录内的模型包;不要依赖运行时下载
|
||
- 更完整的说明见 \`DEPLOYMENT.md\`
|
||
EOF
|
||
}
|
||
|
||
collect_image_archives() {
|
||
local bundle_dir="$1"
|
||
local archives=()
|
||
|
||
case "$BUILD_TYPE" in
|
||
gpu)
|
||
archives+=("qwen3-asr-gpu-${VERSION}-amd64.tar.gz")
|
||
;;
|
||
metax)
|
||
archives+=("qwen3-asr-metax-${VERSION}-amd64.tar.gz")
|
||
;;
|
||
iluvatar)
|
||
archives+=("qwen3-asr-iluvatar-${VERSION}-amd64.tar.gz")
|
||
;;
|
||
mthreads)
|
||
archives+=("qwen3-asr-mthreads-${VERSION}-amd64.tar.gz")
|
||
;;
|
||
cpu)
|
||
archives+=("qwen3-asr-cpu-${VERSION}-amd64.tar.gz")
|
||
;;
|
||
all)
|
||
archives+=(
|
||
"qwen3-asr-cpu-${VERSION}-amd64.tar.gz"
|
||
"qwen3-asr-gpu-${VERSION}-amd64.tar.gz"
|
||
)
|
||
;;
|
||
esac
|
||
|
||
local archive
|
||
for archive in "${archives[@]}"; do
|
||
[[ -f "${bundle_dir}/${archive}" ]] || die "未找到导出的镜像压缩包: ${archive}"
|
||
done
|
||
|
||
printf '%s\n' "${archives[@]}"
|
||
}
|
||
|
||
main() {
|
||
parse_args "$@"
|
||
validate
|
||
|
||
mkdir -p "$OUTPUT_ROOT"
|
||
|
||
local bundle_dir="${OUTPUT_ROOT}/${TIMESTAMP}-${BUILD_TYPE}"
|
||
mkdir -p "$bundle_dir"
|
||
|
||
info "输出目录: ${bundle_dir}"
|
||
info "开始构建 ${BUILD_TYPE} 离线交付包(普通 docker build/save)"
|
||
|
||
build_offline_images "$bundle_dir"
|
||
prepare_offline_models "$bundle_dir"
|
||
|
||
mapfile -t image_archives < <(collect_image_archives "$bundle_dir")
|
||
|
||
copy_bundle_files "$bundle_dir"
|
||
generate_bundle_metadata "$bundle_dir" "${image_archives[@]}"
|
||
generate_bundle_readme "$bundle_dir" "${image_archives[@]}"
|
||
|
||
info "离线交付目录已生成"
|
||
info "目录: ${bundle_dir}"
|
||
local archive
|
||
for archive in "${image_archives[@]}"; do
|
||
info "镜像: ${archive}"
|
||
done
|
||
}
|
||
|
||
main "$@"
|