# Qwen3.8-27B load balancer — metadata-only request log. # Runs as the `lb` container; reaches replicas by compose service name. worker_processes 1; error_log /var/log/qwen/lb-error.log warn; pid /tmp/nginx.pid; events { worker_connections 1024; } http { # --------------------------------------------------------------------------- # PRIVACY: METADATA ONLY. # The team's prompts contain proprietary code. This log records timings, which # replica served, status and byte counts — and nothing else. Never add # $request_body, never log the response, never log $http_authorization. # vLLM's own --enable-log-requests writes prompt TEXT to the container log and # is deliberately NOT enabled anywhere in this stack. # --------------------------------------------------------------------------- # # TTFT WARNING: nginx cannot measure time-to-first-token on a streaming # response. $upstream_header_time is time-to-response-headers, which vLLM emits # immediately when the SSE stream opens — long before the first token. Real TTFT # comes from the Prometheus histogram vllm:time_to_first_token_seconds, collected # per replica by telemetry/scrape.py. Do not read header_time as TTFT. # 64K-context prompts exceed nginx's 1 MB default and are rejected with 413 # BEFORE reaching vLLM. Sized for max-model-len 65536 with headroom. client_max_body_size 32m; log_format qwen escape=json '{"ts":"$time_iso8601","replica":"$upstream_addr","status":$status,' '"request_time":$request_time,"header_time":"$upstream_header_time",' '"upstream_time":"$upstream_response_time","bytes_out":$body_bytes_sent,' '"bytes_in":$request_length,"uri":"$uri","method":"$request_method"}'; access_log /var/log/qwen/requests.jsonl qwen; upstream qwen { # Round-robin (nginx default), equal weight — DELIBERATE. # # least_conn routes toward whichever replica is currently faster. During an # A/B that means the faster arm also receives MORE traffic, so the two arms # end up with unequal sample sizes drawn over different slices of the day. # Per-request rates stay valid either way, but equal-n arms make the # crossover analysis clean and the confidence interval honest. server replica-gpu0:18020 max_fails=3 fail_timeout=30s; server replica-gpu1:18021 max_fails=3 fail_timeout=30s; keepalive 32; } server { listen 18000; location / { proxy_pass http://qwen; proxy_http_version 1.1; proxy_buffering off; # required for streaming proxy_request_buffering off; proxy_read_timeout 1800s; # a 192K-context prefill can take ~6 min proxy_send_timeout 1800s; proxy_set_header Connection ""; # Authorization passes through untouched (and is never logged). } location = /lb-health { access_log off; return 200 "ok\n"; } } }