3月24日、サイバーエージェントのエンジニア・クリエイターによる技術カンファレンス「CyberAgent Developer Conference2022」を開催しました。本記事では、「GitHub Actions runner基盤におけるオンプレミスマルチテナントアプリケーションの運用」の様子をお届けします。

目次

■Cycloudの概要
■Cycloud-hosted runnerにおけるマルチテナント
■myshoes-proxy
■Build Up Alwaysへの取り組み
■継続的なBuild UpのためのCI/CD戦略
■継続的なBuild Upのための運用
■まとめ

■「Cycloud」の概要

サイバーエージェントでは「Cycloud」と呼ばれる社内向けのプライベートクラウドを運用しています。IaaS、KaaS、PaaS、SaaSなどのサービスを、すべて社内向けにカスタマイズして提供しており、例えば「メモリーがすごく必要である」という場合はそれを用意したり、「高速なストレージが必要」という場合であれば、高速なディスクを購入して運用することにより、安価に計算機資源を利用可能にしています。

●動的なrunner作成を行うOSS「myshoes」について

「myshoes」というソフトウェアを紹介します。「myshoes」は、私が開発している開発しているOSSで、「Auto-scaling VirtualMachine runner for GitHub Actions」を命題にしています。GitHub Actionsにおけるself-hosted runnerを、実際にジョブが発生する段階に作成し、終了したら削除するという作業を自動化してくれるソフトウェアです。こうしたソフトウェアは他にもありますが「myshoes」の利点はprovider方式を使うことで、複数のクラウド上にrunnerをデプロイできることがメリットです。

ユーザーがIssueを作成する、あるいはPull Requestを作成する際に、GitHub Actions上でテストを実行したりします。その時、GitHubからwebhookが飛ばされ「myshoes」が受け取ります。「myshoes」が受け取った先でインスタンス、つまりrunnerを、AWS、GCP、Azureといった特定のCloud Providerに偏らないところにデプロイすることができることが、大きな利点です。

・「myshoes」の内部状況や詳細については、過去の発表資料もぜひご参考ください。
【登壇資料】GitHub Actions with your shoes.Development myshoes and Provide Cycloud-hosted runner

・「myshoes」はOSSとしても公開されています。
https://github.com/whywaita/myshoes

●Cycloud-hosted runnerの概要

「myshoes」を開発した上で運用しているのが「Cycloud-hosted runner」で「Cycloud」上で運用しているSaaSです。プライベートクラウドである「Cycloud」上で安価にインスタンスを提供できるため、安価にCycloud-hosted runnerを使えるという利点があります。マルチテナントで提供しており、現在提供しているものはLinuxのみ。Ubuntuのfocalを提供しています。GitHub-hostedと比較した際のもう1つの利点は、2Core、20GBのインスタンスから、16Core、160GBを搭載したとても大きいインスタンスまで利用できることです。

■Cycloud-hosted runnerにおけるマルチテナント

●テナントのカテゴリ

「Cycloud-hosted runner」におけるテナントは、大きく分けて2つにカテゴライズされます。既存の「Cycloud」にはテナントがいくつか存在しており、これはネットワーク的にも完全に疎通ができない形になっていて、区分としては子会社や事業部が異なる場合にテナントを分割しています。その上で、同じ子会社や同じ事業部の中でも、請求区分を分けたいというような場合があるため、「Cycloud-hosted runner」でテナントを分けています。また、GHESを使っている場合はGHES用のテナントを使います。逆に、GHEC、github.comを使っている場合であれば、そちらのテナントを使うというような使い分けをしています。

●Cycloud-hosted runner構成

こちらが全体的な構成図です。

GitHubからwebhookが飛んでくると、それをmyshoes-proxyがリクエストを受け取ります。その上で、そのリクエストがどこのテナントなのかを判定した上で、例えば、テナントAのサービスXであれば、そちらのmyshoesにproxyして、myshoesが受け取ったリクエストを基に、後ろのサーバに対してrunnerを作成します。

●なぜテナントを分けるのか

