game
docker
kubernetes
Factorio
SpeeeDay 13

KubernetesでFactorio (Docker + Kubernetesで作るマルチプレイサーバの構築手順)

この記事は

Speee Advent Calendar 2017の13日目です! ぼちぼち折り返しの時期ですね。
よければ購読お願いします。

前日は、@yt-tanabeSpeeeKaigiのつくりかた(とエンジニアの文化について)でした。

今日は@Tei1988によるKubernetesでFactorioです。
この記事を読めば、Factorioマルチプレイ用サーバをKubernetesで構築することができます。

そもそもFactorioって?

とある星に不時着したプレイヤーが、その星の資源を使ってロケットを作り、宇宙に飛ばして、助けを待つ、というストーリーのゲームです。

公式サイト, Steam, トレーラー

「資源を使ってロケットを作る」のですが、周りには鉄鉱石や銅鉱石といった原料しかないので、それらを精錬したり組み合わせたりして、歯車や電子基板といった、より複雑な部品を作っていきます。

ロケットを作るのに必要な部品は、大量に必要かつ多岐にわたるため手作業で作っていくのは不可能です。
そこで、工場を作って資材をベルトコンベアに流して、生産の自動化を行います。

工場をたくさん作って生産を自動化し、材料がどんどん組み合わされていってベルトコンベア上を流れていくのを眺めていると、謎の達成感を味わえます。

工場の稼働率を向上させたり、別の工場のために、工場の配置や資源の流し方を変えるのですが、プログラミングのリファクタリングに通じるものがあり、普段コードを触っている人はもちろん、触っていない人でもリファクタリングの重要性を認識できる良いゲームだと思います。

ことの始まり

Factorioはマルチプレイが可能です。みんなで協力して、ロケットを飛ばします。
社内でも既に何人か持っている人がおり、マルチプレイをしよう、という話になりました。
さて、マルチプレイができるようにしなければなりません。
せっかくなので、手順などを残すついでに、記事を書くことにしました。

マルチプレイにも方法が二種類あります。

  1. 誰か一人がサーバ役になって、他の人がそこへ接続する

  2. ゲームサーバを構築する

1の方法は、構成が手軽なのですが、サーバ役の人は毎回必ず参加しなければならないなど、負担が大きそうでした。

そこで、今回は2の方法を選択しました。

ゲームサーバを構築する

Factorioは、グラフィック機能の無い、headlessなゲームサーバを用意してくれています。

さらに、立ち上げるだけでOKなDockerイメージも有志によってdtandersen/factorioに置かれています。
なので、今回はこのDockerイメージを利用させてもらうことにしました。

次は、どこにデプロイするかです。
最近、Speee DEVELOPER BLOGI ❤️ Kubernetesという記事を書くぐらいKubernetesを気に入っているので、GKEにデプロイすることにしました。

以下に、デプロイまでの手順を記しておきます。

  1. 準備
    手順の中でgcloudkubectlといったコマンドを使います。
    以下などを参考に、インストールしてください。

    • gcloud
      公式インストールガイドを参考に、インストールしてください。:bow::bow::bow:
      MacOSの場合、brew cask install google-cloud-sdkでもインストールできるようです。
    • kubectl
      kubectlはKubernetesの管理ツールで、基本的にはこのツールを用いて作業を行います。
      MacOSの場合
      Homebrewを使って以下のコマンドで導入可能です。 brew install kubectl
      WindowsやLinuxの場合
      すみません。公式インストールガイドなどを参考にしてください:bow::bow:
  2. GCPのコンソールからプロジェクトを作成します。プロジェクト名やプロジェクトIDは任意のもので大丈夫です。
    公式ガイド
    スクリーンショット 2017-12-11 18.14.09.png

    作成を押すと、以下のような画面に遷移します。
    スクリーンショット 2017-12-11 18.15.39.png

  3. Kubernetes Engineを有効にします。
    左のメニューから、Kubernetes Engine > Kubernetes クラスタ を選びます。

    スクリーンショット 2017-12-11 18.16.15.png

    すると、 有効化するボタンが表示されるので、押します。
    押すと、以下のような表示に切り替わります。
    スクリーンショット 2017-12-12 18.12.21.png

    この画面で少し待つと、有効化が完了します。

  4. クラスタを作ります。

    有効化が完了したのち、再び左のメニューから、Kubernetes Engine > Kubernetes クラスタ を選びます。

    スクリーンショット 2017-12-12 18.15.51.png

    すると、このようが表示になるので、クラスタを作成してください。

    スクリーンショット 2017-12-12 18.19.21.png

    立ち上げるノードのスペックですがg1-smallの1台で大丈夫です。
    ディスク容量を10GBにすると、お安くなります。
    プリエンプティブなノードにするとさらにお安くなります。

    以下の図のような感じで入力できていればOKです。
    スクリーンショット 2017-12-12 18.29.26.png

    その後、緑のチェックが入れば、クラスタの作成は完了です。
    スクリーンショット 2017-12-12 18.56.30.png

  5. GCEでpd-factorioという名前で空のディスクを作ります。
    セーブデータを永続化するために使います。

    左のメニューから、Compute Engine > ディスクを選択します。

    スクリーンショット 2017-12-12 19.19.22.png

    以下のような表示になるので、ディスクを作成をクリックします。

    スクリーンショット 2017-12-12 19.19.37.png

    以下の図のように名前にpd-factorioと入力し、他も同様に選択して、作成を押します。
    スクリーンショット 2017-12-12 19.21.02.png

    以下のようにpd-factorioが画面に表示されれば、ディスクの作成は完了です。
    スクリーンショット 2017-12-12 19.21.19.png

  6. 静的アドレスを確保します。
    外からFactorioのマルチプレイ用サーバに接続する際に使用します。

    左のメニューから、VPC ネットワーク > 外部 IP アドレスを選択します。

    スクリーンショット 2017-12-12 19.38.16.png

    静的アドレスの予約をクリックし、以下のように入力します。

    スクリーンショット 2017-12-12 19.38.47.png

    以下のように、外部IPアドレスにIPが新しく追加されれば、静的アドレスの確保も完了です。
    追加したIPは、後で確保した静的IPとして使います。

    スクリーンショット 2017-12-12 19.39.03.png

  7. gcloudがGCPに接続できるようにします。
    ターミナルに以下を入力してください。
    gcloud auth application-default login
    ブラウザで、Googleアカウントにログインを求められるので、プロジェクトを作ったアカウントでログインしてください。

    ログインできれば、gcloudの設定は完了です。

  8. kubernetesがGKEに接続できるように認証情報を取得します。
    左のメニューから、 Kubernetes Engine > Kubernetes クラスタを選択します。

    先ほど作成したクラスタの横の接続をクリックします。

    出てきたコマンドをコピーし、ターミナルで実行します。

    これで、KubernetesがGKEに接続できるようになりました。

  9. いよいよデプロイです。
    Kubernetesで使うためのサンプル設定ファイルを用意しました。
    以下のコマンドを実行します。

    1. wget https://raw.githubusercontent.com/Tei1988/k8s-factorio/vanilla/k8s.yml.sample
    2. sed 's/<IP>/確保した静的IP/g' k8s.yml.sample > k8s.yml
    3. kubectl create -f k8s.yml --save-config

    これで、Factorioが立ち上がります。
    ※初回の立ち上げはDockerイメージのpullが必要になるため、少し待つ必要があります。

