#!/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 预设默认值在 presets/.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