もともと「Cycloud」を利用している方がたくさんいます。また、その上で、IaaS基盤を使っていろんなサービスを提供している方々もたくさんいます。そこで、こうしたテナント分けをすることによって、既存のIaaSにあるService VMに対してデプロイをしたり、あるいは逆にビルドをする際に依存関係のファイルを持ってくることができます。

●マルチテナントの共有レベル

各テナントの共有レベルについて紹介します。

テナントに関わらず、Kubernetesがひとつのクラスタとして存在しています。その上で、Kubernetes controllerであるmyshoes-controllerや、Orchestratorを使ったMySQLのクラスタがあります。これはCycloudにおけるOpenStackのデータベースなどにも使っていて、我々はかなり信頼性が高いと考えています。

その上で「Cycloud」におけるテナント共有は、Stadiumクラスタ、これは実際にrunnerが動くVMのことをStadiumと呼んでいます。

そのさらに先、プロジェクト単位のCycloud-hosted runnerのテナント共有としては、runnerのみを共有している形になります。

■myshoes-proxy

myshoes-proxyは、github.comやGHESといったインターネットからプライベートクラウドの橋渡しをするproxyで、各テナントに振り分けたり、認証認可を行なっています。

その上で、Kubernetesのcustom controllerとしてmyshoes-controllerを実装しており、テナント毎に「myshoes」のPodなどを実行しています。

●「myshoes」のマイクロサービス

その他にも、下記のような様々なものを「myshoes」のマイクロサービスとして運用しています。

・myshoes-controller
・billing-collector
・shoes-server
・MySQL Operation Pod
・cert-manager
・openstack-cinder-csi

●マルチテナントの種類

マルチテナントの実装は、インターネット上に様々な文献があります。これはIBMが提供している、「Webアプリケーションをマルチテナント型 SaaSソリューションに変換する」という記事から引用した図です。

アプリケーションがテナントごとに分れている例と、その中でデータベースをどのように分割するかについて、最も良いとされているのは右の3番で「myshoes」のアプリケーションも、データベースも3番で共有しています。しかし、Cycloud-hosted runnerにおいては、1番を採用しています。この理由については、myshoes(OSS)の実装指針である「簡単にデプロイ」「Non-Goalの明確化」が関係しています。

●myshoes(OSS)の実装指針

簡単にデプロイ

「myshoes」をOSSとして公開するとき、簡単にデプロイしたいという気持ちが強くありました。最近のソフトウェアだとデプロイするためにOrchestratorが必要になるものが多いですが「myshoes」ではバイナリをポン置きして、その上で諸々設定を置けば動くようにしたかったからです。我々はIaaS基盤の上に「myshoes」を動かしていますが、VPSや自宅サーバであっても動きますし、マルチテナントのためにKubernetes上のPodとして「myshoes」を動かしていますが、tmuxでバックグラウンドジョブとして疑似的に動かしても、ちゃんと動きます。簡単にデプロイできるというのを方針として最初に定めていました。

Non-Goalの明確化

Non-Goalを明確化することによって、後から追加要件がきたときに「myshoes」に実装するのかしないのか、その判断基準が明確になります。一例としては、現在とある認証方式を使っていますが、これをよりセキュアな形で運用したいとなった場合、認証の切り替えが必要になります。そういった場合でも、現状「myshoes」では認証をしておらず、社内proxy、つまりmyshoes-proxyの実装を差し換えさえすれば簡単に切り替えることができるというように、スムーズに追加要件の対応ができたという例があります。
マイクロサービスを運用する上で、「すること」だけではなく「しないこと」を決めることは高速に迷わない方針作りとして重要なことだと考えます。

■Build Up Alwaysへの取り組み

●Cycloud-hosted runnerのあゆみ

Cycloud-hosted runnerは、2021年の7月にパブリックベータとして、社内から広く利用してもらった上で、2021年の9月にGenerally Availableという形で提供開始しました。現在はテナント数が約30、Stadiumの数が50VMほど並んでいます。同時に実行されているrunnerの数は、500〜1000台ぐらいで、常時作ったり消えたりというのを繰り返しています。

●LXDの採用

