LogiClover開発ブログ

LogiCloverは、趣味でFPGAを使った電子工作をしているサークルです。主に開発中の出来事や技術メモを投稿します

スプラトゥーン2の戦績をsplatnet2statinkとGKEを使って自動アップロードする

きっかけ

私(kamiya)は任天堂から発売されているスプラトゥーン2を遊んでいます。

www.nintendo.co.jp

そこでスプラトゥーン上での戦績は、スマホアプリから閲覧できるのですがこれが直近の50戦しか閲覧することができません。

そこで非公式ではあるのですが、この戦績を管理するサービスであるstat.inkとアップロードするツールであるsplatnet2statink を使って戦績を管理しています。(※公式の手法ではないため推奨はいたしません、運営サーバに負荷をかけるような使い方はサービス妨害と見なされ戦績公開自体がなくなってしまうかもしれないのでお辞めください)

今回はVPS上で運用しているsplatnet2statinkをGKE(Google Kubernetes Engine)に移行します。*1 既存のpythonアプリケーションをコンテナ化してクラウド運用する手順の紹介、という立ち位置で読んでいただけると幸いです。

手順

大まかには以下の手順を踏んで行います。3.項移行はgcloud sdkを使う方法とGoogle CloudのWebページからでもどちらでも行なえます。ここはGoogleの公式ドキュメントのチュートリアルに詳しく記載されています。

  1. 単体で動作確認をする

  2. Dockerコンテナ上で動作できるようする

  3. Google Docker Registry(もしくはDockerHub)にイメージをpushする

  4. Kubernetesクラスタを作成

  5. Kubernetesクラスタに登録済のコンテナイメージをデプロイする

splatnet2statinkの動作

pythonで書かれた任天堂のサーバから戦績情報を取得するソフトです。コンテナ化する上でも簡単に動作を確認してみます。*2

github.com

7~40行目

config.txtを読み出すか、作成しています。その内容はapi_key, cookie, user_lang, session_tokenの4つです。

注目すべきはコンフィグファイルの指定はpython実行時のカレントディレクトリになる点です。

1170行目 name == "main"

pythonで直接実行されているときのみ実行されます。今回ではメインルーチンになっています。

1171行目→228行目 : main()

以下の動作をするようです。

  • 224行目 → 194行目 : check_for_update()

どうやらgithubからコードの最新版があるか調べ、gitが実行できる環境ならgit pull、できなければrequest.getをしてリソース更新をしているようです。

よく見ると204行目でアップデートを行うかのプロンプトが必ず表示されるようです。

  • 237行目~270行目

argparseライブラリを使って、コマンドライン引数の設定・取得を行っています。動作としては-M <更新間隔>で 一定時間ごとの自動更新と、-rでアップロードいていない戦績のみを対象にするオプションが使えれば良さそうです。

1175行目→295行目 : monitor_battle()

一定時間ごとに結果を取得してアップしています。

他にもいろいろありますが、以下の3点がコンテナ化する上でのポイントです。

  1. 自身のアップデートにrequest及びgitを使用している

  2. 設定ファイルはconfig.txtを使うこと

  3. 実行時はpython splatnet2statink.py -M <秒数> -rを実行できればよい

単体で動作確認をする

python splatnet2statink.pyで正常に動作できることを確認します。認証などについては詳細は触れません。

Dockerコンテナ上で動作できるようにする

まずは本アプリケーションをコンテナ化します。コンテナ化する上で認証情報などはあとから環境変数などで与えられるようにするのが通例です。

まずはgitを使ったアップデートですが、プロンプトにyを打ち込ませるのも面倒なのでgit submoduleとしてsplatnet2statinkを追加してgitコマンドを叩いてアップロードします。

ですので、実際に実行指定するスクリプトとしてrunner.pyを作成していきます。

$ git submodule add https://github.com/frozenpandaman/splatnet2statink splatnet2statink

pythonからは$ git submodule foreach git pull origin masterを叩ければ良いのでsubprocessモジュールを使って以下のようにします

subprocess.call(["git", "submodule", "foreach", "git", "pull", "origin", "master", ])

