systemd + screen で Minecraft サーバを管理する

最近は暇な時間に tskserver という Minecraft サーバの環境をほじくり回しています. その一環で Minecraft サーバを systemd で管理するようにしました.

systemd で管理することのメリットとしては,systemd は頭がよいので,なんか良い感じに依存関係を考慮しながら起動順序を調整などをしてくれることがあげられます.

Minecraft のサーバは少し曲者で,Nginx や他のデーモンのようにシグナルを受け取ってくれるわけではないので,systemd の Unit file を書くときはサーバを安全に終了する (stop コマンドを走らせる) 方法を考えつつやる必要があります. この記事では,GNU screen を使って systemd から Minecraft サーバにコマンドを投げられるようにして安全な終了を実現します.

環境

OS は Ubuntu Server 16.04.4 LTS,Minecraft サーバの実装は Spigot です.たぶん公式サーバでも起動時の引数に nogui をつければいけるはず.

サーバのディレクトリはひとまず /home/k/tskserver に置いていてユーザ k で動かしています.

この記事でできること

  • Minecraft サーバを systemd で管理する
  • Minecraft サーバを 30 秒のカウントダウンとともに安全に終了できる
  • システム起動時に自動的に Minecraft サーバを起動できる
  • システム終了時でもネットワーク接続を維持しつつカウントダウンを待ち,Minecraft サーバを安全に終了できる

f:id:k5342:20180423023744p:plain

Minecraft サーバにコマンドを投げる

ここで GNU screen を使います. screen は detached なセッションに対して screen -S <session_name> -X stuff 'hogehoge' とコマンドを打つと session_name なセッションに対して hogehoge を標準入力 (?) として投げられるので便利です. 今回はこれを終了時に使います.具体的には終了時に save-allstop コマンドを投げるようにします.

systemd の Unit file を書く

おもむろに /etc/systemd/system/minecraft.service を作成し,以下を書きます.

[Unit]
Description=Minecraft Server
After=network-online.target

[Service]
User=k
Group=k
TimeoutStopSec=120
WorkingDirectory=/home/k/tskserver
ExecStart=/usr/bin/screen -Dm -S tskserver /usr/bin/java -Xms512M -Xmx5G -jar /home/k/tskserver/spigot.jar
ExecStop=/usr/bin/screen -S tskserver -X stuff "say tskserver will be shutdown in 30s\015"
ExecStop=/bin/sleep 10
ExecStop=/usr/bin/screen -S tskserver -X stuff "say tskserver will be shutdown in 20s\015"
ExecStop=/bin/sleep 10
ExecStop=/usr/bin/screen -S tskserver -X stuff "say tskserver will be shutdown in 10s\015"
ExecStop=/bin/sleep 5
ExecStop=/usr/bin/screen -S tskserver -X stuff "say tskserver will be shutdown in 5s\015"
ExecStop=/bin/sleep 5
ExecStop=/usr/bin/screen -S tskserver -X stuff "save-all\015"
ExecStop=/usr/bin/screen -S tskserver -X stuff "stop\015"
Restart=always

[Install]
WantedBy=multi-user.target

[Unit]

After=network-online.target

ネットワークがオンラインになってからこの Unit を起動するようにします. 同様にシステム終了時はこの Unit が終了してからネットワークが落ちるように順序を考慮してくれるようになります.

tskserver は fluentd を使ってログを収集しているのでこれに加えて After=fluentd.service をつけていて,Minecraft サーバが終了するまで fluentd も落ちないようになっています.

[Service]

TimeoutStopSec=

終了時のタイムアウト時間を秒単位で指定します. systemd は終了するぞってなったときに対象プロセスが終了するのを待ちますが,一定時間で SIGTERM が投げられて強制終了されてしまいます. デフォルトは 90 秒なので,これよりも長くカウントダウンを設定したかったり,プラグインを入れすぎて stop に時間がかかりすぎたりする場合は設定しておきましょう.

ExecStop=

終了時に実行するコマンドです.複数指定すると上から順に実行します. screen -S tskserver -X stuff "say hello\015" は,セッション名 tskserver のセッションに対して "say hello" と改行コード (0x0A, \015)を入力として送ります.改行コードなしだったり 8 進数以外だとコマンドが実行されないので注意.

Restart=always

サーバが勝手に終了した場合は常に自動で再起動するようにします.

バックアップなどをいい感じにしたい.あと冗長化構成組んでみたいけど需要あるんだろうか...?

tskserver はこれからも末永く運営していくつもりで,誰でも入れるサーバなので暇なひとは遊びに来てくれ~.[話を聞きに行く↗]

