1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetes × Actions Runner Controller:EphemeralRunner の自己回復を実現する PR #4059 徹底解説

Last updated at Posted at 2025-05-15

Kubernetes × Actions Runner Controller:EphemeralRunner の自己回復を実現する PR #4059 徹底解説

1. 背景 ― なぜ本 PR が必要だったのか?

症状 これまでの実装
Spot インスタンス は時間が来たら終了し、EphemeralRunnerはそれを障害判定してしまう。 EphemeralRunner の status.failures1 回ごとに失敗が蓄積。
Spotインスタンスを利用している状況下で minRunners ≥ 1 にしていると Spotインスタンスの中断に必ず遭遇してしまう。 5 回 失敗(中断)すると Phase=FailedRunner CR が残留
Workflow 側からは「Runner が見つからない」状態になりジョブが詰まる。 Issue #2721 で約2年前から報告

2. これまでの制限と課題

  • 失敗上限 5 回 という定数自体は 以前から 存在

  • 上限到達後は markAsFailed() で Phase を変えるだけ だったため、

    • EphemeralRunnerSet が「Desired=1 だけど既にオブジェクトがある」と判断
    • 新しい EphemeralRunner を生成できず “ゾンビ化” していた

3. PR #4059 で何が変わったのか?

変更点 役割
status.failures型変更
map[string]boolmap[string]metav1.Time
失敗時刻を記録し 最新失敗を時系列で把握
failedRunnerBackoff 配列導入 0 s → 5 s → 10 s → 20 s → 40 s → 80 s の 指数バックオフ (GitHub)
LastFailure() ヘルパー追加 Status から 直近失敗時刻 を取得 (GitHub)
再キュー制御 失敗回数 n ≤ 5 なら RequeueAfter(backoff[n]) 相当の処理 (GitHub)
上限超えの対処を変更 Phase 変更 ではなく EphemeralRunner CR を即 Delete()
EphemeralRunnerSet が新品を再生成可 (GitHub)

💡 ポイント
上限 5 回 という定数そのものは変わっていません
「上限到達後にどうするか」Phase=Failed → オブジェクト削除 に変わり、
EphemeralRunner の 自己回復 が可能になりました


4. 実際の Spot × minRunners シナリオでの動作

  • 6 回目で CR 削除 → 再生成 するため “ゾンビ” は残らない

5. コード解説

1. 失敗を記録する Status フィールド

type EphemeralRunnerStatus struct {
    // …既存フィールド
    Failures map[string]metav1.Time `json:"failures,omitempty"`
}

func (s *EphemeralRunnerStatus) LastFailure() *metav1.Time {
    // map を走査して最新タイムスタンプを返すヘルパー
}
  • キー: Podの.metadata.uid
  • : その失敗が起きた時刻

2. 指数的バックオフ & 再キュー

var failedRunnerBackoff = []time.Duration{
    0, 5 * time.Second, 10 * time.Second,
    20 * time.Second, 40 * time.Second, 80 * time.Second,
}
const maxFailures = 5
if len(er.Status.Failures) <= maxFailures {
    delay := failedRunnerBackoff[len(er.Status.Failures)]
    return ctrl.Result{RequeueAfter: delay}, nil
}
  • 0 秒 → 5 s → 10 s … と再試行
  • 6 回目(len > maxFailures)に到達すると次のセクションへ

3. 6 回目で即削除 → EphemeralRunnerSet が再作成

// 最終フェーズ
if len(er.Status.Failures) > maxFailures {
    log.Info("Maximum failures reached – deleting EphemeralRunner")
    if err := r.Delete(ctx, er); err != nil { ... }
    return ctrl.Result{}, nil
}
  • オブジェクト自体を Delete するので Controller の再試行は終了
  • 上位リソース EphemeralRunnerSet が Desired 数を満たすために新しい EphemeralRunner を生成 → Pod → ジョブ再開

動作フローまとめ


6. まとめ

PR #4059 は Spot × minRunners 環境だと特に頻繁に遭遇する 「5 回中断すると詰む」 という長年の課題(Issue #2721)を解決しました

  • 失敗を時系列で管理し、指数バックオフで再試行
  • 上限超過時はEphemeralRunner CR を削除して EphemeralRunnerSet に再作成させる
  • これにより フルマネージドに近い自己回復 が実現
  • とはいえ ジョブが実行中にSpotの中断に遭遇して実行が阻害されるリスク は残っているので、Spotを利用する場合はその点にも注意する必要があります
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?