# Как исправить ошибки Docker при запуске локальной LLM: GPU, память и проброс портов

> AI Release · @ai_release1 · https://ai-release.org/guides/kak-ispravit-oshibki-docker-pri-zapuske-lokalnoy-llm-gp.html

## TL;DR
- Время выполнения: 25–40 минут (если драйвер NVIDIA уже стоит)
- Уровень сложности: Intermediate
- Понадобится:
  - Linux (Ubuntu/Debian) или Windows 11 с WSL2 и Docker Desktop
  - Docker Engine / Docker Desktop (конкретную версию сверьте с документацией вашей ОС)
  - драйвер NVIDIA и утилита `nvidia-smi` на хосте
  - NVIDIA Container Toolkit (пакет `nvidia-container-toolkit`)
  - образ локальной LLM — тег и имя сверьте с документацией образа
  - 20+ ГБ свободного места под образы и веса моделей

Термины на одну фразу: **GPU** — видеокарта, **VRAM** — её видеопамять, **CUDA** — слой, через который программа считает на GPU NVIDIA, **образ** — упакованный архив с приложением, **контейнер** — запущенный экземпляр образа, **проброс портов** — правило вида `-p <порт-хоста>:<порт-контейнера>`.

## Требования и подготовка
![Иллюстрация к разделу «Требования и подготовка»](https://ai-release.org/guides/img/kak-ispravit-oshibki-docker-pri-zapuske-lokalnoy-llm-gp-ru-1.jpg?v=1792789978)


- [ ] Проверен драйвер: `nvidia-smi` показывает таблицу с именем GPU
- [ ] Установлен Docker и работает демон
- [ ] Установлен NVIDIA Container Toolkit
- [ ] Достаточно свободного места: `df -h`
- [ ] Свободны порты, которые собираетесь публиковать: `ss -ltnp`
- [ ] Есть доступ к реестру образов: `docker login` (если образ приватный)

```bash
docker --version
docker info --format '{{.ServerVersion}} {{.Driver}}'
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv
nvidia-ctk --version
```

**Что сделать:** выполнить четыре команды выше и сохранить их вывод.
**Зачем:** это база для сравнения. Когда контейнер упадёт, вы будете точно знать, сколько VRAM было свободно и какой драйвер стоит, а не гадать.

**Пример:** `nvidia-smi --query-gpu=...` вернёт строку вида `NVIDIA GeForce RTX 4090, 24564 MiB, 550.xx` — то есть 24 ГБ VRAM. Если команда отвечает `command not found`, драйвера нет, и ни одна строка ниже не заработает.

**Как проверить:** `docker info | grep -i runtime` должен перечислить `runc` и (после установки toolkit) `nvidia`. Пустой список или только `runc` — toolkit не подключён, идите в Шаг 1.

Сколько VRAM нужно под конкретный размер модели и квантизацию — разобрано в гайде [«Сколько RAM и VRAM нужно для локальной ИИ-модели»](https://ai-release.org/guides/localnye-ii-modeli-skolko-ram-i-vram.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide); сверьтесь с ним перед выбором тега образа.

## Шаг 1. Подключить GPU к Docker и убедиться, что он виден внутри контейнера

**Что сделать:** зарегистрировать NVIDIA-рантайм в Docker и прогнать тестовый контейнер.

```bash
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
docker info | grep -i nvidia
docker run --rm --gpus all ubuntu nvidia-smi
```

**Зачем:** Docker по умолчанию не знает про видеокарту, он умеет только `runc`. `nvidia-ctk` дописывает в конфиг демона рантайм `nvidia`, который при старте контейнера прокидывает внутрь устройства GPU и библиотеки драйвера. Флаг `--gpus all` говорит: «дай этому контейнеру все видеокарты».

**Пример:** успешный запуск — внутри контейнера печатается та же таблица, что и на хосте:

```
+-----------------------------------------------------------------------------+
| NVIDIA-SMI ...  Driver Version: 550.xx     CUDA Version: 12.x              |
|   0  NVIDIA GeForce RTX 4090   ...  24564MiB / 24564MiB                     |
+-----------------------------------------------------------------------------+
```

**Как проверить:** вывод `docker run --rm --gpus all ubuntu nvidia-smi` содержит имя вашей GPU. Если вместо этого приходит `could not select device driver "" with capabilities: [[gpu]]` — рантайм не подключён, разбор в разделе проблем.

Ограничить одной картой на многоголовой машине: `--gpus '"device=0"'` — строка в кавычках, иначе Docker не разберёт синтаксис.

## Шаг 2. Запустить контейнер с лимитами памяти и пробросом портов
![Иллюстрация к разделу «Шаг 2. Запустить контейнер с лимитами памяти и пробросом портов»](https://ai-release.org/guides/img/kak-ispravit-oshibki-docker-pri-zapuske-lokalnoy-llm-gp-ru-2.jpg?v=1792789982)


**Что сделать:** собрать команду `docker run` из шести групп флагов.

| Флаг | Что делает | Зачем в LLM |
|---|---|---|
| `--gpus all` | прокидывает GPU | без него модель уйдёт в CPU и будет отвечать минутами |
| `--shm-size=8g` | увеличивает `/dev/shm` | по умолчанию Docker даёт мало разделяемой памяти, и загрузчик весов падает на её нехватке (точное значение смотрите в документации своей версии Docker) |
| `--memory` / `--memory-swap` | лимит RAM контейнера | чтобы «прожорливый» процесс не убил хост целиком |
| `-p 127.0.0.1:8080:8080` | публикует порт | снаружи видно только с этой машины |
| `-v <путь>:/root/.cache` | монтирует кэш | веса не скачиваются заново после `docker rm` |
| `-e <ПЕРЕМЕННАЯ>=<значение>` | env-настройки | имена переменных сверьте с документацией образа |

```bash
docker run -d \
  --name llm \
  --gpus all \
  --shm-size=8g \
  --memory=32g --memory-swap=32g \
  -p 127.0.0.1:8080:8080 \
  -v "$HOME/.cache/llm:/root/.cache" \
  <llm-образ>:<тег>

docker ps --filter name=llm
docker logs -f llm
nvidia-smi
```

**Зачем:** каждый флаг закрывает свой класс ошибок. `-d` — в фоне, `--name` — чтобы обращаться по имени, `-v` — чтобы кэш весов пережил пересоздание контейнера.

**Пример:** `docker ps` показывает `Up 30 seconds` и `0.0.0.0:8080->8080/tcp` (или `127.0.0.1:8080->8080/tcp` при привязке к localhost). `nvidia-smi` в колонке процессов показывает процесс внутри контейнера — значит, модель реально на GPU, а не на CPU.

**Как проверить:** `docker logs llm` заканчивается строкой вида `listening on 0.0.0.0:8080` (формулировка зависит от образа). Если в логах `loading model` висит и не двигается, а `nvidia-smi` показывает 0 MiB занятой VRAM — модель грузится в RAM, проверьте `--gpus`.

## Шаг 3. Проверить порт, эндпоинт и финальный результат
![Иллюстрация к разделу «Шаг 3. Проверить порт, эндпоинт и финальный результат»](https://ai-release.org/guides/img/kak-ispravit-oshibki-docker-pri-zapuske-lokalnoy-llm-gp-ru-3.jpg?v=1792789990)


**Что сделать:** проверить порт на хосте, затем достучаться до API.

```bash
ss -ltnp | grep 8080
curl -i http://127.0.0.1:8080/
curl -s http://127.0.0.1:8080/v1/models
docker exec -it llm sh -c 'curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/'
```

**Зачем:** три уровня проверки отделяют три разные поломки. `ss` показывает, слушает ли порт хост. `curl` с хоста — работает ли проброс. `curl` изнутри контейнера — жив ли сам сервер.

**Пример:** `ss -ltnp | grep 8080` печатает `LISTEN 0 4096 127.0.0.1:8080 ... users:(("docker-proxy",...))`. `curl -i` возвращает `HTTP/1.1 200 OK` или `404` — оба ответа означают, что связка «порт → контейнер» работает; `404` просто значит, что корневой путь не отдаёт контент.

**Как проверить:** успех — это одновременно: контейнер в статусе `Up`, `nvidia-smi` показывает занятую VRAM вашим процессом, и HTTP-ответ приходит. Конкретные пути API (`/v1/models`, `/api/tags` и т. п.) зависят от образа — сверьтесь с его документацией.

Наружу порт пока не открыт: `127.0.0.1` доступен только локально. Как выставить сервис в интернет через туннель или reverse proxy, не трогая роутер, — в гайде [«Как открыть доступ к локальной LLM из интернета без проброса портов»](https://ai-release.org/guides/kak-otkryt-dostup-k-lokalnoy-llm-iz-interneta-bez-probr.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide).

## Возможные проблемы и решения

**Проблема. Контейнер падает сразу после старта, GPU не виден**

- Ошибка: `docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]].`
- Причина: Docker не знает рантайма `nvidia` — не установлен NVIDIA Container Toolkit либо демон не перезапущен после `nvidia-ctk runtime configure`.
- Решение: поставить пакет `nvidia-container-toolkit`, затем
  ```bash
  sudo nvidia-ctk runtime configure --runtime=docker
  sudo systemctl restart docker
  docker info | grep -i nvidia
  ```
  Проверка: в `docker info` появилась секция `Runtimes: nvidia runc`.

**Проблема. Модель не влезает в видеопамять**

- Ошибка: `torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate ... MiB` или `CUDA error: out of memory`
- Причина: веса + контекст больше свободной VRAM. Частый случай — на карте уже висит другой процесс или остался прошлый контейнер.
- Решение: сначала освободить память, потом уменьшать запрос.
  ```bash
  nvidia-smi
  docker rm -f llm
  docker run -d --name llm --gpus '"device=0"' --shm-size=8g -p 127.0.0.1:8080:8080 <llm-образ>:<тег>
  ```
  Дальше: уменьшить размер контекста в настройках образа, взять более компактную квантизацию (имена тегов — сверьтесь с документацией образа) или выставить лимит VRAM средствами самого рантайма.

**Проблема. Порт опубликован, но соединения нет**

- Ошибка: `curl: (7) Failed to connect to 127.0.0.1 port 8080 after 0 ms: Connection refused` либо `Error: listen tcp 127.0.0.1:8080: bind: address already in use`
- Причина: либо порт хоста занят другим процессом, либо сервер внутри контейнера слушает `127.0.0.1`, а не `0.0.0.0` — тогда `docker-proxy` до него не достучится.
- Решение:
  ```bash
  ss -ltnp | grep 8080
  docker rm -f llm
  docker run -d --name llm --gpus all -p 127.0.0.1:8081:8080 <llm-образ>:<тег>
  curl -i http://127.0.0.1:8081/
  ```
  Если не помогло — задайте серверу адрес прослушивания `0.0.0.0` через переменную окружения образа (её точное имя и значение сверьтесь с документацией образа) и перезапустите контейнер.

## Резюме и следующие шаги

Вы подключили GPU к Docker, запустили контейнер с лимитами памяти и пробросом портов и подтвердили тремя проверками, что сервис отвечает. Следующий шаг — вынести сервис наружу через туннель или reverse proxy и настроить автозапуск контейнера (`--restart unless-stopped`).

## FAQ

**Почему модель работает, но отвечает очень медленно?**
Скорее всего, она считает на CPU: проверьте `nvidia-smi` во время генерации — если VRAM не растёт, флаг `--gpus all` не сработал или образ собран без поддержки CUDA (сверьтесь с документацией образа).

**Что делает `--shm-size` и почему без него падает загрузка весов?**
Он увеличивает разделяемую память контейнера. По умолчанию её мало, и многопроцессная загрузка весов упирается в этот лимит — увеличьте значение и перезапустите контейнер.

**Как дать контейнеру доступ только к одной видеокарте?**
Используйте `--gpus '"device=0"'`; номер устройства берите из `nvidia-smi`.

**Порт занят, а какой процесс его держит — не понимаю.**
Выполните `ss -ltnp | grep <порт>` и `docker ps` — первая команда покажет процесс на хосте, вторая покажет, не занимает ли порт другой контейнер; затем смените внешний порт в `-p`.