さて、実際に遊べるか、実際にFactorioを起動してマルチプレイをしてみましょう。
Factorioを起動したあと、メニューから マルチプレイ > サーバに接続 を選び、確保した静的IP:34197を入力します。
どうでしょう?ログインできました?

ちなみに、もしFactorioサーバが何かしらの原因で落ちてしまっても、Kubernetesの自動デプロイでPodを復帰させてくれるので、追加でなにか作業が発生することは基本的にありません。

おわりに

ただいま絶賛、Modsを入れて7人程でワイワイ遊んでいるのですが、鉱石の種類が増えたり必要な生産物の種類が増えて、工場がとても複雑になり、リファクタリングする楽しさも増しています。
ボイスチャットをしながらプレイすると捗ります。

IPを教えるだけですぐに入ってもらえるので、マルチプレイの勧誘もとても楽です。

では、良い工場ライフを。

明日は、「SlackStatus とGoogleCalendarの同期をしてみた」や、「非エンジニアが社内wikiにヒートマップを実装してみた話」などの記事を書いた、新しい技術に目がない @nagayamacho3サンタクロースの正体を暴くようです。お楽しみに:tada:

おまけ

Factorioの醍醐味の一つであるModsを導入してみます。

Modsを導入する

Factorioは、Modsを導入することでさらに楽しむことができます。

マルチプレイをしていたメンバーも、通常のFactorioでは満足できなくなっており、Modsを導入することにしました。

クライアント側は、ゲーム画面からModsの導入が可能です。入れたいModsをポチポチしていけば、導入が完了します。
一方でサーバ側は、特定のフォルダに直接Modsを入れていく方法になります。

Modsは、クライアント側のものと共通なので、クライアント側に入れたModsをコピーするのが手っ取り早いです。

今回は、DockerイメージにModsを追加することにしました。
また、設定ファイルもConfigMapではなく、イメージに含めることにしました。

Mods入りのDockerイメージの作成手順は以下です。

  1. Tei1988/k8s-factorioのmodsブランチをcloneする。

  2. files/mods以下に、Modsをコピーする。mod-list.jsonも忘れずに。

  3. 必要であれば、files/config/以下の設定も修正する。

  4. docker build -t gcr.io/プロジェクトID/factorio:mods .

  5. docker push gcr.io/プロジェクトID/factorio:mods

これで、Dockerイメージの作成は完了です。

次に、Kubernetesにデプロイする前に、セーブデータを削除します。
もし、前のセーブで続きをやりたい場合は、ローカルにセーブをコピーしたり、pd-factorioディスクをコピーするなどでバックアップをとってください。
pd-factorioディスクを作り直せば、セーブデータは消えます。

最後に、作ったDockerイメージをKubernetesにデプロイします。
以下のコマンドで、デプロイできると思います。

  1. cat k8s.yml.sample | sed 's/<IP>/確保した静的IP/g' | sed 's/<ProjectName>/プロジェクトID/g' > k8s.yml
  2. kubectl apply -f k8s.yml