3. Setting Up Kubernetes
Before you can deploy anything, you need a cluster — and where that cluster runs depends entirely on your goal. This chapter covers the full spectrum: lightweight local clusters for development, production-grade provisioning on bare metal and managed clouds, and the kubectl command-line tool you will use to drive any of them. Choosing the right setup for the right context saves enormous time and frustration.
3.1 Local Development Clusters
You do not need a fleet of servers to learn or develop on Kubernetes. Several tools spin up a real, functional cluster on a single laptop. This subchapter compares the main options.
Minikube setup and usage
Theory
Minikube is the original and most beginner-friendly tool for running a local single-node (optionally multi-node) Kubernetes cluster. Think of it as a "Kubernetes in a box" — it provisions a small cluster inside a VM or container on your machine, complete with a working control plane and a node, so you can practice realistic workflows without any cloud account.
Minikube's defining strength is its addons system and driver flexibility. It can run on top of several "drivers" (Docker, VirtualBox, Hyper-V, KVM, QEMU) and bundles one-command addons for things newcomers usually struggle to set up: the dashboard, an Ingress controller, the metrics-server, and a LoadBalancer simulator (minikube tunnel). This makes it ideal for learning and demos.
Example
minikube start --driver=docker --nodes=2 # start a 2-node cluster on Docker
minikube status # show control plane / kubelet health
kubectl get nodes # minikube auto-configures kubectl
minikube addons enable ingress # one command to get an Ingress controller
minikube addons enable metrics-server # enable kubectl top
minikube dashboard # open the web UI in a browser
minikube service web --url # get a reachable URL for a Service
minikube delete # tear the cluster down
Exercises
- (Beginner) What does Minikube create on your machine, and what is its main intended audience?
- (Beginner) Name two Minikube addons and what each provides.
- (Intermediate) You created a
type: LoadBalancerService in Minikube and its EXTERNAL-IP is<pending>. Which Minikube command makes it reachable, and why is it needed?- (Interview) Minikube supports multiple "drivers" (docker, virtualbox, hyperv, kvm). Why does abstracting the driver matter for a cross-platform local tool? (Hint: think about what differs between macOS, Windows, and Linux hosts.)
Answers
- Minikube creates a small, local, single- or multi-node Kubernetes cluster (inside a VM or container) on your own machine. Its main audience is learners and developers who want a real cluster locally without a cloud account.
- Any two:
ingress(NGINX Ingress controller),metrics-server(enableskubectl topand HPA),dashboard(web UI),registry(local image registry).- Run
minikube tunnel(in a separate terminal). Minikube has no cloud to provision a real load balancer, so the tunnel creates a network route on the host that assigns the LoadBalancer Service a reachable IP. Alternativelyminikube service <name> --urlexposes it directly.- Different host operating systems provide different virtualization/container backends (Hyper-V on Windows, HyperKit/QEMU on macOS, KVM on Linux, or Docker everywhere). Abstracting the driver lets the same
minikube startworkflow run on any OS by selecting an appropriate backend, so users get a consistent experience regardless of platform.
kind (Kubernetes in Docker)
Theory
kind ("Kubernetes IN Docker") runs each Kubernetes node as a Docker container rather than a VM. Each "node" is a container running its own kubelet and container runtime, and the cluster is bootstrapped with kubeadm inside those containers. Because containers start in seconds and consume little memory, kind clusters are extremely fast to create and destroy.
This speed and reproducibility are why kind is the de facto standard for CI pipelines and testing Kubernetes itself. You can define a multi-node cluster in a small YAML file, spin it up in a GitHub Actions job, run your tests, and tear it down — all in under a minute. It is less focused on a polished local-dev UX (no built-in dashboard/tunnel like Minikube) and more on disposable, scriptable clusters.
Example
# kind-config.yaml: a 1 control-plane + 2 worker cluster
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
kind create cluster --name dev --config kind-config.yaml
kubectl get nodes # three "nodes" that are actually containers
docker ps # you can see them as Docker containers
kind load docker-image myapp:latest --name dev # push a local image into the cluster
kind delete cluster --name dev
Exercises
- (Beginner) How does kind represent a Kubernetes node?
- (Beginner) What is kind's most common use case?
- (Intermediate) Your locally built image
myapp:latestshowsErrImagePullin a kind cluster even though it exists on your machine. Why, and how do you fix it?- (Interview) Why is kind preferred over Minikube for continuous integration, despite Minikube being more feature-rich for local development? (Hint: startup time, footprint, scriptability, nested virtualization in CI.)
Answers
- kind runs each node as a Docker container (running kubelet + a runtime inside), bootstrapped with kubeadm.
- Disposable clusters for CI/CD pipelines and testing (including testing Kubernetes itself), where fast create/teardown and reproducibility matter most.
- The image lives in your host's Docker daemon, not inside the kind node containers' runtime, so the cluster cannot find it. Load it in with
kind load docker-image myapp:latest --name dev(or push to a registry the cluster can reach).- CI runners often cannot do nested hardware virtualization (so VM-based tools are awkward), while Docker is universally available. kind nodes are containers that start in seconds with a small memory footprint, are fully defined in YAML (scriptable/reproducible), and tear down cleanly — ideal for ephemeral test jobs. Minikube's addons and tunnel are valuable for interactive local dev but add weight and setup that CI does not need.
k3s and k3d for lightweight clusters
Theory
k3s (by Rancher/SUSE) is a fully certified Kubernetes distribution stripped down to a single binary under ~100 MB, designed for resource-constrained environments: edge devices, IoT, CI, and ARM boards like a Raspberry Pi. It achieves this by removing legacy/optional features, replacing etcd with an embedded SQLite datastore by default (etcd is still an option for HA), and bundling components into one process. Despite the trimming, it passes the CNCF conformance tests — it is real Kubernetes.
k3d is a wrapper that runs k3s inside Docker (analogous to how kind runs upstream Kubernetes in Docker). It gives you the fast, disposable, multi-node local-cluster experience of kind, but built on the lightweight k3s, so it starts even faster and uses less RAM.
Example
# k3s: install directly on a Linux host/edge device (one command)
curl -sfL https://get.k3s.io | sh -
sudo k3s kubectl get nodes # k3s ships its own kubectl
# k3d: k3s-in-Docker for local multi-node clusters
k3d cluster create dev --servers 1 --agents 2
kubectl get nodes
k3d cluster delete dev
Why k3s is small:
single binary | SQLite default (no external etcd) | legacy/cloud
~ <100 MB | lower memory footprint | features removed
Exercises
- (Beginner) What makes k3s "lightweight" compared to a standard Kubernetes install?
- (Beginner) What is the relationship between k3s and k3d?
- (Intermediate) k3s uses SQLite by default instead of etcd. What is the consequence for high availability, and how do you get HA with k3s?
- (Interview) k3s removes features yet remains CNCF-conformant. What does "conformance" guarantee, and why does it matter that an edge distribution still passes it? (Hint: portability of workloads and APIs.)
Answers
- k3s ships as a single small binary (<100 MB), bundles components into one process, uses an embedded SQLite datastore by default instead of external etcd, and drops legacy/optional in-tree features — drastically reducing footprint and operational overhead.
- k3d runs k3s inside Docker containers, providing a fast, disposable, multi-node local cluster experience built on the lightweight k3s distribution.
- SQLite is a single-file, single-node datastore, so the default k3s setup is not highly available (one control-plane datastore). For HA, configure k3s with an embedded etcd datastore (or an external SQL datastore) across multiple server nodes so control-plane state is replicated.
- CNCF conformance certifies that a distribution implements the standard Kubernetes APIs and behaviors. This guarantees that workloads and manifests written for conformant Kubernetes run the same way on k3s. For an edge distribution it matters because teams can develop against standard Kubernetes and deploy to constrained edge clusters without rewriting manifests or relying on non-portable behavior.
Docker Desktop Kubernetes
Theory
Docker Desktop (on macOS and Windows) includes a built-in, single-node Kubernetes cluster you can enable with a single checkbox in its settings. For developers who already use Docker Desktop, this is the lowest-friction option: no extra tools to install, and kubectl is configured automatically with a docker-desktop context.
The trade-offs: it is single-node only (no multi-node topology testing), tied to Docker Desktop's lifecycle and resource limits, and Docker Desktop requires a paid license for larger organizations. It is excellent for quick local iteration when you are already in the Docker ecosystem, but less suited to testing multi-node behavior (scheduling spread, DaemonSets across nodes) than kind or Minikube.
Example
Docker Desktop -> Settings -> Kubernetes -> [x] Enable Kubernetes -> Apply
kubectl config get-contexts # docker-desktop appears automatically
kubectl config use-context docker-desktop
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# docker-desktop Ready control-plane 1m v1.30.2
Exercises
- (Beginner) How do you enable Kubernetes in Docker Desktop?
- (Beginner) How many nodes does Docker Desktop's Kubernetes provide?
- (Intermediate) You need to verify that a DaemonSet schedules one Pod per node across three nodes. Is Docker Desktop a suitable tool? If not, what would you use?
- (Interview) For a developer already using Docker Desktop daily, what are the pros and cons of using its built-in Kubernetes versus installing kind? (Hint: friction vs. flexibility and multi-node testing.)
Answers
- Open Docker Desktop's Settings, go to the Kubernetes section, check "Enable Kubernetes," and apply — Docker Desktop provisions a single-node cluster and configures kubectl.
- A single node (it is single-node only).
- No — Docker Desktop is single-node, so it cannot demonstrate per-node DaemonSet spread. Use a multi-node tool like kind (
nodes:with multiple workers), Minikube (--nodes=3), or k3d (--agents).- Pros of Docker Desktop K8s: zero extra installation, one-click enable, automatic kubectl context — lowest friction for someone already in Docker Desktop. Cons: single-node only (cannot test multi-node scheduling, spread, or DaemonSets realistically), bound to Docker Desktop's resource settings and release cadence, and licensing costs for larger orgs. kind adds a small install step but gives scriptable, multi-node, disposable clusters better suited to realistic and CI testing.
3.2 Production Cluster Provisioning
Local tools are for development; production demands different tooling that addresses high availability, security, upgrades, and scale. This subchapter covers the main paths to a production cluster.
kubeadm cluster bootstrap
Theory
kubeadm is the official, low-level tool for bootstrapping a production-grade Kubernetes cluster on machines you control. It does not provision infrastructure (you bring your own Linux hosts), but it handles the hard, error-prone parts of standing up a conformant cluster: generating certificates, writing static Pod manifests for the control plane, configuring the kubelet, and producing join tokens for worker nodes.
The mental model is two commands: kubeadm init on the first control-plane node sets up the control plane and prints a kubeadm join command; you run that join command on each worker (and additional control-plane) node. kubeadm deliberately leaves some choices to you — most importantly, you must install a CNI plugin yourself afterward, and you manage the underlying OS, load balancer, and etcd topology. It is the foundation that many higher-level tools (like kubespray) build upon.
Example
# On the first control-plane node:
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
# kubeadm prints a "kubeadm join ..." command — save it.
# Configure kubectl for your user:
mkdir -p $HOME/.kube && sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
# Install a CNI (required — nodes stay NotReady without it):
kubectl apply -f https://.../calico.yaml
# On each worker node, run the printed join command:
sudo kubeadm join 10.0.0.1:6443 --token abcdef.0123456789 \
--discovery-token-ca-cert-hash sha256:<hash>
Exercises
- (Beginner) What does kubeadm do, and what does it deliberately not do?
- (Beginner) After
kubeadm init, why do nodes remainNotReadyuntil you take one more step?- (Intermediate) Describe the two-phase workflow of using kubeadm to create a cluster with one control-plane and two worker nodes.
- (Interview) kubeadm leaves CNI, OS management, and load balancer setup to the operator. Why is this "batteries not included" philosophy appropriate for a production bootstrap tool? (Hint: flexibility, environment-specific choices, separation of concerns.)
Answers
- kubeadm bootstraps a conformant cluster: it generates certificates, brings up the control-plane components (as static Pods), configures the kubelet, and creates join tokens. It does not provision machines/infrastructure, install a CNI plugin, manage the host OS, or set up an external load balancer — those are left to the operator.
- The control plane is up, but Pod networking is not — kubeadm does not install a CNI plugin. Until you apply one (e.g., Calico/Flannel), the kubelet cannot set up Pod networking, so nodes report
NotReady.- Phase 1: on the first control-plane node, run
kubeadm init(sets up control plane, outputs a join command); configure kubectl; install a CNI. Phase 2: on each worker, run thekubeadm joincommand with the token and CA cert hash to register the node with the control plane. Repeat join for each worker.- Production environments differ widely in networking, OS, storage, and HA topology. By not hard-coding these, kubeadm stays flexible and composable: operators choose the CNI that fits their performance/policy needs, manage OS patching with their own tooling, and integrate their own load balancer. This separation of concerns keeps kubeadm focused on correctly bootstrapping the cluster while letting environment-specific decisions be made by those who know the environment.
Managed Kubernetes: EKS, GKE, AKS
Theory
Running your own control plane (etcd backups, API server HA, upgrades, certificate rotation) is significant ongoing work. Managed Kubernetes services — Amazon EKS, Google GKE, Azure AKS — run and operate the control plane for you. You consume the Kubernetes API; the cloud provider handles control-plane availability, patching, and (often) etcd. You typically pay for the worker nodes plus a small per-cluster control-plane fee.
The key division of responsibility: the provider owns the control plane; you own your workloads, node configuration (unless using fully serverless node options like GKE Autopilot or EKS Fargate), and your application security. Managed offerings integrate tightly with their cloud's IAM, load balancers, and storage (via the cloud-controller-manager and CSI drivers). This is the default choice for most organizations because it removes the hardest operational burden.
Example
# GKE
gcloud container clusters create demo --num-nodes=3 --region=us-central1
# EKS (eksctl simplifies the many underlying resources)
eksctl create cluster --name demo --nodes 3 --region us-east-1
# AKS
az aks create --resource-group rg --name demo --node-count 3 --generate-ssh-keys
# All three: fetch credentials so kubectl points at the managed cluster
aws eks update-kubeconfig --name demo --region us-east-1
gcloud container clusters get-credentials demo --region us-central1
az aks get-credentials --resource-group rg --name demo
Exercises
- (Beginner) In a managed Kubernetes service, who is responsible for operating the control plane?
- (Beginner) Name the managed Kubernetes offering of AWS, Google Cloud, and Azure.
- (Intermediate) Describe the shared responsibility split between you and the cloud provider when using EKS/GKE/AKS.
- (Interview) When would a company still choose to self-manage Kubernetes (kubeadm/kubespray) instead of a managed service? (Hint: on-prem/air-gapped, regulatory, cost at scale, control-plane customization.)
Answers
- The cloud provider operates the control plane (API server, scheduler, controller-manager, and usually etcd) — including its availability, patching, and upgrades.
- AWS -> EKS (Elastic Kubernetes Service); Google Cloud -> GKE (Google Kubernetes Engine); Azure -> AKS (Azure Kubernetes Service).
- The provider manages the control plane and its uptime/upgrades. You manage your workloads (Deployments, Services), application security and RBAC, and (unless using serverless node modes) worker node pools, their OS/upgrades, scaling, and resource configuration. Networking, storage, and load balancing integrate via the provider's CCM/CSI but you configure how you use them.
- Reasons to self-manage: on-premises or air-gapped environments with no cloud option; strict regulatory/data-sovereignty requirements; the need to customize the control plane (custom admission, schedulers, API flags) beyond what managed offerings allow; potential cost savings at very large scale or with existing hardware; and avoiding cloud lock-in. The trade-off is taking on the full operational burden of running the control plane.
Bare-metal provisioning with kubespray
Theory
When you have a fleet of bare-metal or VM hosts and want a production, highly-available cluster without a cloud's managed service, kubespray automates the job. It is a collection of Ansible playbooks that install and configure Kubernetes (using kubeadm under the hood) across many nodes — handling the control-plane HA, etcd cluster, CNI installation, and add-ons that you would otherwise wire up by hand.
kubespray's value is repeatability and scale: you declare your inventory (which hosts are control-plane, which are workers, etcd topology) and configuration in files, then run a playbook to provision the whole cluster consistently. It supports many CNIs and OSes, and can also perform upgrades and scaling operations. The cost is the complexity of operating Ansible and understanding the many configuration knobs.
Example
# inventory/mycluster/hosts.ini — declare node roles
[kube_control_plane]
node-1
node-2
node-3
[etcd]
node-1
node-2
node-3
[kube_node]
node-4
node-5
# Run the cluster-provisioning playbook against the inventory:
ansible-playbook -i inventory/mycluster/hosts.ini cluster.yml -b
# Scale later by editing the inventory and running scale.yml / upgrade-cluster.yml
Exercises
- (Beginner) What automation technology does kubespray use to provision clusters?
- (Beginner) What does kubespray use under the hood to actually bootstrap each cluster?
- (Intermediate) What is the main advantage of declaring your cluster topology in an inventory file rather than running kubeadm manually on each host?
- (Interview) A team can already use kubeadm. What does kubespray add on top, and when is that extra layer worth the added complexity? (Hint: many nodes, HA etcd, repeatable upgrades, fleet consistency.)
Answers
- Ansible (kubespray is a set of Ansible playbooks).
- kubeadm — kubespray orchestrates kubeadm-based bootstrap across all the nodes.
- Repeatability and consistency at scale: the inventory + config is declarative and version-controllable, so the entire multi-node, HA cluster is provisioned identically every time. Manual kubeadm across many hosts is error-prone, hard to reproduce, and tedious to keep consistent.
- kubeadm bootstraps one cluster but leaves orchestration across many nodes, HA control-plane and etcd setup, CNI/add-on installation, and upgrades to you. kubespray automates all of that across a fleet via Ansible, with repeatable provisioning, scaling, and upgrade playbooks. The extra complexity is worth it when you manage many bare-metal/VM nodes, need HA, and want consistent, automatable lifecycle operations — versus a small or throwaway cluster where plain kubeadm suffices.
High availability control plane setup
Theory
A single control-plane node is a single point of failure: lose it and you lose the ability to schedule, heal, and manage the cluster (existing Pods keep running, but nothing can change). A highly available (HA) control plane runs multiple control-plane nodes so the cluster keeps operating through a node failure.
Two things must be made redundant:
- The API servers: run 3+ stateless API server replicas behind a load balancer that distributes client traffic and removes failed instances. Because they are stateless, this is straightforward active-active.
- etcd: run a quorum-based etcd cluster (3 or 5 members). There are two topologies — stacked (etcd runs on the same nodes as the control plane) and external (etcd on dedicated machines). Stacked is simpler and cheaper; external isolates etcd failures from control-plane failures and is preferred for larger/critical clusters.
The other control-plane components (scheduler, controller-manager) also run as replicas but use leader election so only one is active at a time.
Example
+------------------+
clients ----> | Load Balancer | (e.g., HAProxy, cloud LB on :6443)
+--------+---------+
+-------------+-------------+
v v v
apiserver-1 apiserver-2 apiserver-3 (active-active, stateless)
| | |
etcd-1 <-------- etcd-2 -------> etcd-3 (Raft quorum = 2 of 3)
scheduler/cm replicas everywhere, but only ONE leader each (leader election)
# kubeadm HA: init the first node with a shared control-plane endpoint (the LB):
sudo kubeadm init --control-plane-endpoint "lb.example.com:6443" --upload-certs
# Then join additional control-plane nodes with the printed "--control-plane" join cmd.
Exercises
- (Beginner) What still works and what stops working if your only control-plane node fails?
- (Beginner) Why can API server replicas run active-active while scheduler/controller-manager replicas use leader election?
- (Intermediate) Compare stacked vs. external etcd topology for an HA control plane.
- (Interview) For an HA control plane you choose 3 etcd members rather than 2 or 4. Justify the number using quorum math and fault tolerance. (Hint: (N/2)+1.)
Answers
- Existing Pods/containers keep running (kubelets operate from cached state), but the cluster cannot make changes: no new scheduling, no self-healing of failed Pods, no API access for
kubectl, and no controller actions — because the control plane is down.- API servers are stateless request handlers backed by shared etcd, so any replica can serve any request simultaneously without conflict. The scheduler and controller-manager take actions (binding Pods, creating/deleting resources); multiple active instances would issue conflicting/duplicate actions, so leader election ensures exactly one acts at a time.
- Stacked: etcd members run on the same machines as the control-plane components — fewer machines, simpler, cheaper, but a node failure removes both an API server and an etcd member, and they compete for resources. External: etcd runs on dedicated hosts — more machines and complexity, but isolates etcd from control-plane failures/resource contention and is recommended for large or critical clusters.
- Quorum is
(N/2)+1. With 3 members, quorum is 2, tolerating 1 failure. With 2 members, quorum is 2, tolerating 0 failures (worse than a single node for availability of writes). With 4 members, quorum is 3, still tolerating only 1 failure — same tolerance as 3 but more cost and more cross-member coordination. So 3 gives the best fault tolerance per node for a small HA cluster; 5 (quorum 3, tolerates 2) is used when more resilience is needed.
3.3 kubectl CLI
kubectl is the primary way humans and scripts interact with a cluster. Mastering its configuration and core commands pays off every single day. This subchapter covers installation, contexts, everyday commands, and extending it with plugins.
Installing and configuring kubectl
Theory
kubectl is the official command-line client for the Kubernetes API. It translates human-friendly commands into the REST calls the API server understands. It is a standalone binary you install on your workstation (or CI runner) — separate from any cluster — and it can talk to any cluster you have credentials for.
A key compatibility rule: kubectl supports a version skew of ±1 minor version relative to the cluster's API server. So a v1.30 kubectl works with v1.29, v1.30, and v1.31 clusters. Using a kubectl far newer or older than the cluster can cause subtle failures, so it is good practice to match (or stay within one minor of) your cluster version. After installing, kubectl version and kubectl cluster-info confirm connectivity.
Example
# Install on Linux (download the matching version):
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client # show kubectl's own version
kubectl cluster-info # verify it can reach the cluster's control plane
# Enable shell autocompletion (huge productivity boost):
source <(kubectl completion bash)
alias k=kubectl # common convenience alias
Exercises
- (Beginner) What is kubectl, and is it part of the cluster or installed separately?
- (Beginner) Which command verifies that kubectl can reach the cluster?
- (Intermediate) Your cluster runs v1.30. Within what range of kubectl minor versions are you officially supported, and why does this matter?
- (Interview) Why does kubectl enforce a version skew policy with the API server, and what kinds of problems can arise from a large skew? (Hint: API fields/resources added or removed between versions.)
Answers
- kubectl is the official CLI client for the Kubernetes API. It is installed separately on your workstation/CI, not part of the cluster, and can connect to any cluster you have credentials for.
kubectl cluster-info(andkubectl version/kubectl get nodes) confirms connectivity to the control plane.- Supported skew is ±1 minor version, so for a v1.30 cluster, kubectl v1.29–v1.31 are supported. It matters because client and server must agree on available API resources and fields; staying within one minor avoids incompatibilities.
- The Kubernetes API evolves between minor versions — resources and fields are added, promoted, deprecated, or removed. A kubectl too new may send fields/resources the server doesn't understand; a kubectl too old may not know about resources or may serialize objects in ways the server handles incorrectly. The ±1 skew policy bounds these differences to changes both sides can tolerate, preventing failed or silently incorrect operations.
kubeconfig and context management
Theory
How does kubectl know which cluster to talk to and as whom? Through a kubeconfig file (default ~/.kube/config). It is a YAML file with three lists that get tied together by contexts:
- clusters: API server URLs and their CA certificates.
- users: credentials (client certs, tokens, exec plugins).
- contexts: a named combination of (cluster + user + default namespace).
The current-context determines what every kubectl command targets by default. This design lets one engineer manage many clusters (dev, staging, prod across multiple clouds) from one config, switching with a single command. You can merge multiple kubeconfig files via the KUBECONFIG environment variable (colon-separated list).
Example
kubectl config get-contexts # list all contexts; * marks the current one
kubectl config current-context # show the active context
kubectl config use-context prod # switch the default target to "prod"
kubectl config set-context --current --namespace=team-a # change default namespace
# Merge two kubeconfig files for this shell session:
export KUBECONFIG=~/.kube/config:~/work/eks-config
kubectl config view --flatten > ~/.kube/merged && mv ~/.kube/merged ~/.kube/config
# Simplified kubeconfig structure:
contexts:
- name: prod
context:
cluster: prod-cluster # which clusters[] entry
user: alice # which users[] entry
namespace: payments # default namespace for this context
current-context: prod
Exercises
- (Beginner) What three things does a kubeconfig "context" tie together?
- (Beginner) Which command switches the active cluster you are operating on?
- (Intermediate) You have separate kubeconfig files for a GKE and an EKS cluster. How do you use both from a single kubectl session?
- (Interview) An engineer accidentally ran a destructive command against prod instead of dev. What kubeconfig/context practices reduce the chance of this happening? (Hint: context naming, namespace defaults, prompt indicators, separate credentials.)
Answers
- A context ties together a cluster (API server + CA), a user (credentials), and a default namespace.
kubectl config use-context <name>switches the current context (the default target cluster/user/namespace).- Point
KUBECONFIGat both files (colon-separated):export KUBECONFIG=~/.kube/gke:~/.kube/eks. kubectl merges them, exposing all contexts; switch between clusters withkubectl config use-context. Optionally flatten/merge them into one file withkubectl config view --flatten.- Use clear, unambiguous context names (e.g.,
prod-paymentsvsdev-payments); set per-context default namespaces; display the current context/namespace in your shell prompt (tools like kube-ps1, kubectx/kubens); keep prod credentials separate and require an explicit switch; consider read-only or restricted RBAC for day-to-day prod access; and add confirmation steps or--contextexplicitly on destructive commands so the target is never implicit.
Basic kubectl commands
Theory
A small set of kubectl verbs covers the vast majority of daily work. They fall into a few mental categories:
- Viewing:
get(list),describe(detailed human-readable view + events),logs(container output),top(resource usage). - Creating/changing:
apply(declarative — apply a manifest),create,edit,scale,set image,delete. - Interacting:
exec(run a command in a container),port-forward(tunnel a local port to a Pod/Service),cp. - Debugging:
describe,logs --previous,get events.
The most important habit is preferring declarative apply over imperative commands for anything you want to keep: kubectl apply -f makes your manifests the source of truth and is idempotent and GitOps-friendly. Imperative commands (create, run, expose) are great for quick experiments. The -o/--output flag (-o yaml, -o json, -o jsonpath=..., -o wide) and -n/--namespace are used constantly.
Example
kubectl get pods -n web -o wide # list Pods with node/IP columns
kubectl describe pod web-abc -n web # detailed status + recent events
kubectl logs -f web-abc -c app # stream logs of container "app"
kubectl logs web-abc --previous # logs from the crashed prior container
kubectl apply -f deployment.yaml # declarative create/update (preferred)
kubectl scale deployment web --replicas=5 # imperative quick scale
kubectl set image deployment/web app=app:2.0 # trigger a rolling update
kubectl exec -it web-abc -- sh # open a shell in the container
kubectl port-forward svc/web 8080:80 # reach the Service at localhost:8080
kubectl get pod web-abc -o jsonpath='{.status.podIP}' # extract a single field
Exercises
- (Beginner) Which command shows a Pod's detailed status and recent events?
- (Beginner) How do you view the logs of a container that has already crashed and restarted?
- (Intermediate) Explain the difference between
kubectl applyandkubectl create, and when you would use each.- (Interview) Why is declarative
kubectl apply -fgenerally preferred over imperative commands in production workflows? (Hint: source of truth, idempotency, reviewability, GitOps.)
Answers
kubectl describe pod <name>— it shows detailed spec/status plus the recent Events that explain scheduling/pull/probe issues.kubectl logs <pod> --previous(optionally-c <container>) shows logs from the previous, crashed container instance.createis imperative and fails if the object already exists; it creates once.applyis declarative: it creates the object if absent and updates it to match the manifest if present (tracking changes via the last-applied-configuration), and it is idempotent. Usecreatefor one-off imperative actions; useapplyto manage resources from version-controlled manifests.apply -ftreats version-controlled manifests as the single source of truth: it is idempotent (safe to re-run), produces reviewable diffs in code review, supports updates the same way as creates, and fits GitOps where Git is authoritative and tooling reconciles the cluster to it. Imperative commands mutate state ad hoc with no recorded source of truth, making changes hard to audit, reproduce, or roll back.
kubectl plugins and krew
Theory
kubectl is extensible: any executable on your PATH named kubectl-<something> becomes a subcommand kubectl <something>. This plugin mechanism lets the community add capabilities without modifying kubectl itself. krew is the official plugin manager for kubectl (itself a kubectl plugin) — it provides a curated index, and handles discovering, installing, and updating plugins across platforms.
Plugins fill gaps in the core CLI. Popular ones include kubectx/kubens (fast context/namespace switching), kubectl-neat (clean up get -o yaml output), stern (multi-Pod log tailing), and kubectl-tree (show ownership hierarchies). The pattern mirrors Kubernetes' broader extensibility philosophy: a small stable core plus a plugin ecosystem.
Example
# Install krew (the plugin manager), then use it to install plugins:
kubectl krew install ctx ns neat tree # install several plugins
kubectl krew list # show installed plugins
kubectl krew update && kubectl krew upgrade
# Now use them as native-feeling subcommands:
kubectl ctx prod # switch context (kubectx)
kubectl ns team-a # switch namespace (kubens)
kubectl tree deployment web # show owned ReplicaSets/Pods
# A plugin is just an executable named kubectl-<name> on PATH:
echo '#!/bin/sh
echo hello from my plugin' > kubectl-hello && chmod +x kubectl-hello
kubectl hello # -> "hello from my plugin"
Exercises
- (Beginner) What naming convention turns an executable into a kubectl plugin?
- (Beginner) What is krew?
- (Intermediate) Write the steps to create a trivial custom kubectl plugin called
kubectl hellothat prints a message.- (Interview) The kubectl plugin model and krew reflect a broader Kubernetes design philosophy. Describe that philosophy and one benefit it brings to the CLI specifically. (Hint: small stable core + extensible ecosystem.)
Answers
- An executable named
kubectl-<name>on yourPATHbecomes the subcommandkubectl <name>(dashes in the filename map to spaces/subcommands).- krew is the official plugin manager for kubectl — a kubectl plugin that provides a curated plugin index and installs, updates, and manages other plugins across platforms.
- Create a file named
kubectl-hellocontaining a script (e.g.,#!/bin/shthenecho "hello"), make it executable (chmod +x kubectl-hello), and place it on yourPATH. Thenkubectl helloruns it.- The philosophy is a small, stable core with a pluggable, extensible ecosystem (the same pattern as CRI/CNI/CSI and CRDs). For the CLI, this means the kubectl maintainers keep the core lean and well-tested, while the community can add specialized capabilities (context switching, log tailing, visualization) independently — users get rich functionality without bloating or destabilizing core kubectl, and plugins evolve on their own schedules.