
【第 3 回 cocone tech talk・前編】AWSでのGitLab Runner オートスケーリング化の取り組み【イベントレポート】
-
2021年8月5日
こんにちは!cocone tech blog編集長のYです!
今回は2021年7月28日に行われた「cocone TECH TALK VOL.3」でお話した内容をまとめたものを前編・後編に分けてご紹介します!
本記事は前編として「AWSでのGitLab Runner オートスケーリング化の取り組み」というテーマについてお話ししていただいた内容を文字起こししたものとなります。
登壇者紹介
ココネのインフラ
早速本編に入る前にココネのインフラについて画像でご紹介します。
ココネでは主にこれらの技術を中心に活用しており、インフラチームではこれらを利用して全社的なインフラ管理を行っています。
目次
GitLab CI/CD 目指す構成
こちらが簡単な構成図になります。
まずはじめにユーザーがGitLabに対してコミットを行います。
その際にgitlab-ci.ymlというCI/CDの定義を記載したファイルを合わせてコミットすることでGitLab Runnerが実際の処理を行います。
またGitLab Runnerはconfig.tomlというファイルで設定を行っています。
こちらはgitlab-ci.ymlです。
このファイルはCI/CDの定義を行うファイルで、リポジトリにプッシュする際に合わせてプッシュします。
今回構築するオートスケーリングでは、ジョブごとにインスタンスが作成されます。
gitlab-ciには多くのオプションが存在しますが、今回はオートスケーリング化にフォーカスするため割愛させていただきます。
詳しくはGitLab公式ドキュメントをご覧になってください。
導入背景と現状の確認
次にココネでオートスケーリングを導入した経緯と現状についてご説明します。
きっかけは開発チームからの相談でした。
相談内容は
- ジョブが同時に1つしか実行できない
- 複数ジョブ/プロジェクトの同時実行で待ち時間が発生
というものでした。
そこで、まず初めにRunnerの設定ファイルの内容を確認しました。
Runnerの設定ファイルは構成図のところに記載した通り、Runner Server内のconfig.tomlです。
現時点で覚えていただきたいものは”concurrent”というもので、こちらはジョブの同時実行数を定義するものです。
上のconfig.tomlを見るとジョブが同時に1つしか実行できないという問題点は、この”concurrent”に原因があることが分かりました。
ではこれで問題が解決したのかというと、そうではありませんでした。
こちらはジョブの同時実行数が1の際に取得していたRunnerのCPUとメモリの使用率です。
ココネではRunner ServerとしてAmazon EC2の”t3.small”を使用しており、この時点でピーク時のCPU使用率が約45%、メモリ使用率が約85%となっていました。
そのため、単純に”concuttent”の値を変更し、ジョブの同時実行数を増加させるだけではリソースが枯渇するだけなので、スペックの増強が必要となりました。
導入パターン
続いて検討した導入パターンについて説明いたします。
今回は
- インスタンスのスペックを増強する
- GitLab公式から機能が提供されているオートスケーリングを利用する
という2つの案が候補として上がりました。
2つの案について詳しく説明します。
まず1つ目のインスタンスのスペックを増強する案についてです。
この案のメリットとして、対応や管理が簡単ということがあげられます。
一方で常時ジョブを実行している訳ではないので、利用していない時間のコストが無駄というデメリットもあります。
そこで実際どの程度のスペックを必要とするのか、おおまかではありますが調査を行いました。
調査方法は1プロジェクトのCI/CDを実行し、CPUとメモリの使用率を計測しました。使用したのは”t3.small”です。
オートスケーリングを導入し、それによって立ち上がったインスタンス6つを合計したところ、CPU使用率が約176%、メモリ使用率がCloudWatchAgentやMuninで監視を行っていないため、CPU使用率の増加量をベースに4〜6GB必要なことが分かりました。
ではこの結果を元にスペックを検討してみます。
こちらは7月初旬に確認したインスタンスタイプと1時間あたりの価格です。
先ほどの結果をクリアするにはlargeだと少しスペック不足で”c5.xlarge”以上のスペックが必要であることが分かります。
“c5.xlarge”の価格と”t3.small”を比較すると、単純な1時間あたりのコストで約7.8倍となってしまいしまいます。
また、xlargeまでスペックを上げたとしてもジョブの実行数を制御しない場合、リソースが枯渇する恐れがあることが判明しました。
そこで、ココネではオートスケーリングによって実装を行うことに決定しました。
オートスケーリングのメリットとしては必要なときに必要な数のインスタンスで処理を実行できるため、リソースやコストの最適化を行うことができる点です。
一方で運用実績がないため、実際にどういう動きになるのか、また実運用が可能なレベルなのか、という不安点もありました。
オートスケーリングの導入
ここからはいよいよオートスケーリングの導入についてです。
準備するものはこちらになります。今回は時間の都合上こちらのものを事前に準備させていただいたものとして、Runnerの実際の構築部分について説明させていただきます。
Runnerは大きく3つに分類することができます。
- Shared : 全プロジェクト共通
- Group : グループのプロジェクト用
- Specific : 特定のプロジェクト用
今回はGitLab上の全プロジェクトで利用可能なShared Runnerを用います。他の種類のRunnerでも同様のことは可能です。
はじめに、CI/CDを実行する際にgitlab-ci.ymlの内容を元に処理を実行するRunner Serverを登録します。
オートスケーリングを実行する場合、config.tomlを配置するServerはマネージャーサーバーとして機能します。
登録を行う前に
<ご自身のgitlab-url>/admin/runners
より登録するRunnerのURLやトークンを確認します。
確認が完了したら、次にGitLab Server上で
gitlab-runner register
というコマンドを入力します。
このコマンドによりRunnerの登録を対話形式で行うことができます。
URLやtokenは先ほど確認したものですね。”description”はGitLab上に表示されるRunnerの名前になります。
次に、”executor”では”docker+machine”を選択して下しあ。これはオートスケーリングにてdockerとdocker-machineを利用するためです。
Docker imageは利用したいdockerイメージを指定します。タグを指定しない場合はイメージの最新版となります。
完了するとRunner Serverの”/etc/gitlab-runner”配下にconfig.tomlという設定ファイルが生成されます。
次に注意するポイントについて説明します。
まず、Runnerを登録した後にRunnerのステータスをGitLab上で確認すると、緑の丸で表示されているアクティブマークが!マークになっていることがあります。
これはRunnerと通信できていない状態をしてしているため
gitlab-runner verify
というコマンドを実行してください。するとRunnerと通信できるようになると思います。
続いてdockerイメージについてです。こちらはオートスケーリングしたServerでdockerコマンドを使用する場合にdocker in dockerイメージを利用する必要があります。
注意点としてdindイメージを利用する場合、イメージサイズをalpineと比較すると約45倍程度大きく、オートスケーリング時にローカルにイメージが存在しない場合Docker Hubより取得する必要があるためダウンロードに時間がかかります。そのためプロジェクトに合わせて最適なイメージを選択していただければと思います。
次にRunnerの設定ファイルであるconfig.tomlについてです。
Runnerはこのファイルでほぼ全ての定義を行っているため、このファイルの内容を理解できればほぼゴールです。
こちらは赤枠で3つに分けて説明していきます。
まずは一番上の赤枠部分です。
ここではジョブの同時実行数の定義である”concurrent”やAWS上で最大何台のインスタンスが起動するかを定義する”limit”というパラメータが存在します。
この2つによって負荷とコストが決まってくるので、利用実態に応じて設定を変更いただければと思います。
なお、config.tomlを変更した際には
systemctl gitlab-runner restart
で設定を反映する必要があります。
次に赤枠中段です。
“privileged”というパラメータはdockerを特権モードで起動するかどうかで、docker in dockerを利用する場合はtrueにする必要があります。
docker in dockerを利用しない場合はfalseにしておいた方が、セキュリティ上は安全です。
“disable_cache”はオートスケーリングを行う際にキャッシュを利用する場合は必須の設定です。
オートスケーリングで立ち上がるそれぞれのインスタンスのローカル上に存在するキャッシュを他のインスタンスから参照できないため、別途S3にキャッシュを保存します。このoptionをtrueにすることで、ローカルのキャッシュ利用を無効化します。
キャッシュはジョブの実行時間を大きく減らせる場合があるので、gitlab-ciの利用方法を確認し可能であれば利用頂くのが良いと思います。
“pull_policy”は”if-not-present”を設定すると、利用するdockerイメージがローカルに存在する場合はそのdockerイメージを利用することができるようになります。この設定は入れておくと便利です。
最後に赤枠下段のインスタンスを作成する設定についてです。
この部分の設定はdocker-machineでインスタンスを作成する際のパラメータとなっています。
まず”IdleCount”ですが、こちらはインスタンスの最低起動台数です。
これはジョブを受け付けた際に即時ジョブを受け付けるインスタンスを何台に設定するか、というものです。
“IdleTime”はジョブの実行後に設定した時間を超過した際にインスタンスが削除されるようになります。
なお、ジョブの終了後に他に受け付けるジョブが存在する際は既存のインスタンスが再利用されます。
“use-private-address”はインスタンスに付与されるpublic/privateどちらのipで通信を行うかというものです。
trueの場合、private ipが通信にしようされるため、private subnetを利用する場合は外部と通信するためのフォワードプロキシが必要となります。
最後にspotインスタンスの設定です。
ココネでは本構成にspotインスタンスを活用しており、”t3.small”の場合オンデマンドインスタンスと比較する約1/3のコストで利用できます。
spot-priceは最高入札額となり、この額をスポットインスタンスの価格が上回った場合はインスタンスが中断してしまうため、比較的余裕を持った設定にすることをおすすめします。スポットインスタンスの価格はAWSコンソールのスポットリクエストから過去3ヶ月の価格を確認できます。
1つ注意点として、”amazonec2-block-duration-minutes”というオプションが存在するのですが、こちらを設定したところココネの環境ではインスタンス作成時にエラーとなり処理ができなくなってしまいました。
おそらく2021年7月1日より新規でこの機能が利用できなくなった事に起因するものと思いますが、このオプションは付与しない方が良いと思います。
もし作成し、動かなくなった場合はdocker-machineからインスタンスを削除し”gitlab-runner verify”を行う必要があります。
これで一連の設定は終了です。この後は実際の動作をみていきます。
まずはcodeと一緒にgitlab-ci.ymlをコミットします。
gitlab-ci.ymlは様々な定義の方法があるので最適な方法で実装いただければと思います。
今回はサンプルでジョブの実行の流れを見ていきます。
コミットすると同時にgitlab-ci.ymlの内容を元にRunnerが起動します。
今回はBuild/Test/Deployというステージを用意し、それぞれジョブが実行されるようにしました。
同一ステージ内のジョブ実行は並行して行われるため、Test Stage内のjob02とjob03は同時に実行されます。
次にこのパイプラインを動かした際のオートスケーリングの挙動について説明します。
今回configで設定していた”IdleCount”は2でしたが、そのうち1台がjob01を受け付けます。
job01を受け付けた事で”IdleCount”のインスタンスが1になったので、Runnerはこの時点で新しいインスタンスの作成を行います。
job01完了後Test Stageとなりますが、Test Stageでは2つのジョブが並列して動くので、再度”IdleCount”を維持するため新規のインスタンス作成が行われます。
そしてジョブが完了後、”IdelTime” で定義された期間が経過したのちに”IdleCount”で定義した数までインスタンスが削除されます。
スポッットインスタンスにはリクエストの有効期限がそんざいしますが、スポット料金を上限価格が上回っている限り、インスタンスは存続し続けます。
ただし、EBSボリュームには結果がどんどん格納されていくので、そのうち容量が足りなくなります。
そのため”IdleCount”を0にするか定期的なローテーションが必要となります。
なのでこちらのスクリプトを用いて定期的にインスタンスを削除するようにしました。
こちらのスクリプトを毎日午前3時にcronで動かしています。
初めにawscliを用いてリクエストの有効期限を確認しています。このスクリプトでは有効期限が明日までのものが抽出されます。
次にgitlabのapiを用いて念のためジョブが動いていないかを確認し、動いていない場合は削除、動いている場合は何も処理をしない、ということをしています。
ちなみにRunnerのログですが、”var/log/syslog”を確認する、もしくはGitLab Runnerをデバッグモードで起動することで確認できます。
スポットインスタンスを利用する場合syslogに特定のエラーが出ますが、実装上の問題であって処理に影響はありません。
性能評価
最後に稼働状況についてまとめたいと思います。
ココネでは6月末よりオートスケーリングを導入しました。
1ヶ月運用し、約3000程度ジョブを受け付けたのですが、オートスケーリングのエラーによるジョブの失敗はありませんでした。
ただし、今回は出なかったのですがスポットインスタンスの特性により、特定のインスタンスプールがなくなることや、入札価格がスポット価格を下回る事でインスタンスが中断する可能性がありますので、注意しておいた方が良いと思います。
また、複数のプロジェクトや同一ステージ間のジョブも並列化できており、当初の複数ジョブ/ プロジェクトでのCI/CDの実行環境を整備するという目的を満たすことができました。
次にコストメリットについてです。
こちらのグラフはprometheusとgrafanaで作成したグラフです。左上のグラフがインスタンスの起動台数、左したのグラフが1時間あたりのインスタンス費用、右のグラフが1日あたりのインスタンス費用となっています。
これらを元にオートスケーリングで発生した費用を算出し、インスタンスのスペックを上げた場合とオートスケーリングを実装した場合のコストを比較してみました。
スペックを上げる場合 0.214(c5.xlarge) * 24(hour) * 30(days) = $154.08 オートスケーリングを行う場合 0.009(t3.small spot) * 24(hour) *30(days) * 平均起動台数(2-4) = 約$25
スペックを上げる案も夜間はリソースを縮小したりすればスクリプトの作成や運用等は必要ですが、もう少しコスト削減は可能できるとは思いますが、1ヶ月あたり約8割のコスト削減が可能という結果になりました。
今回ご説明した内容は決して難しい内容ではなく、性能面やコスト面などで多くのメリットがありました。
気になった方はぜひともお試しください!!
スライド
いかがでしたか?1ヶ月あたり8割のコスト削減が可能というのは素晴らしいですね!
みなさまぜひともオートスケーリング化挑戦してみてください!
以上で「cocone TECH TALK VOL.3」前半の振り返りを終わりたいと思います。
後編では「(Unity)3D表現を豊かに ShaderGraphの実践事例」というテーマでお送りします。
後編の記事はこちらから!
【第 3 回 cocone tech talk・後編】(Unity) 3D表現を豊かに Shader Graphの実践事例【イベントレポート】
また、ココネでは一緒に働く仲間を募集中です。
ご興味のある方は、以下のリンクからぜひご応募ください。