Kubernetesコントローラー開発におけるFinalizerと削除処理の詳細解説
Kubernetesのカスタムコントローラーを開発する際、リソースの作成や更新だけでなく、削除時の適切な処理が重要です。特に、関連する外部リソースのクリーンアップを確実に行うためには、 Finalizer(Finalizer) の活用が不可欠です。
本記事では、HelmReleaseProxy
コントローラーにおける削除処理を詳しく解説し、このパターンが一般的なコントローラー開発にも適用可能であることを説明します。また、実装レベルでの詳細や、リソースのクリーンアップが確実に行われるための工夫についても触れます。
はじめに
HelmReleaseProxy
は、Kubernetes上でHelmチャートを管理するためのカスタムリソースです。このリソースは、HelmChartProxy
を経由して作成されます。リソースの削除時には、関連するHelmリリースや外部リソースを適切にクリーンアップする必要があります。
この削除処理の詳細な実装は、以下のソースコードで確認できます:
削除処理の流れと実装詳細
1. 削除状態の確認
まず、リソースが削除中かどうかを確認します。
if helmReleaseProxy.ObjectMeta.DeletionTimestamp.IsZero() {
// リソースは削除されていない
} else {
// リソースは削除中
}
-
DeletionTimestamp
がゼロ値の場合:リソースは削除されていません。 -
DeletionTimestamp
が設定されている場合:リソースは削除プロセス中です。
2. Finalizerの追加
削除されていないリソースに対して、以下の条件でFinalizerを追加します。
-
条件:
-
DeletionTimestamp
が設定されていない。 - リソースの
finalizers
に、指定したFinalizerが含まれていない。
-
実装:
if !controllerutil.ContainsFinalizer(helmReleaseProxy, finalizerName) {
controllerutil.AddFinalizer(helmReleaseProxy, finalizerName)
if err := patchHelmReleaseProxy(ctx, patchHelper, helmReleaseProxy); err != nil {
return ctrl.Result{}, err
}
}
- 目的:Finalizerを追加することで、削除時に必要なクリーンアップ処理が完了するまでリソースの削除を遅延させます。
- 注意点:Finalizerの追加は、コントローラーがリソースを処理をする一番最初のステップで行う必要があります。例えばリソースを処理した後にFinalizerを追加する様な処理の場合だと、Finalizerを追加する前のステップでコントローラーが強制終了されて起動しない状態になったら、Finalizerが追加できなくなり、クリーンアップ処理が実行できなくなる可能性が出てきます。
3. 削除中の処理とクリーンアップ
リソースが削除中で、かつFinalizerが設定されている場合、関連する外部リソースのクリーンアップを行います。
実装:
if controllerutil.ContainsFinalizer(helmReleaseProxy, finalizerName) {
// クラスタの取得
if err := r.Client.Get(ctx, clusterKey, cluster); err == nil {
// クラスタが存在する場合
// クラスタのkubeconfigを取得
restConfig, err := remote.RESTConfig(ctx, "caaph", r.Client, clusterKey)
if err != nil {
return ctrl.Result{}, err
}
// 外部リソース(Helmリリース)の削除
if err := r.reconcileDelete(ctx, helmReleaseProxy, r.HelmClient, restConfig); err != nil {
return ctrl.Result{}, err
}
} else if apierrors.IsNotFound(err) {
// クラスタが存在しない場合、クリーンアップは不要
} else {
// その他のエラー処理
return ctrl.Result{}, err
}
// Finalizerの削除
controllerutil.RemoveFinalizer(helmReleaseProxy, finalizerName)
if err := patchHelmReleaseProxy(ctx, patchHelper, helmReleaseProxy); err != nil {
return ctrl.Result{}, err
}
}
ポイント
-
クラスタの存在確認:
- クラスタが存在する場合のみ、外部リソースの削除を実行します。
- クラスタが既に削除されている場合、関連する外部リソース(Helmリリース)も削除できないため、その旨をログで記録し、finalizerを完了させます。
4. Runtime hooksとの連携
問題点:
- クラスタが先に削除されると、関連するHelmリリースが残ったままになる可能性があります。
解決策:
-
Runtime hooksを利用:
HelmChartProxy
コントローラーをクラスタのライフサイクルにフックさせ、クラスタ削除前に関連するリソースのクリーンアップを行います。 -
例:
- クラスタの削除要求を検知した際に、関連する
HelmChartProxy
やHelmReleaseProxy
の削除処理を優先的に実行します。
- クラスタの削除要求を検知した際に、関連する
このproposalの詳細は、以下で確認できます:
20220414-runtime-hooks.md · kubernetes-sigs/cluster-api
5. Finalizerの削除
クリーンアップ処理が完了したら、Finalizerを削除します。これにより、Kubernetesはリソースの最終的な削除を進めることができます。
実装:
controllerutil.RemoveFinalizer(helmReleaseProxy, finalizerName)
if err := patchHelmReleaseProxy(ctx, patchHelper, helmReleaseProxy); err != nil {
return ctrl.Result{}, err
}
6. Reconciliationの終了
return ctrl.Result{}, nil
- 削除処理が完了したため、Reconciliationを終了します。
一般的なコントローラー開発への適用
この削除処理のモデルは、他のKubernetesコントローラー開発でも広く適用できます。
Finalizer活用の手順(実装レベル)
-
削除状態の確認:
if resource.ObjectMeta.DeletionTimestamp.IsZero() { // リソースは削除されていない } else { // リソースは削除中 }
-
Finalizerの追加:
-
条件:
-
DeletionTimestamp
が設定されていない。 -
finalizers
に対象のFinalizerが含まれていない。
-
-
実装:
if !controllerutil.ContainsFinalizer(resource, finalizerName) { controllerutil.AddFinalizer(resource, finalizerName) // リソースの更新 }
-
-
削除中のクリーンアップ処理:
-
条件:
DeletionTimestamp
が設定されており、Finalizerが含まれている場合。 -
実装:
if controllerutil.ContainsFinalizer(resource, finalizerName) { // 外部リソースのクリーンアップ // Finalizerの削除 }
-
クリーンアップの確実性とリソースの整合性
- 問題点:関連リソースが削除される順序によっては、クリーンアップが完全には実行できない場合があります。
HelmChartProxy
との関係
-
HelmReleaseProxy
の生成:HelmReleaseProxy
はHelmChartProxy
を経由して作成されます。
実装上のポイント
-
エラーハンドリング:外部リソースの削除中にエラーが発生した場合、適切にログを記録し、リソースの状態を更新して再試行を促します。
-
リソースの状態管理:Conditionsを活用して、リソースの現在の状態やエラー情報を明確にします。
-
依存関係の管理:リソース間の依存関係を明確にし、削除順序を制御します。
まとめ
Kubernetesコントローラー開発において、リソース削除時の適切な処理とFinalizerの活用は、外部リソースの整合性とシステムの健全性を維持する上で不可欠です。本記事で解説したHelmReleaseProxy
コントローラーの削除処理は、その一例です。
このモデルは、他のコントローラー開発でも広く適用可能です。実装レベルでの詳細を把握し、自身のコントローラーに適用することで、より堅牢なシステムを構築できます。
参考リンク
注意:本記事で取り上げたコード例はApache License 2.0の下で提供されています。