namespaceSelector를 사용한 관리 범위 설정
기본 동작
controller-runtime의 Manager는 기본적으로 모든 namespace의 리소스를 감시(watch)합니다. ctrl.NewManager에 별도 cache 설정을 하지 않으면 클러스터 전체를 대상으로 동작합니다.
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
})
특정 namespace만 감시하기
명시적 namespace 지정
cache.Options.DefaultNamespaces에 감시할 namespace 이름을 지정하면, informer cache가 해당 namespace의 리소스만 감시합니다.
managerOpts := ctrl.Options{
Scheme: scheme,
Cache: cache.Options{
DefaultNamespaces: map[string]cache.Config{
"namespace-a": {},
"namespace-b": {},
},
},
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), managerOpts)
DefaultNamespaces에 포함되지 않은 namespace의 리소스는 client.Get, client.List 등으로도 조회할 수 없습니다. 다른 namespace의 리소스를 참조해야 한다면 해당 namespace도 추가해야 합니다.
Label selector로 namespace 선택 (predicate 필터링)
namespace 이름을 하드코딩하는 대신, namespace label을 기반으로 동적 필터링할 수 있습니다. cache는 모든 namespace를 감시하되, predicate에서 namespace label을 확인하여 reconciler가 처리할 리소스를 제한합니다.
import (
"context"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
)
// namespaceLabelPredicate는 리소스가 속한 namespace에 특정 label이 있는 경우만 통과시킵니다.
func namespaceLabelPredicate(c client.Reader, key, value string) predicate.Funcs {
check := func(obj client.Object) bool {
ns := &corev1.Namespace{}
if err := c.Get(context.Background(), client.ObjectKey{Name: obj.GetNamespace()}, ns); err != nil {
return false
}
return ns.Labels[key] == value
}
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool { return check(e.Object) },
UpdateFunc: func(e event.UpdateEvent) bool { return check(e.ObjectNew) },
DeleteFunc: func(e event.DeleteEvent) bool { return check(e.Object) },
GenericFunc: func(e event.GenericEvent) bool { return check(e.Object) },
}
}
func (r *TestReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.Test{}, builder.WithPredicates(
namespaceLabelPredicate(mgr.GetClient(), "my-operator.io/watch", "true"),
)).
Named("test").
Complete(r)
}
DefaultNamespaces는 Manager 시작 시 고정되며 런타임에 변경할 수 없습니다. (controller-runtime#2829)- predicate 방식은 namespace label이 추가/제거되면 다음 이벤트부터 즉시 반영됩니다.
- cache가 모든 namespace를 감시하므로
DefaultNamespaces방식보다 메모리를 더 사용합니다. - Webhook의
namespaceSelector는 API server가 평가하므로 별도 재시작 없이 동적으로 동작합니다.
Webhook namespaceSelector
Webhook은 cache와 별도로, API server가 요청을 webhook에 전달할지 결정합니다. namespaceSelector를 설정하면 특정 namespace의 리소스에 대해서만 webhook이 동작합니다.
matchLabels
namespace에 설정된 label로 필터링합니다.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: my-operator-webhook
webhooks:
- name: validate.my-operator.io
namespaceSelector:
matchLabels:
my-operator.io/watch: "true"
# ...
my-operator.io/watch=true label이 있는 namespace에서 생성/수정되는 리소스만 webhook 검증 대상이 됩니다.
matchExpressions
더 유연한 조건이 필요하면 matchExpressions를 사용합니다.
webhooks:
- name: validate.my-operator.io
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
- "namespace-a"
- "namespace-b"
kubernetes.io/metadata.name은 Kubernetes가 모든 namespace에 자동으로 설정하는 label로, 값은 namespace 이름과 동일합니다. 명시적 namespace 이름으로 필터링할 때 유용합니다.