Namespaces, Contexts, and Declarative Workflows

Namespaces, contexts, and declarative workflows are the first safety layer around every Kubernetes and Helm operation. A namespace scopes names and many policies inside one cluster. A context tells a client which cluster, user credential, and default namespace to use. A declarative workflow stores the desired objects in YAML and asks the API server to converge the cluster toward that description. The outcome is practical: you can aim commands at the right place, separate teams or environments, review changes before they land, and repeat a release without rebuilding it from memory.

In this Kubernetes and Helm course, these ideas sit before charts because Helm releases are installed into namespaces and executed through the same kubeconfig context machinery as kubectl. A chart can be carefully written and still damage the wrong environment if the active context points at production or the release namespace is missing. Mastering this chapter gives you the operating discipline that Helm later automates.

How Kubernetes Interprets Namespaces

A namespace is an API object, but it also becomes part of the identity of most namespaced resources. A Deployment named web in dev is different from a Deployment named web in prod. The API path includes the namespace for namespaced resources, so Kubernetes can store, authorize, list, and watch those objects separately. Cluster-scoped resources, such as Nodes, PersistentVolumes, StorageClasses, CustomResourceDefinitions, and Namespace objects themselves, do not live inside a namespace.

Namespaces are not lightweight virtual clusters. They do not create a separate control plane, separate node pool, or automatic network isolation. By default, a Pod in one namespace may be able to reach a Service in another namespace if networking policies allow it. Namespaces become useful boundaries when combined with RoleBindings, ResourceQuotas, LimitRanges, NetworkPolicies, naming conventions, and release procedures.

Service discovery is namespace-aware. Inside a namespace, a Pod can usually call a Service by short name, such as api. Across namespaces it must use a qualified DNS name such as api.orders.svc.cluster.local, or at least api.orders in many clusters. This detail matters during refactoring: moving a workload to a new namespace can break clients that depended on short Service names.

How Contexts Aim Your Client

A kubeconfig file contains clusters, users, and contexts. A cluster entry identifies an API server endpoint and certificate authority data. A user entry contains or references credentials, such as a token, client certificate, or exec-based login plugin. A context combines one cluster, one user, and optionally one default namespace. The current context is just the selected entry in that local file; it is not a server-side lock or guarantee.

When you run kubectl get pods, kubectl loads kubeconfig, resolves the current context, chooses the cluster endpoint and credential, and uses the context namespace unless the command supplies --namespace. That final request still goes through API server authentication, authorization, admission, persistence in etcd, and controller reconciliation. Contexts reduce operator error, but RBAC is what prevents unauthorized changes after the request reaches the API server.

Declarative Apply Internals

Declarative Kubernetes work starts with object documents. Each object has apiVersion, kind, metadata, and a kind-specific body, usually spec. You send those objects to the API server with kubectl apply -f. The API server validates the schema, defaults omitted fields, runs admission controllers, stores accepted state, and notifies controllers through watches. Controllers then compare desired state with actual state: the Deployment controller creates or updates ReplicaSets, the ReplicaSet controller creates Pods, and the scheduler assigns unscheduled Pods to nodes.

Modern apply workflows often use server-side apply. With server-side apply, the API server tracks field ownership in metadata.managedFields. Managers can own different fields on the same object. If two managers try to own incompatible values for the same field, the API server can report a conflict instead of silently overwriting. Client-side apply uses a last-applied annotation and performs more merge work on the client. Both approaches are declarative, but server-side apply gives the server better information about ownership.

Manifest Anatomy

The namespace can appear in object metadata, in the kubectl command, or in the active context. Prefer explicit namespace metadata for checked-in namespaced resources when a file is environment-specific. Prefer kubectl -n or a context namespace for interactive inspection. Avoid relying on whatever context happens to be active in automation; deployment jobs should pass the target context and namespace deliberately.

apiVersion: v1
kind: Namespace
metadata:
  name: course-dev
  labels:
    purpose: helm-course
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-settings
  namespace: course-dev
data:
  LOG_LEVEL: debug

