diff --git a/.gitignore b/.gitignore index ac99dc0..6174611 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ players/*_config/sunshine.conf # do not publish private Sunshine TLS keys players/*_config/credentials/ + +# local SSH keys for deployment (never commit) +.agent_ssh_key +.agent_ssh_key.pub diff --git a/README.md b/README.md index 184face..fbec11f 100644 --- a/README.md +++ b/README.md @@ -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/` - директория с конфигурациями и данными для каждого игрока - - `p1_config/`, `p2_config/`, `p3_config/`, `p4_config/` - конфигурации для каждого клиента - - `p1_steam/`, `p2_steam/`, `p3_steam/`, `p4_steam/` - данные Steam для каждого клиента +``` +. +├── Dockerfile # Образ: Sunshine, Steam, Xorg, fluxbox, tini, xdotool +├── docker-compose.yml # 4 сервиса player1–player4 +├── 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 # Проверенные практики эксплуатации +``` -## Особенности - -- Поддержка 4 одновременных клиентов -- Использование NVIDIA GPU для ускорения работы -- Автоматическое восстановление контейнеров -- Healthcheck для мониторинга состояния -- Логирование для отладки +Файлы `players/pX_config/` монтируются в `/root/.config/sunshine/` — изменения скриптов применяются **без пересборки образа**. Изменения `Dockerfile` и `scripts/` требуют `docker compose build`. ## Требования -- Docker и Docker Compose -- NVIDIA драйверы -- NVIDIA Container Toolkit -- 2x Tesla K80 (суммарно 4 GPU-чипа) +- Docker и Docker Compose v2 +- NVIDIA драйверы и [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html) +- 2× Tesla K80 (4 GPU-чипа) или аналогичная конфигурация +- На хосте: `sysctl kernel.unprivileged_userns_clone=1` (нужно для Steam runtime) ## Установка -1. Убедитесь, что у вас установлен Docker и Docker Compose -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. Запустите команду: +1. Склонируйте репозиторий на сервер: ```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 кэширующий прокси (опционально) - -Если у вас есть `apt-cacher-ng` (например `http://192.168.1.60:3142`), можно ускорить сборку образов: - -1. Создайте `.env` рядом с `docker-compose.yml`: +2. Создайте каталоги данных Steam (если их ещё нет): ```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 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 -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 | 47984–47990 | 48010 | 47998–48000 | +| player2 | 48014–48020 | 48040 | 48028–48030 | +| player3 | 48044–48050 | 48070 | 48058–48060 | +| player4 | 48074–48080 | 48100 | 48088–48090 | + +## Конфигурация + +### 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 -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, 10–20 минут). Прогресс: + +```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 -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 -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 -4. Улучшена стабильность запуска контейнеров +| Симптом | Причина | Решение | +|---------|---------|---------| +| Окно входа мигает / пропадает | `/dev/shm` переполнен | `shm_size: "2gb"` + `--force-recreate` | +| Нет панели задач внизу | 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). diff --git a/рекомендации.md b/рекомендации.md new file mode 100644 index 0000000..d3a6609 --- /dev/null +++ b/рекомендации.md @@ -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`.