GitLab RunnerをGCPでオートスケールさせて安く運用する

こんにちは。Optimal Bizのサーバーサイドに関する開発を担当している伊藤です。

皆さんCIは何を利用していますでしょうか?
Optimal BizではGitLab CI/CDを利用しています。

単体テスト・ビルド・デプロイ等CIの用途は多岐にわたりますが、実際にそれらを実行するPCを必要な数だけ常に起動しておくと無駄な料金がかかってしまいます。

そこで、今回はGoogle Cloud Platform(以降、GCP)のプリエンプティブル VM インスタンスをGoogle Kubernetes Engine(以降、GKE)でオートスケールさせることで、CIリソースを格安で確保する事例を紹介します。

利用例

Optimal Bizチームの場合は「RSpecをGitLab CI/CDを使って並列実行する」で紹介した大量の単体テストを約20台のノードで並列実行するために利用しています。

ざっくり構成図

図にするとこんな感じです。

f:id:optim-tech:20200513120828p:plain

必要な計算量に応じて、GitLab RunnerのPodが増減することによって必要な数のノードが自動的に立ち上がります。
処理が終わったあとはPodが減ることで、ノードの数が自動的に縮小します。

構築手順

では構築手順を見ていきましょう。簡単にセットアップすることが可能です。

GitLab RunnerをGKEで稼働させる

  1. GitLabにログインし、プロジェクトページの「Kubernetes」を開きます

    f:id:optim-tech:20200204132838p:plain

  2. 今回はGKEを利用していきますので、GKEを選択します

    f:id:optim-tech:20200204133300p:plain

  3. Googleアカウントでのログインを求められたらログインし、クラスタに関する情報を入力します

    • 今回は東京のリージョンということで「asia-northeast1」
    • ここでプリエンプティブル VM インスタンスを選択することができないため、ひとまずVMのスペックは1CPUのもの選択します
    • クラスター名は任意の値で問題なさそうです
      ※ここでmicroインスタンスを選択すると、このあとのHelmとGitLab Runnerがインストールできないので注意が必要です。
      f:id:optim-tech:20200204133321p:plain

これだけで自動的にGCP上にGitLab用のKubernetesクラスタが出来上がります。
数分かかるようなので、しばらく待機しましょう。
その後は画面の指示に従ってHelmとGitLab Runnerの「install」ボタンを順番に押すだけです!

この状態でGitLab Runnerは稼働しており.gitlab-ci.ymlの記載に従ってRunnerが動作します。
しかし、Kubernetesに関する設定を行っていないためノードが処理可能な計算量やメモリを超える量のCIを実行するとノードがパンクしてしまい、応答できない状態に陥ります。
この問題を解決するためにKubernetesによるオートスケールの設定を行っていきます。

GKEのノードをオートスケールさせる

  1. GCPにログインし、Kubernetes EngineのメニューからGitLabによって作成されたクラスタを選択します

  2. ノードプールの追加を選択します

  3. 「自動スケーリングの有効化」にチェックを入れ、お好みのノード数とマシンスペックを選択します

    f:id:optim-tech:20200204134342p:plain

    • ここで、「プリエンプティブルノードを有効化」にチェックを入れます
    • プリエンプティブルノードにすることによって、可用性が保証されなくなりますが、劇的に料金が安くなります

    f:id:optim-tech:20200204134504p:plain

  4. 最初から作成されていた「default-pool」というノードプールを削除します(プリエンプティブルとして作成されていないため)

  5. .gitlab-ci.ymlに環境変数を指定することで、利用するCPUやメモリの量を指定します。

以下のような感じです(過去のエントリの例を引き継いでいます)

rspec:
  stage: test
  tags:
    - cluster
  variables:
    KUBERNETES_CPU_REQUEST: 1      # 必要なCPUのコア数
    KUBERNETES_CPU_LIMIT: 2        # CPUのコア数の上限
    KUBERNETES_MEMORY_REQUEST: 1Gi # 必要なメモリ量
    KUBERNETES_MEMORY_LIMIT: 2Gi   # メモリの上限
  parallel: 20
  script:
    - bundle install
    - bundle exec ruby rspec_files.rb | xargs bundle exec rspec --format RspecJunitFormatter --out rspec.xml
  artifacts:
    reports:
      junit: rspec.xml

これで設定は完了です! たくさんのJobが発生した場合もRunnerがオートスケールしながら処理を行い、完了したら不要になったノードは自動的にシャットダウンされます。

ちなみにお値段は・・・

CPUが4コア,メモリ7G分のマシンを20台、概ね営業時間分動かして50,000円/月ほどでした。
(合計するとCPU80コア、メモリ140GB分のマシンを200時間程度動かしています)
おそらく、CIの計算量の少ないプロダクトであれば、1,000円/月で運用できるでしょう。

ちなみにプリエンプティブルでない通常のインスタンスの料金は約3倍になりますので、150,000円/月ほどです。
それでもかなり安いですね・・・

※2020/06/06以降は2つ目以降のk8sクラスタは料金が別途かかるようになるとのことでしたが上記の計算には含めていません

最後に

今回はGitLab Runnerをオートスケールさせる方法を紹介しました!

オプティムでは開発環境を日々改善していき、より良いアプリケーションを一緒に作るエンジニアを募集しています!