Что создаётся в итоге
Один GPU-инстанс, обслуживающий модель с открытыми весами через HTTP API, говорящий на совместимой с OpenAI схеме, так что все ваши клиентские библиотеки будут работать с ним, просто изменив базовый URL. За nginx, с TLS, API-ключом и systemd-юнитом, переживающим перезагрузки.
Написано на G-L40S: сорок восемь гигабайт видеопамяти на одной карте, проброшенной в инстанс через PCIe, а не разделяемой по времени между арендаторами. Это отличие — разница между предсказуемой задержкой и ожиданием чужой обучающей задачи. Карта ваша, пока вы её не отмените.
Перед началом
- G-L40S с Debian 13. G-ADA на двадцать гигабайт подходит для меньших моделей; арифметика в шаге четыре подскажет, каких именно.
- Два терабайта NVMe по этому плану — правильное место для весов. Скачивать сорок гигабайт дважды из-за того, что кэш был на корневой файловой системе, — раздражающий способ провести час.
- Имя хоста, указывающее на инстанс,
llm.example.comниже.
1. Драйвер
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После его возвращения:
nvidia-smiНужно, чтобы карта была в списке, версия драйвера напечатана, а использование памяти около нуля. Отсутствие карты на этом этапе почти всегда означает устаревший initramfs; update-initramfs -u и перезагрузитесь ещё раз, прежде чем открывать тикет.
nvidia-smi -q -d PERFORMANCE | grep -A3 "Clocks Event Reasons"
nvidia-smi -pm 1Режим персистентности держит драйвер загруженным между процессами, убирая несколько секунд инициализации из каждого перезапуска.
2. Среда выполнения
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Установка тянет большой набор колес, связанных с CUDA, и занимает время. Тем временем решите, какую модель использовать.
3. Выбор подходящего
Веса — это минимум, а не бюджет. Модель в шестнадцатибитной точности требует примерно два гигабайта VRAM на миллиард параметров, плюс кэш ключ-значение для каждого параллельного запроса, плюс около гигабайта на накладные расходы среды выполнения.
| Карта | Веса в 16-бит | Реальный размер модели | Остаток под кэш |
|---|---|---|---|
| L40S, 48 ГБ | 2 ГБ на миллиард | До примерно 20 миллиардов | 6–8 ГБ |
| L40S, 48 ГБ, 8-бит | 1 ГБ на миллиард | До примерно 34 миллиардов | 10 ГБ |
| RTX 4000 Ada, 20 ГБ | 2 ГБ на миллиард | До примерно 7 миллиардов | 5 ГБ |
| Две × L40S, 96 ГБ | 2 ГБ на миллиард | До примерно 40 миллиардов | 12 ГБ |
Столбец кэша определяет, сколько человек могут одновременно пользоваться конечной точкой. Заполните карту весами до краёв — и вы построите очень быструю игрушку для одного пользователя.
4. Обслуживание
Укажите кэш на NVMe и запустите сервер. Подставьте идентификатор репозитория выбранной вами модели с открытыми весами; среда выполнения загрузит её при первом запуске.
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Привязка к loopback намеренная. Проверка API-ключа в среде выполнения — это общий секрет без ограничения скорости за ним, поэтому nginx отвечает за внешний доступ, а среда выполнения не касается публичного интерфейса.
Щедрый таймаут запуска существует потому, что первый запуск скачивает десятки гигабайт, а затем компилирует ядра. Последующие запуски занимают меньше минуты.
5. Прокси и 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 — это строка, которая заставляет потоковые ответы действительно потоковыми. Оставьте её включённой, и токены будут приходить вежливыми порциями, когда nginx решит, что буфер полон, что выглядит в точности как медленная модель, но это не так.
Проверка
Начните с карты:
nvidia-smi --query-gpu=memory.used,memory.total,utilization.gpu --format=csvИспользуемая память должна быть около девяноста процентов от общего объёма, потому что это то, что запросил --gpu-memory-utilization. Затем конечная точка:
curl -s https://llm.example.com/v1/models -H "Authorization: Bearer <the key>" | head -20Одна модель, названная local. Теперь настоящее завершение, и замерьте время:
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}'Наконец, измерьте её под нагрузкой, а не по одному запросу, — это единственная цифра, которую стоит приводить:
/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Прочтите три числа из вывода: суммарную пропускную способность в токенах в секунду, медианное время до первого токена и 99-й процентиль полного времени. На одной L40S с моделью в диапазоне 10–20 миллиардов суммарная пропускная способность в сотни токенов в секунду и время до первого токена значительно меньше секунды — ожидаемая картина. Рост времени до первого токена с ростом параллелизма означает, что кэш ключ-значение слишком мал, поэтому уменьшите --max-model-len или --max-num-seqs и запустите снова.
После
Следите за температурой и троттлингом частоты первую неделю с помощью nvidia-smi dmon, поскольку карта, троттлящая по температуре при постоянной нагрузке, даёт именно ту перемежающуюся медлительность, которую обычно списывают на сеть. С точки зрения счетов помните, что GPU-инстанс стоит одинаково, занята карта или простаивает, поэтому лучше выполнять задачи пакетами, а не держать её прогретой. Цены — на странице GPU.