Configure namespaces and namespace-scoped objects

This page illustrates how to use Config Sync to manage namespaces and namespace-scoped objects.

Configure a namespace

Configuring a namespace functions differently for unstructured and hierarchical repositories. The following examples highlight the differences.

Unstructured repository

Configs for namespaces and namespace-scoped objects can be located anywhere in the repository's directory or subdirectories.

Complete the following steps to configure a namespace called gamestore in each enrolled cluster:

  1. Create a file namespace-gamestore.yaml with the following content:

    apiVersion: v1
    kind: Namespace
    metadata:
      name: gamestore
    

    You only need to create one YAML file that contains the namespace config.

  2. Create a commit that includes the namespace-gamestore.yaml config, and push the commit to the remote repository:

    git add multirepo/root/namespace-gamestore.yaml
    git commit -m "Created gamestore namespace config"
    git push REMOTE_NAME BRANCH_NAME
    

    Replace the following:

    • REMOTE_NAME: the name of your remote repository.
    • BRANCH_NAME: the branch that you want to commit to.

    This example adds the file to the root directory, but you can move this file to any of the repository's subdirectories.

Hierarchical repository

All configs for namespaces and namespace-scoped objects are located within the namespaces/ directory of the hierarchical repository and its descendant directories.

Follow these steps to configure a namespace called gamestore in each enrolled cluster:

  1. In the local clone of your repository, create a namespace directory. A namespace directory contains a config for a namespace. The name of the namespace directory must match the name of the namespace. In this example, the directory is called namespaces/gamestore:

    mkdir namespaces/gamestore
    
  2. In the namespace directory, create a file gamestore.yaml, with the following content:

    apiVersion: v1
    kind: Namespace
    metadata:
      name: gamestore
    

    The metadata.name must match the name of the namespace directory.

  3. Create a commit that includes the gamestore.yaml config, and push the commit to the remote repository:

    git add namespaces/gamestore/gamestore.yaml
    git commit -m "Created gamestore namespace config"
    git push REMOTE_NAME BRANCH_NAME
    

    Replace the following:

    • REMOTE_NAME: the name of your remote repository.
    • BRANCH_NAME: the branch that you want to commit to.

After a few moments, the gamestore namespace is created in each enrolled cluster. To verify, describe the namespace:

kubectl describe namespace gamestore

To remove the config and delete the gamestore namespace from enrolled clusters, create a new commit that removes the file, and push it to the remote repository. Do not remove the config if you want to configure an abstract namespace for a hierarchical repository.

Configure a namespace-scoped object

In unstructured repositories, you can store namespace-scoped objects in any directory or subdirectory without requiring a Namespace config. If a Namespace config is missing, Config Sync automatically creates an implicit Namespace object, and applies all configs to that namespace.

This behavior can be modified using the namespaceStrategy field. If namespaceStrategy is set to explicit, then Config Sync won't automatically create an implicit Namespace object. For more information, see namespace strategy.

In hierarchical repositories, you must explicitly specify a Namespace config in the namespaces/NAMESPACE subdirectory, where NAMESPACE must match the name of the namespace. All other namespace-scoped configs must also be stored in the same subdirectory. If a Namespace config is missing, Config Sync returns a KNV1044 error, indicating a missing Namespace config.

Configure an abstract namespace

This section only applies to hierarchical repositories because abstract namespaces are not supported in unstructured repositories.

