kstatus: k8s リソースの status 判定ツール

公開:2021/02/05
更新:2021/02/06
4 min読了の目安(約4000字TECH技術記事

Flux2 では、kstatus 互換なカスタムリソースを Kustomization リソースのヘルスチェックターゲットにできます。

でもこの kstatus ってなんでしょうか?
全然知らなかったので調べてみました。

概観

kstatus は対象リソースの reconcile が完了しているかどうかを判定するための関数セットと、それに使用する標準的な status.conditions のフォーマットを提供するパッケージです。

このプロジェクトでは kstatus の定義する Condition が CRD の status 設計の指針となることを目指しています。

このライブラリを含む cli-utils リポジトリは SIG-CLI で開発されています。

https://github.com/kubernetes-sigs/cli-utils/tree/master/pkg/kstatus

とはいえここ三ヶ月ほど kstatus にコミットがないので、いまどれくらい熱量を持って取り組まれているのかはよくわかりません。SIG-CLI に詳しい方教えて下さい。

Statusの種類

kstatus は入力されたリソースの Status が次のいずれかであると判定します。

  • InProgress: 理想状態にまだ到達していない。(ふつうはこの状態から始まる。)
  • Failed: 理想状態に到達する前にエラーになっている。
  • Current: 理想状態に到達している。(complete 的な意味。)
  • Terminating: 削除中の状態。
  • NotFound: リソースがクラスタに無い。
  • Unknown: 判定できない。

Conditionとはなにか一応説明

k8s オブジェクトを詳細に確認したことある方なら見覚えがあるかと思いますが、k8s API には status.conditions にそのオブジェクトに起きた状態変更を記録するというパターンがあります。
API Conventions を下地に設計されたプロダクトの conditions の要素はたいてい以下のように type, status, reason, lastTransitionTime などのフィールドから構成されます。

$ kubectl get node kind-control-plane -o json | jq .status.conditions
[
  {
    "lastHeartbeatTime": "2021-02-05T12:13:17Z",
    "lastTransitionTime": "2021-02-04T10:27:54Z",
    "message": "kubelet has sufficient memory available",
    "reason": "KubeletHasSufficientMemory",
    "status": "False",
    "type": "MemoryPressure"
  },
  {
    "lastHeartbeatTime": "2021-02-05T12:13:17Z",
    "lastTransitionTime": "2021-02-04T10:27:54Z",
    "message": "kubelet has no disk pressure",
    "reason": "KubeletHasNoDiskPressure",
    "status": "False",
    "type": "DiskPressure"
  },
...
]

この type というフィールドには何に関する condition なのかを示すための文字列が入れられるのですが、どんな文字列が入ってくるかはユーザー定義です。慣習のようなものはなんとなくありますが、今の所標準化はされていません。

kstatus が定義する Standard Condition

kstatus は以下の condition を standard な type として定義しています。
いずれの condition も安定状態から外れているときに true となるように true/false の向きを揃えて設計されています。

  • Reconciling: 理想状態に収束するための処理がコントローラにより実行中である
  • Stalled: Reconcile の最中にエラーが生じている、あるいはタイムアウトなどが生じている

もちろんこれ以外の type をユーザーが追加定義することもできます。
あくまで reconcile 完了判定に使用する condition type として上記を定義しているだけです。

パッケージ構成

2つのパッケージからなります。

  • polling パッケージ: apiserver と通信し、リソースの Status 変更をウォッチするための channel を提供する
  • status パッケージ: リソースの Status を判定する関数や status.conditions を kstatus の標準形に変換する関数を提供する

status パッケージ

status パッケージの中心は Compute() 関数です。これは入力された Unstructured オブジェクトが、前述の Status のうちどれに類するかを判定する関数です。

2021年2月5日時点の実装では以下の情報を元に Status を判定しています。

  • metadata.deletionTimestamp
  • metadata.generationstatus.observedGeneration
  • status.conditions
  • 一部のビルトインリソースのレガシーなフィールド (例: pod.status.phase )

status.conditions

status.conditions のうち type が Stalled あるいは Reconciling であるものに加えて、慣習として広く使われている type=Ready な condition も判定に使用します。

Reconciling-True な condition が見つかると InProgress Status と判定します。
Stalled-True な condition が見つかると Failed Status と判定します。
Ready-True な condition が見つかると Current Status と判定します。

metadata.deletionTimestamp

metadata.deletionTimestamp が non-nil なら Terminating Status と判定します。

metadata.generationstatus.observedGeneration

metadata.generationstatus.observedGeneration に差があれば InProgress Status と判定します。

一部のビルトインリソースのレガシーなフィールド

今の k8s API では status.conditions にぶら下がっている内容からその Status を判定するのが定石とされているのですが、
その定石が生まれるより前に設計された k8s v1 のビルトインリソースは、
status.phase などのフィールドが生えていて、それが Reconcile 状況を表しています。
これを踏まえ、一部のコアリソースの持つレガシーなフィールドをもとに Status を判定するロジックが実装されています。

判定できない場合はどうなる?

上記のルールに従うといずれのケースにも入らない場合があります。情報不十分なケースもこれに含まれます。
その場合は kstatus は Current Status つまり理想状態に到達していると判定します。

これは安定状態から外れているときに true を返す abnormal-true という設計方針を採用している結果です。
kstatus を使うときは、condition を更新し忘れないようにしましょう。