149 lines
4.0 KiB
Bash
149 lines
4.0 KiB
Bash
|
|
#!/usr/bin/env bash
|
|||
|
|
#
|
|||
|
|
# Llama 4 Scout / Maverick 一键构建 + 启动(Iluvatar BI-V150)
|
|||
|
|
#
|
|||
|
|
# ./run.sh scout
|
|||
|
|
# ./run.sh maverick
|
|||
|
|
#
|
|||
|
|
# 常用覆盖项:
|
|||
|
|
# GPUS=8,9,10,11,12,13,14,15 ./run.sh scout
|
|||
|
|
# MODEL_HOST_DIR=/path/to/model ./run.sh maverick
|
|||
|
|
# EXTRA_ARGS="--max-model-len 8192" ./run.sh scout
|
|||
|
|
# EXTRA_DOCKER_ARGS="-v /mnt/disk1:/data:ro" ./run.sh scout
|
|||
|
|
# BUILD=0 ./run.sh scout # 使用已有镜像,不重新构建
|
|||
|
|
# DRY_RUN=1 ./run.sh scout # 只打印 docker run 命令
|
|||
|
|
#
|
|||
|
|
set -euo pipefail
|
|||
|
|
|
|||
|
|
cd "$(dirname "$0")"
|
|||
|
|
|
|||
|
|
usage() {
|
|||
|
|
cat >&2 <<'EOF'
|
|||
|
|
用法: ./run.sh <scout|maverick>
|
|||
|
|
|
|||
|
|
预设默认值在 presets/<name>.env 中,所有值都可以用环境变量覆盖:
|
|||
|
|
IMAGE NAME PORT MODEL_HOST_DIR MODEL_MOUNT SERVED_NAME
|
|||
|
|
TP PP GPUS MAX_MODEL_LEN MAX_NUM_SEQS GPU_MEM_UTIL
|
|||
|
|
ENFORCE_EAGER EXTRA_ARGS EXTRA_DOCKER_ARGS
|
|||
|
|
BUILD=0/1 DRY_RUN=0/1
|
|||
|
|
EOF
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
VARIANT="${1:-}"
|
|||
|
|
if [[ -z "${VARIANT}" || "${VARIANT}" == "-h" || "${VARIANT}" == "--help" ]]; then
|
|||
|
|
usage
|
|||
|
|
exit 2
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
PRESET="presets/${VARIANT}.env"
|
|||
|
|
if [[ ! -f "${PRESET}" ]]; then
|
|||
|
|
echo "ERROR: 未知预设: ${VARIANT}" >&2
|
|||
|
|
usage
|
|||
|
|
exit 2
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# shellcheck disable=SC1090
|
|||
|
|
source "${PRESET}"
|
|||
|
|
|
|||
|
|
IMAGE="${IMAGE:-${DEFAULT_IMAGE}}"
|
|||
|
|
NAME="${NAME:-${DEFAULT_NAME}}"
|
|||
|
|
PORT="${PORT:-${DEFAULT_PORT}}"
|
|||
|
|
MODEL_HOST_DIR="${MODEL_HOST_DIR:-${DEFAULT_MODEL_HOST_DIR}}"
|
|||
|
|
MODEL_MOUNT="${MODEL_MOUNT:-/models/llama4}"
|
|||
|
|
SERVED_NAME="${SERVED_NAME:-${DEFAULT_SERVED_NAME}}"
|
|||
|
|
TP="${TP:-${DEFAULT_TP}}"
|
|||
|
|
PP="${PP:-${DEFAULT_PP}}"
|
|||
|
|
GPUS="${GPUS:-${CUDA_VISIBLE_DEVICES:-${DEFAULT_GPUS}}}"
|
|||
|
|
MAX_MODEL_LEN="${MAX_MODEL_LEN:-${DEFAULT_MAX_MODEL_LEN}}"
|
|||
|
|
MAX_NUM_SEQS="${MAX_NUM_SEQS:-${DEFAULT_MAX_NUM_SEQS}}"
|
|||
|
|
GPU_MEM_UTIL="${GPU_MEM_UTIL:-${DEFAULT_GPU_MEM_UTIL}}"
|
|||
|
|
ENFORCE_EAGER="${ENFORCE_EAGER:-1}"
|
|||
|
|
EXTRA_ARGS="${EXTRA_ARGS:-}"
|
|||
|
|
EXTRA_DOCKER_ARGS="${EXTRA_DOCKER_ARGS:-}"
|
|||
|
|
BUILD="${BUILD:-1}"
|
|||
|
|
DRY_RUN="${DRY_RUN:-0}"
|
|||
|
|
|
|||
|
|
GPUS="${GPUS// /}"
|
|||
|
|
|
|||
|
|
if [[ ! -f "${MODEL_HOST_DIR}/config.json" ]]; then
|
|||
|
|
echo "ERROR: 模型目录不完整: ${MODEL_HOST_DIR}/config.json" >&2
|
|||
|
|
echo "请先下载模型,或设置 MODEL_HOST_DIR 指向正确目录。" >&2
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
EXPECTED_GPUS=$((TP * PP))
|
|||
|
|
if [[ -n "${GPUS}" ]]; then
|
|||
|
|
IFS=',' read -r -a GPU_LIST <<< "${GPUS}"
|
|||
|
|
if (( ${#GPU_LIST[@]} < EXPECTED_GPUS )); then
|
|||
|
|
echo "ERROR: TP*PP=${EXPECTED_GPUS},但 GPUS 只给了 ${#GPU_LIST[@]} 张: ${GPUS}" >&2
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
if (( ${#GPU_LIST[@]} > EXPECTED_GPUS )); then
|
|||
|
|
echo "WARN: TP*PP=${EXPECTED_GPUS},但 GPUS 给了 ${#GPU_LIST[@]} 张;vLLM 只会使用其中 ${EXPECTED_GPUS} 张。" >&2
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
DOCKER_EXTRA=()
|
|||
|
|
if [[ -n "${EXTRA_DOCKER_ARGS}" ]]; then
|
|||
|
|
# 按空白拆分;路径中不要带空格。
|
|||
|
|
read -r -a DOCKER_EXTRA <<< "${EXTRA_DOCKER_ARGS}"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
DOCKER_RUN=(
|
|||
|
|
docker run -d --name "${NAME}"
|
|||
|
|
--privileged
|
|||
|
|
--pid host
|
|||
|
|
--network host
|
|||
|
|
--ipc host
|
|||
|
|
-v /dev:/dev
|
|||
|
|
-v "${MODEL_HOST_DIR}:${MODEL_MOUNT}:ro"
|
|||
|
|
--ulimit memlock=-1
|
|||
|
|
--ulimit stack=67108864
|
|||
|
|
-e CUDA_VISIBLE_DEVICES="${GPUS}"
|
|||
|
|
-e MODEL_PATH="${MODEL_MOUNT}"
|
|||
|
|
-e SERVED_NAME="${SERVED_NAME}"
|
|||
|
|
-e PORT="${PORT}"
|
|||
|
|
-e TP="${TP}"
|
|||
|
|
-e PP="${PP}"
|
|||
|
|
-e MAX_MODEL_LEN="${MAX_MODEL_LEN}"
|
|||
|
|
-e MAX_NUM_SEQS="${MAX_NUM_SEQS}"
|
|||
|
|
-e GPU_MEM_UTIL="${GPU_MEM_UTIL}"
|
|||
|
|
-e ENFORCE_EAGER="${ENFORCE_EAGER}"
|
|||
|
|
-e EXTRA_ARGS="${EXTRA_ARGS}"
|
|||
|
|
"${DOCKER_EXTRA[@]}"
|
|||
|
|
--restart unless-stopped
|
|||
|
|
"${IMAGE}"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if [[ "${DRY_RUN}" == "1" ]]; then
|
|||
|
|
printf '%q ' "${DOCKER_RUN[@]}"
|
|||
|
|
printf '\n'
|
|||
|
|
exit 0
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
if [[ "${BUILD}" == "1" ]]; then
|
|||
|
|
echo "==> build ${IMAGE}"
|
|||
|
|
docker build -t "${IMAGE}" .
|
|||
|
|
else
|
|||
|
|
echo "==> skip build (BUILD=0), image=${IMAGE}"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo "==> recreate ${NAME}"
|
|||
|
|
docker rm -f "${NAME}" 2>/dev/null || true
|
|||
|
|
|
|||
|
|
echo "==> run ${VARIANT}: GPUs=${GPUS}, TP=${TP}, PP=${PP}, model=${MODEL_HOST_DIR}"
|
|||
|
|
"${DOCKER_RUN[@]}"
|
|||
|
|
|
|||
|
|
echo "==> wait for http://127.0.0.1:${PORT}/health"
|
|||
|
|
for _ in $(seq 1 120); do
|
|||
|
|
if curl -sf "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1; then
|
|||
|
|
echo "ready: http://127.0.0.1:${PORT}/v1/models"
|
|||
|
|
exit 0
|
|||
|
|
fi
|
|||
|
|
sleep 5
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
echo "ERROR: 服务 10 分钟未就绪,查看日志:" >&2
|
|||
|
|
echo " docker logs -f ${NAME}" >&2
|
|||
|
|
exit 1
|