This example extends the example in configuring a namespace by moving the gamestore namespace directory into an abstract namespace containing additional configs inherited by the gamestore namespace.

  1. In the local clone of the repository, create an abstract namespace directory called eng:

    mkdir namespaces/eng
    

    An abstract namespace directory does not contain a config for a namespace, but its descendant namespace directories do.

  2. In the eng abstract namespace directory, create a config for a Role called eng-viewer, that grants get and list on all resources in any namespace that eventually inherits the Role:

    # namespaces/eng/eng-role.yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: eng-viewer
    rules:
    - apiGroups: [""]
      resources: ["*"]
      verbs: ["get", "list"]
    
  3. Create a config for a RoleBinding called eng-admin that binds the eng-viewer Role to group eng@example.com:

    # namespaces/eng/eng-rolebinding.yaml
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: eng-admin
    subjects:
    - kind: Group
      name: eng@example.com
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: Role
      name: eng-viewer
      apiGroup: rbac.authorization.k8s.io
    
  4. Move the gamestore namespace directory from the namespaces/ to the namespaces/eng/ directory:

    mv namespaces/gamestore /namespaces/eng/
    
  5. Commit all of your changes and push them to the remote for the repository.

Config Sync notices the change and applies the new Role and RoleBinding to the gamestore namespace on all enrolled clusters.

To remove the configs and delete the gamestore namespace from enrolled clusters, you can create a new commit that removes the entire eng abstract namespace, and push it to the remote repository.

Limit which clusters a config affects

Normally, Config Sync applies a config to each enrolled cluster. However, if the config is within the namespaces/ subdirectory of a hierarchical repository, Config Sync first creates the namespace within each cluster and then applies all inherited configs to that namespace. To limit which clusters a particular config affects based on each cluster's labels, use a ClusterSelector. To learn more, see Use ClusterSelectors.

Limit which namespaces a config affects

To limit which namespaces a config affects, use a NamespaceSelector. A NamespaceSelector is a special type of config that uses Kubernetes labelSelectors. You can declare NamespaceSelectors in combination with configs for namespace-scoped objects in an unstructured repository or a hierarchical repository to limit which namespaces can inherit that config. NamespaceSelectors are similar, but not identical, to ClusterSelectors. A NamespaceSelector narrows the pool of namespaces a config applies to.

To declare a NamespaceSelector, add either the metadata.namespace or the NamespaceSelector annotation. Declaring both annotations is invalid. If namespace-scoped resources don't declare metadata.namespace or the NamespaceSelector annotation, then Config Sync uses the "default" namespace of the cluster.

NamespaceSelectors in unstructured repositories

An unstructured repository does not have to declare all namespaces for the namespace-scoped objects in the repository. An object can define a metadata.namespace without having a matching namespace object in an unstructured repository. If the namespace already exists on the cluster, Config Sync creates the object within that namespace. If the namespace does not exist on the cluster yet, Config Sync creates the namespace implicitly.

Before you create an unstructured repository with objects that were previously used in a hierarchical repository, verify that your NamespaceSelectors do not apply to additional resources.

When you remove namespace-scoped objects from an unstructured repository, Config Sync deletes those objects, but it does not remove any namespaces that might have been created implicitly for those objects. This behavior happens because Config Sync can't infer when it's safe to delete a namespace that was created implicitly, so it's always left on the cluster.

NamespaceSelector mode

In an unstructured repository, objects that declare the NamespaceSelector annotation are applied to all Namespaces that satisfy the conditions of a NamespaceSelector. In Config Sync versions earlier than 1.17.0, the NamespaceSelector annotation only applies to matching Namespaces that are statically declared in the source of truth. In version 1.17.0 and later, the NamespaceSelector object supports dynamic mode by setting spec.mode to dynamic. In dynamic mode, the selection extends to both statically-declared Namespaces and those dynamically present on the cluster. Namespaces chosen dynamically already exist on the cluster and are therefore not managed by Config Sync. The default mode is static.

NamespaceSelectors in hierarchical repositories

In a hierarchical repository, objects that declare the NamespaceSelector annotation are applied to Namespaces that inherit a given config from an abstract Namespace, regardless of the directory structure of the namespaces/ directory. A ClusterSelector narrows the pool of clusters a config applies to, whether the config targets a cluster-scoped or namespace-scoped object.

NamespaceSelector location

In an unstructured repository, you can place the NamespaceSelectors in any directory or subdirectories.

