如何解决如何为任意对象触发 Kubernetes 控制器协调器?
概述
我正在为 VerticalScaler CRD 编写一个 Kubernetes 控制器,它可以垂直扩展集群中的部署。我的规范引用了集群中现有的 Deployment 对象。如果引用的 Deployment 被修改或删除,我想对 VerticalScaler 的协调请求进行排队。
// VerticalScalerSpec defines the desired state of VerticalScaler.
type VerticalScalerSpec struct {
// Name of the Deployment object which will be auto-scaled.
DeploymentName string `json:"deploymentName"`
}
问题
当该资源不为控制器所有,并且该资源不持有对其资源由控制器管理的对象的引用时,是否有一种观察任意资源的好方法?
我发现了什么
我认为这应该在控制器的 Kubebuilder 标准 SetupWithManager 函数中进行配置,尽管可以在其他地方设置手表。
// SetupWithManager sets up the controller with the Manager.
func (r *VerticalScalerReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1beta1.VerticalScaler{}).
Complete(r)
}
我一直在 controller-runtime/pkg/builder 和 Kubebuilder 文档中寻找好的方法。我找到的最接近的例子是 kubebuilder-v1 docs on watches 中的“观看任意资源”部分:
控制器可以监视任意资源并将它们映射到控制器管理的资源的键。控制器甚至可以将一个事件映射到多个键,为每个键触发 Reconciles。
示例:为了响应集群扩展事件(例如删除或添加节点),控制器将监视节点并将监视事件映射到控制器管理的对象的键。
我的挑战是如何将部署映射到依赖的 VerticalScaler,因为部署中不存在此信息。我可以在 VerticalScaler 上 create an index 并使用字段选择器从 MapFunc 中查找依赖 VerticalScalers,但似乎我不应该在 MapFunc 中进行 I/O。如果 list-Deployments 操作失败,我将无法重试或重新排队更改。
我使用这种不完美的方法使这段代码工作:
const deploymentNameIndexField = ".metadata.deploymentName"
// SetupWithManager sets up the controller with the Manager.
func (r *VerticalScalerReconciler) SetupWithManager(mgr ctrl.Manager) error {
if err := r.createIndices(mgr); err != nil {
return err
}
return ctrl.NewControllerManagedBy(mgr).
For(&v1beta1.VerticalScaler{}).
Watches(
&source.Kind{Type: &appsv1.Deployment{}},handler.EnqueueRequestsFromMapFunc(r.mapDeploymentToRequests)).
Complete(r)
}
func (r *VerticalScalerReconciler) createIndices(mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(
context.Background(),&v1beta1.VerticalScaler{},deploymentNameIndexField,func(object client.Object) []string {
vs := object.(*v1beta1.VerticalScaler)
if vs.Spec.DeploymentName == "" {
return nil
}
return []string{vs.Spec.DeploymentName}
})
}
func (r *VerticalScalerReconciler) mapDeploymentToRequests(object client.Object) []reconcile.Request {
deployment := object.(*appsv1.Deployment)
ctx,cancel := context.WithTimeout(context.Background(),time.Minute)
defer cancel()
var vsList v1beta1.VerticalScalerList
if err := r.List(ctx,&vsList,client.InNamespace(deployment.Namespace),client.MatchingFields{deploymentNameIndexField: deployment.Name},); err != nil {
r.Log.Error(err,"could not list VerticalScalers. " +
"change to Deployment %s.%s will not be reconciled.",deployment.Name,deployment.Namespace)
return nil
}
requests := make([]reconcile.Request,len(vsList.Items))
for i,vs := range vsList.Items {
requests[i] = reconcile.Request{
NamespacedName: client.ObjectKeyFromObject(&vs),}
}
return requests
}
其他考虑过的方法
只是为了涵盖我的基础,我应该提到我不想将 VerticalScaler 设置为 Deployment 的所有者,因为如果 VerticalScaler 被删除,我不想对 Deployment 进行垃圾收集。即使是非控制器 ownerReference 也会导致垃圾回收。
我也考虑过使用 Channel 观察器,但文档说这是针对源自集群外部的事件,而事实并非如此。
我也可以为 Deployment 创建一个单独的控制器,并从该控制器的 Reconcile 函数更新依赖 VerticalScaler(s) 的一些字段,但是我还需要一个终结器来处理在 Deployment 被删除时触发 VerticalScaler 协调,这似乎有点矫枉过正。
我可以让我的 VerticalScaler 协调器向部署添加注释,但如果由 Helm 等管理,部署注释可能会被覆盖。在部署之前创建 VerticalScaler 的情况下,这也不会导致协调请求。
解决方法
您确实使用了地图功能和普通手表。 https://github.com/coderanger/migrations-operator/blob/088a3b832f0acab4bfe02c03a4404628c5ddfd97/components/migrations.go#L64-L91 显示了一个示例。您最终确实经常不得不在 map 函数中执行 I/O 以计算出这件事对应于哪个根对象,但我同意这有点糟糕,如果这些调用,除了 log 或 panic 之外别无他法失败。
您还可以使用非控制器所有者引用或注释作为存储给定部署的映射目标的一种方式,这使得映射功能更简单,但通常响应速度也较慢。总的来说,这取决于这需要有多动态。欢迎访问 #kubebuilder Slack 频道寻求帮助。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。