A namespace-scoped operator watches and manages resources in a single namespace, whereas a cluster-scoped operator watches and manages resources cluster-wide. Namespace-scoped operators are preferred because of their flexibility. They enable decoupled upgrades, namespace isolation for failures and monitoring, and differing API definitions.
However, there are use cases where a cluster-scoped operator may make sense. For example, the cert-manager operator is often deployed with cluster-scoped permissions and watches so that it can manage issuing certificates for an entire cluster.
This scope is ideal for operator projects which will control resources just in one namespace, which is where the operator is deployed.
NOTE: Initial projects created by
operator-sdk
are namespace-scoped by default which means that it will NOT have aClusterRole
defined in thedeploy/role_binding.yaml
.
This scope is ideal for operator projects which will control resources in more than one namespace.
The SDK scaffolds operators to be namespaced by default but with a few modifications to the default manifests the operator can be run as cluster-scoped.
deploy/operator.yaml
:- Set
WATCH_NAMESPACE=""
to watch all namespaces instead of setting it to the pod's namespace
- Set
deploy/role.yaml
:- Use
ClusterRole
instead ofRole
- Use
deploy/role_binding.yaml
:- Use
ClusterRoleBinding
instead ofRoleBinding
- Use
ClusterRole
instead ofRole
forroleRef
- Set the subject namespace to the namespace in which the operator is deployed.
- Use
With the above changes the specified manifests should look as follows:
deploy/operator.yaml
:apiVersion: apps/v1 kind: Deployment ... spec: ... template: ... spec: ... serviceAccountName: memcached-operator containers: - name: memcached-operator ... env: - name: WATCH_NAMESPACE value: ""
deploy/role.yaml
:apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: memcached-operator ...
deploy/role_binding.yaml
:kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: memcached-operator subjects: - kind: ServiceAccount name: memcached-operator namespace: <operator-namespace> roleRef: kind: ClusterRole name: memcached-operator apiGroup: rbac.authorization.k8s.io
Additionally the CustomResourceDefinition (CRD) scope can also be changed for cluster-scoped operators so that there is only a single instance (for a given name) of the CRD to manage across the cluster.
NOTE: Cluster-scoped CRDs are NOT supported with the Helm operator. While Helm releases can create cluster-scoped resources, Helm's design requires the release itself to be created in a specific namespace. Since the Helm operator uses a 1-to-1 mapping between a CR and a Helm release, Helm's namespace-scoped release requirement extends to Helm operator's namespace-scoped CR requirement.
For each CRD that needs to be cluster-scoped, update its manifest to be cluster-scoped.
deploy/crds/<full group>_<resource>_crd.yaml
- Set
spec.scope: Cluster
- Set
To ensure that the CRD is always generated with scope: Cluster
, add the tag // +kubebuilder:resource:path=<resource>,scope=Cluster
, or if already present replace scope={Namespaced -> Cluster}
, above the CRD's Go type definition in pkg/apis/<group>/<version>/<kind>_types.go
. Note that the <resource>
element must be the same lower-case plural value of the CRD's Kind, spec.names.plural
.
This scope is ideal for the cases where an instance(CR) of some Kind(CRD) will be used in more than one namespace instead of a specific one.
NOTE: When a
Manager
instance is created in themain.go
file, it receives the namespace(s) as Options. These namespace(s) should be watched and cached for the Client which is provided by the Controllers. Only clients provided by cluster-scoped projects where theNamespace
attribute is""
will be able to manage cluster-scoped CRD's. For more information see the Manager topic in the user guide and the Manager Options.
- Check the
spec.names.plural
in the CRD's Kind YAML file
deploy/crds/cache_v1alpha1_memcached_crd.yaml
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: memcacheds.cache.example.com spec: group: cache.example.com names: kind: Memcached listKind: MemcachedList plural: memcacheds singular: memcached scope: Namespaced
- Update the
pkg/apis/<group>/<version>/<kind>_types.go
by adding the tag// +kubebuilder:resource:path=<resource>,scope=Cluster
pkg/apis/cache/v1alpha1/memcached_types.go
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // Memcached is the Schema for the memcacheds API // +k8s:openapi-gen=true // +kubebuilder:resource:path=memcacheds,scope=Cluster type Memcached struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec MemcachedSpec `json:"spec,omitempty"` Status MemcachedStatus `json:"status,omitempty"` }
- Execute the command
operator-sdk generate openapi
, then you should be able to check that the CRD was updated with the cluster scope as in the following example:
deploy/crds/cache.example.com_memcacheds_crd.yaml
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: memcacheds.cache.example.com spec: group: cache.example.com ... scope: Cluster