This document creates a namespace and then a ConfigMap inside it. The separator --- lets one file contain multiple YAML documents. Expected behavior: after applying it, kubectl get configmap app-settings -n course-dev returns one ConfigMap named app-settings. If the namespace document is omitted and the namespace does not already exist, the ConfigMap creation fails with a namespace-not-found error.

Example 1: Inspect and Set a Safe Context

The first workflow is local and reversible: inspect where kubectl is aimed, then set a default namespace for a non-production context.

kubectl config get-contexts
kubectl config current-context
kubectl config set-context --current --namespace=course-dev
kubectl config view --minify --output 'jsonpath={..namespace}'
echo

The first command lists available contexts and marks the current one. The second prints only the active context name. The third updates the current context entry in kubeconfig so commands without -n default to course-dev. The JSONPath command should print course-dev. This does not create the namespace and does not change the cluster; it changes local client configuration.

Example 2: Apply Namespaced Workloads

The next example adds a Deployment and Service to the namespace. It uses labels to connect the Service to Pods created by the Deployment.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
  namespace: course-dev
  labels:
    app.kubernetes.io/name: hello
spec:
  replicas: 2
  selector:
    matchLabels:
      app.kubernetes.io/name: hello
  template:
    metadata:
      labels:
        app.kubernetes.io/name: hello
    spec:
      containers:
        - name: hello
          image: registry.k8s.io/e2e-test-images/agnhost:2.39
          args: ["netexec", "--http-port=8080"]
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: hello
  namespace: course-dev
spec:
  selector:
    app.kubernetes.io/name: hello
  ports:
    - name: http
      port: 80
      targetPort: 8080

After kubectl apply -f hello.yaml, expected deterministic API output includes deployment.apps/hello configured or created and service/hello configured or created. Pod readiness depends on image pull and node capacity, so verify convergence with kubectl rollout status deployment/hello -n course-dev. The Service selects Pods by label, not by Deployment name; if the labels diverge, the Service exists but has no endpoints.

Example 3: Preview and Prune a Change

A mature declarative workflow includes preview, apply, verification, and cleanup. Server-side dry run asks the API server to default and validate the object without persisting it.

kubectl apply --server-side --dry-run=server -f hello.yaml
kubectl diff -f hello.yaml
kubectl apply --server-side --field-manager=course-lab -f hello.yaml
kubectl get deploy,svc,endpoints -n course-dev -l app.kubernetes.io/name=hello

The dry run should report objects as created or configured without changing stored state. kubectl diff exits with no printed diff when live state already matches the files, and prints a unified diff when it does not. The field manager name appears in managed fields and helps diagnose ownership conflicts. The final command should show the Deployment, Service, and endpoints selected by the shared label once Pods are ready.

Design Choices and Trade-offs

Choose namespace boundaries around ownership and policy, not only around application names. Common patterns include one namespace per environment, one per team, or one per installed application. Environment namespaces are simple for small clusters but can grow crowded. Team namespaces match RBAC ownership but may mix unrelated runtime profiles. Application namespaces make Helm release cleanup straightforward but can multiply quotas and network policies.

Explicit namespaces in manifests improve reviewability because the target is visible in the diff. They are awkward when the same file must deploy unchanged to many namespaces. Helm commonly solves this with release namespace values and templates, but that power makes review discipline more important. In CI, prefer a small number of explicit deployment parameters over hidden kubeconfig defaults.

Declarative apply is idempotent for the fields it manages: applying the same file again should leave the same desired state. It is less suitable for one-time imperative actions such as database migrations unless those actions are represented by a Kubernetes controller or Job with clear completion semantics. Do not force every operational task into YAML if the task is not actually desired state.

Failure Modes and Troubleshooting

Symptom: resources were created in default instead of the intended namespace. Cause: the manifest had no metadata.namespace, the context had no namespace, and the command omitted -n. Diagnose: run kubectl config view --minify and kubectl get all -A | grep hello. Correct: add explicit namespace metadata or pass -n course-dev, then delete the misplaced resources from default.

