1 回答

TA貢獻1874條經驗 獲得超12個贊
請記住以下幾點:
每個控制器只負責一個資源。
協調請求包含協調 Kubernetes 對象所需的信息。這包括唯一標識對象的信息 - 它的名稱和命名空間。它不包含有關任何特定事件或對象內容本身的信息。
您可以在沒有資源定義的情況下創建第二個控制器。在您的主文件中,兩個控制器都將被注冊。
如果 CRD 根本不相關,或者如果外部資源引用內部資源,這可能很有用,因此您可以在外部協調器中更改內部資源。
kubebuilder create api --group other --version v2 --kind External \
--resource=false --controller=true
這為您提供了一個控制器,其SetupWithManager方法如下所示。
func (r *ExternalReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
// Uncomment the following line adding a pointer to an instance of the controlled resource as an argument
// For().
Complete(r)
}
請注意 For 方法是如何被注釋掉的,因為您需要導入資源以從其他地方觀看并引用它。
import (
...
otherv2 "other.io/external/api/v2"
)
...
func (r *ExternalReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&otherv2.External{}).
Complete(r)
}
如果您無法導入外部資源,您可以回過頭來自己模擬它,但這可能不是一種非常干凈的方法。您真的應該嘗試從其他控制器項目中導入它。
kubebuilder edit --multigroup=true
kubebuilder create api --group=other --version v2 --kind External \
--resource --controller
另一種方式是當資源相互關聯時,內部資源在其規范中引用了外部資源,并且知道如何在其協調時獲取其規范中的外部資源??梢栽谶@里找到一個例子https://book.kubebuilder.io/reference/watching-resources/externally-managed.html
type InternalSpec struct {
// Name of an external resource
ExternalResource string `json:"externalResource,omitempty"`
}
這意味著在每個協調循環中,控制器將查找外部資源并使用它來管理內部資源。
func (r *InternalReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
internal := examplev1.Internal{}
if err := r.Get(context.TODO(), types.NamespacedName{
Name: req.Name,
Namespace: req.Namespace,
}, &internal); err != nil {
return ctrl.Result{}, err
}
external := otherv2.External{}
if err := r.Get(context.TODO(), types.NamespacedName{
// note how the name is taken from the internal spec
Name: internal.Spec.ExternalResource,
Namespace: req.Namespace,
}, &internal); err != nil {
return ctrl.Result{}, err
}
// do something with internal and external here
return ctrl.Result{}, nil
}
這樣做的問題是,當內部資源沒有變化時,即使外部資源發生變化,也不會觸發協調事件。為了解決這個問題,我們可以通過觀察外部資源來觸發協調。注意Watches方法:
func (r *InternalReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&examplev1.Main{}).
Watches(
&source.Kind{Type: &otherv2.ExternalResource{}},
handler.EnqueueRequestsFromMapFunc(r.triggerReconcileBecauseExternalHasChanged),
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
).
Complete(r)
}
為了知道我們應該為哪個內部對象觸發事件,我們使用映射函數來查找所有引用外部資源的內部對象。
func (r *InternalReconciler) triggerReconcileBecauseExternalHasChanged(o client.Object) []reconcile.Request {
usedByInternals := &examplev1.InternalList{}
listOps := &client.ListOptions{
FieldSelector: fields.OneTermEqualSelector(".spec.ExternalResource", o.GetName()),
Namespace: o.GetNamespace(),
}
err := r.List(context.TODO(), usedByInternals, listOps)
if err != nil {
return []reconcile.Request{}
}
requests := make([]reconcile.Request, len(usedByInternals.Items))
for i, item := range usedByInternals.Items {
requests[i] = reconcile.Request{
NamespacedName: types.NamespacedName{
Name: item.GetName(),
Namespace: item.GetNamespace(),
},
}
}
return requests
}
由于您更新了問題,我建議您執行以下操作。
我正在創建一個新項目和 2 個控制器。注意第二個控制器命令沒有資源與控制器一起創建。這是因為控制器將監視外部資源。
mkdir demo && cd demo
go mod init example.io/demo
kubebuilder init --domain example.io --repo example.io/demo --plugins=go/v4-alpha
kubebuilder create api --group=demo --version v1 --kind Internal --controller --resource
kubebuilder create api --group=other --version v2 --kind External --controller --resource=false
$ tree controllers
controllers
├── external_controller.go
├── internal_controller.go
└── suite_test.go
現在我們需要一些共享邏輯,例如將其添加到控制器包中。我們將從兩個調解器中調用它。
// the interface may need tweaking
// depending on what you want to do with
// the reconiler
type reconciler interface {
client.Reader
client.Writer
client.StatusClient
}
func sharedLogic(r reconciler, kobj *demov1.Internal) (ctrl.Result, error) {
// do your shared logic here operating on the internal object struct
// this works out because the external controller will call this passing the
// internal object
return ctrl.Result{}, nil
}
這是內部協調器的示例。
func (r *InternalReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
obj := demov1.Internal{}
if err := r.Get(ctx, req.NamespacedName, &obj); err != nil {
return ctrl.Result{}, err
}
return sharedLogic(r, &obj)
}
在外部協調器中,我們也這樣做。
func (r *ExternalReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
// note, we can use the internal object here as long as the external object
// does contain the same fields we want. That means when unmarshalling the extra
// fields are dropped. If this cannot be done, you could first unmarshal into the external
// resource and then assign the fields you need to the internal one, before passing it down
obj := demov1.Internal{}
if err := r.Get(ctx, req.NamespacedName, &obj); err != nil {
return ctrl.Result{}, err
}
return sharedLogic(r, &obj)
}
func (r *ExternalReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
// note the external resource is imported from another project
// you may be able to watch this without import by creating a minimal
// type with the right GKV
For(otherv2.External{}).
Complete(r)
}
- 1 回答
- 0 關注
- 153 瀏覽
添加回答
舉報