Upgrade to Pro — share decks privately, control downloads, hide ads and more …

大規模レガシーテストを 倒すための CI基盤の作り方 / #CICD2023

大規模レガシーテストを 倒すための CI基盤の作り方 / #CICD2023

KONDO Uchio

March 20, 2023
Tweet

More Decks by KONDO Uchio

Other Decks in Technology

Transcript

  1. アジェンダ
 今日お話しすること
 • ミラティブにおける CI
 • Google Cloud + Cloud

    Buildを選んだ理由
 • Cloud Build を使う上での課題と対応実装
 ◦ GitHub との連携
 ◦ skip ciの罠の回避
 ◦ 大規模なテスト移行に際し挑戦したこと、他
 • まとめ
 ◦ CI基盤を作った上での学び
 ◦ ここでも推測するな、計測せよ

  2. ミラティブにおけるCI
 今回のスコープ=アプリケーションサーバのチームのCI
 定期実行するもの: ・☑ Go のテスト
 ・☑ E2E テスト
 ・☑

    Perl のテスト(prove)
 ・☑ Lint (Go, perlcritic, YAML諸々)
 ・☑ フロントエンド系 (assetsのビルド)
 リリース時に実行するもの: ・☑ イメージ作成+GitHub Release

  3. CI基盤にCloud Buildを選んだ理由
 CIもサーバレスでいこう
 ・例えば Jenkins on Compute、Tekton on GKE のような選択肢もあるが...


    ・今回はCloud Buildを軸に構築した:
 ・実行基盤の管理はマネージドにしたい
 ・細かい機能(トリガー、GitHub checksの更新等)を任せる
 ・従量課金で実行したい

  4. Cloud Functionを最初に叩いて発行
 ・Cloud Functionを経由してトークンを発行する
 🤖
 Function 起動
 Secret Key->JWT
 ->

    Token API
 Token 発行
 出力
 保存して
 その後の
 stepで利用
 Cloud Build Cloud Functions GitHub App
  5. GitHub App でのトークン発行後
 ・Goのプロジェクトの場合、以下の感じで url.*insteadOf を定義すると
  そのまま `go get/go mod

    download` ができて便利
 
 
 ・チェックアウトはもちろん、Permissionsを適切に設定して
  例えばreviewdogのトークンにも使える

  6. 原因: skip ci の際の挙動に問題が
 ・コミットなどに `skip ci` の文字列が含まれていると、
  Cloud Buildのトリガーが走らない...


    
 > [skip ci] または [ci skip] を commit メッセージに追加すると、ビルドは呼び出されません。 
 ・運用上あえてskip ciにしてるコミットがあった(マスタだけ更新等)
 →そのコミットがデプロイ対象の場合、tag pushしても動かない
 普通は問題ないけどたまに困るやつ
 https://cloud.google.com/build/docs/automating-builds/create-manage-triggers?hl=ja#skipping_a_build_trigger
  7. 回避策: Cloud BuildのPub/Subトリガー
 ・これを使うと、コミットの内容に関係なくトリガーを走らせることができる
 💻 リリース作成
 = Tag Push
 Webhook

    で
 Push eventを 送る
 Cloud Function で 受け取って
 Pub/Subに投げる
 Cloud Build
 TriggerでPub/Subを 選択
 Cloud Pub/Subは本当に便利

  8. checks を上書きすることもできる
 ・GitHub Appに checks: write のPermissionを付与する 
 ・GitHub PR

    → Webhook → Cloud Function → Checks API でできる 
 
 Webhook で
 PRのeventを 送る
 1) GitHub Appから
 トークン発行
 2) Checks API経由で更 新

  9. 課題: Perl のテスト (prove) 移行
 ・最初に説明した通り、創業当初からあるコード+テスト
 ・むしろテストはしっかり書いている。しかしどうしても:
 ・古くなる
 ・多くなる ←

    特にこれが大変
 ・だが、テストがある程度サービスを守ってくれたことも事実。
  どうにかして現実的に運用し続ける方法を考えたい
 ただレガシーと片付けるのではなく向き合う

  10. 具体的な流れ
 PRの
 作成
 PRトリガー
 の起動
 ソースコードを
 一度GCSに固める
 テスト対象ファイルを
 Payloadに含めて
 Topic

    Publish
 Topicの数だけ
 テストジョブが起動
 コード、実行イメージを GCSから落とす
 ……
  11. ちなみに: 通知もPub/Subで
 ・Cloud Buildのジョブのキュー、開始、終了など、全てのイベントはcloud-builds というト ピックにpublishされる
 
 
 
 


    ・Cloud Functionでsubscribeして、中身を検査して通知したい時だけ
  Slackに送る、ということをしている

  12. 1) インスタンス、並行数を決定
 ・何も考えずにたくさん詰め込むと遅くなる
 ・所要時間もだが、LAなどリソースのメトリックも見て決定
 1分程度の劣化で収まり、 LA1が5を恒久的に超えない(8コアで)
 1 step 2 steps

    3 steps 4 steps 5 steps 6 steps 所要時間 4m42s 5m10s 5m53s 6m06s 6m59s タイムアウト 増分 +28s +43s +13s +53s LA1 3 ~ 4 4 ~ 5.5 4 ~ 5 5 ~ 6 6 ~ 7.5 メモリ不足 ※ n1-highcpu-8 で同じテストケースで計測 

  13. CPU周りを調整した比較
 設定なし CPU cpuset CPU shares #1 7m56s 7m33s 7m21s

    #2 9m14s 7m24s 7m36s #3 7m24s 7m08s 7m37s AVG. 8m11s 7m21s 7m31s ※ n1-highcpu-8 で同じテストケース/3 stepsで計測 ※ cpuset = MySQL に3コア、 perl テストに 5コア ※ shares = MySQL は shares=4096、perlは shares=2048
  14. 3) ホストネットワークの利用
 ・cloud buildはデフォルト、
  cloudbuildという内部ネットワークにアタッチしてstepを起動
 ・MySQLなど追加サービスを利用する場合は、docker composeなどで
  cloudbuildにアタッチさせて起動
 ・vethという仮想ネットワークを作るので
  少しだけオーバーヘッドがある


     → --network=host で比べてみる
 ・普通は気にならないが
  MySQLのボトルネックを無くしたら
  少し差が出た
 ※ n1-highcpu-8 で同じテストケースで計測 net=host net=cloudbuild #1 7m21s 7m52s #2 7m36s 7m37s #3 7m37s 8m28s AVG. 7m31s 7m59s
  15. 現在の進捗
 定期実行するもの: ・✅ Go のテスト
 ・✅ E2E テスト
 ・🚧 Perl

    のテスト(prove)
 ・✅ Lint (Go, perlcritic, YAML諸々)
 ・✅ フロントエンド系 (assetsのビルド)
 リリース時に実行するもの: ・✅ イメージ作成+GitHub Release
 Perl はもう少し開発基盤チームと
 チューニングしてから移行予定
  16. Cloud Build 導入でやったこと
 ・GitHub といい感じに連携できるようにする
 GitHub App と Cloud Functions

    を使い倒す
 ・skip ciの際の挙動の罠を上手に回避する
 ・数の多いテストを回す仕組みを作る
 ・色々とチューニングをして、コストを最適化する
 もちろんテスト時間の短縮は開発生産性にダイレクトに効く
 今日お話ししたことのまとめ