Symptom: Error from server (NotFound): namespaces "course-dev" not found. Cause: namespaced objects were applied before the Namespace existed, or the Namespace was deleted during cleanup. Diagnose: run kubectl get namespace course-dev. Correct: apply the Namespace first, split cluster bootstrap from app deployment, or include the Namespace document before namespaced resources in the same file.

Symptom: services "hello" not found from one namespace while the Service exists in another. Cause: Service DNS and lookups are namespace-scoped. Diagnose: compare kubectl get svc -A | grep hello with the namespace of the client Pod. Correct: call hello.course-dev from other namespaces or deploy the client and Service into the same namespace when that is the intended boundary.

Symptom: server-side apply reports a field conflict. Cause: another field manager owns the same field with a different value. Diagnose: inspect kubectl get deploy hello -n course-dev -o yaml and review metadata.managedFields. Correct: coordinate ownership, remove the competing manager, or intentionally force conflicts only when you are taking ownership and understand the overwrite.

Security, Reliability, and Performance Implications

Namespaces make least privilege practical. Bind a Role to a ServiceAccount or user inside one namespace when the actor only needs namespaced objects there. Use ClusterRoles and ClusterRoleBindings sparingly because they can cross namespace boundaries or grant access to cluster-scoped objects. Combine namespace RBAC with ResourceQuotas so a runaway test cannot consume all cluster capacity.

Reliability improves when every release command is repeatable and reviewable. Store manifests or Helm values in version control, validate them before apply, and watch rollout conditions after apply. Performance concerns usually come from scale and list-watch behavior: thousands of objects across many namespaces increase API query volume and controller work. Labels and field selectors help operators inspect the relevant subset without listing the entire cluster.

Hands-on Lab

Prerequisites: a working Kubernetes cluster you are allowed to modify, kubectl configured for that cluster, and permission to create namespaces, Deployments, Services, ConfigMaps, and Roles. A local cluster such as kind, minikube, or a disposable training cluster is appropriate.

  1. Create a file named namespace.yaml using the first manifest example.
  2. Run kubectl apply --server-side --dry-run=server -f namespace.yaml and confirm validation succeeds.
  3. Apply it with kubectl apply -f namespace.yaml.
  4. Set your current context namespace with kubectl config set-context --current --namespace=course-dev.
  5. Create hello.yaml from the Deployment and Service example.
  6. Run kubectl diff -f hello.yaml, then kubectl apply --server-side --field-manager=course-lab -f hello.yaml.
  7. Verify with kubectl rollout status deployment/hello -n course-dev and kubectl get deploy,svc,endpoints -n course-dev -l app.kubernetes.io/name=hello.
  8. Troubleshoot deliberately by changing the Service selector to app.kubernetes.io/name: missing, applying it, and observing that endpoints disappear. Restore the correct selector and apply again.
  9. Cleanup with kubectl delete namespace course-dev. This deletes namespaced lab resources. If you changed your context namespace, either set it to your normal namespace or unset it with kubectl config set-context --current --namespace=default.

Assessment Exercises

  1. A CI job runs kubectl apply -f release.yaml without -n. What evidence would you inspect to prove which namespace received the objects, and how would you redesign the job?
  2. You want one Helm chart installed once for staging and once for production in the same cluster. Which fields or command parameters must differ, and which should stay identical?
  3. A Service exists and has a ClusterIP, but clients receive connection failures. List the namespace, selector, endpoint, and DNS checks you would perform in order.
  4. Two teams need to update different fields on the same Deployment. How can server-side apply help, and when would it still report a conflict?
  5. Design a namespace strategy for three teams sharing one training cluster. Include RBAC, quota, and cleanup considerations.

Summary

Namespaces scope most Kubernetes object names and provide a place to attach policy. Contexts aim kubectl and Helm by combining cluster, credential, and default namespace. Declarative workflows make desired state reviewable and repeatable, then rely on the API server and controllers to reconcile actual state. Used together, they reduce wrong-target changes, make Helm releases easier to reason about, and give operators concrete places to validate, troubleshoot, and clean up cluster work.