次に設定ファイルですが、環境変数を読み出してconfig.txtを書き換えるようにします。os.getenvで簡単に取得できるのでこれを利用します。 ファイルの書き込みはjsonモジュールを使うと、dictionaryをそのままjsonテキストにできるのでこれを書きます。

def env_to_config(envs_src):
    envs = dict([(e, os.getenv(e, "")) for e in envs_src])
    envs_available = all(envs.values())
    if envs_available:
        with open('config.txt', 'w') as file:
            file.write(json.dumps(envs))
        print('#update config.txt')

最後にスクリプトの実行ですが、これはgitコマンドの実行と同じです。

args = ["python", "./splatnet2statink/splatnet2statink.py"]
flags = os.getenv("run_flags", "-M 14400 -r").split(" ") # monitor per 4hour
args.extend(flags)
subprocess.call(args)

これらを統合してhttps://github.com/kamiyaowl/splatnet2statink-docker/blob/master/runner.pyとしました。

これをpython runner.pyして問題なく動作することを確認します。

あとはsplatnet2statinkに必要なライブラリをインストールするだけのDockerfileを書きます

FROM python:3.6

COPY . /app
WORKDIR /app

RUN pip3 install --upgrade -r splatnet2statink/requirements.txt

CMD ["python", "-u", "runner.py"]

私は面倒くさがりでコマンドライン引数を大量に書くのが苦手なので、docker-composeファイルも一緒に作成しました。 あくまで動作確認用に作成しましたが、自分で行う場合このファイルの管理は注意してください。

splatnet2-statink:
  build: ./
  # volumes:
    # - cache:/app # splatnet2statinkのリポジトリ更新キャッシュ用
  environment: # Containerサービスの環境変数に設定する
    - api_key=your_api_key
    - cookie=your_cookie
    - session_token=your_session.token
    - user_lang=en-US

あとは正常に実行できることを確認します。

$ docker-compose up

Google Container Registryに作成したイメージをアップする

Google Cloud SDKのセットアップについては公式ドキュメントなどを参照してください。

// イメージ名を確認
$ docker image ls 

// タグ付けを行う
$ docker tag <イメージ名> gcr.io/<プロジェクト名><イメージ名>:<タグ>

//  作成したイメージをGCRにアップする
$ gcloud docker -- push gcr.io/<プロジェクト名><イメージ名>

// イメージ一覧を確認
$ gcloud container images list

不安なのでWebコンソールでも見てみます。

f:id:logiclover:20180716175256p:plain

良さそうです。

Google Kubernetes Engineにアップする

ここからはGUIでもkubectlでもできます。おおまかな説明だけするとまずクラスタを作成します。

構成は任意ですが、動作確認なのでus-central1-a, g1-small, auto-scaling=off, size=1で作成しました。

次にワークロードを作成します。作成時に先程GCRにアップしたイメージをベースイメージとして指定します。この際の環境変数にdocker-composeで指定していたconfig.txtの項目を設定します。

f:id:logiclover:20180716180407p:plain

この設定はGKEのConfigMapに保存されるのであとから編集できます。

静的IPやLBは外部ネットワークが必要ないので設定せず、あとはクラスタが構成されるまで少し待ちます。

無事に立ち上がった後、stat.inkの結果を見るなりログの内容を閲覧して正常に動作していることを確認できれば完了です。

f:id:logiclover:20180716180603p:plain

WebコンソールからStackdriverの起動時のログを見てみましたが、ローカル起動時と同様に出力されており問題なく動作できていることが確認できます。

まとめ

各種サービスをコンテナ化してクラウドサービスに移行していくと、何よりサーバ管理の手間がないので面倒が減るのでかなり楽です。

従量課金の具合などから構成を見直したりする必要はあると思いますが*3、ちょっとした小回りから大掛かりなものまで活用できそうです。

今回作成したものも、githubで公開しておきます。

github.com

2018/7/28追記

CronJobで定期実行できるようにしました。

logiclover.hatenablog.jp

*1:正直Kubernetesが使いたいだけなのでCompute EngineのVMに当てても何ら問題ありません。

*2:2018/07/16現在のコードです

*3:例えばGAEでcronするようなジョブを作ったほうが