k8sでNetHack鯖をたてて、discordにリザルトを通知する
Table of Contents
k8sでNethack3.6.7のPublic Serverをたてて、discordにリザルトを通知するアプリをGoで作る
できたもの

構成
Nethack Public Serverコンテナと、Goで作った通知用Appコンテナをサイドカーとして、それぞれ同一のvolumeを参照するようにk8s上でデプロイする。
nethack-server
下記サイトの手順を参照しながらDockerfileを書くだけ

Installing NH 3.6.6 - Nethack Server
/home/nethack
がchrootディレクトリとなるので、通知用App側とはこのパスを共有する。
nethack-notifier
このアプリがやっているのは、inotifyのGolangラッパーであるfsnotifyを利用し、以下のディレクトリ・ファイルを監視して
/home/nethack/nh367/record
レコードファイル/home/nethack/dgldir/inprogress-nh367
進行中のゲームのttyrecファイル
変更を検知したらファイルの中身やファイル名を読んで、適当にフォーマットしてdiscordのwebhookを叩くということ。
問題その1: “no such file or directory"と言われる
ローカルでビルドと動作確認に成功していたのに、なぜかdockerだとアプリのバイナリがないと言われる。
以下はそのDockerfile。マルチステージビルドをする際は、build時にCGO_ENABLED=0
を指定してやらないといけないらしい。

Docker with GoのMulti Stage Buildで"no such file or directory"と出てハマった話 - @teitei_tk Blog
問題その2: NFS上の一時ファイルのせいでプレイ中のユーザ名が取れない
{ユーザ名}:{タイムスタンプ}.ttyrec
のようなファイルから、ゲームをプレイ中のユーザ名を取得する実装にしていたが、実際k8sにデプロイしてみると、以下のようなログが確認できた。
どうやらファイルの削除時に.nfs{数字24桁}
という一時ファイルとして認識されているようで、これはv4.1以前のNFSでは回避できない挙動らしい。
仕方がないので、アプリ側でユーザ名のリストを持ち、ファイルが削除されたときにそのリストと突合することで削除されたファイルを識別する愚直な実装に変更した。

.nfsXXXX files appearing, what are those?
k8s deploy
nethack-server
はコンテナがUpになる前に/home/nethack
に必要なファイルを作成する。
これをnethack-notifier
側に共有するために永続ストレージ(emptyDirでも可)をマウントするとなると、当然マウントはこれらの処理が終わった後にされるので、事前に永続ストレージ側に必要なファイル群が入っている必要がある。卵が先か鶏が先か、ではないが、このような場合どうするのがベストプラクティスなんだろうか。
結局initコンテナでNFSにファイルが生成されていなければ生成し、/home/nethack
としてマウントし直すという泥臭いやり方になってしまったが、動いているのでまあいいか。
おわりに
今回はdistroless+goシングルバイナリ+サイドカーパターンをお試ししてみたかったので、思いつきでやったみたいなところはある。