初期は、下図で示したStadiumの部分が、大変だった部分でもありました。

GitHub-hostedはVMで動いており、それに近い体験を提供するため、StadiumではLXDを採用していました。GitHub Actionsには、Service コンテナという機能があります。例えば、CIの中でMySQLを起動させたり、本物のコンテナを使ってテストしたい場合、既存の類似プロダクトのようにKubernetesベースのものだと、Dockerコンテナの中でDockerを動かすDinDになってしまい、セキュリティ的にも性能的にも怪しい部分があります。さらに、もともと運用しているものがDinDだった場合、その上でDinDを提供すると、DinDinDというより怪しい形になってしまいます。これをLXDで提供することで既存でDinDを利用していた場合でもそのままで動くようになっています。LXDは古いプロダクトと思われる方もいらっしゃるかもしれませんが、現在も活発に新機能開発が進んでおり、使いやすいプロダクトだと捉えています。LXDの運用に関しては、LXD in Productionという、wikiの記事 https://lxd-ja.readthedocs.io/ja/latest/production-setup/ があり、とても詳細に書かれているので、ご興味ある方はご覧になってみてください。

●myshoes-providerの概要

次に、Cloud Providerが動いている部分について、ご紹介します。

myshoes-providerは、インスタンスを作成したり削除する際の、実行の抽象化層です。AWSや、GCP、LXDの実装が存在しており、Cloudごとに処理が異なってくるので、それを独自のバイナリで抽象化するようにしています。hashicorpが提供してるgo-pluginというプラグインのプラットフォームを使っています。gRPCベースにos.Execを使ってgRPCのサーバを立てて、ネゴシエーションする形です。

●myshoes-providerの内部動作

図に示した共通項目を、shoes-xxxというmyshoes-providerのバイナリに入れて、それをCallした上で、Cloud Providerが実行します。

初期のLXDの実装にはshoes-lxdを使っていました。shoes-lxdのバイナリを「myshoes」が毎回起動した上で、どのStadiumに投げるかを決定し、そのStadiumのLXD APを叩いて実行する流れになっていました。

●各インスタンスの負荷状態

各インスタンスの負荷状態ですが、一番上が100パーセントの部分なので、意外と分散しており、きちんと動いていることがわかります。

ところが、ユーザー数が増えてくると、Stadiumが耐え切れなくなってきます。そうなると、ちゃんと運用していても不調のあるLXDのノードが出てきます。特定のLXDのインスタンスだけAPIにコネクトできないとか、GetInstance()が返ってこないといった不調です。そこで、Build Upすることにしました。

●shoes-lxd-multiの開発

shoes-lxd-multiという名の通り、マルチノードに対応したshoes-lxdを開発しました。shoes-lxdはバイナリ実行を毎回行うためステートがないのがつらいということで、shoes-lxd-multiを作成し、その上でパブリッククラウドと同様にAPI Endpointは1つで、その上で各ホストサーバを管理できるようにしました。

インスタンスを生成するコードは、ほぼshoes-lxdからコピペしました。その上で、サーバクライアントモデルになったので、ステートをいろいろ持てるようになりました。実行するタイミングで、キャパシティがどのぐらいあるかを確認した上で実行するなど、大きく変わりました。

●shoes-lxd-multiの負荷状態

このように、インスタンスを入れる上限がうまくできました。

●収容メトリクス

これは、どのくらいインスタンスが収容されているかというメトリクスです。最大値、N倍以上は収容しないようになっています。もしそれが収容できないようなインスタンスであれば、他に順次、スケジュールすることができるようになりました。

■継続的なBuild UpのためのCI/CD戦略

「myshoes」が実行するときは、外部からバイナリを取得します。これはPLUGINという環境変数でやっていますが、「./shoes-mock」といったような相対パスやURLを書くこともできます。URLを書いた場合は、起動時にそこからダウンロードして持ってきます。これによって、開発環境はVMにnginxを入れて差し換えたり、本番はGitHub Releaseから取るといったように、変えられるようになりました。

上記が先ほど説明したKubernetes Controllerであるmyshoes-controllerのCRD、Custom Resource Definitionであるテナントリソースです。

