Finalizer 的用途
Finalizer 提供了一种机制,允许在对象生命周期的结束阶段执行自定义的清理逻辑。这对于那些需要在对象被删除前执行特定操作的应用来说非常有用。例如,你可能希望在删除一个 Pod 之前先将其日志导出,或者在删除一个带有持久卷的自定义资源之前先删除对应的卷。
如何使用 Finalizers
在 Kubernetes 中,你可以通过更新对象的 metadata.finalizers 字段来使用 finalizers。以下是一个简单的例子,展示了如何为一个 Pod 对象添加一个 finalizer:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
finalizers:
- my.example.com/my-finalizer
spec:
containers:
- name: my-container
image: my-image
在上面的例子中,我们为名为 "my-pod" 的 Pod 添加了一个名为 "my.example.com/my-finalizer" 的 finalizer。当这个 Pod 被删除时,Kubernetes 会尝试执行与 "my.example.com/my-finalizer" 对应的清理逻辑。
package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
kubeconfig := flag.String("kubeconfig", "", "Path to the kubeconfig file")
flag.Parse()
// 创建 Kubernetes 客户端
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
panic(err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
// 创建一个资源的 Informer
stopCh := make(chan struct{})
defer close(stopCh)
resourceInformer := createResourceInformer(clientset)
// 监听资源删除事件
resourceInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
DeleteFunc: func(obj interface{}) {
resource := obj.(*corev1.MyResource)
finalizers := resource.GetFinalizers()
for _, finalizer := range finalizers {
if finalizer == "finalizer.example.com/cleanup" {
// 执行清理逻辑,比如发送通知邮件
err := cleanupResource(resource)
if err != nil {
fmt.Printf("Failed to cleanup resource: %v\n", err)
} else {
fmt.Println("Resource successfully cleaned up")
}
// 移除 finalizer
resource.SetFinalizers(removeFinalizer(resource.GetFinalizers(), finalizer))
_, err = clientset.ExampleV1().MyResources(resource.GetNamespace()).Update(context.TODO(), resource, metav1.UpdateOptions{})
if err != nil {
fmt.Printf("Failed to remove finalizer: %v\n", err)
} else {
fmt.Println("Finalizer removed")
}
}
}
},
})
// 启动 Informer
go resourceInformer.Run(stopCh)
// 等待中断信号
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM)
<-signalCh
}
// 创建资源的 Informer
func createResourceInformer(clientset kubernetes.Interface) cache.SharedIndexInformer {
resourceInformerFactory := informers.NewSharedInformerFactoryWithOptions(
clientset,
time.Second*30,
informers.WithNamespace(corev1.NamespaceAll),
)
resourceInformer := resourceInformerFactory.Example().V1().MyResources().Informer()
return resourceInformer
}
// 执行清理逻辑
func cleanupResource(resource *corev1.MyResource) error {
// 执行你的清理逻辑,比如发送通知邮件等
fmt.Printf("Cleaning up resource: %s\n", resource.Name)
return nil
}
// 移除 finalizer
func removeFinalizer(finalizers []string, finalizerToRemove string) []string {
result := []string{}
for _, finalizer := range finalizers {
if finalizer != finalizerToRemove {
result = append(result, finalizer)
}
}
return result
}
执行清理逻辑
为了执行与 finalizer 对应的清理逻辑,你需要实现一个控制器来监听对象的删除事件,并检查 metadata.finalizers 字段。当控制器发现一个对象包含它感兴趣的 finalizer 时,它可以执行相应的清理操作,并在完成后从 metadata.finalizers 列表中移除该 finalizer。
请注意,控制器需要以某种方式通知 Kubernetes 清理操作已完成。这通常通过更新对象的 metadata.finalizers 字段来实现。一旦所有的 finalizer 都被移除,对象就会被从 etcd 中永久删除。
处理 Finalizer 失败的情况
如果清理操作失败,你需要决定如何处理这种情况。一种策略是重试清理操作,直到成功为止。另一种策略是将失败记录在对象中(例如,通过添加一个状态字段),然后让管理员手动介入解决问题。无论你选择哪种策略,都应该确保在最终移除 finalizer 之前,清理操作能够正确完成或得到妥善处理。
总结
Finalizer 是 Kubernetes 对象中一个强大的特性,它允许你在对象被删除前执行自定义的清理逻辑。通过正确使用 finalizers,你可以确保在对象生命周期的结束阶段执行必要的操作,从而避免数据丢失或其他潜在问题。然而,使用 finalizers 时也需要注意处理清理操作失败的情况,以确保系统的稳定性和可靠性。