Getting Started with Kubernetes
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages. |
This page gets a working local Kubernetes cluster and a first deployment running. It targets kubectl 1.37, kind
v0.33 (node image kindest/node:v1.37.0) and minikube v1.39, against a Kubernetes v1.37 control plane; see
Introduction and Architecture for what those pieces
are and the section landing page for the full reading path. Everything here
runs on a laptop — no cloud account is needed.
Installing kubectl
kubectl is the CLI client that talks to a cluster’s API server over HTTPS; it does not need a running cluster to
be installed. There are three supported ways to get it on a developer machine:
| Method | Notes |
|---|---|
Homebrew (macOS, Linux) |
|
The |
|
Direct binary download |
A single static binary from |
The pkgs.k8s.io repositories
The legacy Google-hosted apt.kubernetes.io/yum.kubernetes.io repositories used by older books and tutorials are
gone; the project now publishes one repository per minor version under pkgs.k8s.io, so installing from the
v1.37 repository can never silently pull in a v1.38 package:
# Debian/Ubuntu
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.37/deb/Release.key \
| sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.37/deb/ /' \
| sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl
# Fedora/RHEL/CentOS Stream (dnf/yum)
cat <<'EOF' | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.37/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.37/rpm/repodata/repomd.xml.key
EOF
sudo dnf install -y kubectl
Derived from that page and the
pkgs.k8s.io introduction blog post. To
move to the next minor later, edit the repository URL to v1.38 and reinstall — the package manager then upgrades
kubectl deliberately instead of picking it up on a routine apt upgrade.
Binary download
curl -LO "https://dl.k8s.io/release/v1.37.1/bin/linux/amd64/kubectl"
curl -LO "https://dl.k8s.io/release/v1.37.1/bin/linux/amd64/kubectl.sha256"
echo "$(cat kubectl.sha256) kubectl" | sha256sum --check
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
macOS and Windows binaries are published under the same dl.k8s.io/release/<version>/bin/<os>/<arch>/ layout; see
Install Tools.
The version-skew policy
kubectl is supported within one minor version of the API server, in either direction: a v1.37 client works
against a v1.36, v1.37 or v1.38 server, but not against v1.35 or v1.39. Server-to-server components
(kubelet, etcd) have tighter or different skew rules of their own. Practical consequences:
-
Pin
kubectlto the newest API server version you talk to, not the oldest — an older client against a newer server risks missing a field the server expects. -
A CI pipeline that targets several clusters (dev on
v1.36, prod onv1.37) needs onekubectlbinary within skew of both, or one binary per cluster. -
kubectl versionprints both the client and server (Server Version:) so a skew problem shows up immediately.
Derived from Kubernetes version skew policy.
Local clusters
A local cluster removes the cost of a cloud account while learning the API. All the examples in this section run
on one of the three options below; Cluster Setup and
Administration covers kubeadm and real multi-node clusters, and
Managed Kubernetes and Distributions covers
cloud-managed options.
kind (Kubernetes in Docker)
kind runs each cluster node as a Docker (or Podman) container, using a purpose-built node image that itself runs containerd and kubelet. It starts and tears down in seconds, supports multiple nodes on one machine, and is the tool the Kubernetes project itself uses to test Kubernetes.
brew install kind # or: go install sigs.k8s.io/kind@v0.33.0
kind create cluster --name bookshelf --image kindest/node:v1.37.0
kubectl cluster-info --context kind-bookshelf
Source: kind Quick Start
A single-node cluster is enough for most of this section, but several later pages — the Ingress/Gateway API pages in particular — need a node that can receive traffic on host ports 80 and 443, and a cluster with more than one node to show pod anti-affinity, topology spread and DaemonSets meaningfully. The following multi-node configuration provides both, and is reused by name later in this section:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- role: worker
- role: worker
kind create cluster --name bookshelf --image kindest/node:v1.37.0 --config kind-config.yaml
kubectl get nodes
kubectl get node bookshelf-control-plane -o jsonpath='{.metadata.labels.ingress-ready}'
extraPortMappings forwards the host’s ports 80/443 to the control-plane container’s ports 80/443. An
ingress controller scheduled onto the node the ingress-ready=true label marks (via a nodeSelector) and bound
to that node’s ports 80/443 with hostPort then becomes reachable at http://localhost from the developer’s own
machine, without any cloud load balancer. A plain NodePort Service would not do: its port comes from the
30000-32767 range, not port 80. Later Gateway API pages install a gateway controller onto this same cluster and
reuse kind-config.yaml unchanged.
See also kind — Ingress.
Load a locally built image into the cluster’s containerd store without pushing it to a registry:
docker build -t bookshelf-web:dev .
kind load docker-image bookshelf-web:dev --name bookshelf
Delete the cluster with kind delete cluster --name bookshelf when done.
minikube
minikube runs a single- or multi-node cluster inside a VM or container driver (Docker, Podman, HyperKit, Hyper-V, KVM2, …). Since v1.39 the default node runtime inside the cluster is containerd rather than Docker:
brew install minikube
minikube start --kubernetes-version=v1.37.1 --driver=docker
kubectl get nodes
minikube dashboard # opens the bundled web UI
minikube addons list # ingress, metrics-server, registry, ...
minikube image load bookshelf-web:dev
minikube stop
Source: minikube start
minikube ships add-ons (an Ingress-NGINX
controller, metrics-server, a local registry) as one-line toggles, which kind leaves to the reader to install
separately; see also Hello Minikube.
Docker Desktop
Docker Desktop bundles a Kubernetes cluster that runs inside the Desktop VM next to the Docker Engine: enable it
once from Settings, and kubectl picks up a docker-desktop context automatically. It offers two provisioning
modes:
-
kubeadm — the original mode: always single-node, with the Kubernetes version fixed by the Docker Desktop release. It can use the Docker Engine’s image store, so an image built with
docker buildis visible to the cluster with no load step at all. -
kind — available since Docker Desktop 4.38: multi-node, with a selectable Kubernetes version. Its nodes do not share the Docker Engine’s image store, so a locally built image must be pushed to a registry or loaded into the nodes, as with standalone kind.
See Getting Started with Docker for installing Docker Desktop itself.
Comparison
| kind | minikube | Docker Desktop | |
|---|---|---|---|
Nodes |
Multiple, each a container; easy to script ( |
One or more, each a VM or container depending on the driver |
One (kubeadm mode) or several (kind mode) |
Runtime inside the cluster |
containerd |
containerd (default since v1.39) |
containerd |
Start-up time |
Seconds |
Tens of seconds |
Already running once Kubernetes is enabled |
Loading a local image |
|
|
Automatic in kubeadm mode (same image store as |
Add-ons |
None built in; install an ingress controller etc. yourself |
Built-in toggles ( |
A fixed, non-configurable set |
Best for |
CI, multi-node scenarios, testing against a specific |
A full-featured single machine with add-ons and a dashboard |
Developers who already run Docker Desktop and want minimal extra setup |
Kubeconfig contexts
A kubeconfig file (~/.kube/config by default, or the KUBECONFIG environment variable) holds one or more
clusters (an API server address and CA certificate), users (credentials) and contexts (a cluster/user/
namespace triple with a name). kind and minikube each add their own cluster, user and context automatically
when a cluster starts.
kubectl config get-contexts
kubectl config use-context kind-bookshelf
kubectl config set-context --current --namespace=bookshelf
kubectl config view --minify # just the active context
kubectl config current-context
kubectl config use-context switches which cluster and user every subsequent command targets; set-context
--current --namespace changes only the default namespace for the active context, so kubectl get pods means
kubectl get pods -n bookshelf without repeating -n on every command. Both changes are stored in the kubeconfig
file itself, so they persist across shells. Namespaces themselves — what they are for and when to use them — are
covered in Objects and the Kubernetes API.
Derived also from kubectl config
reference.
The first Bookshelf deploy
The rest of this section builds out the full Bookshelf application — catalog-api, reviews-api, web,
PostgreSQL, Redis and a notifier worker — but the very first deploy only needs one piece of it, following the
same imperative-command flow as the official
Kubernetes Basics tutorial: create a Deployment, expose
it, look at it, scale it, update it, and delete it.
kubectl create deployment web --image=ghcr.io/example/bookshelf-web:1.0.0
kubectl get deployments,pods -o wide
kubectl describe deployment web
kubectl expose deployment web --port=80 --target-port=8080 --name=web
kubectl get service web
create deployment wraps the Pod template in a Deployment (see
ReplicaSets and Deployments for what that buys you over a
bare Pod), and expose creates a matching ClusterIP Service selecting the Deployment’s pods (see
Services). Reach the Service from the local machine with port-forward,
which tunnels a local TCP port straight to a Pod without going through the cluster’s networking at all:
kubectl port-forward service/web 8080:80
# in another shell:
curl -s http://localhost:8080/ | head -n 5
Scale it, roll out a new version and watch the rollout, then roll back:
kubectl scale deployment web --replicas=3
kubectl get pods -l app=web -o wide
kubectl set image deployment/web bookshelf-web=ghcr.io/example/bookshelf-web:1.1.0
kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl create deployment names the container after the image (bookshelf-web), which is the name set
image targets. set image changes the Pod template’s image, which the Deployment controller turns into a new ReplicaSet and a
rolling update; rollout status blocks until the update finishes (or reports why it hasn’t), and rollout undo
reverts to the previous ReplicaSet. ReplicaSets and
Deployments covers the update strategy fields (maxSurge, maxUnavailable) and the rest of kubectl rollout
behind these commands.
Clean up:
kubectl delete service web
kubectl delete deployment web
Source: Kubernetes Basics
(in particular the "Using kubectl to Create a Deployment", "Viewing Your Application", "Using kubectl to Expose an App", "Scale an Application" and "Update an Application" modules); see also Hello Minikube, which walks the same flow specifically against minikube.
Shell completion
kubectl ships a completion script generator so the shell can tab-complete subcommands, flags, resource types and
even resource names fetched live from the cluster:
# bash (needs bash-completion installed)
echo 'source <(kubectl completion bash)' >> ~/.bashrc
# zsh
echo 'source <(kubectl completion zsh)' >> ~/.zshrc
# a short alias also gets completion, once registered:
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc # bash
echo 'alias k=kubectl' >> ~/.zshrc
echo 'compdef k=kubectl' >> ~/.zshrc # zsh
Source: Optional kubectl configurations
Derived from that page and kubectl completion --help.
The local dev loop
Putting the pieces above together, a typical inner-loop iteration on a kind cluster looks like this: change code, rebuild the image, load it into the cluster (no registry needed), re-apply the manifest so the new image is picked up, and re-open the port-forward to try it.
kubectl apply (rather than set image) is the declarative form used from here on, once a manifest exists on
disk; kubectl Essentials contrasts the imperative commands used
above with apply in depth. imagePullPolicy: IfNotPresent (or Never) on the Deployment’s container keeps
kubectl apply from trying to pull bookshelf-web:dev from a registry that does not have it, now that
kind load docker-image has already placed it on every node.
Where to go next
Read kubectl Essentials for the rest of the kubectl surface used
throughout this section, then Objects and the Kubernetes API for
the object model and namespaces this page only introduced in passing. The full reading order is on the
section landing page.