マイクラサーバをGitHubで運用する
こんちわ〜.珍しく1ヶ月という短期間でブログ記事を書くsksatです. 今回はここ数ヶ月運用しているマインクラフトサーバについて書きます.
Minecraft
みなさんマイクラやってますか?僕はそんなにやってないです.運用するのはまあまあおもしろい.
とはいえそんなにやってないわけでもなくて,5月頃はけっこうやっていて,空中から海をブチ抜いて地中まで刺さっている畑などがあります. まあかなりのcontribution(?)は他のオタク各位ですけどね.
ちなみに幣マイクラサーバですが,
— sksat (@sksat_tty) 2021年5月20日
・空中から巨大な段々畑を生やす
・畑が海水面に衝突したので水抜きして畑が海に突入
・畑の建設中に巨大な昆布栽培場所に水が垂れ海水面上昇
・畑のための土が足りない
・土より昆布ブロックの方が安い
・拠点周辺の土を剥いで昆布ブロックに置き換える
という状態
これはその畑です.海には接していないように見えるがそれは描画限界で実際は海に突入している pic.twitter.com/DpzxJ26cNS
— sksat (@sksat_tty) 2021年5月20日
で,このマイクラサーバなんですが,以下のようなかんじで運用しています.
- サーバ: PaperMC 1.17.1
- モード: サバイバル
- Mod/Plugin: 基本バニラ.使ってるものは以下.
- PrometheusExporter
- DiscordSRV
- CoreProtect
- ユーザ管理: ホワイトリスト.GitHub repoへのPull Requestで追加
GitHubでのマインクラフト運用
というわけで本題ですが,このサーバはGitHubを使って運用しています.
どういうことだよ,となると思うんですが,見ればわかる.
このサーバはdocker-compose
を使って運用していて,このリポジトリにはマイクラサーバをデプロイするためのスクリプト群がある,ということです.かんたんだね.
じゃあこのリポジトリをcloneしてきて,docker-compose up
すればマイクラサーバが立ち上がってうれしい,ということかと思うじゃないですか.
それは半分正解です.しかしそれだけではありません.
正確には,このリポジトリは常にmainブランチが本番環境に同期されるようになっています.
そしてよく見てほしいのですが,このリポジトリにはdata/whitelist.json
があります.
そしてこのサーバに参加しているオタクが登録されています.
極めつけはREADMEの一番下.
how to join
Edit whitelist.json and send a pull request
はい.GitHubで運用するということは,つまりそういうことです.
なので,このマイクラサーバに参加したい人がいる時,僕はサーバにSSHしてwhitelist.json
を書き換える必要はまったくありません.
必要なのは,「あ,じゃあmc.yohane.suのREADME読んでプルリク送ってね」の一言と,マージボタンを押すことだけです.
CD(Continuous Deployment)
みなさんマイクラサーバ運用でCDしてますか?僕はしてる.
mainブランチが本番環境に同期されるというのはこれの話です.
CD,したいですよね.でもこれdocker-compose
なんですよね.
CDするための良さげなツールとかないんですよ(あったら教えてほしい).
Kubernetes使ってたらArgoCDでポン,みたいなかんじでいいんですけどね.
というかできるならそのうちそっちに移行したい.
じゃあどうしてるのかというと,雑なスクリプトを書きました.
「docker-compose
でもCDをしたい!」ということで,その名もcompose-cd
.
これはmc.yohahe.suのためではなく,kuso-subdomain-adderというものの運用自動化のために書いたスクリプトなんですが,けっこう便利なので最近はよく使ってます.
kuso-subdomain-adderはteleka.suというクソドメインのサブドメインを生やすためだけのWebサービス(?)です. ここらへんの話はVRC-LT #9でやりました.
というのはさておき,compose-cd
の話に戻りますが,使い方は超簡単.
各リポジトリのdocker-compose.yml
があるディレクトリに.compose-cd
という設定ファイルを置くだけです.
compose-cd
では,docker-compose
なプロジェクトで使うために,デプロイが走るタイミングとしてリポジトリ自体が更新される場合と,リポジトリで使っているイメージが更新される場合を想定しています.
リポジトリが更新される場合というのは,リポジトリでコンテナからマウントされるような設定ファイル(マイクラであればwhitelist.json)を管理していて,それが更新される場合です.
次にイメージの更新ですが,これは新しいバージョンのイメージが出たら更新する,という話ではありません. CDなんてことをする以上,ある時点でのリポジトリからはそのリポジトリ内で設定されているバージョンのモノがデプロイされるべきです. 更新してupstreamのリポジトリも更新するようにすればよいかもしれませんが,それは管理のしやすさ的にもセキュリティ的にもよくないでしょう.
端的に言うと,compose-cd
の責任範囲はupstreamのリポジトリとイメージの更新を確認し,更新があればデプロイを実行する,ということまでだということです.
ただし,latest
のようなイメージタグを使っている場合は例外です.
まあlatest
なんて基本的に使うべきではないわけですが,別にプロダクションではなく趣味なので雑に使いたい時もありますよね.
最近は後述するRenovateのおかげでほぼないけども...
これらの2つの場合に対応するため,.compose-cd
では基本的にリポジトリ,イメージ,イメージタグを指定します.
例えば,kuso-subdomain-adder
ではこんなかんじ.ghcr.io
にも対応しています.
REPO="https://github.com/sksat/kuso-subdomain-adder" REGISTRY="ghcr.io" IMAGE="sksat/kuso-subdomain-adder" IMG_TAG=master
compose-cd
がやっていることはめちゃくちゃ単純です.
実際実装も300行程度のBashスクリプトとsystemd timerです.
compose-cd.timerがsystemd timerの設定で,毎分compose-cd.serviceを呼び出すだけです.
compose-cd.serviceはcompose-cd update
を呼び出すだけです.
compose-cd update
では,SEARCH_ROOT
以下のディレクトリで.compose-cd
があるディレクトリを探索し,.compose-cd
にある設定に従ってリポジトリとイメージの更新を確認し,更新があればgit pull
ないしdocker-compose pull
をして再起動(docker-compose down
, docker-compose up -d
)します.
まあ超単純なんでそんなに書くことはないですね.
ghcr.io
だとうまくいくのにDockerHubだとうまくいかなかったりとか,
うまくいっていたghcr.io
が突然動かなくなったりしたのは面白かったですが*1.
ところで,今これを書いていてイメージだけの更新を使う場合に複数イメージ使うことを一切想定してないな,という気付きがあったので気が向いたら実装します.
とはいってもそんな場合は自分で使っている分にはほぼないので別に要らないかな...ちゃんとdocker-compose.yml
でハッシュまで指定してくれ.
CI(Continuous Integration)
みなさんマイクラサーバ運用でCIしてますか?僕はしてる.
mc.yohane.suでは前述の通りwhitelist.json
をGitHubで管理しており,ユーザの追加はPull Requestで行うのですが,
JSONって手書きするのダルいですよね.いやそんなにダルくない人もいるだろうし別にwhitelist.jsonは全然ダルくないんですが,プルリクが送られてきたのを何も見ずにマージするのも渋い.そこでCIです.
というわけでできたのがこちら.minecraft-whitelist-validatorです.
これはRustでちょろっと書きました.
またしてもやってることは超単純で,whitelist.jsonをserde_jsonで読んで,Mojang APIを使ってユーザとそのUUIDを検証するだけです.
そして,Docker Imageを作ってGitHub Action化もしたので,uses: sksat/minecraft-whitelist-validator@v0.2.0
みたいなかんじで使えます.やったね.
https://t.co/T9GyPDZa4h
— sksat (@sksat_tty) 2021年7月19日
ということでできた本日の要らないものがこちら.マイクラのwhitelist.jsonをserdeで読んでmojangのAPI叩いてUUIDが合ってるか見るだけコマンド.たぶん使わない.
そしてcargo init
した後に気付いたんですが,これってcurlとjqでいいんですよね.まあRust書きたかったのでいいでしょう.
マイクラマルチ鯖をGitHub repoでIaCしてwhitelist.jsonへのPRを募っていて, UUIDとかが合ってるかぐらいをCIでチェックしたい人がもし万が一僕以外にもいれば使ってあげてください(?)
コンテナイメージ
さてここまでwhitelist.jsonをGitHubとプルリクでやっている話をしましたが,たぶんみなさんが気になるところがあるとすればdocker-compose
でどんなイメージを使っているかでしょう.
もちろんこれも自作です.
まあDocker/docker-composeでマイクラマルチサーバやるなら普通有名なitzg/docker-minecraft-server
使えばいいとは思うんですが,ここまで自作の物体でガチャガチャやっておいてイメージも自作しないわけはありません.
というわけで作ったのがこちら.papermc-dockerです.
PaperMCというのはJava版マイクラサーバに大量のパッチを当てまくることでなんかいいかんじにすることで有名なSpigotのforkのPaperというやつのことです.
なんかパフォーマンスがいいらしく,オタクにも人気らしい.ということで使っています.詳しいことは知らない.
ちなみにitzg/docker-minecraft-server
にもPaperMCのexampleはあります.マジで要らないね.
と思ったけど僕の方がイメージサイズが小さいな.じゃあ勝ちということで.
ちなみに,マイクラサーバをコンテナ化するにあたって,SpigotとかPaperMCみたいに公式のJava版サーバにパッチを当てまくる系の奴は注意が必要です. docker build時に元の公式サーバのjarファイルを含めた上でイメージをpublicにしてしまうと再配布になってしまいますからね. しかしPaperMCなら安心.Paperclipというものがあります.
Paperclipはマインクラフトサーバのランチャーであると同時に,Paperのパッチをバニラのサーバに適用するシステムです. これは普通に起動するだけでマイクラサーバとして使えるのですが, 初回起動時にバニラのサーバのjarファイルをダウンロードしてきて,それにパッチを当てて起動します. 2回目以降の起動時はパッチ適用後のjarファイルを直接呼び出します.
なので,これを使ってpapermc-dockerを作ったというわけです.
papermc-dockerでは,GitHub ActionsでイメージをビルドしてGitHub Container RegistryとDockerHubにイメージをpushしています.
で,このビルドなんですが,Paperは普通にいいかんじにビルドされて各commit毎のビルド済みバイナリが公開されているので,それを取ってきてイメージに埋め込めばいいだけです.
だけなんですが,そんなつまらないことはしておらず,ソースからビルドしています.
しかも,masterに追従するようにしています.
毎日自動でmasterのPaperをcloneして,更新があったらupdate/paper
ブランチに自動でcommitされるようにはなっているので,適当なタイミングでプルリクを作ってマージしています.
マージはともかくプルリク作るのは自動化したいですね.
papermc-dockerの自動更新
さて,そんなわけでmc.yohane.suではpapermc-dockerを使っているのですが,papermc-dockerでやっているイメージのビルドなどは元々mc.yohane.suでやっていました. これを分離したのは,単にゴチャッとしていて気持ち悪かったのもありますが,イメージの自動更新をいいかんじに,というかRenovateを使ってやりたかったからです.
Renovateはdependencyの更新をするPull Requestを自動で生やしてくれる奴です.
そして,Renovateはdocker-compose
に対応しているので,docker-compose.yml
内で指定されているイメージが更新されたらプルリクを作ってくれます.
これをcompose-cd
と組み合わせるとCI/CDってやつで優勝できるってワケよ.
papermc-docker
分離以前は,
mc.yohane.suでPaperのcommitを更新→イメージのビルド開始→compose-cd
でgit pull
・再起動→イメージのビルド終了→compose-cd
でdocker-compose pull
・再起動
となっていたのが,
papermc-docker
のupdate/paper
をマージ→main
でのイメージビルド→Renovateがdocker-compose.yml
の更新PRを生成→PRをマージ→compose-cd
でgit/docker-compoe pull
・再起動
というかんじになりました.無意味な再起動が減ってうれしいですね.
ちなみに,mc.yohane.su
の.compose-cd
の中身はこんなかんじです.
REPO="https://github.com/sksat/mc.yohane.su" UPDATE_REPO_ONLY=true UPDATE_IMAGE_BY_REPO=true
UPDATE_REPO_ONLY
はイメージの更新をスキップしてリポジトリの更新のみをcompose-cd
が行うオプションで,
UPDATE_IMAGE_BY_REPO
はイメージの更新の責任をリポジトリが負うことにするオプションです.
これらはつまり,イメージのマニフェストを見に行ってdocker-compose pull
することはしないが,
git pull
時にdocker-compose pull
もする,ということです.
こんなことをやってるので,mc.yohane.suのdocker-compose.yml
ではイメージの指定にタグではなくハッシュ値を指定しています.
ちなみに,compose-cd
はログをDiscordに流す機能もあるので,こんなかんじの見た目になります.
papermc-dockerもRenovateで自動更新(2021/09/11追記)
実はRenovate先生は独自フォーマットのファイルの更新もできちゃいます.
ということで,papermc-dockerもRenovateで更新できるのでは?ということに気付きました.
まあ元々はこれできるの知らなくて,自分でGitHub Actions組んでやろうとしていたんですけどね.
書くの忘れてたけどupdate/paper
ブランチなるものはまさにそれで,
GitHub Actionsのcronで毎晩update/paper
ブランチの,env
ファイル(Paperのcommit hashが入っている)を更新していました.
なので定期的にupdate/paper
をマージすればよかった,というわけです.
本当はこういうPull Requestも自動で出したかったんですけど,
GitHub Actionsからsecrets.GITHUB_TOKEN
を使ってプルリクを出してしまうとCIが走ってくれないという罠があったりするんですよね.
まあやってなかったのは面倒だったというだけなんですが.
workflowはこんなかんじ. github.com
で,これもRenovateでできた,というわけです.Renovateすげ〜.
実際のrenovate.json
はこんなかんじになりました.
{ "extends": [ "config:base" ], "regexManagers": [ { "fileMatch": [".env"], "matchStrings": ["depName=(?<depName>.*?)?\\s.*?_COMMIT=(?<currentValue>)(?<currentDigest>.*?)\\s"], "versioningTemplate": "git", "datasourceTemplate": "git-refs" } ] }
papermc-docker
はPaperMC/Paperの(めちゃくちゃ更新される)masterブランチに追従しようとしている狂ったイメージなので,datasourceにはgit-refs
を使っています.
そもそもPaperはタグ打ちもリリースもしてくれないのでこうするしかないんですがね!(公式サイトにcommit毎のビルド済みバイナリあるんだからそれでいいんだよなあ)
で,無事にRenovate先生がプルリクを出してくれるようになりました.やったぜ.
プラグインの自動更新(2021/09/11追記)
みなさんマイクラサーバ運用でプラグインアップデート自動化してますか?僕はしてる.
Renovateで独自フォーマットのファイルも更新できることが分かりました. あっこれプラグインの更新もできますねえ.できるならやるしかない.
実は元々プラグインってやつの管理どうしたらいいかな〜とは思っていたんですよね. マイクラのプラグインってやつはフォーラムにバイナリがポン置きされてるとかいうマジで狂ったWindowsとかいうクソボケOSみたいな文化が蔓延っているらしいんですが, 幸いにも僕が欲しいものはGitHub上で開発されてタグ打ちされてリリースも出されているようなやつだったので,なんとかなりました*2.
これはどうやって実装したかというと,特定バージョンのプラグインをダウンロードして配置するシェルスクリプトを書き,
これをcompose-cd
のカスタムスクリプト機能で呼び出す,という方法です.
compose-cd
には再起動前ないし後にカスタムスクリプトを実行する機能がある*3ので,
このスクリプトを再起動前に実行します.
プラグインのデプロイスクリプトのバージョン指定はこんなかんじ.
PLUGINS=( # datasource=github-releases versioning=docker "PlayPro/CoreProtect v20.1 CoreProtect" # datasource=github-releases "DiscordSRV/DiscordSRV v1.24.0 DiscordSRV-Build" # datasource=github-releases "sladkoff/minecraft-prometheus-exporter v2.4.2 minecraft-prometheus-exporter" )
あとはrenovate.json
からこんなかんじで拾って上げれば大丈夫です.
mc.yohane.su/renovate.json at 43c4f50829a6153ccfbddef1bd9e9a0218f5da04 · sksat/mc.yohane.su · GitHub
{ "extends": [ "config:base" ], "regexManagers": [ { "fileMatch": ["deploy-plugin.sh"], "matchStrings": [ "datasource=(?<datasource>.*?)( versioning=(?<versioning>.*?))?\n \"(?<depName>.*?) (?<currentValue>.*) .*?\"\n" ], "versioningTemplate": "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}" } ] }
というわけで,こんなかんじのプルリクが生えてくれるようになりました.やったね!
ちなみに,CoreProtectのバージョニングがdocker
なのはバージョニングがクソボケだからです.
クソボケなのでクソボケな正規表現をversioning=regex:~~~
みたいに書いたろ,と思ったんですがそれは無理でした.
無理だったんですが,みなさんだいすきなドッカーイメージってやつも頻繁によくクソボケなバージョニングがされているので,
docker
を指定してお茶を濁しています.
問題はなかった.
あと,プラグインを色々入れるなら入れた後もちゃんと起動するか確認したいですよね. というわけでCIの出番です.
まあこれはpapermc-docker
で既にやってたんでそれをパクってきただけですね*4.