In a hierarchical repository, you can place NamespaceSelectors in any abstract namespace directory, but not in a namespace directory.

The following example repository architecture shows valid and invalid locations for NamespaceSelectors if you are using a hierarchical repository:

namespace-inheritance
...
├── namespaces
│   ├── eng
│   │   ├── gamestore
│   │   │   ├── namespace.yaml
│   │   │   └── ns_selector.yaml  # invalid
│   │   └── ns_selector.yaml  # valid
│   ├── ns_selector.yaml  # valid
│   ├── rnd
│   │   ├── incubator-1
│   │   │   ├── namespace.yaml
│   │   │   └── ns_selector.yaml  # invalid
│   │   └── ns_selector.yaml  # valid

Since the namespaces, eng, and rnd directories represent abstract namespaces, you can put a selector in them. However, because the gamestore and incubator-1 directories represent actual namespaces, you can't put a NamespaceSelector in them.

NamespaceSelector examples

You can use a NamespaceSelector with label selectors to include or exclude namespaces. Kubernetes supports equality-based and set-based selectors. You can combine both types of selectors to further refine which namespaces are selected.

Equality-based label selector

The following config creates a NamespaceSelector called gamestore-selector. If another config references this NamespaceSelector, that config can only be applied to objects in namespaces that have the app: gamestore label.

kind: NamespaceSelector
apiVersion: configmanagement.gke.io/v1
metadata:
  name: gamestore-selector
spec:
  selector:
    matchLabels:
      app: gamestore

To reference a NamespaceSelector in a config, you set the configmanagement.gke.io/namespace-selector annotation to the name of the NamespaceSelector.

A NamespaceSelector has no effect until you reference it in another config. If the gamestore-selector NamespaceSelector is in the same hierarchy as the following ResourceQuota, quota, the ResourceQuota is only created in namespaces that have the app: gamestore label:

kind: ResourceQuota
apiVersion: v1
metadata:
  name: quota
  annotations:
    configmanagement.gke.io/namespace-selector: gamestore-selector
spec:
  hard:
    pods: "1"
    cpu: "200m"
    memory: "200Mi"

To summarize, using a NamespaceSelector is a three-step process:

  1. Add labels to Namespaces.
  2. Create a NamespaceSelector config.
  3. Reference the NamespaceSelector object in another config.

Set-based label selector

You can use namespace selectors to exempt particular namespaces from inheriting a resource in the tree by using set-based label selectors.

In this example, when the ResourceQuota is annotated with the NamespaceSelector by setting the annotation configmanagement.gke.io/namespace-selector: excludes-exempt-namespaces, the ResourceQuota is created in every namespace except any namespaces that are labeled quota-exempt: exempt:

kind: NamespaceSelector
 apiVersion: configmanagement.gke.io/v1
 metadata:
   name: excludes-exempt-namespaces
 spec:
   selector:
     matchExpressions:
       - key: quota-exempt
         operator: NotIn
          values:
            - exempt

Integration with team scopes and fleet namespaces

Fleet namespaces created in Google Cloud automatically have the label fleet.gke.io/fleet-scope: your-scope. All namespaces also have the Kubernetes kubernetes.io/metadata.name: your-namespace label. You can use these default labels to set up the NamespaceSelector for selecting fleet namespaces.

Disable inheritance for an object type

HierarchyConfig Kubernetes objects are not supported in unstructured repositories. The following example only applies to the hierarchical repositories.

You can selectively disable inheritance for any config by setting the hierarchyMode field to none. HierarchyConfigs are stored in the system/ directory of the repository. This example disables inheritance for RoleBindings.

# system/hierarchy-config.yaml
kind: HierarchyConfig
apiVersion: configmanagement.gke.io/v1
metadata:
  name: rbac
spec:
  resources:
  # Configure Role to only be allowed in leaf namespaces.
  - group: rbac.authorization.k8s.io
    kinds: [ "RoleBinding" ]
    hierarchyMode: none

What's next