test/scripts/build_docker.sh

470 lines
15 KiB
Bash

#!/bin/bash
#
# Qwen3-ASR Docker Build Tool
# Step-by-step interactive or CLI parameter mode.
set -euo pipefail
# =============================================================================
# Defaults
# =============================================================================
REGISTRY="unis"
IMAGE_NAME="qwen3-asr"
VERSION="latest"
BUILD_TYPE="all"
PLATFORM="linux/amd64"
PUSH="false"
EXPORT_TAR="false"
EXPORT_DIR="."
NO_CACHE="false"
LANG_MODE="zh"
# =============================================================================
# Localization
# =============================================================================
_resolve_lang() {
printf "%s" "$LANG_MODE"
}
_msg() {
local k="$1" l; l="$(_resolve_lang)"
if [[ "$l" == "zh" ]]; then
case "$k" in
title) echo "Qwen3-ASR Docker 构建工具" ;;
subtitle) echo "按提示选择或直接回车使用默认值" ;;
q_lang) echo "界面语言" ;;
q_type) echo "构建目标" ;;
q_arch) echo "目标架构" ;;
q_version) echo "镜像版本" ;;
q_push) echo "推送到仓库" ;;
q_export) echo "导出 tar.gz" ;;
q_output) echo "输出目录" ;;
q_reg) echo "仓库命名空间" ;;
q_cache) echo "禁用构建缓存" ;;
q_confirm) echo "确认并开始构建" ;;
summary) echo "构建摘要" ;;
cancel) echo "已取消" ;;
start) echo "开始构建..." ;;
done) echo "构建完成" ;;
recent) echo "最近镜像" ;;
opt_cpu) echo "CPU" ;;
opt_gpu) echo "GPU" ;;
opt_metax) echo "沐曦 GPU" ;;
opt_iluvatar) echo "天数 GPU" ;;
opt_mthreads) echo "摩尔线程 GPU" ;;
opt_all) echo "全部 (CPU + GPU)" ;;
opt_amd64) echo "amd64 (x86_64)" ;;
opt_arm64) echo "arm64 (Apple Silicon / ARM)" ;;
opt_multi) echo "多架构 (amd64 + arm64)" ;;
err_inv) echo "无效输入" ;;
err_opt) echo "未知选项" ;;
err_gpu) echo "GPU 构建仅支持 amd64" ;;
err_bx) echo "需要 Docker Buildx" ;;
yes) echo "是" ;;
no) echo "否" ;;
esac
else
case "$k" in
title) echo "Qwen3-ASR Docker Build Tool" ;;
subtitle) echo "Select options or press Enter for defaults" ;;
q_lang) echo "Language" ;;
q_type) echo "Build target" ;;
q_arch) echo "Architecture" ;;
q_version) echo "Image version" ;;
q_push) echo "Push to registry" ;;
q_export) echo "Export tar.gz" ;;
q_output) echo "Output directory" ;;
q_reg) echo "Registry namespace" ;;
q_cache) echo "Disable build cache" ;;
q_confirm) echo "Confirm and start build" ;;
summary) echo "Build summary" ;;
cancel) echo "Cancelled" ;;
start) echo "Starting build..." ;;
done) echo "Build complete" ;;
recent) echo "Recent images" ;;
opt_cpu) echo "CPU" ;;
opt_gpu) echo "GPU" ;;
opt_metax) echo "MetaX GPU" ;;
opt_iluvatar) echo "Iluvatar GPU" ;;
opt_mthreads) echo "Moore Threads GPU" ;;
opt_all) echo "All (CPU + GPU)" ;;
opt_amd64) echo "amd64 (x86_64)" ;;
opt_arm64) echo "arm64 (Apple Silicon / ARM)" ;;
opt_multi) echo "Multi-arch (amd64 + arm64)" ;;
err_inv) echo "Invalid input" ;;
err_opt) echo "Unknown option" ;;
err_gpu) echo "GPU build only supports amd64" ;;
err_bx) echo "Docker Buildx is required" ;;
yes) echo "yes" ;;
no) echo "no" ;;
esac
fi
}
_label_bool() { [[ "$1" == "true" ]] && _msg yes || _msg no; }
# =============================================================================
# Helpers
# =============================================================================
info() { echo "[INFO] $1" >&2; }
warn() { echo "[WARN] $1" >&2; }
die() { echo "[ERROR] $1" >&2; exit 1; }
parse_arch() {
case "$1" in
amd64) echo "linux/amd64" ;;
arm64) echo "linux/arm64" ;;
multi) echo "linux/amd64,linux/arm64" ;;
*) die "$(_msg err_inv): $1" ;;
esac
}
arch_label() {
case "$1" in
linux/amd64) echo "amd64" ;;
linux/arm64) echo "arm64" ;;
linux/amd64,linux/arm64) echo "multi" ;;
*) echo "$1" ;;
esac
}
ensure_buildx() {
docker buildx version >/dev/null 2>&1 || die "$(_msg err_bx)"
if ! docker buildx inspect qwen3-asr-builder >/dev/null 2>&1; then
info "Creating buildx builder: qwen3-asr-builder"
docker buildx create --name qwen3-asr-builder --driver docker-container --use >/dev/null
else
docker buildx use qwen3-asr-builder >/dev/null
fi
}
export_compressor() {
command -v pigz >/dev/null 2>&1 && echo "pigz -f" || echo "gzip -f"
}
# =============================================================================
# Build Core
# =============================================================================
build_image() {
local target="$1" dockerfile="$2" tag="$3" platform="$4"
local args=(buildx build --platform "$platform" -f "$dockerfile" -t "$tag")
[[ "$NO_CACHE" == "true" ]] && args+=(--no-cache)
if [[ "$target" == "metax" ]]; then
args+=(--build-arg "METAX_BASE_IMAGE=${METAX_BASE_IMAGE}")
fi
if [[ "$target" == "iluvatar" ]]; then
args+=(--build-arg "ILUVATAR_BASE_IMAGE=${ILUVATAR_BASE_IMAGE}")
fi
if [[ "$target" == "mthreads" ]]; then
args+=(--build-arg "MTHREADS_BASE_IMAGE=${MTHREADS_BASE_IMAGE}")
fi
if [[ "$platform" == *","* ]]; then
[[ "$EXPORT_TAR" == "true" ]] && warn "Multi-arch cannot export tar.gz"
args+=(--push)
elif [[ "$PUSH" == "true" ]]; then
args+=(--push)
elif [[ "$EXPORT_TAR" == "true" ]]; then
mkdir -p "$EXPORT_DIR"
local suffix="${target}-${VERSION}-$(basename "$platform")"
local tar="${EXPORT_DIR}/${IMAGE_NAME}-${suffix}.tar"
args+=(--output "type=docker,dest=${tar}")
else
args+=(--load)
fi
info "Building ${target}: ${tag} (${platform})"
docker "${args[@]}" .
if [[ "$EXPORT_TAR" == "true" && "$platform" != *","* && "$PUSH" != "true" ]]; then
local suffix="${target}-${VERSION}-$(basename "$platform")"
local tar="${EXPORT_DIR}/${IMAGE_NAME}-${suffix}.tar"
if [[ -f "$tar" ]]; then
info "Compressing ${tar}"
$(export_compressor) "$tar"
info "Exported ${tar}.gz"
fi
fi
}
build_cpu() {
local tag="${REGISTRY}/${IMAGE_NAME}:${VERSION}"
[[ "$VERSION" == "latest" ]] && tag="${REGISTRY}/${IMAGE_NAME}:cpu-latest"
build_image "cpu" "Dockerfile.cpu" "$tag" "$PLATFORM"
}
build_gpu() {
[[ "$PLATFORM" == *"arm64"* ]] && die "$(_msg err_gpu)"
build_image "gpu" "Dockerfile.gpu" "${REGISTRY}/${IMAGE_NAME}:gpu-${VERSION}" "linux/amd64"
}
build_metax() {
[[ "$PLATFORM" == *"arm64"* ]] && die "$(_msg err_gpu)"
[[ -n "${METAX_BASE_IMAGE:-}" ]] || die "METAX_BASE_IMAGE is required for metax builds. Use an official MetaX vLLM image."
build_image "metax" "Dockerfile.metax" "${REGISTRY}/${IMAGE_NAME}:metax-${VERSION}" "linux/amd64"
}
build_iluvatar() {
[[ "$PLATFORM" == *"arm64"* ]] && die "$(_msg err_gpu)"
[[ -n "${ILUVATAR_BASE_IMAGE:-}" ]] || die "ILUVATAR_BASE_IMAGE is required for iluvatar builds. Use the official Iluvatar vLLM image."
build_image "iluvatar" "Dockerfile.iluvatar" "${REGISTRY}/${IMAGE_NAME}:iluvatar-${VERSION}" "linux/amd64"
}
build_mthreads() {
[[ "$PLATFORM" == *"arm64"* ]] && die "$(_msg err_gpu)"
[[ -n "${MTHREADS_BASE_IMAGE:-}" ]] || die "MTHREADS_BASE_IMAGE is required for mthreads builds. Use the official Moore Threads vLLM image."
build_image "mthreads" "Dockerfile.mthreads" "${REGISTRY}/${IMAGE_NAME}:mthreads-${VERSION}" "linux/amd64"
}
# =============================================================================
# Interactive: step-by-step
# =============================================================================
_ask() {
local __var_name="$1" prompt="$2" default="$3" l
local val
l="$(_resolve_lang)"
if [[ "$l" == "zh" ]]; then
read -r -p "${prompt} (默认: ${default}): " val
else
read -r -p "${prompt} (default: ${default}): " val
fi
printf -v "$__var_name" "%s" "${val:-$default}"
}
_ask_bool() {
local __var_name="$1" prompt="$2" default="$3" hint l
local val
l="$(_resolve_lang)"
if [[ "$default" == "true" ]]; then
hint="[Y/n]"
else
hint="[y/N]"
fi
if [[ "$l" == "zh" ]]; then
read -r -p "${prompt} ${hint}: " val
else
read -r -p "${prompt} ${hint}: " val
fi
case "${val:-}" in
[Yy]|[Yy][Ee][Ss]|是) printf -v "$__var_name" "%s" "true" ;;
[Nn]|[Nn][Oo]|否) printf -v "$__var_name" "%s" "false" ;;
"") printf -v "$__var_name" "%s" "$default" ;;
*) warn "$(_msg err_inv): ${val}"; printf -v "$__var_name" "%s" "$default" ;;
esac
}
_ask_choice() {
local __var_name="$1" prompt="$2" default="$3" l
shift 3
local labels=() values=() i=1
while [[ $# -ge 2 ]]; do
labels+=("$1")
values+=("$2")
shift 2
done
l="$(_resolve_lang)"
echo >&2
echo "${prompt}:" >&2
local idx=1
for label in "${labels[@]}"; do
if [[ "$idx" -eq "$default" ]]; then
if [[ "$l" == "zh" ]]; then
echo " ${idx}) ${label} [默认]" >&2
else
echo " ${idx}) ${label} [default]" >&2
fi
else
echo " ${idx}) ${label}" >&2
fi
((idx++))
done
local val
if [[ "$l" == "zh" ]]; then
read -r -p "请选择 [1-${#labels[@]}] (默认: ${default}): " val
else
read -r -p "Select [1-${#labels[@]}] (default: ${default}): " val
fi
val="${val:-$default}"
if [[ "$val" =~ ^[0-9]+$ ]] && [[ "$val" -ge 1 && "$val" -le "${#labels[@]}" ]]; then
printf -v "$__var_name" "%s" "${values[$((val-1))]}"
else
if [[ "$l" == "zh" ]]; then
warn "$(_msg err_inv): ${val},使用默认值 ${default}"
else
warn "$(_msg err_inv): ${val}, using default ${default}"
fi
printf -v "$__var_name" "%s" "${values[$((default-1))]}"
fi
}
interactive_mode() {
echo
echo "========================================"
echo "$(_msg title)"
echo "$(_msg subtitle)"
echo "========================================"
echo
# 1. Language first so subsequent prompts use it
_ask_choice LANG_MODE "$(_msg q_lang)" 1 \
"中文" "zh" \
"English" "en"
echo
# 2. Build type (numbered)
_ask_choice BUILD_TYPE "$(_msg q_type)" 5 \
"$(_msg opt_cpu)" "cpu" \
"$(_msg opt_gpu)" "gpu" \
"$(_msg opt_metax)" "metax" \
"$(_msg opt_iluvatar)" "iluvatar" \
"$(_msg opt_mthreads)" "mthreads" \
"$(_msg opt_all)" "all"
# 3. Architecture (numbered)
local default_arch=1
[[ "$(arch_label "$PLATFORM")" == "arm64" ]] && default_arch=2
[[ "$(arch_label "$PLATFORM")" == "multi" ]] && default_arch=3
local arch_val
_ask_choice arch_val "$(_msg q_arch)" "$default_arch" \
"$(_msg opt_amd64)" "amd64" \
"$(_msg opt_arm64)" "arm64" \
"$(_msg opt_multi)" "multi"
PLATFORM="$(parse_arch "$arch_val")"
# 4. Free-form inputs
_ask VERSION "$(_msg q_version)" "$VERSION"
_ask_bool PUSH "$(_msg q_push)" "$PUSH"
_ask_bool EXPORT_TAR "$(_msg q_export)" "$EXPORT_TAR"
[[ "$EXPORT_TAR" == "true" ]] && _ask EXPORT_DIR "$(_msg q_output)" "$EXPORT_DIR"
_ask REGISTRY "$(_msg q_reg)" "$REGISTRY"
_ask_bool NO_CACHE "$(_msg q_cache)" "$NO_CACHE"
# Summary
echo
echo "----------------------------------------"
echo "$(_msg summary)"
echo "----------------------------------------"
echo "$(_msg q_lang): $LANG_MODE"
echo "$(_msg q_type): $BUILD_TYPE"
echo "$(_msg q_arch): $(arch_label "$PLATFORM")"
echo "$(_msg q_version): $VERSION"
echo "$(_msg q_push): $(_label_bool "$PUSH")"
echo "$(_msg q_export): $(_label_bool "$EXPORT_TAR")"
[[ "$EXPORT_TAR" == "true" ]] && echo "$(_msg q_output): $EXPORT_DIR"
echo "$(_msg q_reg): $REGISTRY"
echo "$(_msg q_cache): $(_label_bool "$NO_CACHE")"
echo
local confirm
read -r -p "$(_msg q_confirm) [Y/n]: " confirm
if [[ "$confirm" =~ ^[Nn]$ ]]; then info "$(_msg cancel)"; exit 0; fi
info "$(_msg start)"
}
# =============================================================================
# CLI Mode
# =============================================================================
show_help() {
cat <<EOF
$(_msg title)
Usage: ./scripts/build_docker.sh [options]
Options:
-t, --type TYPE $(_msg q_type): cpu, gpu, metax, iluvatar, mthreads, all (default: all)
-a, --arch ARCH $(_msg q_arch): amd64, arm64, multi (default: amd64)
-v, --version VER $(_msg q_version) (default: latest)
-p, --push $(_msg q_push)
-e, --export $(_msg q_export)
-o, --output DIR $(_msg q_output) (default: .)
-r, --registry REG $(_msg q_reg) (default: unis)
-n, --no-cache $(_msg q_cache)
-l, --lang LANG $(_msg q_lang): zh, en (default: zh)
--metax-base IMAGE Official MetaX vLLM base image for -t metax
--iluvatar-base IMAGE
Official Iluvatar vLLM base image for -t iluvatar
--mthreads-base IMAGE
Official Moore Threads vLLM base image for -t mthreads
-h, --help Show this help
Examples:
./scripts/build_docker.sh
./scripts/build_docker.sh -t gpu -a amd64
./scripts/build_docker.sh -t metax -a amd64
./scripts/build_docker.sh -t iluvatar -a amd64
./scripts/build_docker.sh -t iluvatar --iluvatar-base registry.iluvatar.com.cn:10443/customer/sz/vllm0.17.0-4.4.0-x86:v5
./scripts/build_docker.sh -t mthreads --mthreads-base registry.mthreads.com/presale/devtech/vllm_musa:s4000_4.3.5_d0519
./scripts/build_docker.sh -t cpu -a multi -p
./scripts/build_docker.sh -t all -v 1.0.0 -p -l zh
EOF
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
-t|--type) BUILD_TYPE="$2"; shift 2 ;;
-a|--arch) PLATFORM="$(parse_arch "$2")"; shift 2 ;;
-v|--version) VERSION="$2"; shift 2 ;;
-p|--push) PUSH="true"; shift ;;
-e|--export) EXPORT_TAR="true"; shift ;;
-o|--output) EXPORT_DIR="$2"; shift 2 ;;
-r|--registry) REGISTRY="$2"; shift 2 ;;
-n|--no-cache) NO_CACHE="true"; shift ;;
-l|--lang) LANG_MODE="$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 ;;
-h|--help) show_help; exit 0 ;;
*) die "$(_msg err_opt): $1" ;;
esac
done
}
validate() {
case "$BUILD_TYPE" in cpu|gpu|metax|iluvatar|mthreads|all) ;; *) die "$(_msg err_inv): type=$BUILD_TYPE" ;; esac
}
# =============================================================================
# Main
# =============================================================================
main() {
if [[ $# -eq 0 && -t 0 ]]; then
interactive_mode
else
parse_args "$@"
validate
fi
ensure_buildx
case "$BUILD_TYPE" in
cpu) build_cpu ;;
gpu) build_gpu ;;
metax) build_metax ;;
iluvatar) build_iluvatar ;;
mthreads) build_mthreads ;;
all) build_cpu; build_gpu ;;
esac
if [[ "$PUSH" != "true" && "$EXPORT_TAR" != "true" ]]; then
info "$(_msg recent):"
docker images | grep "${REGISTRY}/${IMAGE_NAME}" | head -10 || true
fi
info "$(_msg done)"
}
main "$@"