Let's Encrypt の証明書をワイルドカードなやつにして自動更新できるようにした

2018 年 3 月,ついに Let's Encrypt がワイルドカードな証明書の発行に対応したので,所有するサーバの証明書をワイルドカードなものにリプレイスしてみました. 今まではサブジェクト代替名でサブドメインを列挙するスタイルをとっていて地味に面倒だったのと,証明書を読むと internal 向けのドメインがバレちゃうというのがあり,ワイルドカードな証明書に置き換えることでこれらを解決することができました.

この記事のゴール

この記事では以下について述べます.今回作業する環境は ArchLinux ですが,certbotcertbot の cloudflare プラグインのパッケージさえあれば他の OS も動くと思います.

  1. ksswre.net*.ksswre.net の証明書を manual モードで取得する
  2. 上で取得した証明書を自動更新できるようにする (自動更新のため CloudFlare の Free プランを併用)

試行錯誤をしながら作業を行ったので 1 時間弱くらいかかりましたが,最終的にはワイルドカードな証明書が certbot renew で自動更新できるようになりました.

前提

certbot を使います.ArchLinux なので pacman -S certbot すると certbot が降ってきた気がします.なければいれましょう.

ワイルドカードな証明書を取得する

おもむろに以下のコマンドをうちます.ワイルドカード証明書を取得するには dns-01 認証が必要なので,manual モードにして認証形式を手動で設定します. また,2018 年 4 月現在でワイルドカード証明書を発行するには ACME v2 が必要なので --server オプションを付けて認証サーバを変更します.メールアドレスや IP アドレスを記録するが良いかなどとインタラクティブな操作で確認されるので Y か N を打って答えます.

# certbot certonly --manual -d '*.ksswre.net' -d ksswre.net -m 'mail@example.com' --agree-tos --preferred-challenges dns-01 --server https://acme-v02.api.letsencrypt.org/directory

しばらくすると以下のような画面がでます.これは dns-01 認証のためのもので,ドメインを本当に所有しているかどうか DNS の TXT にワンタイムトークンを追加することで認証します.

-------------------------------------------------------------------------------
Please deploy a DNS TXT record under the name
_acme-challenge.ksswre.net with the following value:

XXXXXXXXXXXXXXXXXXXXXXXXXXXX

Before continuing, verify the record is deployed.
-------------------------------------------------------------------------------
Press Enter to Continue

ドメイン管理画面を開き (以下は StarDomain の例),ワンタイムトークンを DNS の TXT レコードに追記します. f:id:k5342:20180406230657p:plain

追記したら Enter を押します.うまいこといくと以下のような表示があるはずです.

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/ksswre.net-0002/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/ksswre.net-0002/privkey.pem
   Your cert will expire on 2018-07-05. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

これより,サーバ証明書などが取得できたので nginx.conf を編集し,証明書を新しいものに切り替えます.nginx.conf の抜粋.

ssl_certificate /etc/letsencrypt/live/ksswre.net-0002/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ksswre.net-0002/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/ksswre.net-0002/chain.pem;

Nginx を再起動すると,うまいこといってるのがわかります.PostfixDovecot の設定なども同様にお忘れなくやりましょう. f:id:k5342:20180406232900p:plain

このままでは自動更新できない

証明書が更新できるか確認しましょう.--force-renewal オプションを付けて強制更新モードにして一旦走らせてみると,エラーでコケることがわかります.

# certbot renew --force-renewal
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/ksswre.net-0002.conf
-------------------------------------------------------------------------------
Could not choose appropriate plugin: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.',)
Attempting to renew cert (ksswre.net-0002) from /etc/letsencrypt/renewal/ksswre.net-0002.conf produced an unexpected error: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.',). Skipping.
All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/ksswre.net-0002/fullchain.pem (failure)

manual モードの場合はインタラクティブな操作が必要で certbot renew で更新するには --manual-auth-hook オプションで認証用ホックを自前で用意する必要があるそうです.ホックを自前で書くのは面倒なので調べてみたら,certbot の CloudFlare プラグインを使う方法があるそうです.以下のサイトが有用でしたので参考にさせていただきました. a244.hateblo.jp

CloudFlare プラグインを併用して自動更新する

CloudFlare プラグインを使うためには,既存のネームサーバを CloudFlare に変更する必要がありますが,CloudFlare に変更すると DNS TXT レコードの書き換えと dns-01 認証をプラグインが全て自動で行ってくれます.便利です.CloudFlare は個人サイトの規模であれば Free プランで十分なので,無料で運用できます.

このプラグインを使うには,ArchLinux では新しいパッケージをインストールする必要があるのでインストールしておきます.

