main
sh 87 lines 3.6 KB
Raw
1 #!/bin/sh
2 # Entrypoint wrapper for the opt-in `vllm-openai-nonroot` image.
3 #
4 # The image also ships a `vllm` user (UID 2000, GID 0) with HOME /home/vllm
5 # and a group-0-writable home directory. When the container is launched with
6 # `--user 2000:0` (or any other UID in group 0) the passwd entry is enough on
7 # its own: Docker picks up HOME=/home/vllm, getpass.getuser() resolves to
8 # "vllm", and every cache dir (HF, Triton, Inductor, vLLM, Numba, Outlines)
9 # that defaults to `$HOME/.cache/...` lands in a writable location.
10 #
11 # This wrapper exists for the *arbitrary-UID* case (e.g. OpenShift's
12 # `runAsUser: 1000540000` Restricted Pod Security Standard) where the caller
13 # UID is not in /etc/passwd at all. In that case:
14 # * $HOME may be unset or resolve to "/" (unwritable).
15 # * getpass.getuser() falls back to pwd.getpwuid() -> KeyError.
16 #
17 # The wrapper re-points $HOME to /home/vllm when writable, /tmp/vllm-home.XXXXXX
18 # otherwise, and defaults $USER to "vllm" so the pwd-lookup path is never
19 # taken. Everything else is forwarded to `vllm serve`.
20 #
21 # Non-empty caller-set env vars (HOME, USER, LOGNAME) are preserved, so
22 # existing K8s manifests and `docker run -e ...` keep working unchanged.
23 # Unset or empty values fall through to the wrapper's defaults, matching
24 # what shell code typically expects from "unset".
25
26 set -eu
27
28 if [ -z "${HOME:-}" ] || [ ! -w "${HOME}" ]; then
29 if [ -w /home/vllm ]; then
30 export HOME=/home/vllm
31 else
32 if _h="$(mktemp -d /tmp/vllm-home.XXXXXX 2>/dev/null)"; then
33 export HOME="$_h"
34 chmod 0700 "$HOME" 2>/dev/null || true
35 else
36 export HOME=/tmp
37 fi
38 unset _h
39 fi
40 fi
41
42 # Preserve the caller's cwd whenever it's still usable. A read-only mount
43 # (e.g. `docker run -w /models ... --model ./llama.gguf` where /models is
44 # the user's model share) is a legitimate, usable cwd — vllm only needs to
45 # *read* relative paths from there. We only fall back to $HOME when the
46 # cwd itself is truly inaccessible (no search bit, deleted inode, mount
47 # gone, etc.), which is when `cd .` actually fails.
48 #
49 # This is the accessibility check, not a writability check; the latter
50 # would silently rewrite cwd for any read-only workflow and break relative
51 # argv like `--model ./llama.gguf`, `--chat-template ./t.jinja`, relative
52 # TLS cert paths, etc.
53 if ! cd . 2>/dev/null; then
54 cd "$HOME"
55 fi
56
57 # getpass.getuser() prefers $USER/$LOGNAME/etc. before hitting getpwuid();
58 # setting it here makes the "UID not in passwd" path a no-op for everything
59 # in the process tree.
60 if [ -z "${USER:-}" ]; then
61 export USER=vllm
62 fi
63 if [ -z "${LOGNAME:-}" ]; then
64 export LOGNAME="$USER"
65 fi
66
67 # Shell-level tooling (`whoami`, bash's `\u` prompt, `id -un`, `sudo`) does
68 # NOT consult $USER; it calls getpwuid(geteuid()) directly. For arbitrary
69 # runtime UIDs in OpenShift-style deploys this returns "I have no name!".
70 # If /etc/passwd is group-0 writable (set at build time) and doesn't yet
71 # have an entry for this UID, append a synthetic one so every downstream
72 # consumer sees a consistent "vllm" identity.
73 #
74 # We parse the passwd file directly instead of calling `getent` because
75 # the container's NSS is typically just files anyway, and this lets us
76 # unit-test via the VLLM_PASSWD_FILE hook (undocumented; production uses
77 # /etc/passwd).
78 _passwd_file="${VLLM_PASSWD_FILE:-/etc/passwd}"
79 _uid="$(id -u)"
80 if [ -w "$_passwd_file" ] \
81 && ! awk -F: -v u="$_uid" '$3==u {found=1; exit} END {exit !found}' "$_passwd_file" 2>/dev/null; then
82 printf 'vllm:x:%s:%s:vllm:%s:/bin/bash\n' \
83 "$_uid" "$(id -g)" "$HOME" >> "$_passwd_file"
84 fi
85 unset _uid _passwd_file
86
87 exec vllm serve "$@"