Тема
Перенос моделей Hugging Face в локальное S3 для запуска vLLM в закрытом контуре
Инструкция описывает загрузку модели с Hugging Face на машину с доступом в Интернет, перенос полного снапшота модели в закрытый контур и загрузку его в локальное S3-совместимое хранилище. После этого vLLM запускается внутри закрытого контура и получает модель из локального S3 либо из предварительно загруженного каталога на PVC.
Когда это пригодится
- В закрытом контуре без доступа к сети Интернет необходимо запустить инференс модели через
vLLM - Требуется контролируемая поставка моделей: фиксация версии, контроль целостности, исключение обращений к внешним ресурсам
- Требуется развернуть модель в Kubernetes-кластере с GPU-нодами, где модель хранится в локальном S3
Основной сценарий:
text
INTERNET
│
▼
┌───────────────────┐
│ Internet Host │
│ Hugging Face │
│ huggingface_hub │
└─────────┬─────────┘
│ download
▼
┌───────────────────┐
│ Model Package │
│ config.json │
│ tokenizer.* │
│ *.safetensors │
│ generation_config │
└─────────┬─────────┘
│ SHA256
▼
┌───────────────────┐
│ Transfer Package │
└─────────┬─────────┘
│ USB / SSD / контролируемый канал
▼
┌───────────────────┐
│ Air-gapped Host │
└─────────┬─────────┘
│ upload
▼
┌───────────────────┐
│ Local S3 │
│ s3://models/... │
└─────────┬─────────┘
▼
┌───────────────────┐
│ Kubernetes / vLLM │
│ model from S3 │
└───────────────────┘Обозначения в документе
| Плейсхолдер | Значение |
|---|---|
<COMMIT-SHA> | SHA-1 коммита модели в репозитории Hugging Face; фиксируется на шаге 1 и используется как immutable-префикс в S3 |
s3.example.local | Адрес S3-совместимого хранилища в закрытом контуре |
registry.example.local | Адрес локального container registry в закрытом контуре |
Qwen/Qwen3-8B | Пример репозитория модели; заменяйте на вашу модель |
vllm.example.local | Адрес сервиса vLLM для проверки API |
Предварительные требования
Internet-хост (машина с доступом к Hugging Face):
- Python 3
- Пакет
huggingface_hub(CLIhf) - Токен Hugging Face для gated/private-моделей (для публичных моделей не требуется)
- Опционально: GPU для полной проверки модели через
vLLMдо переноса
Закрытый контур:
- S3-совместимое хранилище: endpoint, bucket, credentials
- Kubernetes-кластер с GPU-нодами (см. GPU-оператор Metax)
vLLM-образ в локальном registryStorageClassиPVCдля хранения модели (см. Longhorn и Velero — контекст S3-хранилищ)- Достаточный объём VRAM и дискового пространства под модель
Быстрый путь: минимальный вариант для разовой поставки
Если требуется максимально простой сценарий без полных проверок — выполните следующее. Детальный сценарий с фиксацией commit SHA, inventory, контрольными суммами и промышленной схемой описан в шагах 1–5.
На Internet-хосте:
bash
hf download \
Qwen/Qwen3-8B \
--revision <COMMIT-SHA> \
--local-dir ./Qwen3-8BПроверьте структуру:
bash
find Qwen3-8B -type f | sortСоздайте контрольные суммы:
bash
find Qwen3-8B \
-type f \
-exec sha256sum {} \; \
> SHA256SUMSЗаархивируйте и посчитайте SHA-256 архива:
bash
tar -cf Qwen3-8B.tar Qwen3-8B
sha256sum Qwen3-8B.tar > Qwen3-8B.tar.sha256Перенесите архив на USB.
В закрытом контуре:
bash
sha256sum -c Qwen3-8B.tar.sha256bash
tar -xf Qwen3-8B.tarЗагрузите в S3:
bash
aws s3 sync \
Qwen3-8B/ \
s3://models/qwen/Qwen3-8B/<COMMIT-SHA>/ \
--endpoint-url https://s3.example.localПроверьте содержимое:
bash
aws s3 ls \
s3://models/qwen/Qwen3-8B/<COMMIT-SHA>/ \
--recursive \
--endpoint-url https://s3.example.localПосле этого модель можно использовать для vLLM deployment.
Ограничения быстрого пути
В быстром пути не фиксируются commit SHA модели, inventory и контрольные суммы файлов до переноса, не проверяется повторно целостность после загрузки в S3. Для воспроизводимой и проверяемой поставки используйте шаги 1–5.
Шаг 1. Скачивание снапшота модели на Internet-хосте
1.1. Подготовка Internet-хоста
Проверьте Python и установите huggingface_hub:
bash
python3 --versionbash
python3 -m pip install -U huggingface_hubПроверьте CLI. В современных версиях huggingface_hub основным CLI является hf:
bash
huggingface-cli --helpbash
hf --help1.2. Авторизация в Hugging Face
Для публичной модели авторизация может не потребоваться. Для gated/private-моделей необходим токен:
bash
hf auth loginВведите Hugging Face Access Token. Проверьте авторизацию:
bash
hf auth whoami1.3. Создание рабочего каталога
bash
export WORKDIR=/opt/model-transferbash
mkdir -p "$WORKDIR"/{models,manifests,checksums,logs,scripts}Результат:
text
/opt/model-transfer/
├── models/
├── manifests/
├── checksums/
├── logs/
└── scripts/1.4. Фиксация модели: repository, revision, commit SHA
Не используйте только имя репозитория (Qwen/Qwen3-8B) как идентификатор поставки — ветка main может измениться. Фиксируйте три параметра:
| Параметр | Значение |
|---|---|
| Repository | Qwen/Qwen3-8B |
| Revision | main |
| Commit SHA | <COMMIT-SHA> |
Commit SHA однозначно идентифицирует состояние модели.
1.5. Получение информации о модели
Посмотрите, какие файлы будут скачаны, и оцените размер:
bash
hf download Qwen/Qwen3-8B --dry-runДля конкретного revision:
bash
hf download \
Qwen/Qwen3-8B \
--revision <COMMIT-SHA> \
--dry-run1.6. Скачивание снапшота целиком
Скачивайте снапшот целиком, а не только веса
vLLM требуется как минимум config.json + веса + tokenizer. В зависимости от модели могут понадобиться generation_config.json, tokenizer_config.json, special_tokens_map.json, chat_template.jinja, индекс весов (model.safetensors.index.json) и custom-код модели. Правильная единица поставки — весь снапшот (Model Release), а не отдельные *.safetensors.
bash
hf download \
Qwen/Qwen3-8B \
--revision <COMMIT-SHA> \
--local-dir "$WORKDIR/models/Qwen3-8B"Используйте --local-dir, а не стандартный cache ~/.cache/huggingface/: модель сразу лежит в самостоятельном каталоге, который можно проверить, заархивировать, посчитать checksum и перенести.
Почему нельзя скачать только *.safetensors
Распространённая ошибка — «мне нужны веса модели, скачаю *.safetensors». Для vLLM этого недостаточно: без config.json и tokenizer модель не загрузится. Правильная единица поставки — Model Release, а не model.safetensors.
1.7. Проверка снапшота на Internet-хосте
Проверьте структуру и размер:
bash
find "$WORKDIR/models/Qwen3-8B" -type f | sortbash
du -sh "$WORKDIR/models/Qwen3-8B"Пример вывода:
text
42G /opt/model-transfer/models/Qwen3-8BПроверьте обязательные файлы — конфигурацию, токенизатор и веса:
bash
test -f "$WORKDIR/models/Qwen3-8B/config.json"bash
find "$WORKDIR/models/Qwen3-8B" -maxdepth 1 -type f | grep tokenizerbash
find "$WORKDIR/models/Qwen3-8B" \
-type f \
\( -name '*.safetensors' -o -name '*.bin' -o -name '*.pt' \)Посмотрите содержимое config.json — особенно поля architectures, model_type, torch_dtype, hidden_size, num_hidden_layers, num_attention_heads:
bash
jq '{ architectures, model_type, torch_dtype, hidden_size, num_hidden_layers, num_attention_heads }' \
"$WORKDIR/models/Qwen3-8B/config.json"Убедитесь, что снапшот корректно загружается через Transformers. Установите зависимости:
bash
python3 -m pip install transformers torchВыполните тест офлайн-загрузки:
python
from transformers import AutoTokenizer, AutoConfig
path = "/opt/model-transfer/models/Qwen3-8B"
config = AutoConfig.from_pretrained(
path,
local_files_only=True
)
tokenizer = AutoTokenizer.from_pretrained(
path,
local_files_only=True
)
print(config)
print(tokenizer)local_files_only=True
Параметр local_files_only=True гарантирует, что библиотека не попытается скачать недостающие файлы из Интернета. Используйте его во всех офлайн-проверках.
Если на Internet-хосте есть GPU, выполните полный тест vLLM до переноса:
bash
vllm serve \
/opt/model-transfer/models/Qwen3-8B \
--served-model-name qwen3-8bЗафиксируйте версии окружения (vLLM, CUDA, NVIDIA driver, PyTorch, архитектура модели и GPU) — они понадобятся для environment manifest.
Если Internet Host не имеет GPU
Это не проблема: можно проверить tokenizer, config и файлы модели офлайн. Полный тест vLLM выполните уже в закрытом контуре.
python
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(
"/opt/model-transfer/models/Qwen3-8B",
local_files_only=True
)
print("Tokenizer OK")1.8. Особенности отдельных моделей
- Квантованные модели. Если модель квантована (
AWQ,GPTQ,GGUF,FP8,INT8), явно отразите это в manifest:Qwen3-8BиQwen3-8B-AWQ— разные артефакты, нельзя считать их одной моделью. - Custom-код. Некоторые модели требуют дополнительных Python-файлов в снапшоте (
modeling_xxx.py,configuration_xxx.py,tokenization_xxx.py). Эти файлы должны быть перенесены вместе со снапшотом.
trust_remote_code
Флаг --trust-remote-code означает выполнение кода, поставляемого вместе с моделью. В защищённом контуре включайте его только для заранее проверенных и утверждённых моделей.
- Python-зависимости. Custom-код может требовать пакеты (
einops,sentencepiece,protobuf,tiktokenи др.). В air-gapped среде они тоже должны быть доступны — при необходимости перенесите их в локальный PyPI/package repository. - Токенизатор. Проверьте наличие файлов токенизатора — в зависимости от модели это
tokenizer.json,tokenizer.model,vocab.json,merges.txt,special_tokens_map.json,tokenizer_config.json:
bash
find "$WORKDIR/models/Qwen3-8B" -maxdepth 1 -type f | grep -Ei 'token|vocab|merges|spiece'- Chat template. Для instruct/chat-моделей важен
chat_template.jinja(или chat template внутриtokenizer_config.json). Без него модель может технически загрузиться, но поведение chat API будет отличаться от ожидаемого. Chat template — часть Model Release.
Шаг 2. Фиксация версии и подготовка transfer package
2.1. Получение commit SHA
Зафиксируйте commit SHA модели через Python:
python
from huggingface_hub import model_info
info = model_info(
"Qwen/Qwen3-8B"
)
print(info.sha)Или для конкретной revision:
python
from huggingface_hub import model_info
info = model_info(
"Qwen/Qwen3-8B",
revision="main"
)
print(info.sha)Сохраните результат в manifests/model-revision.txt:
text
repository=Qwen/Qwen3-8B
revision=main
commit=<COMMIT-SHA>2.2. Inventory и контрольные суммы
Создайте inventory всех файлов модели:
bash
find \
"$WORKDIR/models/Qwen3-8B" \
-type f \
-printf '%P\n' \
| sort \
> "$WORKDIR/manifests/model-files.txt"Посчитайте SHA-256 каждого файла:
bash
cd "$WORKDIR/models/Qwen3-8B"
find . \
-type f \
-exec sha256sum {} \; \
> "$WORKDIR/checksums/model-SHA256SUMS"Проверьте контрольные суммы:
bash
cd "$WORKDIR/models/Qwen3-8B"
sha256sum -c "$WORKDIR/checksums/model-SHA256SUMS"Все файлы должны вернуть OK.
Сохраните размер и количество файлов:
bash
du -sh "$WORKDIR/models/Qwen3-8B" > "$WORKDIR/manifests/model-size.txt"bash
find "$WORKDIR/models/Qwen3-8B" -type f | wc -l > "$WORKDIR/manifests/model-file-count.txt"2.3. Model manifest
Пример manifests/model.yaml:
yaml
model:
name: Qwen3-8B
repository: Qwen/Qwen3-8B
revision: main
commit: "<COMMIT-SHA>"
files:
count: 42
size_bytes: 123456789012
format:
weights: safetensors
source:
type: huggingface
url: https://huggingface.co/Qwen/Qwen3-8B
target:
storage: s3
bucket: models
prefix: qwen/Qwen3-8B/<COMMIT-SHA>/2.4. Сборка transfer package
Рекомендуемая структура пакета:
text
Qwen3-8B-<COMMIT-SHA>/
├── README.md
├── manifest.yaml
├── model/
│ ├── config.json
│ ├── generation_config.json
│ ├── tokenizer.json
│ ├── tokenizer_config.json
│ ├── model-00001-of-00004.safetensors
│ └── ...
├── manifests/
│ ├── model-files.txt
│ ├── model-revision.txt
│ ├── model-size.txt
│ └── environment.txt
└── checksums/
└── model-SHA256SUMSСоберите пакет:
bash
mkdir -p "$WORKDIR/package"
cp -r "$WORKDIR/models/Qwen3-8B" "$WORKDIR/package/model"
cp -r "$WORKDIR/manifests" "$WORKDIR/package/"
cp -r "$WORKDIR/checksums" "$WORKDIR/package/"Положите в пакет README.txt с информацией о модели:
text
Model: Qwen3-8B
Hugging Face repository: Qwen/Qwen3-8B
Revision: main
Commit: <COMMIT-SHA>
Download date: 2026-08-31
Weight format: safetensors
Total size: XX GB
Files: XX
SHA256: checksums/model-SHA256SUMS2.5. Создание архива и его SHA-256
bash
tar \
-czf \
Qwen3-8B-<COMMIT-SHA>-airgap.tar.gz \
-C "$WORKDIR/package" \
model \
manifests \
checksumsgzip для больших моделей
Сжатие safetensors обычно даёт небольшой выигрыш — веса уже не являются хорошо сжимаемыми текстовыми данными. Для моделей на десятки-сотни гигабайт используйте обычный tar без сжатия:
bash
tar \
-cf \
Qwen3-8B-<COMMIT-SHA>-airgap.tar \
-C "$WORKDIR/package" \
model \
manifests \
checksumsМожно переносить каталог и без архива, если транспорт это позволяет.
Создайте SHA-256 архива:
bash
sha256sum \
Qwen3-8B-<COMMIT-SHA>-airgap.tar \
> \
Qwen3-8B-<COMMIT-SHA>-airgap.tar.sha2562.6. Перенос через USB
bash
cp Qwen3-8B-<COMMIT-SHA>-airgap.tar /media/usb/
cp Qwen3-8B-<COMMIT-SHA>-airgap.tar.sha256 /media/usb/
sync
umount /media/usbШаг 3. Проверка и распаковка в закрытом контуре
3.1. Проверка SHA-256 архива
После подключения USB в закрытом контуре:
bash
sha256sum -c Qwen3-8B-<COMMIT-SHA>-airgap.tar.sha256Ожидаемый результат:
text
Qwen3-8B-<COMMIT-SHA>-airgap.tar: OKНЕ ПРОДОЛЖАТЬ ИМПОРТ
Если checksum не совпадает — не продолжайте импорт. Повторите перенос или разберитесь с причиной повреждения до загрузки в S3.
3.2. Распаковка
bash
mkdir -p /opt/model-import
tar -xf Qwen3-8B-<COMMIT-SHA>-airgap.tar -C /opt/model-importРезультат:
text
/opt/model-import/
├── model/
├── manifests/
└── checksums/3.3. Повторная проверка checksum файлов
bash
cd /opt/model-import/model
sha256sum -c ../checksums/model-SHA256SUMSВсе файлы должны пройти проверку.
Шаг 4. Загрузка модели в локальное S3
4.1. Подготовка AWS CLI и credentials
Пример параметров:
- Endpoint:
https://s3.example.local - Bucket:
models
Проверьте наличие AWS CLI:
bash
aws --versionНастройте credentials:
bash
aws configureУкажите:
text
AWS Access Key ID:
AWS Secret Access Key:
Default region:Для S3-совместимого хранилища endpoint задаётся отдельно через --endpoint-url.
4.2. Проверка доступа к S3
bash
aws s3 \
--endpoint-url https://s3.example.local \
lsПроверьте bucket:
bash
aws s3 \
--endpoint-url https://s3.example.local \
ls \
s3://models/4.3. Если используется внутренний CA
Если S3 использует сертификат внутреннего CA — не отключайте проверку TLS.
Не используйте --no-verify-ssl в production
Флаг --no-verify-ssl отключает проверку TLS. В production вместо него установите внутренний CA:
bash
export AWS_CA_BUNDLE=/etc/pki/ca-trust/source/anchors/internal-ca.crt(или соответствующий путь для конкретной ОС). После этого проверьте доступ:
bash
aws s3 \
--endpoint-url https://s3.example.local \
ls4.4. Выбор immutable prefix
Рекомендуемый префикс:
text
s3://models/qwen/Qwen3-8B/<COMMIT-SHA>/Не перезаписывайте существующую модель
Не загружайте новую версию поверх s3://models/qwen/Qwen3-8B/. Используйте immutable prefix <COMMIT-SHA> — это позволяет хранить несколько релизов модели (Model Release 1/2/3) и выполнять rollback.
Почему в S3 лучше использовать commit SHA
Хранение только по имени модели (s3://models/qwen/Qwen3-8B/) не позволяет отличить версии. Префикс с commit SHA — неизменяемый артефакт. Для нескольких моделей структура выглядит так:
text
s3://models/
│
├── qwen/
│ ├── Qwen3-8B/
│ │ ├── <COMMIT-1>/
│ │ └── <COMMIT-2>/
│ │
│ └── Qwen3-32B/
│ └── <COMMIT-1>/
│
├── meta-llama/
│ └── Llama-3.3-70B-Instruct/
│ └── <COMMIT>/
│
└── mistralai/
└── Mistral-Small/
└── <COMMIT>/При необходимости можно вести логический alias current:
text
qwen/Qwen3-8B/
├── abc123/
├── def456/
└── current/ # current → def456current — логический alias на утверждённую версию, а не единственное хранилище модели.
4.5. Загрузка модели в S3
bash
aws s3 sync \
/opt/model-import/model/ \
s3://models/qwen/Qwen3-8B/<COMMIT-SHA>/ \
--endpoint-url https://s3.example.localAWS CLI рекурсивно загрузит весь каталог.
4.6. Проверка содержимого S3
bash
aws s3 ls \
s3://models/qwen/Qwen3-8B/<COMMIT-SHA>/ \
--recursive \
--endpoint-url https://s3.example.localДолжны присутствовать config.json, tokenizer.json, tokenizer_config.json, *.safetensors и остальные файлы модели.
Проверьте, что количество файлов совпадает. Локально:
bash
find /opt/model-import/model -type f | wc -lВ S3:
bash
aws s3 ls \
s3://models/qwen/Qwen3-8B/<COMMIT-SHA>/ \
--recursive \
--endpoint-url https://s3.example.local \
| wc -lКоличество должно совпадать.
Проверьте размер. Локально:
bash
du -sb /opt/model-import/modelВ S3:
bash
aws s3 ls \
s3://models/qwen/Qwen3-8B/<COMMIT-SHA>/ \
--recursive \
--summarize \
--human-readable \
--endpoint-url https://s3.example.localСравните общий размер.
4.7. Строгая проверка: повторная SHA-256 из S3
Количество файлов и размер — недостаточная проверка: содержимое могло измениться. Скачайте модель из S3 во временный каталог и сверьте SHA-256:
bash
mkdir -p /tmp/model-verification
aws s3 sync \
s3://models/qwen/Qwen3-8B/<COMMIT-SHA>/ \
/tmp/model-verification/ \
--endpoint-url https://s3.example.localbash
cd /tmp/model-verification
sha256sum -c /opt/model-import/checksums/model-SHA256SUMSЕсли все файлы вернули OK — модель успешно перенесена в S3.
ETag ≠ MD5
Не используйте ETag == MD5 как универсальную проверку целостности: для multipart-загрузки ETag обычно не является простым MD5 всего файла. Источник истины — собственные SHA-256 из checksums/model-SHA256SUMS.
Шаг 5. Подготовка PVC и запуск vLLM
5.1. Рекомендуемая схема: initContainer + PVC
Для production Kubernetes модель загружается из S3 в PVC через initContainer:
text
Local S3
│ aws s3 sync
▼
initContainer
│
▼
PVC /models
│
▼
vLLMS3 используется как master storage, PVC — как локальный cache. Это удобно для моделей размером 10–500 ГБ.
Не используйте emptyDir для больших моделей
При использовании emptyDir модель исчезает при пересоздании Pod — при каждом рестарте пришлось бы скачивать модель заново:
text
Pod restart
↓
emptyDir lost
↓
100 GB download
↓
vLLM startИспользуйте PVC как cache: S3 → PVC → vLLM.
Альтернатива: скачать модель на PVC заранее
Можно один раз вручную скопировать модель из S3 на PVC (без initContainer). Этот вариант проще, но обновление модели требует ручных операций.
5.2. Credentials и права доступа
Не прописывайте AWS_ACCESS_KEY_ID и AWS_SECRET_ACCESS_KEY непосредственно в Deployment. Создайте Secret:
bash
kubectl create secret generic s3-model-credentials \
--from-literal=AWS_ACCESS_KEY_ID='...' \
--from-literal=AWS_SECRET_ACCESS_KEY='...' \
-n inferenceОтдельный пользователь S3
Для vLLM/model downloader не используйте административный S3-аккаунт. Создайте отдельного пользователя vllm-model-reader с правами только s3:GetObject и s3:ListBucket на models/qwen/Qwen3-8B/*. Запретите PutObject и DeleteObject.
Модель — immutable-артефакт: после утверждения s3://models/qwen/Qwen3-8B/<COMMIT-SHA>/ vLLM имеет доступ только на чтение (READ, не WRITE). Это разделяет процессы загрузки (Model ingestion) и обслуживания (Model serving) модели.
5.3. Манифест Deployment
5.4. Запуск vLLM
После того как модель появилась на PVC (/models/Qwen3-8B), vLLM запускается обычным образом:
bash
vllm serve \
/models/Qwen3-8B \
--served-model-name qwen3-8bВ Kubernetes аргументы задаются в манифесте Deployment (см. шаг 5.3).
5.5. Офлайн-режим и сетевая изоляция
vLLM должен запускаться без доступа к huggingface.co, github.com, pypi.org, docker.io. Если модель полностью локальная, vllm serve /models/Qwen3-8B не должен обращаться к Hugging Face.
HF_HUB_OFFLINE=1
Переменная HF_HUB_OFFLINE=1 запрещает Hugging Face Hub client обращаться в Интернет за отсутствующими файлами. Задайте её в контейнере:
yaml
env:
- name: HF_HUB_OFFLINE
value: "1"Это особенно полезно для тестирования air-gapped-развёртывания.
Не полагайтесь только на переменные окружения: ограничьте сетевой доступ namespace/Pod через NetworkPolicy (см. шаг 5.3, промышленный вариант).
Промышленный вариант: рекомендуемая архитектура
Для production рекомендуемая архитектура:
text
INTERNET
│
▼
┌──────────────────┐
│ Model Collector │ huggingface_hub
└────────┬─────────┘
▼
┌──────────────────┐
│ Model Validator │ commit SHA, SHA256, inventory, tokenizer, config
└────────┬─────────┘
▼
┌──────────────────┐
│ Transfer Package │
└────────┬─────────┘
USB
▼
┌──────────────────┐
│ Airgap Importer │
└────────┬─────────┘
▼
┌──────────────────┐
│ Local S3 │ model releases
└────────┬─────────┘
read-only access
▼
┌──────────────────┐
│ Model Downloader │ initContainer
└────────┬─────────┘
▼
┌──────────────────┐
│ PersistentVolume │
└────────┬─────────┘
▼
┌──────────────────┐
│ vLLM (GPU) │
└────────┬─────────┘
▼
OpenAI-compatible APIГлавная идея: Hugging Face — внешний источник модели, S3 — внутренний master storage, PVC — локальный cache для inference, а vLLM не должен знать о существовании Интернета.
Единица поставки — Model Release, а не отдельный файл весов:
text
Qwen3-8B Model Release
├── Hugging Face repository
├── Commit SHA
├── Model configuration
├── Tokenizer
├── Chat template
├── Weight files
├── Weight index
├── Generation configuration
├── Custom code
├── Python dependencies
├── File inventory
├── SHA256
└── Validation informationПосле импорта модель живёт в S3 с immutable prefix:
text
s3://models/
└── qwen/
└── Qwen3-8B/
└── <COMMIT-SHA>/
├── config.json
├── tokenizer.json
├── tokenizer_config.json
├── model-00001-of-00004.safetensors
├── ...
└── model.safetensors.index.jsonСохраните также environment manifest — сведения об окружении, на котором модель проверялась:
text
Python: 3.x
huggingface_hub: x.y.z
transformers: x.y.z
torch: x.y.z
vLLM: x.y.z
CUDA: 12.x
NVIDIA Driver: xxx.xxЭто позволит через год ответить на вопрос: «На каком окружении эта модель была проверена?»
Проверка результата
После загрузки модели в S3 и запуска Deployment выполните итоговые проверки.
- Целостность архива и файлов — шаги 3.1 и 3.3:
sha256sum -cдля архива и для файлов модели. - Повторная SHA-256 из S3 — шаг 4.7: скачайте модель из S3 и сверьте контрольные суммы.
- Запуск vLLM:
bash
vllm serve /models/Qwen3-8B --served-model-name qwen3-8b- Проверка API vLLM. Список моделей:
bash
curl http://vllm.example.local/v1/modelsОжидаемый ответ:
json
{
"data": [
{
"id": "qwen3-8b"
}
]
}Тестовый запрос:
bash
curl http://vllm.example.local/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "qwen3-8b",
"messages": [
{
"role": "user",
"content": "Hello"
}
]
}'- Тест air-gap: запустите vLLM, убедитесь, что модель загружается, полностью запретите Интернет, перезапустите Pod и убедитесь, что vLLM снова успешно стартует. Если это работает — deployment действительно независим от Интернета.
text
Internet OFF
▼
Pod restart
▼
S3 local
▼
Model
▼
vLLM READYЧастые ошибки
| Симптом | Вероятная причина | Решение |
|---|---|---|
| vLLM не загружает модель, ошибка на старте | Перенесены только веса, отсутствует tokenizer или config.json | Скачивайте снапшот целиком (шаг 1.6), проверяйте обязательные файлы (шаг 1.7) |
| vLLM обращается в Интернет при старте | Не задан HF_HUB_OFFLINE, нет NetworkPolicy | Задайте HF_HUB_OFFLINE=1 и ограничьте сеть (шаг 5.5) |
| В S3 новая версия модели перезаписала старую | Нарушение immutable: загрузка без префикса <COMMIT-SHA> | Всегда используйте immutable prefix (шаг 4.4) |
| Проверка целостности по ETag даёт неверный результат | Для multipart-upload ETag ≠ MD5 всего файла | Используйте собственные SHA-256 (шаг 4.7) |
| initContainer не скачивает модель | Неверные credentials, endpoint или права пользователя S3 | Проверьте Secret, --endpoint-url, права s3:GetObject/s3:ListBucket (шаг 5.2) |
| Модель скачивается заново при каждом рестарте Pod | Используется emptyDir вместо PVC | Используйте PVC как cache (шаг 5.1) |
| Не воспроизводимый образ initContainer | Используется latest-тег | Зафиксируйте версию или digest образа (шаг 5.3) |
| Модель с custom code не запускается в закрытом контуре | Не перенесены Python-зависимости | Перенесите зависимости в локальный PyPI (шаг 1.8) |