# pacman -S certbot-cloudflare

上のサイトを参考にしながら CloudFlare でアカウントをとり,ネームサーバを CloudFlare に変更し,管理パネルの Get your API key から Global API Key を取得し,certbot に設定しておきます.CloudFlare の CDN (Proxy) は今回は必要ないのでオフにしておき,ネームサーバだけ有効にしておきます.

設定ができたら,certbot で上のセクションで取得した証明書をリニューアルします.以下のコマンドを走らせます.

# certbot certonly --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini -d ksswre.net -d '*.ksswre.net' --server https://acme-v02.api.letsencrypt.org/directory

プラグインとして CloudFlare 用のものが選択されていることがわかります.

Plugins selected: Authenticator dns-cloudflare, Installer None
Cert not yet due for renewal

しばらくすると,既に同じドメインで取得した証明書をリプレイスして良いか聞いてくるので,そうしてもらいます. そのあと少し放置すると,CloudFlare のネームサーバを通して dns-01 認証を行われ,正常に証明書が更新されます. この間,上のセクションで使用した manual モードのようにインタラクティブな操作は発生せず,全て自動で進みます.便利.

You have an existing certificate that has exactly the same domains or certificate name you requested and isn't close to expiry.
(ref: /etc/letsencrypt/renewal/ksswre.net-0002.conf)

What would you like to do?
-------------------------------------------------------------------------------
1: Keep the existing certificate for now
2: Renew & replace the cert (limit ~5 per 7 days)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for ksswre.net
dns-01 challenge for ksswre.net
Waiting 10 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/ksswre.net-0002/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/ksswre.net-0002/privkey.pem
   Your cert will expire on 2018-07-05. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

これで CloudFlare プラグインを使ってワイルドカードな証明書が自動更新できるようになりました.

# certbot renew --force-renewal
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/ksswre.net-0002.conf
-------------------------------------------------------------------------------
Plugins selected: Authenticator dns-cloudflare, Installer None
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for ksswre.net
dns-01 challenge for ksswre.net
Waiting 10 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges

-------------------------------------------------------------------------------
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/ksswre.net-0002/fullchain.pem
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/ksswre.net-0002/fullchain.pem (success)
-------------------------------------------------------------------------------

この後は,certbot renew を定期的に投げれば良いので,crontab や systemd-timer を使って定期的にコマンドが走るようにします.これで自動更新は完成です.

キャプチャボード Blackmagic Design Intensity Shuttle を買った

先日 HDMI キャプチャボード Intensity Shuttle For USB 3.0 を購入しました.

このキャプチャボードは 2012 年に発売されたもので少々古いですが,HDMIRCA 端子 や S 端子とそれらのパススルーに対応しているのが特徴です. またキャプチャ用ソフトウェアが Windows だけでなく Mac にも対応していて,Mac 対応で HDMI キャプチャできる数少ない商品のうちの一つと思います. 対応コーデックなどの詳しい仕様はこのページを参照してください.

できたこと

  • HDMI 出力の PC 画面のキャプチャ (1080p30, 1080i, 720p, etc.)
    • WindowsDebian 搭載の PC は接続するだけで認識しました
    • Mac はシステム環境設定で出力設定を手動で変更する必要があります
  • Nintendo Switch の画面キャプチャとパススルー (720p)
  • ゲームキューブの画面・音声キャプチャと HDMI 出力
  • キャプチャした映像を配信ソフトウェア OBS で配信・録画する

できないこと

  • 1080p60 でキャプチャ (Nintendo Switch で 1080p キャプチャできないのはこのせい)
  • VGA 出力の PC 画面のキャプチャ
  • 電源供給だけでパススルーする
    • 例えば HDMI 入力するだけで HDMI パススルーされるわけではない
    • 映像入力した状態で PC 側でキャプチャ画面を開いてる間じゃないとパススルーされない
  • キャプチャボードを複数ソフトウェアから利用する
    • 付属のキャプチャソフトで録画しながら OBS で配信できない

未確認

使い方の流れ

キャプチャを行う PC の準備

公式サイトからドライバとキャプチャ用ソフトウェアをダウンロードします. 少々分かりにくいですが,ソフトウェアアップデート欄にある Desktop Video をダウンロードすれば良いです. ダウンロードしたらセットアップを起動し,ガイドに従ってインストールを行います.

インストールが終わったら,Intensity Shuttle を PC に接続し,キャプチャしたい映像ソースを接続します. 映像ソースによっては出力設定を変更する必要があるので,それについては以下に個別でまとめるので参照してください. 接続後は Desktop Video Setup というアプリケーションを起動し,Intensity Shuttle のどのポートから信号が入力されるかを選択します. f:id:k5342:20180223152830p:plain

