Document current architecture and operational practices.

Update README with Steam launch scripts, shm sizing, Moonlight setup, and troubleshooting; add рекомендации.md and ignore local SSH keys.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-27 18:49:02 +03:00
parent ab4ee8b9d6
commit 1dd56ee73d
3 changed files with 275 additions and 101 deletions

4
.gitignore vendored
View File

@@ -10,3 +10,7 @@ players/*_config/sunshine.conf
# do not publish private Sunshine TLS keys # do not publish private Sunshine TLS keys
players/*_config/credentials/ players/*_config/credentials/
# local SSH keys for deployment (never commit)
.agent_ssh_key
.agent_ssh_key.pub

264
README.md
View File

@@ -1,140 +1,202 @@
# Game Server с использованием Sunshine и Steam # Game Server (Sunshine + Steam)
Этот проект предоставляет Docker-конфигурацию для запуска игрового сервера с использованием Sunshine и Steam, позволяя запускать игры на нескольких клиентах одновременно. Docker-конфигурация для запуска до четырёх игровых сессий через **Moonlight**. Каждый контейнер стримит виртуальный рабочий стол 1920×1080 с **Steam Big Picture** и отдельным профилем Steam на выделенном GPU.
## Архитектура
```
Moonlight → Sunshine → Xorg dummy (:99) → fluxbox + Steam (CEF)
tini (PID 1, перезапуск дочерних процессов)
```
- **Sunshine v0.20.0** — стриминг и захват экрана
- **Xorg dummy** — виртуальный дисплей 1920×1080 (корректный ввод из Moonlight)
- **fluxbox** — панель задач внизу экрана
- **Steam** — запускается от пользователя `steam` через `start-steam.sh`
- **tini** — init-процесс контейнера, предотвращает zombie-процессы fluxbox
## Структура проекта ## Структура проекта
- `Dockerfile` - файл для создания Docker-образа с Sunshine и Steam ```
- `docker-compose.yml` - конфигурация для запуска нескольких контейнеров .
- `players/` - директория с конфигурациями и данными для каждого игрока ├── Dockerfile # Образ: Sunshine, Steam, Xorg, fluxbox, tini, xdotool
- `p1_config/`, `p2_config/`, `p3_config/`, `p4_config/` - конфигурации для каждого клиента ├── docker-compose.yml # 4 сервиса player1player4
- `p1_steam/`, `p2_steam/`, `p3_steam/`, `p4_steam/` - данные Steam для каждого клиента ├── scripts/
│ ├── entrypoint.sh # Запуск DBus, Xorg, fluxbox, Sunshine
│ └── fluxbox/init # Конфиг панели задач (в образе)
├── players/
│ ├── p1_config/ # Конфиг Sunshine для player1 (монтируется в контейнер)
│ │ ├── apps.json
│ │ ├── start-steam.sh # Сценарий запуска Steam
│ │ └── keep-steam-window.sh
│ ├── p2_config/ … p4_config/
│ └── p1_steam/ … p4_steam/ # Данные Steam (не в git, создаются на сервере)
├── .env.example # Опциональный APT-прокси для сборки
└── рекомендации.md # Проверенные практики эксплуатации
```
## Особенности Файлы `players/pX_config/` монтируются в `/root/.config/sunshine/` — изменения скриптов применяются **без пересборки образа**. Изменения `Dockerfile` и `scripts/` требуют `docker compose build`.
- Поддержка 4 одновременных клиентов
- Использование NVIDIA GPU для ускорения работы
- Автоматическое восстановление контейнеров
- Healthcheck для мониторинга состояния
- Логирование для отладки
## Требования ## Требования
- Docker и Docker Compose - Docker и Docker Compose v2
- NVIDIA драйверы - NVIDIA драйверы и [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html)
- NVIDIA Container Toolkit - 2× Tesla K80 (4 GPU-чипа) или аналогичная конфигурация
- 2x Tesla K80 (суммарно 4 GPU-чипа) - На хосте: `sysctl kernel.unprivileged_userns_clone=1` (нужно для Steam runtime)
## Установка ## Установка
1. Убедитесь, что у вас установлен Docker и Docker Compose 1. Склонируйте репозиторий на сервер:
2. Склонируйте репозиторий
3. Настройте пути к папкам Steam в `docker-compose.yml`
4. Проверьте назначение GPU-чипов в `NVIDIA_VISIBLE_DEVICES`:
- `player1` -> GPU `1`
- `player2` -> GPU `2`
- `player3` -> GPU `3`
- `player4` -> GPU `4`
GPU `0` в этой конфигурации обычно занят отдельной видеокартой (например, Quadro) и не используется для игровых контейнеров.
5. Запустите команду:
```bash ```bash
docker-compose up -d git clone https://git.core.com.ru/bilal/game-server.git /DATA/AppData/game-server
cd /DATA/AppData/game-server
``` ```
### APT кэширующий прокси (опционально) 2. Создайте каталоги данных Steam (если их ещё нет):
Если у вас есть `apt-cacher-ng` (например `http://192.168.1.60:3142`), можно ускорить сборку образов:
1. Создайте `.env` рядом с `docker-compose.yml`:
```bash ```bash
echo 'APT_PROXY=http://192.168.1.60:3142' > .env mkdir -p players/p{1,2,3,4}_steam
``` ```
2. Пересоберите контейнеры:
3. При необходимости измените пути volume в `docker-compose.yml` (по умолчанию `/DATA/AppData/game-server/players/...`).
4. Назначение GPU (`NVIDIA_VISIBLE_DEVICES`):
| Сервис | GPU |
|----------|-----|
| player1 | 1 |
| player2 | 2 |
| player3 | 3 |
| player4 | 4 |
GPU `0` обычно занят отдельной видеокартой хоста и не используется игровыми контейнерами.
5. Соберите и запустите:
```bash ```bash
docker compose up -d --build docker compose up -d --build
``` ```
Прокси передается в `Dockerfile` через build-arg `APT_PROXY` и автоматически записывается в `/etc/apt/apt.conf.d/01proxy` при сборке. ### APT-прокси (опционально)
## Настройка Для ускорения сборки через `apt-cacher-ng`:
### Конфигурация Sunshine
Конфигурационные файлы Sunshine находятся в директориях `players/pX_config/`:
- `apps.json` - список установленных игр
- `credentials/` - SSL сертификаты
`sunshine.conf` не хранится в репозитории и генерируется Sunshine автоматически при первом запуске контейнера.
`Steam Big Picture` запускается от отдельного пользователя `steam` внутри контейнера. Данные Steam сохраняются в `players/pX_steam` и не теряются после перезапуска контейнеров/сервера.
Важно: для корректной обработки ввода из Moonlight в контейнерах используется `Xorg dummy` (вместо `Xvfb`) и проброс `/run/udev` в контейнер. Это нужно, чтобы виртуальные устройства ввода Sunshine попадали в тот же X-сеанс, который стримится.
### Порты
В Moonlight при ручном добавлении указывается один адрес в формате `IP:base_port`.
`base_port` для каждого клиента:
- `player1` -> `47989` (можно просто `IP`, это дефолт)
- `player2` -> `48019`
- `player3` -> `48049`
- `player4` -> `48079`
Порты проброшены для каждого клиента отдельно (формат: `host:container`):
- `player1`
- TCP: `47984-47990:47984-47990`
- TCP/UDP: `48010:48010`
- UDP: `47998-48000:47998-48000`
- `player2`
- TCP: `48014-48020:47984-47990`
- TCP/UDP: `48040:48010`
- UDP: `48028-48030:47998-48000`
- `player3`
- TCP: `48044-48050:47984-47990`
- TCP/UDP: `48070:48010`
- UDP: `48058-48060:47998-48000`
- `player4`
- TCP: `48074-48080:47984-47990`
- TCP/UDP: `48100:48010`
- UDP: `48088-48090:47998-48000`
Moonlight использует `base_port` и вычисляет остальные порты автоматически.
### Первый запуск Steam
При первом запуске Steam скачивает и распаковывает клиент (может занять 10-20 минут, в зависимости от канала). В это время в Moonlight может отображаться пустой/черный рабочий стол.
Проверка прогресса для `player1`:
```bash ```bash
docker exec -it game-player-1 bash -lc 'tail -f /home/steam/.local/share/Steam/debian-installation/logs/bootstrap_log.txt' cp .env.example .env
# отредактируйте APT_PROXY при необходимости
docker compose up -d --build
``` ```
После завершения первичной загрузки повторно откройте сессию Moonlight и запустите `Steam Big Picture`. ## Подключение Moonlight
В Moonlight добавьте хост вручную: `IP:base_port`.
| Сервис | base_port | Web UI Sunshine |
|---------|-----------|-----------------|
| player1 | 47989 | https://IP:47990 |
| player2 | 48019 | https://IP:48019 |
| player3 | 48049 | https://IP:48049 |
| player4 | 48079 | https://IP:48079 |
Запускайте приложение **Steam Big Picture**, не Desktop.
### Порты (host → container)
| Сервис | TCP | TCP/UDP | UDP |
|---------|------------------|-----------|------------------|
| player1 | 4798447990 | 48010 | 4799848000 |
| player2 | 4801448020 | 48040 | 4802848030 |
| player3 | 4804448050 | 48070 | 4805848060 |
| player4 | 4807448080 | 48100 | 4808848090 |
## Конфигурация
### Sunshine
- `players/pX_config/apps.json` — список приложений Moonlight
- `players/pX_config/credentials/` — TLS-сертификаты (генерируются при первом запуске, не в git)
- `sunshine.conf` — создаётся автоматически при первом запуске, не хранится в репозитории
### Запуск Steam
`start-steam.sh` вызывается из `apps.json` при выборе **Steam Big Picture**:
1. Проверяет и поднимает system DBus и fluxbox
2. Если Steam уже работает и UI виден — **не перезапускает** процесс
3. Иначе: чистый старт `-silent -no-cef-sandbox`, затем однократно `steam://open/gamepadui`
4. `keep-steam-window.sh` удерживает окно входа на экране и центрирует его
Steam работает от пользователя `steam`; данные сохраняются в `players/pX_steam/`.
### Shared memory
В `docker-compose.yml` задано `shm_size: "2gb"`. Стандартные 64 MB Docker недостаточны для CEF (Steam UI) — окно входа начинает мигать и пропадать. После изменения `shm_size` контейнер нужно **пересоздать**:
Если запуск Steam прерывается после выхода из терминала (`Hangup`), используйте лог:
```bash ```bash
docker exec -it game-player-1 bash -lc 'tail -f /home/steam/steam-app.log' docker compose up -d --force-recreate player1
``` ```
### Первичная загрузка Steam
При первом запуске Steam скачивает клиент (~500 MB, 1020 минут). Прогресс:
```bash
docker exec -it game-player-1 bash -lc \
'tail -f /home/steam/.local/share/Steam/debian-installation/logs/bootstrap_log.txt'
```
После завершения снова откройте **Steam Big Picture** в Moonlight.
## Отладка ## Отладка
Для проверки состояния контейнеров используйте: ### Быстрая проверка
```bash ```bash
docker-compose ps docker compose ps
docker compose logs --tail=100 player1
docker exec -it game-player-1 bash -lc '
echo "PID1: $(cat /proc/1/comm)" # должно быть tini
pgrep -a sunshine
pgrep -a "steam -srt" || echo "steam not running"
df -h /dev/shm # должно быть ~2G, занято <20%
DISPLAY=:99 xwininfo -root -tree | grep -iE "Sign in|Big Picture|1918x"
'
``` ```
Для просмотра логов: ### Логи Steam
```bash ```bash
docker-compose logs player1 docker exec -it game-player-1 bash -lc 'tail -f /home/steam/steam-app.log'
docker exec -it game-player-1 bash -lc \
'tail -50 /home/steam/.local/share/Steam/debian-installation/logs/cef_log.txt'
``` ```
## Улучшения ### Типичные проблемы
1. Добавлен healthcheck для лучшей стабильности | Симптом | Причина | Решение |
2. Добавлено логирование для отладки |---------|---------|---------|
3. Оптимизирован Dockerfile | Окно входа мигает / пропадает | `/dev/shm` переполнен | `shm_size: "2gb"` + `--force-recreate` |
4. Улучшена стабильность запуска контейнеров | Нет панели задач внизу | fluxbox zombie или `-tenfoot` fullscreen | Пересоздать контейнер; не использовать `-tenfoot` |
| Чёрный экран, Steam не виден | Bootstrap ещё идёт или webhelper упал | Смотреть `bootstrap_log.txt`, `webhelper-linux.txt` |
| Steam перезапускается при каждом клике | Старый inline-скрипт в apps.json | Использовать `start-steam.sh` из репозитория |
### Пересборка после изменений
| Что изменили | Команда |
|--------------|---------|
| `start-steam.sh`, `apps.json` | Достаточно перезапустить Steam в Moonlight |
| `Dockerfile`, `scripts/` | `docker compose up -d --build --force-recreate player1` |
| `docker-compose.yml` (shm, GPU, порты) | `docker compose up -d --force-recreate` |
## Обновление из репозитория
```bash
cd /DATA/AppData/game-server
git pull
docker compose up -d --build
```
Скрипты в `players/pX_config/` обновятся автоматически через bind-mount. Если `git pull` перезаписал локальные правки на сервере — проверьте diff перед перезапуском.
## Дополнительно
Подробные рекомендации по эксплуатации — в файле [рекомендации.md](рекомендации.md).

108
рекомендации.md Normal file
View File

@@ -0,0 +1,108 @@
# Проверенные рекомендации
Ниже только те пункты, которые подтверждаются текущей конфигурацией проекта и практикой запуска на сервере.
## 1) Healthcheck Sunshine
Текущий вариант `["CMD", "pgrep", "sunshine"]` рабочий в этой сборке: в контейнере есть `/usr/bin/sunshine`, процесс виден как `sunshine`.
```yaml
healthcheck:
test: ["CMD", "pgrep", "sunshine"]
interval: 30s
timeout: 10s
retries: 3
```
## 2) Entrypoint и tini
- Контейнер стартует через `tini``/entrypoint.sh``sunshine`.
- `tini` нужен, чтобы fluxbox и другие дочерние процессы не превращались в zombie и панель задач оставалась видимой.
- Проверка: `docker exec game-player-1 cat /proc/1/comm``tini`.
## 3) Shared memory (`/dev/shm`)
- Для Steam CEF обязательно `shm_size: "2gb"` в `docker-compose.yml`.
- При 64 MB (дефолт Docker) в `cef_log.txt` появляется `Less than 64MB of free space` — окно входа мигает.
- После изменения `shm_size` нужен `--force-recreate`, а не просто `restart`.
```bash
docker exec game-player-1 df -h /dev/shm
```
## 4) Права на volume Steam
Проблема прав на bind-mount критична. Правильный подход — точечный `chown` в `entrypoint.sh` и `start-steam.sh`:
```bash
mkdir -p /home/steam/.local/share/Steam /tmp/xdg-steam /home/steam/tmp
ln -sfn /home/steam/.local/share/Steam /home/steam/.steam
chown -R steam:steam /home/steam /tmp/xdg-steam
```
Не рекомендуется: `chmod -R 777 /DATA/AppData/game-server/players/`.
## 5) Xorg dummy и ввод Moonlight
- Используется минимальный `xorg-dummy.conf` без привязки к `/dev/input/mice`.
- Добавление `InputDevice` с физическим устройством может вернуть проблему, когда ввод уходит в хостовую консоль.
- Ввод Moonlight обрабатывается через Sunshine/XTest в изолированном X-сессии `:99`.
## 6) Запуск Steam (`start-steam.sh`)
Текущий подход:
- Запуск от `runuser -u steam` с `DISPLAY=:99`, `XDG_RUNTIME_DIR=/tmp/xdg-steam`, `TMPDIR=/home/steam/tmp`
- Двухфазный старт: `-silent -no-cef-sandbox`, затем однократно `steam://open/gamepadui`
- Блокировка `flock` — защита от двойного запуска при быстрых кликах в Moonlight
- Если Steam уже работает и UI виден — процесс **не убивается**
- `keep-steam-window.sh` держит окно «Sign in to Steam» на экране (без растягивания на 1920×1080)
Не использовать прямой `-gamepadui -fullscreen` в `apps.json` — это вызывает ChunkLoadError и нестабильность CEF.
## 7) System DBus
Steam webhelper требует рабочий system DBus. В `entrypoint.sh` и `start-steam.sh` DBus поднимается автоматически:
```bash
dbus-daemon --system --fork
```
Проверка:
```bash
docker exec game-player-1 runuser -u steam -- \
dbus-send --system --print-reply --dest=org.freedesktop.DBus \
/org/freedesktop/DBus org.freedesktop.DBus.ListNames
```
## 8) Что проверить на хосте
1. Установлен `nvidia-container-toolkit`.
2. User namespaces для Steam:
```bash
sysctl kernel.unprivileged_userns_clone
```
Ожидается `1`.
3. Контейнеры со смягчёнными профилями (как в `docker-compose.yml`):
```yaml
security_opt:
- seccomp:unconfined
- apparmor:unconfined
```
## 9) Быстрая проверка после перезапуска
```bash
docker compose ps
docker compose logs --tail=200 player1
docker exec -it game-player-1 bash -lc '
cat /proc/1/comm
pgrep -a sunshine
pgrep -a "steam -srt" || true
df -h /dev/shm
DISPLAY=:99 xwininfo -root -tree | grep -iE "Sign in|1918x" || true
'
```
Если сервисы healthy, а Steam не открывается в Moonlight — смотреть `steam-app.log`, `cef_log.txt`, `webhelper-linux.txt` и дерево окон на `DISPLAY=:99`.