上記が各テナントに入っているイメージです。

赤字で示した、plugin_urlのところは、GitHub Releaseを指しており、開発時はPrivate IPでVMに差し込んだりしていました。

「myshoes」も各コミットごとにコンテナイメージをビルドしています。これはGHECやGHESとともに、GitHub Actionsを利用して運用しています。Releaseコミットのコンテナをビルドして提供するのではなくて、全コミットでビルドを行っています。そうすることで、特定のテナントだけこのブランチを適用したいといった運用にも対応できます。

上記の赤字で示したmyshoes_versionの部分は、Release versionをshaのこのコミットのもの、というのを指定できるようになりました。ここまでくると、コミュニケーションも活発に運用できるようになってきます。他にも、GitHub Appsの切り替えや、このテナントにだけテスト用のStadium VMを入れたいといった差し換えもできるようになりました。

同じく、runnerイメージも定期的にビルドしています。GitHub-hostedからもvirtual-environmentsが提供されていますが、これもLXD向けに、毎日、定期的にイメージ更新することによって、ユーザとしても新しいversionが使えるようになります。

こうした改善の結果、ユーザが増えていき、後ろのStadiumをどんどん増設することになったのですが、ジョブ実行が遅延する事態が発生します。不備を直したり、可能な限り並列化の処理を入れたり、LXDのGo SDKをGoのcontextに対応するためのコントリビュートを入れたりを進めていきました。

■継続的なBuild Upのための運用

監視:Prometheus

監視は、基本的に/merticsのパスにGETする形で統一しています。Cycloud VM上で動いている冗長化されたPrometheusを使っています。何か障害が起きた際に、その起因をメトリクスとして追加して、アラートとして監視を入れていきました。

監視メトリクスの一例

一例としては、GitHubのAPIのRate LimitのExceededの問題に対するメトリクスを入れました。また、並列作成数や削除数がどのぐらいあるかや、Stadiumの利用率、動作しているインスタンスのリストを、Prometheus経由で取得できるようになっています。

インスタンス数から見える業務時間

上記は同時に実行しているインスタンス数の数を積み重ねたグラフです。業務を開始するとrunnerが作成されるので、どのくらいから業務を始めたのかといったことが可視化できたのは偶然ながらも面白かったポイントです。

ログを正しく取得する

runnerとしてログを正しく取得することはとても重要です。このジョブがおかしかったと言われたときに、きちんとそれを追えるような態勢も整えています。LXDはコンテナログをホストマシンに吐いてくれるので、これを定期的に、cronとrsyncを使って取得しています。それ以外にも、Kubernetes上でCronjobを使ってMySQLを取得したり、アプリケーションのログは全て、Prometheusに送って、Googleの提供してるmtailというソフトウェアを使って運用しています。ログを正しく取得して、それを確実に後追いできるようなプラットフォームを作っています。

■まとめ

正しく運用しやすいアプリケーションを開発するということは、正しく継続的なBuild Upができるような体制になるということです。逆にそれができていないと、一つひとつの積み重ねが段々とつらくなり、結果的に正しく継続的なBuild Upができなくなります。そのことを強く意識しながら、運用していきたいと思います。そして、まだ先の話になりますが、既にOSSになっているshoes-agentを使って、これからも順次、Build Upに取り組んでいく予定です。

「CyberAgent Developer Conference 2022」のアーカイブ動画・登壇資料は公式サイトにて公開しています。ぜひご覧ください。


https://cadc.cyberagent.co.jp/2022/

■採用情報

CIUでは、サイバーエージェントグループのインフラの困りごとをともに解決するメンバーを募集しています。
多くの取り組みが存在しているため、興味のあるかたはぜひ採用ページからご応募ください。

新卒採用 インフラ(プライベートクラウド): https://www.cyberagent.co.jp/careers/special/students/tech/?ver=2023-1.0.0
中途採用: https://hrmos.co/pages/cyberagent-group/jobs/0000653

また、学生向けには通年でインターンの受け入れなども行っておりますので、興味のある方はぜひお問い合わせください。