Ensure the cluster state continuously matches a desired state.
Rely on the `kube-controller-manager`. It runs control loops that watch resources (e.g., ReplicaSets, Deployments) and reconcile differences.
Why: This is the core declarative, self-healing mechanism. If a Pod managed by a ReplicaSet dies, the controller automatically replaces it.
Automatically assign newly created Pods to the most suitable worker node.
Rely on the `kube-scheduler`. It filters nodes based on Pod requirements (e.g., resource requests) and scores them to pick the best fit.
Why: The scheduler makes placement decisions based on policy, affinity, and availability, abstracting node selection from the user.
Ensure containers specified in Pods are running and healthy on a given worker node.
The `kubelet` agent runs on every node, communicates with the API server, and manages the container lifecycle (start, stop, health checks) via a container runtime.
Why: Kubelet is the link between the control plane and the worker node; it executes the Pod specifications.
Persist the entire state and configuration of the Kubernetes cluster reliably.
Use `etcd`, a consistent and highly-available key-value store. It serves as the single source of truth for the cluster.
Why: All cluster objects (Pods, Services, etc.) are stored in etcd. Only the API server communicates directly with it.
Implement network rules on each node to enable communication via Kubernetes Services.
The `kube-proxy` component on each node maintains network rules (e.g., iptables, IPVS) that forward traffic from a Service IP to the correct backend Pods.
Why: Kube-proxy is the implementation detail behind the Service abstraction, handling load balancing and routing.
Logically partition a single Kubernetes cluster for multiple teams, projects, or environments.
Create `Namespace` resources. Namespaces provide a scope for names and a way to attach authorization and policies (e.g., ResourceQuotas).
Why: Namespaces enable multi-tenancy and resource organization without the overhead of multiple clusters.
Provide a stable network endpoint (IP and DNS) for a set of ephemeral Pods.
Define a `Service` resource that targets a set of Pods using a label selector.
Why: Pods are ephemeral and their IPs change. A Service provides a durable abstraction that load balances traffic to the correct Pods.
Expose an application running in Pods to different network scopes.
Choose a Service `type`: `ClusterIP` (internal only, default), `NodePort` (exposes on each node IP:port), or `LoadBalancer` (provisions a cloud load balancer).
Why: The Service type determines the accessibility of the application, from purely internal to fully external.
Enable direct network discovery of individual Pods, bypassing the Service proxy.
Create a `Service` with `clusterIP: None`. This creates DNS A records for each Pod, allowing clients to connect to Pods directly.
Why: Essential for stateful applications like databases (often with StatefulSets) where peer-to-peer communication or stable Pod identity is required.
Organize and select a subset of Kubernetes objects.
Attach key-value `labels` to objects (e.g., `app: my-api`). Use `label selectors` in other objects (e.g., Services, Deployments) to target them.
Why: Labels are the core grouping mechanism in Kubernetes, enabling loose coupling between resources.
Decouple application configuration from the container image.
Store non-sensitive configuration data in a `ConfigMap`. Mount it as a volume or inject keys as environment variables into Pods.
Why: This allows configuration to be managed independently of the application code, following 12-Factor App principles.
Store sensitive data like passwords, tokens, or API keys for application use.
Use a `Secret` object. Mount as a volume or inject as an environment variable.
Why: Secrets are specifically for sensitive data and handled more securely than ConfigMaps (e.g., not shown in `kubectl describe` by default, can be encrypted at rest).
Provide stateful applications with storage that survives Pod restarts.
A Pod creates a `PersistentVolumeClaim` (PVC) to request storage. An administrator provisions a `PersistentVolume` (PV) that fulfills the claim.
Why: This decouples storage consumption (PVC) from storage provisioning (PV), allowing for portable workload definitions.
Manage CPU and memory allocation for containers.
Set `resources.requests` for guaranteed resources (used for scheduling) and `resources.limits` for the maximum allowed usage (enforced at runtime).
Why: Requests ensure Pods have enough resources to run; Limits prevent Pods from consuming too many resources and impacting other workloads.
Set aggregate resource constraints on a namespace.
Create a `ResourceQuota` object to limit the total amount of CPU, memory, or number of objects (Pods, Services) that can be created in a namespace.
Why: ResourceQuotas are essential for multi-tenant environments to ensure fair resource sharing and prevent over-consumption.
Manage Kubernetes resources using version-controlled configuration files.
Use `kubectl apply -f <filename.yaml>`. This command creates or updates resources based on the file content.
Why: `apply` is declarative, making it ideal for GitOps and CI/CD. It tracks changes and performs a three-way merge, which is safer than the imperative `create` or `replace`.
Diagnose why a Pod is not running correctly (e.g., stuck in Pending, ContainerCreating, or CrashLoopBackOff).
Use `kubectl describe pod <pod-name>`. Check the `Events` section at the bottom for detailed messages from the scheduler, kubelet, or controllers.
Why: `describe` provides a chronological event log that is the primary tool for debugging resource lifecycle issues.
Provide networking functionality for containers, enabling Pod-to-Pod communication across the cluster.
Use a Container Network Interface (CNI) plugin (e.g., Calico, Flannel, Cilium). The kubelet on each node uses the CNI plugin to configure networking for each Pod.
Why: CNI provides a standard interface, allowing Kubernetes to be integrated with various networking solutions without modifying core components.
Control access to Kubernetes API resources for users and applications.
Use Role-Based Access Control (RBAC). Define a `Role` (namespaced) or `ClusterRole` (cluster-wide) with permissions, and bind it to a subject (User, Group, ServiceAccount) using a `RoleBinding` or `ClusterRoleBinding`.
Why: RBAC is the standard for securing Kubernetes, enabling the principle of least privilege for all API interactions.
Deploy a stateful application (e.g., database) that requires stable network identity and storage.
Use a `StatefulSet` workload. It provides each Pod with a stable, unique hostname and persistent storage that follows it across restarts.
Why: Unlike Deployments, StatefulSets manage Pods with identity, ensuring ordered deployment and scaling, which is critical for stateful systems.
Deploy an agent (e.g., log collector, monitoring agent) on every node in the cluster.
Use a `DaemonSet` workload. It ensures that a copy of a Pod runs on each node (or a subset of nodes).
Why: DaemonSets automate the distribution of node-level services, automatically scaling to new nodes as they join the cluster.
Run a finite, one-time task that needs to execute to completion.
Use a `Job` resource. It creates one or more Pods and ensures they successfully terminate.
Why: Jobs are for batch processing, unlike Deployments which are for continuous services. The Pods are not replaced after successful completion.
Run a task on a recurring schedule (e.g., nightly backups, reports).
Use a `CronJob` resource. It creates Jobs based on a cron schedule string.
Why: CronJobs provide a native Kubernetes way to manage time-based, recurring tasks.
Automatically restart a container that has become unresponsive (e.g., deadlock).
Configure a `livenessProbe` in the container spec. If the probe fails, the kubelet restarts the container.
Why: Liveness probes provide a powerful self-healing mechanism for applications that can get stuck in a broken state without crashing.
Prevent traffic from being sent to a container that is not yet ready to serve requests.
Configure a `readinessProbe` in the container spec. The Pod is only added to Service endpoints after the probe succeeds.
Why: Readiness probes are critical for zero-downtime rolling updates, ensuring new Pods are fully initialized before receiving production traffic.
Run setup tasks or wait for dependencies to be ready before starting the main application container.
Define one or more `initContainers` in the Pod spec. They run to completion in sequence before any app containers start.
Why: Init containers provide a clean separation for setup logic, ensuring prerequisites are met without cluttering the main application container.
Ensure Pods are scheduled onto nodes with specific characteristics (e.g., nodes with GPUs, SSDs).
Use `nodeAffinity` in the Pod spec to set rules based on node labels. Can be a "required" (hard) or "preferred" (soft) constraint.
Why: Node affinity is more expressive than `nodeSelector` and is the modern way to control Pod placement based on node properties.
Control the co-location of Pods relative to each other for performance or high availability.
Use `podAffinity` to schedule Pods together (e.g., on the same node) or `podAntiAffinity` to spread them apart (e.g., across different nodes or zones).
Why: Anti-affinity is crucial for ensuring replicas of a service are not on the same failure domain, thereby increasing availability.
Prevent general-purpose Pods from being scheduled onto dedicated or special-purpose nodes.
Apply a `Taint` to a node. Pods must have a matching `Toleration` in their spec to be scheduled on that node.
Why: Taints and tolerations ensure that nodes are reserved for workloads that are explicitly permitted to run there.
Ensure high availability by distributing Pods evenly across failure domains like zones or nodes.
Define `topologySpreadConstraints` in the Pod spec to control how Pods are spread based on labels and topology keys (e.g., `topology.kubernetes.io/zone`).
Why: This provides more fine-grained control over HA than pod anti-affinity, preventing all replicas from being concentrated in a single location.
Automatically scale the number of application replicas based on observed load.
Create a `HorizontalPodAutoscaler` (HPA) resource that targets a Deployment and specifies a metric (e.g., CPU utilization) and target value.
Why: HPA enables elastic scaling, ensuring performance under load while saving costs during quiet periods, without manual intervention.
Automatically add or remove worker nodes from the cluster to match resource demand.
Deploy the `Cluster Autoscaler`. It watches for Pods that cannot be scheduled (due to resource scarcity) and adds nodes, or removes underutilized nodes.
Why: The Cluster Autoscaler manages infrastructure-level elasticity, working with cloud providers to adjust the cluster size based on workload needs.
Ensure a minimum number of application replicas remain available during voluntary disruptions (e.g., node upgrades).
Create a `PodDisruptionBudget` (PDB) specifying `minAvailable` or `maxUnavailable` for a set of Pods.
Why: PDBs prevent actions like `kubectl drain` from taking down too many replicas at once, safeguarding application availability.
Perform a zero-downtime update for a stateless application.
Use a `Deployment` with the default `RollingUpdate` strategy. Configure `maxSurge` and `maxUnavailable` to control the update process.
Why: Rolling updates gradually replace old Pods with new ones, ensuring the service remains available throughout the update.
Cloud Native Application Delivery
Manage infrastructure and application deployments declaratively with version control and an audit trail.
Implement GitOps. Use a Git repository as the single source of truth. Use a tool like Argo CD or Flux to automatically sync the cluster state with Git.
Why: GitOps provides a clear, auditable history of all changes and enables easy rollbacks by reverting Git commits. It operationalizes Infrastructure as Code.
Package, configure, and deploy complex Kubernetes applications in a reusable and versioned way.
Use `Helm`, the package manager for Kubernetes. Package applications as `Charts` with templated manifests and configurable `values.yaml` files.
Why: Helm simplifies managing complex applications with many components, handling dependencies, versioning, and lifecycle management.
Customize Kubernetes manifests for different environments without using templates.
Use `Kustomize`. Define a `kustomization.yaml` file that specifies a base configuration and applies patches or overlays for each environment.
Why: Kustomize offers a declarative, template-free way to manage configuration variants, which can be simpler and less error-prone than text-based templating.
Test a new application version with a small subset of production traffic before a full rollout.
Deploy the new version alongside the old. Use a service mesh or Ingress controller to route a small percentage of traffic (e.g., 5%) to the new "canary" version.
Why: Canary releases reduce the risk of introducing a bad release by limiting the blast radius and allowing for production testing.
Deploy a new application version with zero downtime and instant rollback capability.
Deploy the new "green" version alongside the existing "blue" version. Once the green version is verified, switch 100% of traffic from blue to green at the Service/router level.
Why: Blue-green deployments eliminate downtime. Rollback is as simple as switching traffic back to the blue environment.
Cloud Native Architecture
Design a complex application as a collection of small, independent, and loosely coupled services.
Structure the application as microservices, each organized around a business capability. Each service should own its own data and communicate via well-defined APIs.
Why: This architecture enables independent development, deployment, and scaling of services, improving agility and resilience.
Build a portable, scalable, and cloud-native application following established best practices.
Adhere to the Twelve-Factor App methodology. A key principle is storing all configuration that varies between environments in environment variables.
Why: This strictly separates config from code, allowing the same container image to be promoted across environments without changes.
Manage complex service-to-service communication, providing traffic management, security, and observability.
Implement a service mesh (e.g., Istio, Linkerd). It injects a sidecar proxy into each Pod to intercept and manage all network traffic.
Why: A service mesh abstracts network concerns (mTLS, retries, circuit breaking) away from application code, enforcing them at the platform level.
Extend or enhance the functionality of an application container without modifying its code.
Deploy a "sidecar" container in the same Pod as the main application. It shares the same network and storage.
Why: Sidecars are used for cross-cutting concerns like logging, monitoring, or proxying (as in a service mesh), promoting separation of concerns.
Gain deep insight into the behavior of a distributed system to facilitate troubleshooting.
Implement the three pillars of observability: `Metrics` (aggregated numerical data), `Logs` (discrete events), and `Traces` (end-to-end request flows).
Why: Together, these data types provide a comprehensive view of system health and performance, which is essential for complex microservices architectures.