Doze construções

Inferência local em uma GPU com passagem direta e endpoint compatível com OpenAI

Driver, runtime e modelo em uma G-L40S, servidos por trás de TLS com chave de API, e um teste de carga que reporta tokens reais por segundo em vez de um dado de marketing.

O que isso constrói

Uma instância de GPU servindo um modelo de pesos abertos por uma API HTTP que fala o esquema compatível com OpenAI, então toda biblioteca de cliente que você já tem funciona contra ela com uma URL base alterada. Atrás do nginx, com TLS, uma chave de API e uma unit do systemd que sobrevive a reinicializações.

Escrito em uma G-L40S: quarenta e oito gigabytes de VRAM em uma única placa, passada para a instância via PCIe em vez de fatiada por tempo entre locatários. Essa distinção é a diferença entre latência previsível e esperar atrás do job de treinamento de outra pessoa. A placa é sua até você cancelar.

Antes de começar

  • Uma G-L40S com Debian 13. A G-ADA com vinte gigabytes funciona para modelos menores; a aritmética no passo quatro diz quais.
  • Dois terabytes de NVMe neste plano, que é o lugar certo para pesos. Baixar quarenta gigabytes duas vezes porque o cache estava no filesystem raiz é um jeito irritante de gastar uma hora.
  • Um hostname apontando para a instância, llm.example.com abaixo.

1. Driver

sed -i "s/ main$/ main contrib non-free-firmware non-free/" /etc/apt/sources.list.d/debian.sources
apt update
apt install -y nvidia-driver firmware-misc-nonfree build-essential
reboot

Depois que ele voltar:

nvidia-smi

Você quer a placa listada, a versão do driver impressa e o uso de memória perto de zero. Uma placa ausente neste ponto é quase sempre um initramfs desatualizado; update-initramfs -u e reinicie mais uma vez antes de abrir um ticket.

nvidia-smi -q -d PERFORMANCE | grep -A3 "Clocks Event Reasons"
nvidia-smi -pm 1

O modo de persistência mantém o driver carregado entre processos, o que remove vários segundos de inicialização de cada reinício.

2. Runtime

apt install -y python3-venv python3-pip
install -d -o root -g root /srv/models /opt/vllm
python3 -m venv /opt/vllm/venv
/opt/vllm/venv/bin/pip install --upgrade pip
/opt/vllm/venv/bin/pip install vllm

A instalação puxa um grande conjunto de wheels ligados a CUDA e leva um tempo. Enquanto isso, decida sobre um modelo.

3. Escolhendo o que cabe

Os pesos são o piso, não o orçamento. Um modelo em precisão de dezesseis bits precisa de aproximadamente dois gigabytes de VRAM por bilhão de parâmetros, mais o cache de chave-valor para cada requisição concorrente, mais um gigabyte ou mais de overhead de runtime.

PlacaPesos em 16 bitsTamanho realista de modeloSobra para cache
L40S, 48 GB2 GB por bilhãoAté cerca de 20 bilhões6 a 8 GB
L40S, 48 GB, 8 bits1 GB por bilhãoAté cerca de 34 bilhões10 GB
RTX 4000 Ada, 20 GB2 GB por bilhãoAté cerca de 7 bilhões5 GB
Duas × L40S, 96 GB2 GB por bilhãoAté cerca de 40 bilhões12 GB

A coluna de cache é o que determina quantas pessoas podem usar o endpoint de uma vez. Encha a placa até a borda com pesos e você construiu um brinquedo de usuário único muito rápido.

4. Servindo

Aponte o cache para o NVMe e inicie o servidor. Substitua o identificador do repositório do modelo de pesos abertos que você escolheu; o runtime o busca no primeiro início.

cat > /etc/systemd/system/vllm.service <<EOF
[Unit]
Description=vLLM inference server
After=network-online.target

[Service]
Environment=HF_HOME=/srv/models
Environment=VLLM_API_KEY=<a long random string>
ExecStart=/opt/vllm/venv/bin/vllm serve <org>/<model> \\
  --host 127.0.0.1 --port 8000 \\
  --served-model-name local \\
  --max-model-len 16384 \\
  --gpu-memory-utilization 0.92 \\
  --max-num-seqs 32
Restart=on-failure
RestartSec=10
TimeoutStartSec=1800

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable --now vllm
journalctl -fu vllm

Vincular ao loopback é proposital. A verificação de chave de API no runtime é um único segredo compartilhado sem rate limiting por trás, então o nginx faz a exposição e o runtime nunca toca a interface pública.

O timeout generoso de início existe porque o primeiro lançamento baixa dezenas de gigabytes e então compila kernels. Inícios subsequentes levam menos de um minuto.

5. Proxy e TLS

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  http2 on;
  server_name llm.example.com;

  ssl_certificate     /etc/letsencrypt/live/llm.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/llm.example.com/privkey.pem;

  location /v1/ {
    proxy_pass http://127.0.0.1:8000;
    proxy_buffering off;
    proxy_read_timeout 600s;
    proxy_set_header Host $host;
  }
}

proxy_buffering off é a linha que faz respostas de streaming fluir. Deixe-a ligada e tokens chegam em lotes educados sempre que o nginx sente que um buffer está cheio, o que parece exatamente um modelo lento e não é.

Verifique

Comece com a placa:

nvidia-smi --query-gpu=memory.used,memory.total,utilization.gpu --format=csv

A memória usada deve estar em torno de noventa por cento do total, porque é o que --gpu-memory-utilization pediu. Depois o endpoint:

curl -s https://llm.example.com/v1/models -H "Authorization: Bearer <the key>" | head -20

Um modelo, chamado local. Agora uma conclusão real, e cronometre-a:

time curl -s https://llm.example.com/v1/chat/completions \
  -H "Authorization: Bearer <the key>" \
  -H "Content-Type: application/json" \
  -d '{"model":"local","messages":[{"role":"user","content":"List three prime numbers."}],"max_tokens":64}'

Finalmente, meça sob carga em vez de uma requisição por vez, que é o único número que vale citar:

/opt/vllm/venv/bin/vllm bench serve \
  --backend openai-chat \
  --base-url https://llm.example.com \
  --endpoint /v1/chat/completions \
  --model local --num-prompts 200 --request-rate 8

Leia três números da saída: tokens de saída por segundo em agregado, mediana do tempo até o primeiro token e o percentil noventa e nove de ponta a ponta. Em uma única L40S com um modelo na faixa de dez a vinte bilhões, a produção agregada na casa das centenas de tokens por segundo e um tempo até o primeiro token bem abaixo de um segundo é a forma esperada. Tempo até o primeiro token que sobe com a concorrência significa que seu cache de chave-valor é pequeno demais, então diminua --max-model-len ou --max-num-seqs e rode novamente.

Depois

Monitore a temperatura e o throttling de clock durante a primeira semana com nvidia-smi dmon, já que uma placa que sofre throttling térmico sob carga sustentada produz exatamente o tipo de lentidão intermitente que é culpada pela rede. Em termos de fatura, lembre-se de que uma instância de GPU custa o mesmo esteja a placa ocupada ou ociosa, então faça trabalhos em lote em vez de deixá-la quente. Os preços estão em a página de GPU.

Pronto quando você estiver

Escolha uma cidade. Escolha um tamanho. Pague em cripto.

Sem formulários sobre quem você é, sem esperar aprovação de um humano, sem ligação para verificar nada. O pagamento é confirmado e as credenciais chegam na sua caixa de entrada.