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

# この記事は
[Speee Advent Calendar 2017](https://qiita.com/advent-calendar/2017/speee)の13日目です！ ぼちぼち折り返しの時期ですね。
よければ購読お願いします。

前日は、@yt-tanabe の [SpeeeKaigiのつくりかた（とエンジニアの文化について）](http://tech.speee.jp/entry/2017/12/12/162845)でした。

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

# そもそもFactorioって？

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

[公式サイト](https://www.factorio.com/), [Steam](http://store.steampowered.com/app/427520/Factorio/?l=japanese), [トレーラー](https://www.youtube.com/embed/DR01YdFtWFI)

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

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

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

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

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

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

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

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

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

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

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

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

次は、どこにデプロイするかです。
最近、[Speee DEVELOPER BLOG](http://tech.speee.jp/)に[I ❤️ Kubernetes](http://tech.speee.jp/entry/2017/12/08/114939)という記事を書くぐらいKubernetesを気に入っているので、GKEにデプロイすることにしました。

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

1. 準備
  手順の中で`gcloud`や`kubectl`といったコマンドを使います。
  以下などを参考に、インストールしてください。
   - `gcloud`  
      [公式インストールガイド](https://cloud.google.com/sdk/downloads?hl=ja)を参考に、インストールしてください。:bow::bow::bow:  
    MacOSの場合、`brew cask install google-cloud-sdk`でもインストールできるようです。
   - `kubectl`  
    kubectlはKubernetesの管理ツールで、基本的にはこのツールを用いて作業を行います。  
    **MacOSの場合**  
    Homebrewを使って以下のコマンドで導入可能です。
    `brew install kubectl`  
    **WindowsやLinuxの場合**  
    すみません。[公式インストールガイド](https://kubernetes.io/docs/tasks/tools/install-kubectl/)などを参考にしてください:bow::bow:

1. GCPのコンソールからプロジェクトを作成します。プロジェクト名やプロジェクトIDは任意のもので大丈夫です。
    [公式ガイド](https://cloud.google.com/resource-manager/docs/creating-managing-projects?hl=ja)
    ![スクリーンショット 2017-12-11 18.14.09.png](https://qiita-image-store.s3.amazonaws.com/0/77225/d2356bdb-f642-d2ed-b001-8040286f49a4.png)
 
    作成を押すと、以下のような画面に遷移します。
    ![スクリーンショット 2017-12-11 18.15.39.png](https://qiita-image-store.s3.amazonaws.com/0/77225/5885641b-9a86-af5f-e1c2-64ad782a6704.png)

1. Kubernetes Engineを有効にします。
    左のメニューから、**Kubernetes Engine > Kubernetes クラスタ** を選びます。
  
    ![スクリーンショット 2017-12-11 18.16.15.png](https://qiita-image-store.s3.amazonaws.com/0/77225/ec1d1e8c-5abd-a2e9-1224-e9e849dff165.png)
    
    すると、  **有効化**するボタンが表示されるので、押します。
    押すと、以下のような表示に切り替わります。
    ![スクリーンショット 2017-12-12 18.12.21.png](https://qiita-image-store.s3.amazonaws.com/0/77225/06c9dcd8-c561-b62e-d5fe-796858ce571c.png)

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

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

    
    ![スクリーンショット 2017-12-12 18.15.51.png](https://qiita-image-store.s3.amazonaws.com/0/77225/301c004e-ae5f-0681-62f6-5171fb012eae.png)

    すると、このようが表示になるので、**クラスタを作成**してください。
    
    ![スクリーンショット 2017-12-12 18.19.21.png](https://qiita-image-store.s3.amazonaws.com/0/77225/83a81939-e871-f427-cdca-f8fe9709738b.png)
    
    立ち上げるノードのスペックですがg1-smallの1台で大丈夫です。
    ディスク容量を10GBにすると、お安くなります。
    プリエンプティブなノードにするとさらにお安くなります。
    
    以下の図のような感じで入力できていればOKです。
    ![スクリーンショット 2017-12-12 18.29.26.png](https://qiita-image-store.s3.amazonaws.com/0/77225/0f065a2c-d731-d717-4ad1-c1b7072ead6b.png)

    その後、緑のチェックが入れば、クラスタの作成は完了です。
    ![スクリーンショット 2017-12-12 18.56.30.png](https://qiita-image-store.s3.amazonaws.com/0/77225/ad537538-18ad-95cd-7b8e-2e600397dfa3.png)

1. GCEでpd-factorioという名前で空のディスクを作ります。
    セーブデータを永続化するために使います。
    
    左のメニューから、**Compute Engine > ディスク**を選択します。
    
    ![スクリーンショット 2017-12-12 19.19.22.png](https://qiita-image-store.s3.amazonaws.com/0/77225/3bcb9e1f-e9bf-f573-9b00-b7febdcbfabc.png)

    以下のような表示になるので、**ディスクを作成**をクリックします。
    
    ![スクリーンショット 2017-12-12 19.19.37.png](https://qiita-image-store.s3.amazonaws.com/0/77225/c3dd8e66-8439-cb0f-4518-33bb8f57b446.png)

    以下の図のように名前に`pd-factorio`と入力し、他も同様に選択して、**作成**を押します。
    ![スクリーンショット 2017-12-12 19.21.02.png](https://qiita-image-store.s3.amazonaws.com/0/77225/9e353801-d5f1-9f73-8660-4f32e3aff051.png)
    
    以下のように`pd-factorio`が画面に表示されれば、ディスクの作成は完了です。
    ![スクリーンショット 2017-12-12 19.21.19.png](https://qiita-image-store.s3.amazonaws.com/0/77225/56635b0f-bd47-7ef8-b59d-326761031f46.png)
 
1. 静的アドレスを確保します。
    外からFactorioのマルチプレイ用サーバに接続する際に使用します。
    
    左のメニューから、**VPC ネットワーク > 外部 IP アドレス**を選択します。
    
    ![スクリーンショット 2017-12-12 19.38.16.png](https://qiita-image-store.s3.amazonaws.com/0/77225/4370d1eb-f03d-5426-fd89-f22da34630bf.png)
    
    **静的アドレスの予約**をクリックし、以下のように入力します。
    
    ![スクリーンショット 2017-12-12 19.38.47.png](https://qiita-image-store.s3.amazonaws.com/0/77225/b12b4813-d1e9-f610-a860-b5e651ce8840.png)
    
    以下のように、外部IPアドレスにIPが新しく追加されれば、静的アドレスの確保も完了です。
    追加したIPは、後で**確保した静的IP**として使います。
    
    ![スクリーンショット 2017-12-12 19.39.03.png](https://qiita-image-store.s3.amazonaws.com/0/77225/c9241b3c-fda9-33bb-110e-801b679d2788.png)
    
1. `gcloud`がGCPに接続できるようにします。
    ターミナルに以下を入力してください。
    `gcloud auth application-default login`
    ブラウザで、Googleアカウントにログインを求められるので、プロジェクトを作ったアカウントでログインしてください。

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

1. kubernetesがGKEに接続できるように認証情報を取得します。
    左のメニューから、 **Kubernetes Engine > Kubernetes クラスタ**を選択します。
    
    先ほど作成したクラスタの横の**接続**をクリックします。

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

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

1. いよいよデプロイです。
    [Kubernetesで使うためのサンプル設定ファイル](https://github.com/Tei1988/k8s-factorio/blob/vanilla/k8s.yml.sample)を用意しました。
    以下のコマンドを実行します。
       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の同期をしてみた](http://tech.speee.jp/entry/2017/04/24/114000)」や、「[非エンジニアが社内wikiにヒートマップを実装してみた話](http://tech.speee.jp/entry/2017/10/17/105500)」などの記事を書いた、新しい技術に目がない @nagayamacho3 が[サンタクロースの正体を暴く](http://cho3.hatenablog.jp/entry/2017/12/14/094815)ようです。お楽しみに:tada:

# おまけ

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

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

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

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

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

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

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

1. [Tei1988/k8s-factorio](https://github.com/Tei1988/k8s-factorio/tree/mods)のmodsブランチをcloneする。

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

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

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

1. `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`
1. `kubectl apply -f k8s.yml`