From f8aabd5717869b03f15b646693fbaae108e68286 Mon Sep 17 00:00:00 2001 From: xiejiahao Date: Wed, 28 Oct 2026 14:46:32 +0800 Subject: [PATCH] Honour vllm flags appended after the image, highest priority Previously any arguments after the image in docker run were silently dropped: entrypoint.sh never forwarded "$@", so docker run ... --served-model-name mymodel started fine but kept serving as llm with no warning. - entrypoint.sh: forward "$@"; skip script-generated flags the user already provided so vllm never sees duplicate keys; strip those keys from EXTRA_ARGS when the command line also sets them - precedence: command line > EXTRA_ARGS > same-name -e > detected default - warn when --port / --served-model-name is overridden, since the host -p side and the client model field must change too - README: document the three ways and their precedence --- README.md | 25 +++++++++++++- entrypoint.sh | 93 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 102 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 0716b57..2178f68 100644 --- a/README.md +++ b/README.md @@ -421,7 +421,30 @@ docker run -d --name gev-26b-decide \ 两个变体共用挂载点 `/models/gemma4`,靠 `config.json` 区分;TP / eager / 显存 / 并发数都不用传,入口脚本会自动判定(见第 3 节)。 容器内端口恒为 80、模型名恒为 `llm`,所以两个变体可以复用同一个宿主端口 24396,串行切换时客户端不用改地址也不用改 `model` 字段。 -需要覆盖时加同名 `-e`,例如 `-e MAX_MODEL_LEN=16384`。 +### 覆盖参数:三种写法,优先级从高到低 + +| 优先级 | 写法 | 例子 | +|---|---|---| +| 1 | 在镜像后面直接跟 vllm 参数 | `... gemma4-bi150:v0.1.0 --served-model-name mymodel` | +| 2 | `-e EXTRA_ARGS="..."` | `-e EXTRA_ARGS="--max-model-len 16384"` | +| 3 | 同名 `-e` | `-e MAX_MODEL_LEN=16384 -e SERVED_NAME=mymodel` | +| 4 | 默认值 / config.json 检测 | 什么都不传 | + +三种写法都能生效,不会重复传参(脚本会跳过自己已经不需要传的项)。 +注意覆盖 `--port` 或 `--served-model-name` 时会打印 WARN,因为宿主机 `-p` 的右侧端口和客户端 `model` 字段也要同步改。 + +```bash +# 写法 1:镜像后直接跟参数(最符合 vllm 习惯) +docker run -d --name gemma4-31b \ + --entrypoint /usr/local/bin/gemma4-entrypoint.sh --workdir /opt/gemma4 \ + --privileged --pid host --ipc host -v /dev:/dev \ + -v /mnt/disk0/models/google/gemma-4-31B-it:/models/gemma4:ro \ + --ulimit memlock=-1 --ulimit stack=67108864 \ + -p 24396:80 \ + -e CUDA_VISIBLE_DEVICES=15,14,13,12 -e MODEL_PATH=/models/gemma4 -e AUTO_DETECT=1 \ + harbor.4pd.io/modelhubxc/xiejiahao/gemma4-bi150:v0.1.0 \ + --served-model-name mymodel --max-model-len 16384 +``` **注意**:并行跑两个变体时必须给不同的 `-p` 左侧端口,例如 `-p 24397:80`。 diff --git a/entrypoint.sh b/entrypoint.sh index 813a584..3e0a607 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -63,11 +63,6 @@ MAX_IMAGES="${MAX_IMAGES:-4}" ENFORCE_EAGER="${ENFORCE_EAGER:-0}" EXTRA_ARGS="${EXTRA_ARGS:-}" -EAGER_ARGS=() -if [[ "${ENFORCE_EAGER}" == "1" ]]; then - EAGER_ARGS=(--enforce-eager) -fi - echo "============================================================" echo "Gemma 4 family (BI-V150 unified image)" echo " model kind : ${MODEL_KIND}" @@ -91,15 +86,83 @@ if [[ -n "${DETECTED_HOST_PORT_HINT}" ]]; then fi echo "============================================================" +# ---------------------------------------------------------------- 用户参数 +# 优先级(高 -> 低): +# 1. docker run 之后追加的 vllm 参数("$@") +# 2. -e EXTRA_ARGS="..." +# 3. -e 显式设置(SERVED_NAME / PORT / TP / MAX_MODEL_LEN ...) +# 4. config.json 自动检测 +# 用户已经给了的 flag,本脚本不再重复传,避免 vllm 报 duplicate key。 +# 末尾参数的 flag 优先于 EXTRA_ARGS 里的同名 flag:把后者摘掉,避免 vllm 报 duplicate key。 +declare -A _CLI_FLAGS=() +if (( $# > 0 )); then + for _t in "$@"; do + [[ "${_t}" == --* ]] && _CLI_FLAGS["${_t%%=*}"]=1 + done +fi + +EXTRA_FILTERED=() +if [[ -n "${EXTRA_ARGS}" ]]; then + read -r -a _extra_tokens <<< "${EXTRA_ARGS}" + _skip=0 + for _t in "${_extra_tokens[@]}"; do + if (( _skip > 0 )); then + _skip=0 + continue + fi + if [[ "${_t}" == --* ]]; then + _name="${_t%%=*}" + if [[ -n "${_CLI_FLAGS[${_name}]:-}" ]]; then + # 该 flag 在命令行里也给了,跳过它(以及紧跟的取值) + [[ "${_t}" != *=* ]] && _skip=1 + continue + fi + fi + EXTRA_FILTERED+=("${_t}") + done +fi + +USER_TOKENS=("${EXTRA_FILTERED[@]}") +if (( $# > 0 )); then + USER_TOKENS+=("$@") +fi + +user_has() { + local want="$1" tok + (( ${#USER_TOKENS[@]} == 0 )) && return 1 + for tok in "${USER_TOKENS[@]}"; do + if [[ "${tok}" == "${want}" || "${tok}" == "${want}="* ]]; then + return 0 + fi + done + return 1 +} + +if (( $# > 0 )); then + echo "用户追加参数 : $*" + echo "(优先级最高,覆盖 -e 与自动检测)" + user_has --port && echo " WARN: --port 会改变容器内监听端口,宿主机 -p 的右侧端口也要同步改" >&2 + user_has --served-model-name && echo " WARN: --served-model-name 已覆盖默认的 llm,客户端 model 字段需同步修改" >&2 + echo "------------------------------------------------------------" +fi + +SERVE_ARGS=() +user_has --host || SERVE_ARGS+=(--host "${HOST}") +user_has --port || SERVE_ARGS+=(--port "${PORT}") +user_has --served-model-name || SERVE_ARGS+=(--served-model-name "${SERVED_NAME}") +user_has --tensor-parallel-size || SERVE_ARGS+=(--tensor-parallel-size "${TP}") +user_has --max-model-len || SERVE_ARGS+=(--max-model-len "${MAX_MODEL_LEN}") +user_has --max-num-seqs || SERVE_ARGS+=(--max-num-seqs "${MAX_NUM_SEQS}") +user_has --gpu-memory-utilization || SERVE_ARGS+=(--gpu-memory-utilization "${GPU_MEM_UTIL}") +user_has --limit-mm-per-prompt || SERVE_ARGS+=(--limit-mm-per-prompt "{\"image\": ${MAX_IMAGES}}") +if ! user_has --enforce-eager && ! user_has --no-enforce-eager; then + if [[ "${ENFORCE_EAGER}" == "1" ]]; then + SERVE_ARGS+=(--enforce-eager) + fi +fi + # shellcheck disable=SC2086 exec "${VLLM_BIN}" serve "${MODEL_PATH}" \ - --host "${HOST}" \ - --port "${PORT}" \ - --served-model-name "${SERVED_NAME}" \ - --tensor-parallel-size "${TP}" \ - --max-model-len "${MAX_MODEL_LEN}" \ - --max-num-seqs "${MAX_NUM_SEQS}" \ - --gpu-memory-utilization "${GPU_MEM_UTIL}" \ - --limit-mm-per-prompt "{\"image\": ${MAX_IMAGES}}" \ - "${EAGER_ARGS[@]}" \ - ${EXTRA_ARGS} + "${SERVE_ARGS[@]}" \ + "${EXTRA_FILTERED[@]}" \ + "$@"