その後 Blackmagic Media Express というアプリケーションを開き,正常にキャプチャできているか確認します. 正常に設定ができていて,キャプチャボードが映像ソースを認識できれば,このアプリケーションを開いた段階でやっとパススルーが使えるようになります.

他のパソコン画面のキャプチャ

Windows の場合

WindowsHDMI 出力を Intensity Shuttle の HDMI IN 端子に接続するだけで OK で,勝手に解像度を認識してくれるようです.

Mac の場合

Mac はシステム環境設定で出力設定を変更する必要があります.

  1. HDMI 出力を Intensity Shuttle の HDMI IN 端子に接続します
  2. システム環境設定→ディスプレイの設定を開きます
  3. 解像度の設定で BMD HDMI を選択します
  4. ラジオボタンを ディスプレイに最適 から 変更 に変更し,出力解像度を 1080i か 720p に変更します

f:id:k5342:20180223150936p:plain

Nintendo Switch のキャプチャ

Nintendo Switch の画面をキャプチャする場合は,出力解像度の設定を変更したうえで接続します. 設定 → テレビ出力 → テレビの解像度 と移動し,値を 自動 から 720p に変更します.

ゲームキューブのキャプチャ

なんかつなぐだけでできた. 以前どこかの記事で「ゲームキューブは独自の信号拡張を行っていてこのキャプチャボードだとキャプチャできない」ってみた気がしたので代替として IO-DATA の GV-USB2 を使っていましたが,あっさりできたわ.しかし PS2N64 に対応しているかは未確認です.

Demo

スプラトゥーン 2 のプレイ風景を OBS で録画してみました.

youtu.be

不満点

キャプチャボードが時折クラッシュ?というか,キャプチャされる映像が真っ黒になることがあります. 他にも,付属のソフトウェアでキャプチャボードが認識されなくなることがあります.

また,その状態で USB ケーブルを抜き差しすると「前回のドライバのインスタンスがメモリに残っている」旨のエラー (code 38) が発生し,上記の症状が改善されないことがあります. これを解決する方法はキャプチャを行う PC を再起動するしかないとされていて,ちょっと不便です. これらは Windows 上でキャプチャした場合に発生したのですが,もしかすると Mac だと違うかもしれません.

ストアからインストールした Ubuntu で LANG=ja_JP.UTF-8 を使う

最近 ThinkPad を購入しWindows で遊んでいます.

Fall Creators Update (Build 16215) 以降は Windows Store から Ubuntu がインストールできるようになったそうです.超便利ですよね.

docs.microsoft.com

www.microsoft.com

それで,以下のガイドに従ってストアからインストールして dotfiles を流し込んだところ,日本語が化けました. 右が WSL のターミナル,左が MSYS2 のターミナルで Windows ファイルシステムの同一ディレクトリを表示していて,日本語文字が ??? に化けていることがわかります. f:id:k5342:20180128211620p:plain

2018-01-28 23:34 追記

dotfiles 中の .zshrc が LANG=ja_JP.UTF-8 と指定していたようで,システム上には ja_JP.UTF-8ロケールが有効化されてないため,これが文字化けの原因となっていました.

LANG=ja_JP.UTF-8 を使えるようにしてデフォルト設定にする

  1. 適当なエディタで /etc/locale.gen を開く
  2. ja_JP.UTF-8コメントアウトされているので # を削除して上書き保存
  3. sudo locale-gen を実行
  4. 適当なエディタで /etc/locale.conf を開き (無ければ作成) 以下を書いて保存し,再起動
  LANG=ja_JP.UTF-8

これで僕の環境では日本語が表示できるようになりました. f:id:k5342:20180128213145p:plain

しかし完全に文字化けが治ってるわけではないようで,どうやらⅠとⅡが・に化けているようでした.なぜだろう. ひとまず日本語は表示できるようになりましたが,この原因はまた調べます.

Blogger からはてなブログに移行した

Blogger からはてなブログに移行しました.旧 URL は http://blog.ksswre.net です.

遠い昔に登録していた記憶はあったのですが,どうやら 6 年以上放置していたようです.こっちは Markdown で書けて便利ですね. いろいろ使い倒してみたいと思います. f:id:k5342:20180128210734p:plain

Blogger からの記事の移行は,google-blog-converters-appengine というツールで BloggerXML 形式から MovableType 形式に変換する必要があるようなので,そのうちやります.

Google Code Archive - Long-term storage for Google Code Project Hosting.