proxmox のLXCにMinecraftサーバーを立てる

Memo
この記事は約24分で読めます。

背景

サーバー構成などの前情報はこちらを参照してください

概要

proxmox VE 8.3 のLXCコンテナ上に、Minecraft (spigot) を立てます。
コンテナに割り振っているIPは 192.168.1.10 とします。

コンテナのテンプレートは「rockylinux-9-default_20240912_amd64」
メモリは13GB、プロセッサは6コア与えています。
コンテナストレージは32GBとしています。

ディレクトリ構造は以下のように構成してる前提としています。
/home/mcuser/
    ├ bin/     // minecraftサーバー本体
    ├ buildtool/  // spigotサーバー生成ツール
    ├ scripts/   // minecraftサーバー起動管理スクリプト等
    └ tmp/    // 一時ファイル

前準備

サーバーのファイル管理等で便利なので smb を導入しておきます

[root@minecraft ~]# dnf install samba samba-client samba-common -y

Linuxユーザーを作成していなければ作成しておきます。
Windowsのアカウント名と合わせておくと便利かもしれません。(その前提で進めます)
ここでは例として “mcuser” とします。

[root@minecraft ~]# adduser mcuser
[root@minecraft ~]# passwd mcuser

smbにユーザーを登録します

[root@minecraft ~]# pdbedit -a mcuser

vim /etc/samba/smb.conf で smbを設定します

[global]
        passdb backend = tdbsam

        printing = cups
        printcap name = cups
        load printers = yes
        cups options = raw

        # 文字コード設定
        unix charset = UTF-8
        dos charset = CP932
        # workgroup 設定 (WindowsのWORKGROUP名と一致させる。)
        workgroup = WORKGROUP
        # 認証設定
        security = user
        # シンボリックリンクを参照できるようにする
        wide links = yes
        unix extensions = no

[homes]
        comment = Home Directories
        valid users = %S, %D%w%S
        browseable = No
        read only = No
        inherit acls = Yes

smbの自動起動設定と起動

[root@minecraft ~]# systemctl enable smb.service
[root@minecraft ~]# systemctl enable nmb.service
[root@minecraft ~]# systemctl restart smb.service
[root@minecraft ~]# systemctl restart nmb.service

Java等のインストール
MC1.20.5以降の推奨Javaバージョンは 21 とのことなので、Java21 を導入します。

[root@minecraft ~]# yum install -y git
[root@minecraft ~]# yum install -y tmux
[root@minecraft ~]# yum install -y java-21-openjdk-devel

インストール

buildtool

まずは buildtool 環境を整えます

[root@minecraft ~]# mkdir /home/mcuser/buildtool
[root@minecraft ~]# cd /home/mcuser/buildtool
[root@minecraft buildtool]# curl -o BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar
[root@minecraft buildtool]# java -jar BuildTools.jar

java -jar BuildTools.jar を叩くと最新版のサーバーが作られます。
(でも公式最新リリースから間もなくは最新版にはならないようです?)
バージョンを指定する場合は –rev 1.21.7 のようにバージョン指定を付け加えてください。

正常にビルドが完了すると、 BuildTools.jar がある場所に、spigot-1.21.7.jar といったファイルが作成され、これがサーバーになります。
これを、/user/mcuser/bin にコピーしておいてください。

起動管理スクリプト

私が昔どこからか参考にしたスクリプトをベースに私なりに色々改造した独自起動管理スクリプトを紹介します。
Minecraftは起動時に色々トラぶることが多いので、変にデーモン化するより手の届く範囲でやったほうがやりやすいと永遠にLinux初心者の私は思います。

/user/mcuser/scripts/ に mcscript.sh を作成します。
vim scripts/mcscript.sh

#!/usr/local/bin/bash

# 応急起動用メモ
# java -server -Dfile.encoding=UTF-8 -Xms8G -Xmx8G -jar server.jar

