環境構築手順 を追加

2026-09-06 20:33:06 +09:00
コミット a0296507ca
+943
@@ -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
```