diff --git a/%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89%E6%89%8B%E9%A0%86.md b/%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89%E6%89%8B%E9%A0%86.md new file mode 100644 index 0000000..e4f1356 --- /dev/null +++ b/%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89%E6%89%8B%E9%A0%86.md @@ -0,0 +1,943 @@ +# サーバー環境構築 + +## 概要 + +このページでは `nizika_broadcast` V1 を Ubuntu 26.04 LTS 上で稼働させるための環境構築手順を記載する。 + +現在の運用対象は V1。 + +`nizika_broadcast_v2` は deprecated とし、今回の環境構築では使用しない。 + +本番構成: + +```text +nizika_broadcast + │ + ├─ pygame + ├─ nizika_ai + ├─ nizika_nico + │ + ├─ Xvfb :99 + │ + ├─ PulseAudio + │ └─ stream + │ └─ stream.monitor + │ + ├─ AqKanji2Koe + ├─ AquesTalk2 Eva + │ + └─ ffmpeg + │ + └─ YouTube RTMPS +``` + +repository: + +```text +/opt/nizika_broadcast +``` + +service user: + +```text +nizika +``` + +--- + +# 1. service user + +```bash +useradd --system \ + --home /var/lib/nizika \ + --create-home \ + --shell /usr/sbin/nologin \ + nizika +``` + +確認: + +```bash +id nizika +``` + +--- + +# 2. repository + +信頼できる Gitea の最新 `main` を取得し、 + +```text +/opt/nizika_broadcast +``` + +へ配置する。 + +旧サーバーの working directory をそのままコピーしない。 + +関連 repository: + +```text +/opt/nizika-nico +``` + +`nizika_ai` もコードおよび DB 接続上の依存として利用される。 + +--- + +# 3. OS package + +```bash +apt update + +apt install -y \ + git curl unzip \ + ffmpeg \ + xvfb \ + pulseaudio \ + pulseaudio-utils \ + python3 \ + python3-pip \ + python3-pygame \ + python3-opencv \ + python3-numpy \ + python3-ephem \ + python3-emoji +``` + +--- + +# 4. Python package + +この環境では venv を使用せず system Python を使用する。 + +pip install 前: + +```bash +umask 022 +export HOME=/root +``` + +必要 package: + +```bash +python3 -m pip install \ + --break-system-packages \ + 'requests==2.32.5' \ + 'pytchat==0.5.5' \ + 'httpx[http2]==0.27.2' \ + pydub \ + audioop-lts \ + simplejson \ + 'eloquent==0.5' \ + 'mysqlclient==2.2.8' +``` + +--- + +# 5. Python 3.14 の注意 + +Python 3.14 では標準 `audioop` が削除されている。 + +`pydub` を利用するため: + +```text +audioop-lts +``` + +を追加する。 + +--- + +# 6. pip permission の注意 + +pip install 時に `umask 077` が設定されていると、 + +```text +/usr/local/lib/python3.14/dist-packages +``` + +配下が: + +```text +drwx------ +-rw------- +``` + +となる場合がある。 + +この状態では `nizika` user から package が正常に見えず、 + +```text +No module named 'pytchat.core' +module 'httpx' has no attribute 'Client' +``` + +等の不可解な error が発生する場合がある。 + +確認: + +```bash +find /usr/local/lib/python3.14/dist-packages \ + -type d ! -perm -005 -o -type f ! -perm -004 \ + -print \ + | head -100 +``` + +必要なら: + +```bash +chmod -R a+rX \ + /usr/local/lib/python3.14/dist-packages +``` + +--- + +# 7. Python import test + +```bash +runuser -u nizika -- python3 - <<'PY' +mods = [ + "httpx", + "pytchat", + "pydub", + "eloquent", + "MySQLdb", + "pygame", + "cv2", + "numpy", +] + +for mod in mods: + try: + m = __import__(mod) + print(f"{mod}: OK {getattr(m, '__file__', None)}") + except Exception as e: + print(f"{mod}: NG {type(e).__name__}: {e}") +PY +``` + +--- + +# 8. AqKanji2Koe + +repository 内の: + +```text +aques +``` + +は `libAqKanji2Koe.so.4` を利用する。 + +旧サーバーの binary をコピーせず、A-Quest 公式 Linux package から取得する。 + +64bit library を `/usr/local/lib` へ配置する。 + +最終的に: + +```text +/usr/local/lib/libAqKanji2Koe.so.4.1 +/usr/local/lib/libAqKanji2Koe.so.4 +/usr/local/lib/libAqKanji2Koe.so +``` + +となるよう symlink を作成する。 + +反映: + +```bash +ldconfig +``` + +動作確認: + +```bash +cd /opt/nizika_broadcast + +printf 'こんにちは\n' | ./aques +``` + +`aques` は WAV generator ではなく、漢字仮名交じり文から AquesTalk 用の音声記号列を生成する。 + +--- + +# 9. AquesTalk2 Eva + +`aques.py` は: + +```text +libAquesTalk2Eva.so +``` + +を利用する。 + +A-Quest 公式 Linux evaluation package から取得する。 + +配置例: + +```text +/usr/local/lib/libAquesTalk2Eva.so.2.3 +/usr/local/lib/libAquesTalk2Eva.so.2 +/usr/local/lib/libAquesTalk2Eva.so +``` + +反映: + +```bash +ldconfig +``` + +この環境では: + +```dotenv +LD_LIBRARY_PATH=/usr/local/lib +``` + +も設定する。 + +確認: + +```bash +LD_LIBRARY_PATH=/usr/local/lib \ +python3 - <<'PY' +from ctypes import cdll + +cdll.LoadLibrary('libAquesTalk2Eva.so') + +print('libAquesTalk2Eva: OK') +PY +``` + +--- + +# 10. PulseAudio + +directory: + +```bash +mkdir -p /etc/nizika/pulse + +chmod 755 \ + /etc/nizika \ + /etc/nizika/pulse +``` + +以下を作成: + +```text +/etc/nizika/pulse/default.pa +``` + +内容: + +```text +#!/usr/bin/pulseaudio -nF + +load-module module-native-protocol-unix +load-module module-device-restore +load-module module-stream-restore +load-module module-card-restore +load-module module-augment-properties +load-module module-default-device-restore +load-module module-always-sink + +load-module module-null-sink sink_name=stream sink_properties=device.description=StreamSink + +set-default-sink stream +set-default-source stream.monitor +``` + +権限: + +```bash +chmod 644 \ + /etc/nizika/pulse/default.pa +``` + +--- + +# 11. pulseaudio-headless.service + +作成: + +```text +/etc/systemd/system/pulseaudio-headless.service +``` + +内容: + +```ini +[Unit] +Description=Nizika virtual audio + +[Service] +Type=simple +User=nizika +Group=nizika + +Environment=HOME=/var/lib/nizika +Environment=XDG_RUNTIME_DIR=/run/nizika +Environment=PULSE_SERVER=unix:/run/nizika/pulse/native + +RuntimeDirectory=nizika +RuntimeDirectoryMode=0700 + +ExecStart=/usr/bin/pulseaudio \ + --daemonize=no \ + --exit-idle-time=-1 \ + --log-target=journal \ + -nF /etc/nizika/pulse/default.pa + +Restart=on-failure +RestartSec=2 + +[Install] +WantedBy=multi-user.target +``` + +反映: + +```bash +systemctl daemon-reload + +systemctl enable \ + pulseaudio-headless.service + +systemctl start \ + pulseaudio-headless.service +``` + +確認: + +```bash +systemctl --no-pager --full status \ + pulseaudio-headless.service +``` + +--- + +# 12. PulseAudio device 確認 + +sink: + +```bash +runuser -u nizika -- env \ + HOME=/var/lib/nizika \ + XDG_RUNTIME_DIR=/run/nizika \ + PULSE_SERVER=unix:/run/nizika/pulse/native \ + pactl list short sinks +``` + +期待: + +```text +stream +``` + +source: + +```bash +runuser -u nizika -- env \ + HOME=/var/lib/nizika \ + XDG_RUNTIME_DIR=/run/nizika \ + PULSE_SERVER=unix:/run/nizika/pulse/native \ + pactl list short sources +``` + +期待: + +```text +stream.monitor +``` + +headless 環境では PulseAudio が cookie や D-Bus 警告を表示する場合がある。 + +service が `active` で、`pactl` から `stream` と `stream.monitor` が取得できていれば、この構成では致命的ではない。 + +--- + +# 13. Xvfb + +作成: + +```text +/etc/systemd/system/xvfb.service +``` + +内容: + +```ini +[Unit] +Description=Nizika virtual display + +[Service] +Type=simple +User=nizika +Group=nizika + +Environment=HOME=/var/lib/nizika + +ExecStart=/usr/bin/Xvfb \ + :99 \ + -screen 0 480x360x16 \ + -nolisten tcp \ + -noreset \ + -dpi 96 \ + -extension RANDR \ + -extension RENDER \ + -extension DAMAGE \ + -extension COMPOSITE \ + -extension GLX + +Restart=always +RestartSec=1 + +[Install] +WantedBy=multi-user.target +``` + +反映: + +```bash +systemctl daemon-reload + +systemctl enable \ + xvfb.service + +systemctl start \ + xvfb.service +``` + +確認: + +```bash +systemctl --no-pager --full status \ + xvfb.service +``` + +この設定では RANDR を無効化しているため: + +```text +Xlib: extension "RANDR" missing on display ":99". +``` + +という warning が出る場合がある。 + +--- + +# 14. broadcast environment + +作成: + +```text +/etc/nizika-broadcast.env +``` + +内容: + +```dotenv +NIZIKA_AI_MYSQL_USER=... +NIZIKA_AI_MYSQL_PASS=... + +NIZIKA_NICO_MYSQL_USER=... +NIZIKA_NICO_MYSQL_PASS=... + +NIZIKA_NICO_DIR=/opt/nizika-nico + +BROADCAST_CODE=... + +LD_LIBRARY_PATH=/usr/local/lib +``` + +権限: + +```bash +chown root:root \ + /etc/nizika-broadcast.env + +chmod 600 \ + /etc/nizika-broadcast.env +``` + +--- + +# 15. foreground smoke test + +systemd 化する前に確認する。 + +```bash +cd /opt/nizika_broadcast + +set -a +. /etc/nizika-broadcast.env + +export HOME=/var/lib/nizika +export DISPLAY=:99 +export SDL_AUDIODRIVER=pulse +export XDG_RUNTIME_DIR=/run/nizika +export PULSE_SERVER=unix:/run/nizika/pulse/native + +set +a + +timeout 20s \ + runuser -u nizika \ + --preserve-environment \ + -- \ + python3 main.py + +rc=$? + +echo "exit=$rc" +``` + +期待: + +```text +exit=124 +``` + +`124` は application crash ではなく、20 秒後に `timeout` が終了させたことを意味する。 + +--- + +# 16. nizika_broadcast.service + +作成: + +```text +/etc/systemd/system/nizika_broadcast.service +``` + +内容: + +```ini +[Unit] +Description=Deerjika Broadcasting System + +After=network-online.target mysql.service pulseaudio-headless.service xvfb.service + +Wants=network-online.target mysql.service + +Requires=pulseaudio-headless.service xvfb.service + +[Service] +Type=simple + +User=nizika +Group=nizika + +WorkingDirectory=/opt/nizika_broadcast + +EnvironmentFile=/etc/nizika-broadcast.env + +Environment=HOME=/var/lib/nizika +Environment=DISPLAY=:99 +Environment=SDL_AUDIODRIVER=pulse +Environment=XDG_RUNTIME_DIR=/run/nizika +Environment=PULSE_SERVER=unix:/run/nizika/pulse/native + +ExecStartPre=/bin/bash -lc 'for i in {1..50}; do pactl list short sources | grep -Fq stream.monitor && exit 0; sleep 0.2; done; echo "stream.monitor not found" >&2; exit 1' + +ExecStartPre=/bin/bash -lc 'for i in {1..50}; do test -S /tmp/.X11-unix/X99 && exit 0; sleep 0.2; done; echo "Xvfb :99 not ready" >&2; exit 1' + +ExecStart=/usr/bin/python3 \ + /opt/nizika_broadcast/main.py + +Restart=always +RestartSec=2 + +[Install] +WantedBy=multi-user.target +``` + +反映: + +```bash +systemctl daemon-reload + +systemctl enable \ + nizika_broadcast.service + +systemctl start \ + nizika_broadcast.service +``` + +確認: + +```bash +systemctl --no-pager --full status \ + nizika_broadcast.service +``` + +--- + +# 17. YouTube stream key + +stream key は source code や systemd unit へ直接記載しない。 + +作成: + +```text +/etc/nizika-stream.env +``` + +内容: + +```dotenv +YOUTUBE_STREAM_KEY=... +``` + +権限: + +```bash +chown root:root \ + /etc/nizika-stream.env + +chmod 600 \ + /etc/nizika-stream.env +``` + +stream key をログや Wiki に記載しない。 + +漏洩した場合は古い key を無効化して新規発行する。 + +--- + +# 18. ffmpeg systemd service + +作成: + +```text +/etc/systemd/system/nizika.service +``` + +内容: + +```ini +[Unit] +Description=Nizika YouTube Stream + +After=network-online.target pulseaudio-headless.service xvfb.service nizika_broadcast.service + +Wants=network-online.target + +Requires=pulseaudio-headless.service xvfb.service nizika_broadcast.service + +[Service] +Type=simple + +User=nizika +Group=nizika + +WorkingDirectory=/opt/nizika_broadcast + +EnvironmentFile=/etc/nizika-stream.env + +Environment=HOME=/var/lib/nizika +Environment=DISPLAY=:99 +Environment=XDG_RUNTIME_DIR=/run/nizika +Environment=PULSE_SERVER=unix:/run/nizika/pulse/native + +ExecStartPre=/bin/bash -lc 'for i in {1..50}; do pactl list short sources | grep -Fq stream.monitor && exit 0; sleep 0.2; done; echo "stream.monitor not found" >&2; exit 1' + +ExecStartPre=/bin/bash -lc 'for i in {1..50}; do test -S /tmp/.X11-unix/X99 && exit 0; sleep 0.2; done; echo "Xvfb :99 not ready" >&2; exit 1' + +ExecStart=/usr/bin/ffmpeg \ + -nostdin \ + -f x11grab \ + -video_size 480x360 \ + -framerate 6 \ + -draw_mouse 0 \ + -i :99 \ + -f pulse \ + -i stream.monitor \ + -c:v libx264 \ + -preset ultrafast \ + -pix_fmt yuv420p \ + -b:v 100k \ + -maxrate 100k \ + -bufsize 200k \ + -g 12 \ + -r 6 \ + -c:a aac \ + -b:a 18k \ + -ar 11025 \ + -f flv \ + rtmps://a.rtmps.youtube.com:443/live2/${YOUTUBE_STREAM_KEY} + +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target +``` + +反映: + +```bash +systemctl daemon-reload + +systemctl enable \ + nizika.service + +systemctl start \ + nizika.service +``` + +確認: + +```bash +systemctl --no-pager --full status \ + nizika.service +``` + +--- + +# 19. stream key と process list + +ffmpeg の RTMPS URL は process argv に含まれる。 + +したがって: + +```bash +systemctl status nizika.service +``` + +や: + +```bash +ps +``` + +の出力に stream key が表示される場合がある。 + +外部へログを共有する際は必ず stream key を伏せる。 + +--- + +# 20. 全 service 確認 + +```bash +systemctl --no-pager --full status \ + pulseaudio-headless.service \ + xvfb.service \ + nizika_broadcast.service \ + nizika.service +``` + +すべて: + +```text +active (running) +``` + +であることを確認する。 + +--- + +# 21. 自動起動 + +確認: + +```bash +systemctl is-enabled \ + pulseaudio-headless.service \ + xvfb.service \ + nizika_broadcast.service \ + nizika.service +``` + +すべて: + +```text +enabled +``` + +であれば、通常の reboot 後に systemd が自動起動する。 + +--- + +# 22. reboot test + +最終的には一度再起動して確認する。 + +```bash +reboot +``` + +再ログイン後: + +```bash +systemctl is-active \ + pulseaudio-headless.service \ + xvfb.service \ + nizika_broadcast.service \ + nizika.service +``` + +すべて: + +```text +active +``` + +が目標。 + +--- + +# 23. nullsink.service + +旧環境で使用していた: + +```text +nullsink.service +``` + +は移行しない。 + +PulseAudio の: + +```text +/etc/nizika/pulse/default.pa +``` + +内で: + +```text +load-module module-null-sink sink_name=stream +``` + +を実行しているため、独立した null sink 用 service は不要。 + +--- + +# 24. 関連 repository + +## nizika_ai + +AI 関連処理と `nizika_ai` DB を利用する。 + +environment variable: + +```text +NIZIKA_AI_MYSQL_USER +NIZIKA_AI_MYSQL_PASS +``` + +## nizika_nico + +共通処理や DB を利用する。 + +配置: + +```text +/opt/nizika-nico +``` + +environment variable: + +```text +NIZIKA_NICO_MYSQL_USER +NIZIKA_NICO_MYSQL_PASS +NIZIKA_NICO_DIR +``` \ No newline at end of file