#===== Settings =====
SERVICE='server.jar'
SESSION='minecraft'
OPTIONS='nogui'
USERNAME='mcuser'
ME=`whoami`
WORLD='world'
MCPATH='/home/mcuser/bin'
BACKUPPATH='/home/mcuser/backup'
UPDATEWORKPATH='/home/mcuser/tmp'
LOGFILE='/home/mcuser/scriptlog.log'
MAXHEAP=10G
MINHEAP=4G
NEWHEAP=1G
HISTORY=1024
CPU_COUNT=6
INVOCATION="java -server -Dfile.encoding=UTF-8 -Xms${MINHEAP} -Xmx${MAXHEAP} -Xmn${NEWHEAP} -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -jar $SERVICE $OPTIONS"


#----------------------------------------------------------
# 実行ユーザー確認
# 違うユーザーでスクリプトを実行したときは su する
#----------------------------------------------------------
as_user() {
    if [ $ME == $USERNAME ] ; then
        bash -c "$1"
    else
        su - $USERNAME -c "$1"
    fi
}


#----------------------------------------------------------
# サーバー起動
#----------------------------------------------------------
mc_start() {
    # サーバーが動作中か確認
    if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
        echo "$SERVICE is already running!"
    else
        echo "Starting $SERVICE..."
        cd $MCPATH
        tmux new -s $SESSION -d "$INVOCATION"
        # 起動待ち
        echo "wait 7sec..."
        sleep 7
        
        # サーバーが動作中か確認
        if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
            echo "$SERVICE is now running."
        else
            echo "Error! Could not start $SERVICE!"
        fi
    fi
}


#----------------------------------------------------------
# ゲームの自動セーブを停止
#----------------------------------------------------------
mc_saveoff() {
    # サーバーが動作中か確認
    if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
        echo "$SERVICE is running... suspending saves"
        tmux send-keys -t $SESSION "say SERVER BACKUP STARTING. Server going readonly..." ENTER
        tmux send-keys -t $SESSION "save-off" ENTER
        sync
        echo "wait 10sec..."
        sleep 10
    else
        echo "$SERVICE is not running. Not suspending saves."
    fi
}


#----------------------------------------------------------
# ゲームの自動セーブを有効化
#----------------------------------------------------------
mc_saveon() {
    # サーバーが動作中か確認
    if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
        echo "$SERVICE is running... re-enabling saves"
        tmux send-keys -t $SESSION "save-on" ENTER
        tmux send-keys -t $SESSION "say SERVER BACKUP ENDED. Server going read-write..." ENTER
    else
        echo "$SERVICE is not running. Not resuming saves."
    fi
}


#----------------------------------------------------------
# サーバーの停止
#----------------------------------------------------------
mc_stop() {
    # サーバーが動作中か確認
    if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
        echo [`date '+%F %T'`] 'Server stop script start'
        echo "Announce Server stop call @ 30sec"
        tmux send-keys -t $SESSION "say メンテナンスのため、30秒後にセーブが実行され、その5秒後にサーバーが停止します" ENTER
        sleep 10
        echo "@ 20sec"
        tmux send-keys -t $SESSION "say メンテナンスのため、20秒後にセーブが実行され、その5秒後にサーバーが停止します" ENTER
        sleep 10
        echo "@ 10sec"
        tmux send-keys -t $SESSION "say メンテナンスのため、10秒後にセーブが実行され、その5秒後にサーバーが停止します" ENTER
        sleep 10

        mc_stopf
    else
        echo [`date '+%F %T'`] 'server is not runnning'
    fi
}


#----------------------------------------------------------
# サーバーの即時停止
#----------------------------------------------------------
mc_stopf() {
    # サーバーが動作中か確認
    if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
        echo "Exec save-all command"
        tmux send-keys -t $SESSION "save-all" ENTER

        echo "wait 5sec..."
        sleep 5

        echo "Exec stop command"
        tmux send-keys -t $SESSION "stop" ENTER
        
        echo "Waiting for server to shut down"
        for i in {1..60}; do
            # 指定したプロセスが存在するか確認
            if ! [ -n "$(tmux list-sessions | grep -o "${SESSION}")" ]; then
                # プロセスが存在しない場合、終了
                break
            fi
            # 1秒待機
            sleep 1
            echo "Waiting for server to shut down"
        done
    else
        echo [`date '+%F %T'`] 'server is not runnning'
    fi
    
    # サーバーが動作中か確認
    if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
        echo "Error! $SERVICE could not be stopped."
    else
        echo "$SERVICE is stopped."
    fi
}


