left4dead2サーバー用のDockerイメージを作り、GCE上で動かしてみた #1（使い方）

##はじめに
コンテナ（Docker）について学んだため、何かアウトプットしたいと思いl4d2用のサーバーのコンテナを作成し、googleが提供するIaaSであるGoogle Compute Engine上で動かしてみました。

今回は#1ということで、作成したコンテナの使い方を紹介します。
「コンテナなんて使ったことない、鯖なんて立てたことないよ」という人でも立てられることを目指しました。

以下は記事を読み進めるにあたっての留意いただきたい事項です。

- 今回は「とりあえず」使えるサーバーを構築してみることが目的なので、dockerfileの中身がイマイチです（この詳細は、別途記事(#2)を作って説明します）。この点はご容赦ください。
- #1は「使い方」ですが、#2で「コンテナ作成の過程で得た知見」、#3で「サーバー構築までで苦労したこと」を書く予定です。

##使い方
###1. GCE上の基本設定（登録からファイアウォール設定まで）

まずは[GCEのコンソール画面](https://cloud.google.com/compute?hl=ja)を開きます（今回はGUIを使います）。
初めての方は「無料トライアル」を選択して登録を行います（2020/12時点で無料トライアル$300, 90日間が利用可能）。

最初にファイアウォールの設定をします。l4d2はデフォルトで27015番ポートを使用するので接続を許可する必要があります。
画面左上にあるGCPのナビゲーションメニューを選択し、ネットワーキングの項目のVPCネットワーク、ファイアウォールを選択します。
![eaf9a85cf0859b662344a5b13ab4fcb2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933139/f8f5b2f7-967a-2b43-e3be-a8fc6fe7760a.png)
次に、「ファイアウォール ルールの作成」を選択し、任意の名前を設定してターゲットタグ（ここではl4d2とします）、ソースIPの範囲（どこからでも繋げられるよう、0.0.0.0/0を設定）、指定したプロトコルとポート（TCP, UDPの27015を許可）の３つを入力します。
![3d7fed3e8ef4b537b8b4304ba28b7dee.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933139/b12ac4b6-3707-50cc-1418-262e6677399b.png)
###2. GCE上の基本設定（インスタンス起動まで）
次に、再びナビゲーションメニューを選択し、コンピューティングの項目のCompute Engine(GCE)、VMインスタンスを選択します。そしてVMインスタンスの作成を選択。
![accf63a3f15737012b73b833d73e353c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933139/16b0f3f6-62c3-2fa8-89de-58fe910dfc00.png)
インスタンスの名前は任意で問題無く、リージョンは近いところ（東京）を選んでおけば大丈夫です。
マシンタイプは2vCPU, 8GBメモリにします（4GBでも動くと思いますが、極端に遅いような気がしたので8GBにしています）。
コンテナイメージのデプロイを選択し、コンテナイメージを入力（ここでは、今回dockerイメージのリポジトリをdocker hub上に作成しているので、「ekarunian/l4d2-server:1.0」を入力します。
ブートディスクはデフォルトだと10GBですが、コンテナが重いので30GBを選択します（25GBはダメでした）。
![b6dcc7220c5ce12d75b61809a12d4184.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933139/e08c9a39-18f3-6209-f280-2e572510a468.png)
下にスクロールして、「管理、セキュリティ、ディスク、ネットワーキング、単一テナンシー」の折りたたみメニューを展開し、ネットワーキングタブのネットワークタグにファイアウォール設定で設定したターゲットタグ（l4d2）を入力します。これでこのインスタンスは27015ポート(TCP/UDP)を許可します。
![1a08f094f2b1319e467723dc77d58b92.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933139/2b5938e4-671f-88c2-cbab-91288bb5d83c.png)
次に管理タブを選択し、プリエンプティブをオンにします。
今回は友人と一日だけ遊ぶような場合を想定しており24時間でインスタンスが消えても大丈夫なので、これを選択します（いないと思いますが、24時間以上遊ぶ場合はオフにします）。
これを設定するとサーバーコストが下がります。今までの設定をプリエンプティブオフで作成すると1時間に約\$0.087（2020/12/06時点）かかりますが、オンでは1時間に約$0.027（同日時点）となります。
![67be3f55fe4ab3b46864704409a3e99f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933139/013eb457-dbdc-497b-0add-de55bd515e1a.png)
ここまで設定したら一番下の「作成」を選択し、インスタンスを起動します。
しばらくすると、以下のような画面が出ると思いますので、外部IPを控えておいてください（サーバーに接続するのに必要です）。また、SSH接続を選択してターミナルに接続します。
![d9eeb02541f0e47df54e887a8f60d034.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933139/4e85b07c-67a4-537a-4c12-68382c14d8ea.png)
（今回、外部IPは固定していないので特に隠していません）

###3. インスタンス内での操作（コンテナ、サーバー起動まで）

インスタンスを起動（して、ターミナルにssh接続）したら、数分待ちます。
ターミナルに「docker images」と入力し、下記のように「ekarunian/l4d2-server」がリポジトリにあればOKです。

```:（GCE上の）ローカルにあるdockerイメージの一覧
ekaru2an@instance-2 ~ $ docker images
REPOSITORY                                            TAG                 
ekarunian/l4d2-server                                 1.0
gcr.io/gce-containers/konlet                          v.0.11-latest
gcr.io/stackdriver-agents/stackdriver-logging-agent
```
確認出来たら、「docker run -it -p 27015:27015 -p 27015:27015/udp --name l4d2 ekarunian/l4d2-server:1.0 bash」でコンテナを起動します。
-pオプションでサーバーからコンテナへポートの転送設定（今回はクライアントからの送信がデフォルトで27015なので、サーバー側（コロンの前）を27015にしています。コンテナ側もこれに合わせています。）を行い、bashを対話型で実行します。
最初は反応がないですが、少しすると下記のように２行目が出てきます。

```:コンテナを起動
ekaru2an@instance-2 ~ $ docker run -it -p 27015:27015 -p 27015:27015/udp --name l4d2 ekarunian/l4d2-server:1.0 bash
steam@534a1f30218e:~/l4d2server/left4dead2$ 
```
ここに「../srcds_run -game left4dead2 +map c1m1_hotel coop」を入力してエンターを押下すると色々メッセージが出て（エラーメッセージも含まれておりますが、問題ありません）、下記の２行が出ればサーバー起動成功です。

```:ゲームサーバーを起動
steam@534a1f30218e:~/l4d2server/left4dead2$ ../srcds_run -game left4dead2 +map c1m1_hotel coop
　---（省略）---
Connection to Steam servers successful.
  VAC secure mode is activated.
```
「c1m1_hotel coop」は起動時のマップ指定となり、ここでマップを変更できます。
例えば、[Left 4 Dead 2 Dedicated Server Guide](https://steamcommunity.com/sharedfiles/filedetails/?id=276173458)の下部にマップ名が列挙されております。
また、サーバー起動後はこの画面でl4d2のコンソール機能がadmin権限で使えます。例えば「god 1」などとすれば無敵になります（ただし、ゲーム中はログが爆速で流れるので注意が必要です）。
チートコマンドは[wiki](https://wikiwiki.jp/left4dead/%E3%83%81%E3%83%BC%E3%83%88%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89#ff675d6b)などを参考にしてください。
サーバーを閉じるときは「exit」などを入れれば終了します。

###4. ゲーム側の設定（部屋に入るまで）

ここからはl4d2ゲーム側の設定です。l4d2を起動したら、設定->キーボード/マウス->開発者コンソールを許可　を有効にします。
有効にすると、"`" キーまたは "半角/全角" キーでコンソールを表示できるようになります。
コンソールを表示したらGCEのインスタンス作成時に控えた外部IPを利用し、以下を入力してサーバーを有効にし、connectでサーバーに入ります。
これで、先ほど「../srcds_run・・・」で設定したマップに入れます。

```console:コンソール画面に以下を入力
mm_dedicated_force_servers [インスタンスの外部IP]
connect [インスタンスの外部IP]
```
招待が出来ないので、友人側にも同様にコンソールを有効にしてもらい、「connect [インスタンスの外部IP]」を入力してもらうことで、一緒に遊ぶことが可能となります。
なお、本サーバーは知らない人が入りにくい設定にしておりますが、入れない設定ではないので注意してください（少なくとも、私は友人と3時間プレイして際は入ってきませんでした）。

あとは、ゲームを楽しんで、終わったらサーバーを閉じてGCEインスタンスを停止したらおしまいです。

###5. まとめ

今回は、誰でもl4d2 dedicatedサーバーが構築できることを目指してコンテナの作成、Qiita記事の作成をしました。結果、1時間3円程度のサーバーが構築できました。
「この手順に従ってもおかしなことが起きたよ」などがありましたらコメントいただけますとありがたいです。

また、今更言うなという感じですが、今回起動したサーバーは通常設定のサーバーではなく、色々な設定をハチャメチャに弄っております（通常設定のサーバーならローカル環境でもつくれるので。。。）。
どうなっているのかは実際に遊んで確認してみてください。
ちなみに、下記のサイトを参考に、色々なプラグインを入れております。

- 涼月蒼菜様のブログ「気まま研究所」の記事：[Left 4 Dead 2 Dedicated サーバの建て方](https://kimamalab.azurewebsites.net/Left4Dead2/BuildServer#SourceMod_Install)
- ztar様のブログ「SunriseBlue」の記事：[L4D2でうごく！自作（改造）プラグイン置き場](http://ztar.blog7.fc2.com/blog-entry-925.html)

その他、今回のdockerfileは[steamCMDのdockerイメージ](https://hub.docker.com/r/cm2network/steamcmd)をベースに作成しております

## 今後やりたいこと

- 今回はdockerfileだけでゴリ押してしまったので、サーバー設定を容易に変更できるようコンテナを綺麗にし直す（優先度：高）。
 - #2で詳細を記載しますが、もう少しクレバーな見た目になるようにdocker-composeを作りつつ再整理します。
- アドオンマップ等modの導入（優先度：高）
 - これはl4d2 dedicated serverの設定を変えるだけなのでそんなに難しくない（はず）
- ゲームプレイ中にサーバーへ生じた変更をGCSにバックアップする仕組みを作る（優先度：中）
 - l4d2だとプレイ結果をサーバーにフィードバックする必要がないので不要ですが、今後別のゲームでdedicated serverが作れるようになったらバックアップ用コンテナを作成し、GCSと連携（定期バックアップ等）が出来るようにしたいです。今のところ、[Craftopia](https://store.steampowered.com/app/1307550/Craftopia/?l=japanese)というゲームの開発ロードマップに「使えるようにするよ！」的なことが書いてあるので、完成したら触ってみます。
- Kubernetes（GKE）を使ってサーバーを構築してみたい（優先度：低）
 - 一方、個人利用の範囲ではスケーリングやヘルスチェック等のコンテナオーケストレーションのメリットは享受できないので、構築することそのものが目的になってしまう。

##おわりに
今回、DockerとGCEを用いてサーバーを作成しましたが、何故この手法を選んだのかは以下の通りです。

- まず、個人PC上でサーバーを構築するとその環境に依存してしまいます（例えばポートの設定・回線・動作の安定性等が人によって異なりますので、他者の環境で確実に動作するかが不明です）。
 - そこで、GCE上で動作させることによりこれらの問題を解消します（AWSのECSでも同様のことが出来ると思いますが、今回は個人的に好きな方(GCE)を使っています）。
- サーバー自体はsteamCMDというsteamクライアントのCLI版が存在するので、スクリプトで頑張って書こうと思えばサーバー構築もできると思いますが、サーバー設定を弄る時にテスト環境と本番環境とで環境の違いによるエラーが出るかもしれないのが嫌でした。
 - 動作環境を含めてコンテナでパッケージ化すれば、サーバー設定を弄っても環境の違いによるエラーは出ませんし、更に今後steamの別ゲームで同様にdedicated serverが作れるようになった時に再利用できると思いました。（ただし、今回はこの点で結構ミスってしまい変更容易性や再利用性が悪くなっています。この点は#2で詳述します。）

こういった点でも、コンテナとクラウドの組合せは相性が良いことを実感することが出来ました。
さて、今回の記事でサーバーの動作を確認出来ると、次は必然的に「サーバー設定を自分で弄りたい！」となるわけですが、これをやろうとすると（何度も言いますが）このコンテナがべスプラでないことが分かってきます（恥ずかしい限りですが、動いたのでOKです(\*'ω'*)）。この理由は[次回の記事](https://qiita.com/ekaru/items/201250bbb3def0dbe8eb)で書きたいと思います。
最後まで読んでいただきありがとうございます\_(.\_.)\_