MinetestサーバーのバックエンドをPostgreSQLにする

nogajun
nogajun

前にも書きましたが、minetestサーバーを地味に公開しています

ぼちぼち安定した感じですが、minetestmapperというツールを使ってバックアップからマップを生成すると、いろんな場所が開拓されていてワールドがめちゃくちゃ広くなってました。(ワールドの地図画像。ファイルサイズがデカイです。)

このままだとヤバくなりそう…。ということで、minetestサーバーのバックエンドをsqlite3からPostgreSQLに移行するためのテストをメモしました。

参考資料ですが、スペイン語で書いてたブログ記事がとても役に立ちました。自分の記事は、この記事をDockerに置き換えただけです。

DockerでMinetestサーバーを動かすおさらい

以前「DockerでMinetestサーバーが動いた」という記事を書きました。

今回は、この記事のdocker-compose.ymlをベースにバックエンドをPostgreSQLに置き換えるので、一度もMinetestサーバーを設置したことが無い人は先にこちらを試して、動くことを確認してからPostgreSQLへ移行を試してください。

使用するファイルについて

docker-composeを使うのは変わりませんが、以前のファイル構造から少し変わってdbinitdbフォルダとDockerfileを追加しています。

./
├ conf/
│ └ minetest.conf
├ data/
│ └ .minetest/
├ db/ (今回追加) 
│ └ (PostgreSQLのデータが置かれる)
├ initdb/ (今回追加)
│ └ init-user-db.sh     
└ docker-compose.yml

dbは、PostgreSQLのデータベース保存のために用意しています。中身は空ですがマウントされてここにPostgreSQLのデータベースが保存されます。

initdbには、PostgreSQLにユーザーとデータベースを追加するスクリプトを置いています。PostgreSQLのDockerイメージの/docker-entrypoint-initdb.d/フォルダに.shまたは.sql拡張子のファイルがあると自動実行されます。(ドキュメントの「Initialization scripts」参照)。この場所をマウントしてユーザーとデータベースの追加を実行します。

docker-compose.yml

docker-compose.ymlですが、PostgreSQLを追加するのでpostgresを追加しています。それに伴ってminetestも変更しています。最後のadminerはPostgreSQLのデータベースを見るために追加してます。不要ならなくてもOKです。

version: "3"

services:
  minetest:
    container_name: minetest
    image: registry.gitlab.com/minetest/minetest/server:5.5.0
    volumes:
      - "./conf/:/etc/minetest/"
      - "./data/:/var/lib/minetest/"
    ports:
      - "30000:30000/udp"
    depends_on:
      postgres:
        condition: service_healthy
    links:
      - postgres

  postgres:
    container_name: postgres
    image: postgres
    environment:
      POSTGRES_PASSWORD: "admin"
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
      TZ: "Asia/Tokyo"
    volumes:
      - "./db/:/var/lib/postgresql/data/"
      - "./initdb/:/docker-entrypoint-initdb.d/"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U mtuser -d mtworld"]
      interval: 10s
      timeout: 5s
      retries: 5

  adminer:
    container_name: adminer
    image: adminer
    ports:
      - "8080:8080"
    links:
      - postgres

postgresのenvironmentですが、POSTGRES_PASSWORDDockerイメージのドキュメントにもあるように必須なので設定しています。POSTGRES_INITDB_ARGSは、データベースのデフォルト設定です。データベースのエンコーディングをUTF-8にするために入れてます。TZは、タイムゾーンです。

volumesは、上で説明したデータベースを保存するdbフォルダと設定スクリプトを置いてるinitdbをマウントしています。

healthcheckは、コンテナが起動したかをチェックしています。ここが今回一番悩んだ部分です。

コンテナを再起動したとき、postgresコンテナが起動して接続OKになった後にminetestコンテナを起動してほしいのですが、postgresコンテナの起動処理に時間がかかり、後から起動したminetestコンテナが先に起動していました。その結果、minetestコンテナは接続先が見つからず落ちることが、たびたび起こっていました。

最初、depends_onで起動順序を指定しました。しかし、この設定は必要ですが、マシンの電源の入れる順番を設定するようなものなので問題は解決しません。

次にシャットダウンのメッセージを見るときちんと終了しないままシャットダウンされたように見えたので、stop_grace_periodで終了処理をきちんと終わらせるようにすればと思って設定しましたが、これも問題は解決しませんでした。

結局、起動処理の問題だから起動チェックをするようにすればいいと思って調べると公式ドキュメントに「Control startup and shutdown order in Compose | Docker Documentation」という、そのものズバリのものがありました。しかし、「version: "2"」と書いあって古いのでさらに調べると同じような疑問を持った人がいてzennで「DockerCompose を用いて起動順序を制御する」という文章を書いてました。

これでやっとhealthcheckの設定がわかったので、さらに検索してこのドキュメント公式ドキュメントを見て、やっと解決したのでした。長かった…。

ということで、pg_isreadyを使って起動したかどうかチェックをしています。

minetestdepends_onですが、前述のようにpostgresが起動した後にminetestコンテナを起動させたいので追加してます。これはqiitaの「DockerのHEALTHCHECKの動きを理解する - Qiita」を参考にしました。

linksは、minetestコンテナからpostgresコンテナをを参照するために追加しています。

adminerは、データベースマネージャーです。PostgreSQLサーバーのデータベースを見るために追加しています。minetestサーバーに直接関係は無いのでなくても構いません。

Minetestサーバーに使うPostgreSQLユーザーとデータベース

