GS2 Blog

Game Server Services(https://gs2.io/) の最新情報をお届けします

CircleCI から Semaphore CI に移行した話

みなさんこんにちは。Game Server Services の丹羽です。

Circle CI はとてもいいサービスです。しかし、私たちのニーズに合わなくなってきました。
そこで、様々な SaaS を検討した結果 Semaphore CI にたどり着きましたので、今日はその話をしたいと思います。

なぜ Circle CI をやめようと思ったのか

"なぜ?" を共有するには
そもそも、私たちが何を開発しているのかを簡単に説明する必要があります。

私たちは、ゲームサーバをSaaSとして提供しています。
フルパッケージでモノリスなゲームサーバではなく、マイクロサービスとして、機能単位でつまみ食いして利用できるよう設計されています。
AWS がすさまじい量のサービスを提供していますが、そのすべてを使用する必要がないのと似ています。

現在提供しているマイクロサービスの種類は「課金通貨の残高管理」「メッセージボックス」「経験値・レベル管理」「イベントスケジュール管理」など 36 にのぼります。
これらのサービスは様々なプログラミング言語から利用できるようSDKも提供しており、Python / PHP / TypeScript / C# といったものから
ゲームエンジンから利用しやすいよう、Unity / Unreal Engine 4 といった個別のプロダクトに特化したSDKも提供しています。

その結果、管理するリポジトリの数が爆発して困っていました。
なにか修正作業を行うとき、特定のリポジトリに収まらず ほかのリポジトリにも手を入れることになったとき、ブランチを切り替えわすれていてため息をつくのは日常的でした。

そして、たどり着いた結果はマイクロリポジトリをやめて、モノリシックなリポジトリに変えることです。
誤解をされないよう注意書きをしておくと、マイクロサービスをやめるわけではありません。リポジトリをまとめるだけです。

そのとき一番の障害として挙がってきたのが CI でした。

Circle CI をはじめとした CI ツールの大半はリポジトリと密結合しています。
これは、リポジトリに push したときや、 PR が作られたときに CI を動かすためにそうなっているのだと思いますが、モノレポとは非常に相性が悪いです。
なぜなら、モノレポに大量のマイクロサービスを入れると、変更のたびに走るテストが膨大になってしまう為です。

そのため、差分を検知して変更されていない部分のみテストをするなどの工夫が必要となりますが、そこは私たちにとって事業の本質ではありません。
最初は Circle CI の中で実現しようと、差分検知などの仕組みを作ってもみましたが、ビルド履歴に様々なマイクロサービスのビルド結果が入り混じってしまい、視認性が非常に悪く「正直 使い物にならない」という状態になってしまいました。

こうして、私たちは Circle CI から離れる決断をしました。

Semaphore CI は何がいいの?

モノレポ対応

まず、私たちの要件にマッチしている段階でお察しの通り、リポジトリの部分変更検知は標準機能で乗っています。
デフォルトでは、master(main) ブランチとの差を見て、変化があった部分のみCIするロジックが用意されています。

また、ビルド結果の一覧からどこが実行されたのかも一目瞭然です。

f:id:kazutomo:20210514004444p:plain

並列実行の手軽さ

最も他の CI と異なるのはそのUIでしょう。

f:id:kazutomo:20210514001658p:plain

ノードベースでビルドパイプラインを組み立てていきます。
直感的ではないというか、他のCIと大きく違うのは各ビルドタスクごとにビルドマシンが変わることです。
Circle CI をはじめとしたさまざまな CI は「セットアップステップ」 -> 「ビルドステップ」 -> 「テストステップ」 というように記述していきますが
このルールをそのまま Semaphore CI に持ってきても、「ビルドステップ」はまったく新しいマシンで実行されるため、セットアップ結果は反映されていません。

上のスクリーンショットで、箱の中に複数行ある部分はビルドマシンを共有するのか?と思いますが、そうではありません。
ここで記述されたタスクは並列実行されます。すべてのタスクが終わると次の箱に進む。というわけです。

ビルドプロセスを組み立てる上で、ここは実は並列でやってもいいんだけど、いい感じの yaml を書くのがめんどくさいなあ。と直列で書きがちですが、並列実行を手軽に直感的に使えるのもいい部分です。
なお、テスト実行を複数のマシンで並列処理して高速化したいようなニーズに対する対応は Circle CI と似たようなものです。

プロモート機能

平たく言うと、ビルド定義の yaml ファイルを分割する機能です。
他の yaml ファイルを呼び出す処理のことを Semaphore CI ではプロモートと呼んでいます。
(本当は 特定の yaml が完了したら実行してほしい。と登録する逆向きの参照の CI を求めていたのですが…)

これによって、Circle CI では超大作になりがちな yaml ファイルを分割することができます。
プロモートを自動的に実行する条件を設定できます「特定のブランチなら」「特定のディレクトリ以下に変更があれば」「タスクが成功していたら」という具合です。

/.semaphore/.semaphore.yaml
 github の push で実行される
 /account に変更があれば /account/.semaphore/.semaphore.yaml をプロモートする
 /inventory に変更があれば /inventory/.semaphore/.semaphore.yaml をプロモートする

/account/.semaphore/.semaphore.yaml
 account マイクロサービスのテストを実行
 テストを通ってかつ develop ブランチであれば、開発環境にデプロイ
 テストを通ってかつ master ブランチであれば、本番環境にデプロイ

/inventory/.semaphore/.semaphore.yaml
 inventory マイクロサービスのテストを実行
 テストを通ってかつ develop ブランチであれば、開発環境にデプロイ
 テストを通ってかつ master ブランチであれば、本番環境にデプロイ

みたいに組み立てることができます。

プロモートは自動的に実行しないようにすることもできます。
これを使えば、本番環境にデプロイ する部分だけ別の yaml に切り出して、そのプロモートは手動で行うようにする。というようなことも出来るでしょう。

virtual env が標準でついている

最近の定番は Docker コンテナを DockerHub から指定していい感じにビルドするというものです。
当然、Semaphore CI でもこの方法を取れますが、Linux マシンでビルドするときには標準で virtual env が使用できます。

sem python 3.8

とコマンドを実行するだけで、python と実行すると 3.8系のランタイムで実行できます。
Docker Hub にアクセスして、コンテナを探す必要はありません。

ビルド対象は golang 1.15系 なんだけど、ビルドツールが python 3.8系 なんだよなあ。というときも
合わせ技のコンテナを探したりする不毛な作業が必要なくなり、QOL が上がりました。

お値段

実行時間に対する料金は Circle CI とさほど変わりません。
しかし、シート料金がかからないため、その点では融通しやすいかもしれません。
(Circle CI で私たちの場合は、管理部門が領収書を回収するためだけに1シート使ったりしていたので、少し嬉しいですかね)

Semaphore CI は何がだめなの?

CI をする分には申し分ない機能をもっていますので致命的な問題はありません。
しかし、私は良い製品だと思いますが何故かマイナーなので、Web上で資料がすさまじく少ないです。

プロモートするときに引数や環境変数を付けて起動できると、いろいろまとめられそうな気がするのですが、そのような機能もありません。
(ま、そもそも yaml を分割できない CI SaaS のほうが多いので、贅沢な話なんですが…)

定番の求人情報

私たちは一緒にサービスを盛り上げてくれるメンバーを募集しています。
今絶賛募集中なのは、いわゆるソリューションアーキテクトと、新機能をバンバン作っていくというよりは、お客様目線でサービスを改善していくエンジニア(サーバエンジニアというよりはクライアントエンジニア寄りのほうが向いてるかもしれません)です。
gs2.io

それでは。また。

(C) Game Server Services, Inc.