#----------------------------------------------------------
# バックアップの実行
#----------------------------------------------------------
mc_backup() {
    # ゲームを save
    mc_save
    echo "wait 10sec..."
    sleep 10
    
    # バックアップ中にsaveが動かないように止める
    mc_saveoff
    
    # ファイル名の作成
    NOW=`date "+%Y-%m-%d_%H%M"`
    BACKUP_FILE="$BACKUPPATH/${SESSION}_${NOW}.tar"
    
    # バックアップを実行 (tarアーカイブ)
    echo "Backing up $SERVICE"
    as_user "tar -C $MCPATH -cf $BACKUP_FILE -X $MCPATH/exclude.conf $MCPATH "
    
    # 止めていた save を有効化
    mc_saveon
    
    # バックアップの実行 (gzip圧縮)
    echo "Compressing backup..."
    as_user "gzip -f $BACKUP_FILE"
    echo "Done."
    
    # 古いバックアップのローテーション
    if test -n "`ls -t $BACKUPPATH/* | tail -n+5`";then
        echo "古いファイルを削除します"
        rm -v `ls -t $BACKUPPATH/* | tail -n+5`
    fi
    echo "完了"
}


#----------------------------------------------------------
# サーバーコンソールからゲーム内にコマンドを発行する
#----------------------------------------------------------
mc_command() {
    # 引数を代入
    command="$1";
    
    # サーバーが動作中か確認
    if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
        # tmuxのスクロールバックバッファをクリア (TODO:意図通りにできていない)
        tmux send-keys -t $SESSION 'clear' ENTER
        tmux clear-history -t $SESSION

        # ゲーム内にコマンドを発行
        tmux send-keys -t $SESSION "$command" ENTER
        
        # パネルの内容をキャプチャ
        tmux capture-pane -t $SESSION
        
        # キャプチャした内容を変数に保存して出力
        OUTPUT=$(tmux show-buffer)
        echo "$OUTPUT"
    fi
}


#----------------------------------------------------------
# ゲームのセーブ
#----------------------------------------------------------
mc_save() {
    echo "Exec save-all command"
    tmux send-keys -t $SESSION "save-all" ENTER
}


#----------------------------------------------------------
# アップデート
#----------------------------------------------------------
mc_update_sub(){
    local url="$1"        # 最初の引数(URL)
    local filename="$2"   # 2番目の引数(ファイル名)

    # プラグインダウンロード
    curl -sS -L ${url} --output ${UPDATEWORKPATH}/${filename}

    # ファイルを比較して違っていれば更新
    if ! cmp -s ${UPDATEWORKPATH}/${filename} ${MCPATH}/plugins/${filename}; then
        echo "${filename}に差異がありました。プラグインを更新します" | tee -a "$LOGFILE"
        \cp -f ${UPDATEWORKPATH}/${filename} ${MCPATH}/plugins/${filename}
        return 0
    fi
    return 1
}
mc_update() {
    echo "update command"
    update_flg=false

    # 作業フォルダが無ければ作る
    [ ! -d $UPDATEWORKPATH ] && mkdir $UPDATEWORKPATH

    # プラグイン更新
    mc_update_sub https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot Geyser-Spigot.jar && update_flg=true 
    mc_update_sub https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot floodgate-spigot.jar && update_flg=true 

    # サーバー動作中に更新があればプラグインをリロード
    if [ "$update_flg" = true ]; then
        if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
            tmux send-keys -t $SESSION "reload" ENTER
            echo "$(date '+%Y-%m-%d %H:%M:%S') update & reloaded" | tee -a "$LOGFILE"
        fi
    fi
}