PostgreSQLに追加するユーザーとデータベース名は以下にしています。

  • ユーザー名: mtuser
  • パスワード: mtpass
  • データベース: mtworld

initdb/init-user-db.sh

initdbフォルダに置いてある、このスクリプトはPostgreSQLのユーザーとデータベースを作成するスクリプトです。シェルスクリプトの内容は、minetest用のユーザーとデータベースを作って、ユーザーに全権限を付けてます。

#!/bin/bash
set -e

psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
  CREATE USER mtuser WITH PASSWORD 'mtpass';
  CREATE DATABASE mtworld WITH OWNER mtuser;
  GRANT ALL PRIVILEGES ON DATABASE mtworld TO mtuser;
EOSQL

コンテナを起動する

docker-compose upしてコンテナを起動します。minetestサーバーは、まだsqlite3のままで動いています。

PostgreSQLにユーザーとデータベースが作成されているかチェックします。コマンドで確認していますが、adminerで確認しても構いません。 adminerでログインする場合の注意ですが、ログインするサーバーは、localhostではなくコンテナ名のpostgresを指定します。

    $ docker exec -it postgres psql -U mtuser -d mtworld -c "\l"

                                                                     List of databases
         Name    |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges   
    -----------+----------+----------+------------+------------+-----------------------
     mtworld   | mtuser   | UTF8     | en_US.utf8 | en_US.utf8 | =Tc/mtuser           +
                         |          |          |            |            | mtuser=CTc/mtuser
     postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 | 
     template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
                         |          |          |            |            | postgres=CTc/postgres
     template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
                         |          |          |            |            | postgres=CTc/postgres
    (4 rows)

ワールド設定ファイルworld.mtにPostgreSQLの設定を追加

データベースに問題がなければ、minetestコンテナを止めてminetestのworld.mtにPostgreSQLの接続設定を追加します。まずはコンテナを止めます。

    $ docker-copose stop minetest

そしてMinetestデータのワールド設定ファイルdata/.minetest/worlds/(ワールド名)/world.mtに以下のPostgreSQL接続の設定を追加します。この設定のhostですが、docker-compose.ymlのlinksで参照しているコンテナ名(ここではpostgres)を書きます。

    pgsql_connection = host=postgres port=5432 user=mtuser password=mtpass dbname=mtworld
    pgsql_auth_connection = host=postgres port=5432 user=mtuser password=mtpass dbname=mtworld
    pgsql_player_connection = host=postgres port=5432 user=mtuser password=mtpass dbname=mtworld

データベースをマイグレーションする

設定を追記したらminetestコンテナを再び起動してデータベースをマイグレーションします。

    $ docker-copose start minetest

起動したらminetestコンテナからデータベースのマイグレーションを実行します。

    $ docker exec -it minetest minetestserver --migrate postgresql --map-dir /var/lib/minetest/.minetest/worlds/(ワールド名)/
    $ docker exec -it minetest minetestserver --migrate-auth postgresql --map-dir /var/lib/minetest/.minetest/worlds/(ワールド名)/
    $ docker exec -it minetest minetestserver --migrate-players postgresql --map-dir /var/lib/minetest/.minetest/worlds/(ワールド名)/

最初のワールドのマイグレーションはとても時間がかかるので気長に待ちます。認証とユーザーは、すぐに終わります。

終わったらマイグレーションできているか確認します。

data/.minetest/worlds/(ワールド名)/world.mtを見ると、backend, auth_backend, player_backendがpostgresqlになっているはずです。sqlite3のままの場合はマイグレーションされていません。

mod_storage_backendは、PostgreSQLバックエンドが無いようでマイグレーションを実行するとエラーが出てマイグレーションできないので、これはsqlite3のままでOKです。

    mod_storage_backend = sqlite3
    auth_backend = postgresql
    player_backend = postgresql
    backend = postgresql

データベースを確認します。テーブルが作成されていればOKです。こちらもadminerで確認しても構いません。

    $ docker exec -it postgres psql -U mtuser -d mtworld -c "\dt"

                                    List of relations
     Schema |          Name          | Type  | Owner  
    --------+------------------------+-------+--------
     public | auth                   | table | mtuser
     public | blocks                 | table | mtuser
     public | player                 | table | mtuser
     public | player_inventories     | table | mtuser
     public | player_inventory_items | table | mtuser
     public | player_metadata        | table | mtuser
     public | user_privileges        | table | mtuser
    (7 rows)

問題がないようならデータベースのバックアップを取ります。

    $ docker exec -it postgres pg_dump -U mtuser mtworld > mtworld.sql

バックアップを取ったら、docker-compose downしてdocker-compose upしても、きちんと起動するか確認します。

    $ docker-compose down
    $ docker-compose up

minetest、postgresql両方のコンテナがきちんと起動したなら、minetestクライアントからサーバーにログインして動作を確認してください。それで問題なければ移行は成功です。

sqlite3に戻したい場合

何か問題があってマイグレーションできなかった場合ですが、sqlite3のデータベースは残っているのでworld.mtpostgresqlの記述を下のようにsqlite3戻せば古いsqlite3データベースに戻せます。

    mod_storage_backend = sqlite3
    auth_backend = sqlite3
    player_backend = sqlite3
    backend = sqlite3

最後に

長かった。大丈夫だと思ってた設定がダメダメすぎて、このままお蔵入りかと思ったけど解決できてよかった。

PostgreSQLが使えるようになったので、次はテクスチャなどを配信するリモートメディアサーバーを置いて(これはすぐにできそう)、マップサーバーを設置すれば理想のサーバー環境ができるはず。あと少し。