CIが全部BuildFailedで落ちた原因は、コードではなく請求ロックでした

 ・ 6

photo by Cristina Gottardi(https://unsplash.com/@cristina_gottardi?utm_source=templater_proxy&utm_medium=referral) on Unsplash

CIが急に全部レッドになると、たいていはコードや設定をまず疑いますよね。でも今回はコードでもYAMLでもrunnerでもありませんでした。今日は開発の問題というより、運用の問題に近いトラブルシューティングをしたんです。

始まりはシンプルでした。私たちの組織のいくつかのprivate repoで、GitHub Actions CIが全部失敗していたんです。最初はself-hosted runnerの問題だと思いました。最近ローカルのMac miniをGitHub Actionsのrunnerとして接続して、複数のrepoのCIをself-hosted runnerで回すように変えたばかりだったからです。

ところが症状が変でした。

普通、runnerに問題があると、jobが作られたあとにqueued状態で止まったり、「matching runnerがない」といった形で失敗します。ところが今回はjobそのものがありませんでした。

代表的なrunの状態はこうでした。

conclusion: startup_failure
workflowName: ""
path: BuildFailed
jobs: []
logs: 404

ログもありませんでした。正確には、ログが作られる前に死んでいたんです。

最初に疑ったもの: self-hosted runnerの設定#

まず疑ったのはローカルのrunnerでした。

実際、過去の設定には問題がありました。runnerがデフォルトの_workフォルダを使っていて、外付けSSDに作業ディレクトリを置くには--workオプションを渡す必要があったんです。

今はこのように直っていました。

workFolder: /mnt/ssd/actions-work/my-org

runnerもonline状態でした。

runner: local-mac-mini
status: online
busy: false
labels:
- self-hosted
- macOS
- ARM64
- local
- mac-mini

それでも問題は続きました。

そこで、self-hosted runnerをまったく使わない、ごくシンプルなsmoke workflowも作ってみました。

name: Actions Smoke Test
 
on:
  push:
    branches:
      - test/actions-smoke
  workflow_dispatch:
 
jobs:
  smoke:
    runs-on: ubuntu-latest
    steps:
      - name: Print smoke marker
        run: echo "actions-smoke-ok"

ところがこれも同じように失敗しました。

conclusion: startup_failure
workflowName: ""
path: BuildFailed
jobs: []

この時点で、runnerだけの問題ではないと見るべきでした。ubuntu-latestもrunnerに到達する前に死んでいたからです。

二番目の疑い: Enterprise / Organizationのrunner group の絡まり#

次に疑ったのはGitHub Enterpriseの設定でした。

最近Enterpriseの利用が終わったのですが、Organization側のActions runner group画面にこんな警告が出ていたんです。

Runner group Default already exists at the Enterprise level.
Please consider renaming this group.

これは十分に怪しかったです。

organizationにはDefaultというrunner groupがあり、Enterprise levelにもDefaultが残っているように見えました。しかもAPIで見ると、Default runner groupが二つあるように見えたんです。

id 1: Default, visibility=all
id 3: Default, visibility=selected

そのためしばらくは、「Enterpriseが終わったのに、runner group policyが残ってorgのActionsが絡まったんじゃないか?」と考えていました。

この仮説も悪くはありませんでした。実際、Enterprise終了後の残りの設定は整理するのが正しいですし、runner groupの名前もDefaultよりlocal-mac-miniのような区別できる名前に変えたほうがいいです。

でもこれも決定的な原因ではありませんでした。というのも、private repoだけでなく、GitHub-hosted runnerを使うsmoke workflowまでBuildFailedで死んだからです。runner groupが原因なら、少なくともubuntu-latestのworkflowは違う形で失敗するはずでした。

GitHub Supportの回答: billing lock#

結局GitHub Supportに問い合わせました。回答は意外とシンプルでした。

The BuildFailed behavior occurs when an account's billing is locked,
blocking access to billable features, like Actions.

つまり原因はコードでもYAMLでもrunnerでもありませんでした。billing lock(請求ロック)のせいでActionsそのものがブロックされた状態だったんです。

この説明なら、すべての症状が噛み合います。

  • 複数のprivate repoで同時に失敗
  • self-hosted runnerも失敗
  • ubuntu-latestも失敗
  • jobが作られない
  • logsエンドポイントが404
  • workflow nameが空
  • BuildFailedというsynthetic workflowができる

GitHub Actionsはprivate repoではbillable feature(課金対象の機能)として扱われます。billing accountがlocked状態だと、runnerの種類に関係なくworkflow startupの段階でブロックされることがあるんです。

問題だったのはorganizationアカウントでした#

さらにSupportに確認した結果、問題だったbilling accountは個人アカウントではなく、organizationアカウントでした。

これで範囲が確定しました。

最初は個人アカウントの問題かもしれないし、終了したEnterpriseの問題かもしれないし、organizationの問題かもしれませんでした。でも最終的にはorganizationのbilling lockでした。

Supportからは、billing teamへ新しいチケットを開くよう案内されました。

今回学んだこと#

今回のトラブルシューティングで得た教訓はかなり明確です。

1. BuildFailed + startup_failure + jobs=[]はrunnerの問題ではないかもしれない#

runnerの問題なら、たいていjobは作られます。たとえばこんな形になります。

queued
waiting for runner
no matching runner

ところが今回のようにこう出るなら:

workflowName: ""
path: BuildFailed
jobs: []
logs: 404

workflowがrunnerに到達する前に死んだということです。この場合、YAMLやrunnerのlabel、runnerがonlineかどうかだけを追いかけていると、多くの時間を無駄にしかねません。

2. private repoのActionsはbilling状態の影響を受ける#

self-hosted runnerを使っても、ActionsそのものはGitHubのbillable featureの領域に引っかかります。だから「自分のrunnerで回っているのに、なぜbillingが問題なんだ?」と考えてはいけません。

Actionsのorchestration(作業をキューに入れてrunnerに割り当てる過程)そのものがGitHubのfeatureだからです。

3. Enterprise終了後はbilling / accountのscopeを必ず確認する#

Enterpriseを終了したからといって、残りの状態がすべてきれいに消えるわけではなさそうです。少なくとも今回は、これら全部が絡み合っていました。

  • Enterpriseの終了
  • Organization billing
  • Actions runner group
  • private repoのActions
  • Support ticketのrouting

Enterpriseが終わったなら、次を確認してみてください。

Personal billing
Organization billing
Enterprise billing
Actions access
Runner groups
Outstanding balance
Seats
Billing cycle

特に、支払い情報を追加する前に「いくらが即座に請求されるのか」を確認するのが大事です。

最終まとめ#

今回の問題のroot causeはこう整理できます。

organization billing lock
→ GitHub Actions billable feature blocked
→ all private repo workflows fail at startup
→ synthetic BuildFailed workflow
→ jobs=[]
→ logs=404

次のアクションはrunnerを再インストールすることではなく、billing teamに確認することです。確認すべき要点はこれくらいです。

  1. 未払い残高(outstanding balance)があるか、あるなら正確な金額はいくらか
  2. 支払い情報を追加すると即座にいくら請求されるか(月次請求への切り替えが可能かを含む)
  3. 終了したEnterpriseのbilling scopeとまだ紐づいているか

今日の結論はこれです。CIが落ちたからといって、いつもコードやrunnerの問題とは限りません。 ときには請求状態が、いちばん低いレイヤーのインフラ障害のように現れるんです。症状がどう見てもインフラっぽいのに何も引っかからないなら、billingの状態も一度開いてみてください。


When you have eliminated the impossible, whatever remains, however improbable, must be the truth.

— Arthur Conan Doyle


他の投稿
開業届を出す前に確認しておきたいこと 커버 이미지
 ・ 4

開業届を出す前に確認しておきたいこと

新しい会社への入社前、エンジニアが準備しておくとよいこと 커버 이미지
 ・ 6

新しい会社への入社前、エンジニアが準備しておくとよいこと

特許・起業家精神・IP起業の入門 커버 이미지
 ・ 10

特許・起業家精神・IP起業の入門