#==========================================================
# Start-Stop here
#==========================================================
case "$1" in
    start)
        mc_start
        ;;
    stop)
        mc_stop
        ;;
    stopf)
        mc_stopf
        ;;
    restart)
        mc_stop
        mc_start
        ;;
    backup)
        mc_backup
        ;;
    save)
        mc_save
        ;;
    status)
        if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
            echo "$SERVICE is running."
        else
            echo "$SERVICE is not running."
        fi
        ;;
    check)
        if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
            echo "$SERVICE is running."
        else
            echo "$SERVICE is not running. wait 120sec..."
            sleep 120
            if [ -n "$(tmux list-sessions 2>/dev/null | grep -o "${SESSION}")" ]; then
                echo "$SERVICE is running."
            else
                mc_start
            fi
        fi
        ;;
    command)
        if [ $# -gt 1 ]; then
            shift
            mc_command "$*"
        else
            echo "Must specify server command (try 'help'?)"
        fi
        ;;
    attach)
        echo "Minecraftサーバーコンソールにアタッチします"
        echo "デタッチ方法は Ctrl + b -> d です (Ctrl+bしてから一度放した後に d キー押下)"
        echo "Ctrl + c はプロセスを殺してしまうので注意してください"
        echo "Escキーを押下するとキャンセルします。その他キーを押下するとアタッチを開始します..."
        read -n 1 -s key
        if [[ $key == $'\e' ]]; then
            # Escキーを押下したとき
            echo "Attach canceled"
        else
            # その他キーを押下したとき
            tmux a -t $SESSION
        fi
        ;;
    update)
        mc_update
        ;;
    *)
        echo "Usage: $0 {start|stop|backup|status|restart|command \"server command\"}"
        exit 1
        ;;
esac

exit 0

使用方法は、
bash mcscript start
という感じです。

ざっくり引数の説明としては
・start : 起動
・stop:ゲーム内警告付き30秒後停止
・stopf:警告無し即時停止
・restart:start と stop を続けて実行する
・backup:バックアップを実行
・status:サーバーの稼働状態を表示
・check:サーバーが起動していたらなにもしない、停止していたら起動する(定期的に落ちる鯖の定期起動用)
・command:ゲーム内コマンドを呼び出す
・update:geyser と floodgate のプラグインをダウンロードし、変化があればプラグインを更新する(自分用です、使う場合はうまくカスタマイズしてください)

おまけ:各種プラグインの設定等

dynmap用データベース

Webマッププラグインdynmapはデータの管理にデータベースを使用することが出来ます。
パフォーマンス的にはデータベースを使った方が良いはずなので設定しておきます。

[root@minecraft ~]# dnf install -y mariadb-server
[root@minecraft ~]# systemctl enable --now mariadb;systemctl restart mariadb

初期設定をします。
mariadbのバージョンが新しい場合は mariadb_secure_installation に名前が変わっているので注意。

mysql_secure_installation
    (dbrootpass)
    Switch to unix_socket auth: n
    Change root pass: n
    Remove anonymous users: y
    Deallow remote root login: y
    Remove test db: y
    Reload privilege table now: y

vim /etc/my.cnf.d/mariadb-server.cnf で mysql.sockの場所を確認しておきます。
私の環境では socket=/var/lib/mysql/mysql.sock でした。

データベースを作成します。
dynmapの初期設定に合わせて、DB名、User名、Password 全て dynmap にしてます。

mysql -u root -pdbrootpass
    CREATE DATABASE dynmap;
    GRANT ALL PRIVILEGES ON dynmap.* TO "dynmap"@"localhost" IDENTIFIED BY 'dynmap';
    FLUSH PRIVILEGES;

間違った場合など、消す場合は、 drop database dynmap;

データベース設定

plugins/dynmap/configuration.txt

type: filetree をコメントアウト
以下のコメントアウトを外す
    type: mysql
    port: 3306
    database: dynmap
    userid: dynmap
    password: dynmap

type は mariadb を設定すると説明しているところもあるが、Dynmap 自体が MariaDB ドライバーでコンパイルされていないと機能しないらしい。mysqlでも問題ないみたい。

タイトルとURLをコピーしました