340 lines
18 KiB
Markdown
340 lines
18 KiB
Markdown
---
|
||
title: tver 本番配置手順
|
||
tags:
|
||
- homelab
|
||
- tver
|
||
- deployment
|
||
---
|
||
|
||
# tver 本番配置手順
|
||
|
||
> [!info] 対象
|
||
> Synology NAS `ds923p`(`192.168.10.201`)に PostgreSQL 17 と動画共有を用意し、dockage(`192.168.10.155`)の `/opt/stacks/tver` で web・worker を起動する。
|
||
|
||
> [!danger] 秘匿値を記録しない
|
||
> PostgreSQL のパスワードや完成した `DATABASE_URL` は、この文書や Git 管理下のファイルに書かない。NAS と dockage の `.env` にだけ保存し、権限を `600` にする。
|
||
|
||
## 事前に確認する値
|
||
|
||
| 項目 | 値 |
|
||
|---|---|
|
||
| NAS | `ds923p` / `192.168.10.201` |
|
||
| dockage | `192.168.10.155` |
|
||
| NAS のNFS共有 | `/volume1/video` |
|
||
| dockage のNFSマウント | `/mnt/nas-tver` |
|
||
| tverの動画保存先 | NAS: `/volume1/video/tver`、dockage: `/mnt/nas-tver/tver` |
|
||
| PostgreSQL 公開ポート | `15434` |
|
||
| PostgreSQL ユーザー / DB | `tver` / `tver` |
|
||
| アプリ配置先 | `/opt/stacks/tver` |
|
||
| Gitea配置用ユーザー | `tver-deploy`(リポジトリ読み取りのみ) |
|
||
|
||
## 1. NASを準備する
|
||
|
||
### 1.1 NFSサービスを有効にする
|
||
|
||
1. DSMの「コントロール パネル」→「ファイル サービス」→「NFS」でNFSサービスを有効にする。
|
||
2. 共有フォルダ `video` が `/volume1/video` にあることを確認する。
|
||
3. `video` の「編集」→「NFS 権限」で次を追加する。
|
||
|
||
| 設定 | 値 |
|
||
|---|---|
|
||
| ホスト名またはIP | `192.168.10.155` |
|
||
| 権限 | 読み書き |
|
||
| Squash | すべてのユーザーをadminに割り当てる |
|
||
| セキュリティ | `sys` |
|
||
| 非同期を有効にする | オン |
|
||
|
||
> [!success] adminへの割り当てを確認済み
|
||
> DSMの`admin`アカウントが無効な状態でも、上記設定でUID `65532`からフォルダ作成・書き込み・読み込み・rename・削除に成功した。NAS上の所有者はUID `1024`・GID `100`だった。[実機] 2026-10-04 確認
|
||
|
||
### 1.2 PostgreSQLのファイルを用意する
|
||
|
||
1. NAS上に次のディレクトリを事前作成する。
|
||
|
||
```text
|
||
/volume1/docker/tver-db/
|
||
/volume1/docker/tver-db/pgdata/
|
||
```
|
||
|
||
2. [[../deploy/nas/tver-db/compose.yaml|NAS用compose.yaml]]、`.env.example`、`backup-db.sh`、`restore-test.sh`を`/volume1/docker/tver-db/`に配置する。ディレクトリはroot以外がファイルを削除・置換できないようにし、スクリプトはrootだけが実行できるようにする。
|
||
|
||
```bash
|
||
chmod 755 /volume1/docker/tver-db
|
||
chmod 700 /volume1/docker/tver-db/backup-db.sh
|
||
chmod 700 /volume1/docker/tver-db/restore-test.sh
|
||
ls -ld /volume1/docker/tver-db
|
||
ls -l /volume1/docker/tver-db/backup-db.sh /volume1/docker/tver-db/restore-test.sh
|
||
```
|
||
|
||
DSMのACLが付いている場合、`chmod`後の`ls`でパーミッション表示の末尾から`+`が外れ、ディレクトリが`drwxr-xr-x`、スクリプトが`-rwx------`になったことを確認する。`/volume1/docker`では全員書き込み可のACLが確認されたため、スクリプトだけでなく配置ディレクトリにも`chmod 755`を適用する。
|
||
|
||
3. `.env.example`を`.env`へコピーし、`POSTGRES_PASSWORD`に十分長いランダムな値を設定する。値には、URLへの埋め込みで追加のエスケープが不要な16進文字列を使える。
|
||
|
||
```bash
|
||
cd /volume1/docker/tver-db
|
||
umask 077
|
||
cp .env.example .env
|
||
# openssl rand -hex 64 で値を生成し、.env の POSTGRES_PASSWORD に設定する。
|
||
chmod 600 .env
|
||
```
|
||
|
||
> [!important] DBとアプリで同じパスワードを使う
|
||
> ここで設定した値を、後述するdockage側の`DATABASE_URL`にも使う。値そのものは文書やGitへ残さない。
|
||
|
||
### 1.3 Container ManagerでPostgreSQLを起動する
|
||
|
||
1. Container Managerの「プロジェクト」で、`/volume1/docker/tver-db/compose.yaml`を使う`tver-db`プロジェクトを作成する。
|
||
2. 起動前に、他のプロジェクトとポート一覧を確認し、`15434`が空いていることを確認する。
|
||
3. プロジェクトをビルド・起動する。
|
||
4. `tver-postgres`が`healthy`になることを確認する。
|
||
5. コンテナログに`database system is ready to accept connections`があることを確認する。
|
||
|
||
初期化に失敗した場合は、先に`pgdata`の所有権・権限とログを確認する。空のDBを作り直す場合を除き、既存の`pgdata`を削除しない。
|
||
|
||
### 1.4 DSMファイアウォールを設定する
|
||
|
||
TCP `15434`について、送信元`192.168.10.155`だけを許可するルールを追加する。他の送信元を許可する広いルールより前に配置し、LAN内の別ホストからDBへ接続できない構成にする。
|
||
|
||
DSMファイアウォールを設定した状態で、dockageからNASのTCP `15434`へ到達できることを確認済みである。[実機] 2026-10-04 確認
|
||
|
||
## 2. dockageを準備する
|
||
|
||
### 2.1 NFSクライアントとマウント先を用意する
|
||
|
||
```bash
|
||
sudo apt update
|
||
sudo apt install -y nfs-common
|
||
sudo mkdir -p /mnt/nas-tver
|
||
```
|
||
|
||
`/etc/fstab`へ次の1行を追加する。`_netdev`でネットワーク後に扱い、`nofail`でNAS停止時もdockageの起動を妨げない。
|
||
|
||
```fstab
|
||
192.168.10.201:/volume1/video /mnt/nas-tver nfs defaults,_netdev,nofail 0 0
|
||
```
|
||
|
||
設定を反映し、想定したNFS共有がマウントされたことを確認する。
|
||
|
||
```bash
|
||
sudo mount -a
|
||
findmnt --target /mnt/nas-tver
|
||
```
|
||
|
||
実機ではNFSv3でマウントできた。NFS権限を追加した直後の1回目は`access denied`となり、直後の2回目で成功した。NFS権限の反映待ちと考えられるため、設定内容が正しい場合は少し待って`sudo mount -a`を再実行する。[実機] 2026-10-04 確認
|
||
|
||
### 2.2 workerのUIDで書き込みを確認する
|
||
|
||
既存の`anime`・`drama`・`misc`・`personal`・`variety`とアプリのファイルを分け、admin相当の書き込み範囲を限定するため、tver専用サブフォルダを使う。Dockerグループの`kazu634`で、workerコンテナと同じUID・GID `65532:65532`の確認用コンテナを実行し、フォルダ作成、書き込み、読み取り、rename、削除を確認する。`sudo`は不要である。
|
||
|
||
```bash
|
||
docker run --rm --user 65532:65532 -v /mnt/nas-tver/tver:/videos busybox:stable sh -c 'mkdir /videos/.tver-write-test && echo ok > /videos/.tver-write-test/f && cat /videos/.tver-write-test/f && mv /videos/.tver-write-test/f /videos/.tver-write-test/g && ls -ln /videos/.tver-write-test && rm -r /videos/.tver-write-test && echo cleanup-ok'
|
||
```
|
||
|
||
期待する結果は、内容の`ok`、`ls -ln`で表示される`g`の所有者`1024 100`、最後の`cleanup-ok`である。確認後は確認用イメージを削除する。
|
||
|
||
```bash
|
||
docker rmi busybox:stable
|
||
```
|
||
|
||
`/mnt/nas-tver/tver`は作成済みで、上記のDockerコンテナによる操作は実機確認済みである。[実機] 2026-10-04 確認。別環境へ作り直す場合も、書き込み確認が成功するまでworkerを起動しない。
|
||
|
||
### 2.3 Giteaに配置用ユーザーを用意する
|
||
|
||
Giteaは`REQUIRE_SIGNIN_VIEW = true`かつ`DISABLE_SSH = true`で動作している。匿名HTTPSの`info/refs`は`401`となり、SSHも使えないため、配置専用の読み取り資格情報を使う。[実機] 2026-10-04 確認
|
||
|
||
1. Giteaの管理画面で、配置専用ユーザー`tver-deploy`を作成する。
|
||
2. `kazu634/tver`リポジトリの「設定」→「共同作業者」で`tver-deploy`を追加し、権限を「読み取り」にする。
|
||
3. `tver-deploy`でGiteaへログインし、ユーザー設定の「アプリケーション」からrepository読み取り権限だけを持つトークンを作成する。
|
||
4. トークンは作成時に安全な場所へ控え、この文書、シェル履歴、Git管理下のファイルへ実値を書かない。
|
||
|
||
### 2.4 dockageにclone用トークンを保存する
|
||
|
||
Dockerホストの管理ユーザー`kazu634`で次を実行する。トークンは非表示で読み取り、権限`600`の専用credentialファイルへ保存する。credential helperはGiteaのHTTPS URLにだけ設定する。
|
||
|
||
```bash
|
||
install -d -m 700 ~/.config/git
|
||
read -rs -p 'Gitea token: ' TOKEN; echo
|
||
umask 077
|
||
printf 'https://tver-deploy:%s@gitea.kazu634.com\n' "$TOKEN" > ~/.config/git/credentials-tver
|
||
unset TOKEN
|
||
git config --global credential.https://gitea.kazu634.com.helper 'store --file ~/.config/git/credentials-tver'
|
||
git clone https://tver-deploy@gitea.kazu634.com/kazu634/tver.git /opt/stacks/tver
|
||
cd /opt/stacks/tver
|
||
```
|
||
|
||
この手順で認証付きcloneに成功した。[実機] 2026-10-04 確認。トークンを失効・更新した場合は、同じ手順で`credentials-tver`を作り直す。
|
||
|
||
### 2.5 アプリの環境変数を用意する
|
||
|
||
`.env.example`をGit管理外の`.env`へコピーし、`DATABASE_URL`へパスワード用の伏字を入れる。ファイルを`600`にしてから、パスワードを非表示で読み取り、伏字だけを置換する。
|
||
|
||
```bash
|
||
cd /opt/stacks/tver
|
||
umask 077
|
||
cp .env.example .env
|
||
sed -i 's|^DATABASE_URL=.*|DATABASE_URL=postgres://tver:__SET_PASSWORD__@192.168.10.201:15434/tver?sslmode=disable|' .env
|
||
chmod 600 .env
|
||
read -rs -p 'PostgreSQL password: ' DB_PASSWORD; echo
|
||
sed -i "s|__SET_PASSWORD__|${DB_PASSWORD}|" .env
|
||
unset DB_PASSWORD
|
||
if grep -q '__SET_PASSWORD__' .env; then echo 'DATABASE_URLのパスワードが未設定です' >&2; exit 1; fi
|
||
```
|
||
|
||
完成した`.env`は次の項目を持つ。`DATABASE_URL`の実値は表示したり、文書へ転記したりしない。
|
||
|
||
```dotenv
|
||
DATABASE_URL=<設定済み・表示しない>
|
||
VIDEO_HOST_DIR=/mnt/nas-tver/tver
|
||
VIDEO_DIR=/videos
|
||
WORKER_CONCURRENCY=1
|
||
```
|
||
|
||
> [!warning] パスワードの文字種
|
||
> この手順で使うPostgreSQLパスワードには、URLや`sed`の区切りに影響する`@`・`:`・`/`・`?`・`#`・`%`・`|`を含めない。NAS準備時に16進文字列で生成すれば、この制約を満たせる。
|
||
|
||
### 2.6 起動する
|
||
|
||
`/opt/stacks/tver`でwebとworkerをビルドして起動する。
|
||
|
||
```bash
|
||
cd /opt/stacks/tver
|
||
docker compose up -d --build
|
||
```
|
||
|
||
この方法で起動すると、Dockgeの画面にも`tver`スタックとして表示された。[実機] 2026-10-04 確認。Dockge上でweb・workerがともにrunningになり、再起動を繰り返していないことを確認する。
|
||
|
||
コマンドでも状態とログを確認できる。
|
||
|
||
```bash
|
||
cd /opt/stacks/tver
|
||
docker compose ps
|
||
docker compose logs --tail=100 web worker
|
||
```
|
||
|
||
## 3. 動作を確認する
|
||
|
||
1. ヘルスチェックが`200`と`ok`を返すことを確認する。
|
||
|
||
```bash
|
||
curl --fail --show-error http://192.168.10.155:8080/healthz
|
||
```
|
||
|
||
2. ブラウザで`http://192.168.10.155:8080/`を開き、新着一覧が表示されることを確認する。
|
||
3. 番組またはエピソードを1件登録し、`/queue`で待ち行列、進捗、完了を確認する。
|
||
4. `/library`から動画を再生し、シークできることを確認する。
|
||
5. NASの`/volume1/video/tver`に規則どおりのMP4が作成されたことを確認する。
|
||
6. `tver-postgres`のログとweb・workerのログに、接続失敗やマイグレーション失敗がないことを確認する。
|
||
|
||
> [!success] 本番の動作確認結果
|
||
> `/healthz`はHTTP `200`、新着バラエティは98件を表示した。4分の本編`ep5hcgopp2`を登録すると20秒以内に完了し、NASの`video/tver/<番組名>/`へ92,340,468バイトの1080p H.264/AAC MP4が保存された。所有者はUID `1024`・GID `100`、`.incoming`は空だった。`/library`の表示・再生とHTTP Range `206`も確認した。[実機] 2026-10-04 確認
|
||
|
||
> [!note] Synologyの`@eaDir`
|
||
> NASが番組フォルダ内に所有者UID `0`の`@eaDir`を作る。アプリのライブラリ一覧はファイルシステムの走査ではなくDBから作るため、表示や再生には影響しない。[実機] 2026-10-04 確認
|
||
|
||
## 4. 更新する
|
||
|
||
```bash
|
||
cd /opt/stacks/tver
|
||
git status --short
|
||
git pull --ff-only
|
||
```
|
||
|
||
`git status --short`に追跡対象ファイルの変更がある場合は、内容を確認してから更新する。pull後、Dockgeで`tver`スタックを再ビルドして再配置し、[[#3. 動作を確認する]]を繰り返す。
|
||
|
||
yt-dlpを更新するときは`Dockerfile.worker`の`YT_DLP_VERSION`の既定値を変更してリポジトリへ反映し、workerイメージをキャッシュなしで再ビルドする。更新後はworkerログと少数の実動画で抽出・保存を確認する。
|
||
|
||
## 5. PostgreSQLをバックアップする
|
||
|
||
バックアップはNAS上のDSMタスクスケジューラから毎日実行する。`tver-postgres`コンテナ内の`pg_dump`を使って`/volume1/backups/tver/tver_<YYYYmmdd_HHMMSS>.sql.gz`へ保存し、既定では14日より古い世代を削除する。
|
||
|
||
> [!warning] バックアップ先も同じNASにある
|
||
> このバックアップはDBの誤操作や破損からの復元用であり、NAS本体の故障には備えられない。`/volume1/backups/tver/`はSynology Hyper Backupの対象にも含める。
|
||
|
||
### 5.1 DSMの日次タスクを登録する
|
||
|
||
DSMの「コントロール パネル」→「タスク スケジューラ」で「スケジュールされたタスク」→「ユーザー定義のスクリプト」を作成する。
|
||
|
||
| 設定 | 値 |
|
||
|---|---|
|
||
| ユーザー | `root` |
|
||
| 実行周期 | 毎日 |
|
||
| ユーザー定義のスクリプト | `bash /volume1/docker/tver-db/backup-db.sh` |
|
||
|
||
`DOCKER`、`CONTAINER`、`BACKUP_DIR`、`KEEP_DAYS`、`DB_USER`、`DB_NAME`は環境変数で上書きできる。通常運用では既定値を使う。
|
||
|
||
> [!success] NASで手動実行を確認済み
|
||
> DSMタスクスケジューラへrootの日次タスクを登録し、手動実行で2つのgzipダンプを作成した。復元確認も成功している。翌日以降の日次自動実行は未確認である。[実機] 2026-10-04 確認
|
||
|
||
> [!todo] 日次自動実行を確認する
|
||
> 翌日以降にDSMタスクの実行履歴と新しいダンプを確認する。[要確認]
|
||
|
||
### 5.2 手動でバックアップを確認する
|
||
|
||
NASへSSH接続し、初回は手動で実行する。
|
||
|
||
```bash
|
||
sudo bash /volume1/docker/tver-db/backup-db.sh
|
||
LATEST=$(sudo find /volume1/backups/tver -name 'tver_*.sql.gz' -type f -print | sort | tail -n 1)
|
||
test -n "$LATEST"
|
||
sudo test -s "$LATEST"
|
||
sudo gzip -t "$LATEST"
|
||
sudo ls -lh "$LATEST"
|
||
```
|
||
|
||
スクリプトが`OK:`、保存先、バイト数を表示し、`gzip -t`が成功することを確認する。`pg_dump`の失敗や空のダンプでは非0で終了し、完成ファイルを残さない。同じ秒の完成ファイルが既にある場合も非0で終了し、既存ファイルを上書きしない。同時実行時は同一ディレクトリ内のハードリンク作成に成功した一方だけが完成ファイルを公開する。
|
||
|
||
> [!success] NASでのバックアップ結果
|
||
> DSMタスクスケジューラから実行し、`tver_20261004_182838.sql.gz`(2,286バイト)と`tver_20261004_183256.sql.gz`(2,289バイト)を`/volume1/backups/tver/`へ作成した。ダンプはroot所有・権限600で、バックアップディレクトリはroot所有・`drwxr-xr-x`だった。[実機] 2026-10-04 確認
|
||
|
||
### 5.3 別DBへの復元を確認する
|
||
|
||
最新ダンプを本番DBへ戻す前に、同じコンテナ内の一時DB`tver_restore_test`へ復元する。
|
||
|
||
```bash
|
||
sudo bash /volume1/docker/tver-db/restore-test.sh
|
||
sudo tail -n 100 /volume1/backups/tver/restore-test.log
|
||
```
|
||
|
||
`restore-test.sh`はrootで実行する。バックアップディレクトリとログがroot所有のため、一般ユーザーでは`restore-test.log`への書き込みに失敗する。`download_jobs`、`subscriptions`、`known_episodes`、`schema_migrations`の行数が本番DBと一致し、最後に「復元確認に成功」と表示されることを確認する。復元SQLの通常出力は抑制され、ログには開始、各表の結果、成功または失敗が残る。SQLエラーは標準エラーとしてログに残る。一時DBは成功・失敗のどちらでもEXIT trapで削除される。本番DBに対しては行数取得の`SELECT`だけを実行する。
|
||
|
||
> [!success] NASでの復元確認結果
|
||
> 一般ユーザー`kazu634`での実行は`restore-test.log`へ書き込めず、`touch: Permission denied`で終了した。`sudo`で実行すると、`download_jobs` 1件、`subscriptions` 1件、`known_episodes` 1件、`schema_migrations` 3件が本番DBと一時DBで一致し、「復元確認に成功」と表示された。同じ結果がログに残り、一時DBも削除された。[実機] 2026-10-04 確認
|
||
|
||
> [!note] 配置ディレクトリの権限変更後の状態
|
||
> `/volume1/docker/tver-db`を`chmod 755`にした後も、本番アプリの`/queue`と`/subscriptions`はHTTP `200`を返し、DBを読み取れた。PostgreSQLコンテナを再起動した後の動作は未確認である。[要確認]
|
||
|
||
### 5.4 障害時に本番DBへ復元する
|
||
|
||
> [!danger] 本番DBを置き換える操作
|
||
> 先に[[#5.3 別DBへの復元を確認する]]を成功させ、復元対象のダンプを明示してから実行する。`tver`以外のDB名へ読み替えない。
|
||
|
||
1. dockageの`/opt/stacks/tver`でwebとworkerを停止し、DBへの書き込みを止める。
|
||
|
||
```bash
|
||
cd /opt/stacks/tver
|
||
docker compose stop web worker
|
||
```
|
||
|
||
2. NASで復元対象を指定し、存在・サイズ・gzip形式を確認する。
|
||
|
||
```bash
|
||
DUMP=/volume1/backups/tver/tver_<YYYYmmdd_HHMMSS>.sql.gz
|
||
test -s "$DUMP"
|
||
gzip -t "$DUMP"
|
||
```
|
||
|
||
3. NASで本番DBを作り直し、ダンプを復元する。
|
||
|
||
```bash
|
||
docker exec tver-postgres dropdb --if-exists -U tver tver
|
||
docker exec tver-postgres createdb -U tver tver
|
||
gzip -dc "$DUMP" | docker exec -i tver-postgres psql -v ON_ERROR_STOP=1 -U tver -d tver
|
||
```
|
||
|
||
4. dockageでwebとworkerを再開し、[[#3. 動作を確認する]]を実施する。
|
||
|
||
```bash
|
||
cd /opt/stacks/tver
|
||
docker compose start web worker
|
||
```
|