0%
Installation and Configuration

Installation

A disposable local cluster, and a kubectl that points to the right place.

15-20 min

Installation and Configuration

Learning Kubernetes on a production cluster is a bad idea for a simple reason: you are going to break things, and that is exactly what you should be doing. You need a cluster you can destroy with one command and recreate in two minutes, where a mistake costs nothing.

There are two ways to get that on a workstation, and the choice matters. minikube simulates a full cluster inside a virtual machine or a container, with ready-to-use add-ons: a dashboard, an ingress controller, a load balancer simulator. kind (Kubernetes in Docker) runs each node as a Docker container: it is lighter, starts faster, and is the usual choice in continuous integration.

But before the cluster comes kubectl. This is the point beginners often get backwards: kubectl is not a cluster component, it is an HTTP client that talks to a remote API. It is installed independently, works with any cluster, and reads which cluster it should talk to from a local configuration file. Half of the “it doesn’t work” reports in the first few days come from this: kubectl is querying a different cluster than the one you think.

This lesson installs the tooling, then dwells on that configuration file — contexts and namespaces — because it is what will one day keep you from applying a manifest meant for your workstation to production.

Installing kubectl

kubectl is a single binary with no dependencies. Choose the method for your system:

# macOS with Homebrew
brew install kubectl

# Linux (Debian/Ubuntu): official Kubernetes repository
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.31/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.31/deb/ /' \
  | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update && sudo apt-get install -y kubectl
# Windows with winget
winget install -e --id Kubernetes.kubectl

Then verify the installation. The following command displays the client version, then tries to reach a server:

kubectl version --client

As long as no cluster is running, a command like kubectl get nodes will fail with a connection refused message. That is normal, and it is already useful information: the client works, it just has no one to talk to.

Which version to use

Kubernetes tolerates a one-minor-version skew between the client and the server. A kubectl 1.31 talks properly to a 1.30, 1.31, or 1.32 cluster. Beyond that, some resources may become invisible to the client without a clear message. If you see inexplicable behavior, the first thing to check is this:

# Compare the client version with the server version
kubectl version

Option A: minikube

minikube starts a single-node cluster in a virtualization driver. The docker driver is the simplest if Docker is already installed.

# Installation (macOS)
brew install minikube

# Start: the first launch downloads the node image, allow a few minutes
minikube start --driver=docker --cpus=2 --memory=4096

# minikube wrote the connection configuration for kubectl: let's check
kubectl get nodes

Expected output:

NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   47s   v1.31.0

A single node, carrying both the control plane and your workloads. In production these roles are separated; here the simplification has no impact on learning.

The lifecycle management commands:

minikube status          # state of the cluster and its components
minikube stop            # stops without destroying: state is preserved
minikube start           # restarts where you left off
minikube delete          # destroys everything, including data
minikube dashboard       # opens the web interface in the browser

minikube provides add-ons you can enable. Two are useful for the rest of the course:

# The ingress controller, required for the Ingresses in lesson 6
minikube addons enable ingress

# The metrics server, required for kubectl top and autoscaling
minikube addons enable metrics-server

minikube addons list     # see everything that is available

Option B: kind

kind makes each node a Docker container. It starts faster, uses fewer resources, and above all lets you simulate multiple nodes — useful for observing Pod rescheduling.

# Installation (macOS)
brew install kind

# Single-node cluster
kind create cluster --name atelier

# Verification
kubectl cluster-info --context kind-atelier

For a multi-node cluster, kind reads a configuration file. Note: this file uses kind’s API, not Kubernetes’, hence the unusual-looking apiVersion.

# kind-cluster.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: atelier
nodes:
  - role: control-plane
  - role: worker
  - role: worker
kind create cluster --config kind-cluster.yaml

kubectl get nodes
# atelier-control-plane, atelier-worker, atelier-worker2

kind delete cluster --name atelier   # deletion

One difference to know right away: kind does not see the images in your local Docker daemon. An image built locally must be explicitly loaded into the cluster, otherwise the Pod stays stuck in ErrImagePull even though docker images shows it as present.

docker build -t mon-api:dev .
kind load docker-image mon-api:dev --name atelier

With minikube, the equivalent is to point the Docker client at the cluster’s internal daemon:

eval $(minikube docker-env)
docker build -t mon-api:dev .   # the image is built inside the cluster

In both cases, specify imagePullPolicy: IfNotPresent in your manifests, otherwise Kubernetes will try to pull the dev image from a public registry where it does not exist.

The kubeconfig File and Contexts

This file is the piece people ignore for too long. By default ~/.kube/config, it contains three independent lists:

  • clusters: the addresses of API servers and their certificates;
  • users: the identities used to authenticate;
  • contexts: the associations (cluster, user, namespace).

A context is therefore simply the triplet that kubectl uses for every call. Changing context means changing the target cluster — hence the need to always know which one is active.

# List contexts; the asterisk marks the active one
kubectl config get-contexts

# Display only the name of the active context
kubectl config current-context

# Switch clusters
kubectl config use-context kind-atelier

# View the full configuration, secrets masked
kubectl config view

Get into the habit of checking the context before any write command. It is a reflex that seems excessive until the day it saves you from a kubectl delete on the wrong cluster:

# Make the context visible in the prompt, or at least check it
kubectl config current-context && kubectl get nodes

Namespaces

A namespace is a logical partition of the cluster. It lets you isolate sets of resources by team, environment, or application, and serves as a basis for quotas and access rules.

An important point that often surprises people: a namespace does not isolate the network. By default, a Pod in the dev namespace can reach a Pod in the prod namespace. That kind of isolation requires NetworkPolicy objects, which depend on the installed network plugin.

# The namespaces created by default
kubectl get namespaces
# default, kube-system, kube-public, kube-node-lease

kube-system hosts the cluster components. You don’t deploy anything of your own there, and you avoid inadvertently applying a manifest to it — yet another reason to keep an eye on your context.

Create a working namespace, preferably declaratively, so that its definition lives in your repository:

apiVersion: v1
kind: Namespace
metadata:
  name: atelier
  labels:
    environnement: developpement
kubectl apply -f namespace.yaml

# Imperative equivalent, handy for throwaway use
kubectl create namespace atelier

All read and write commands accept -n (or --namespace). Without this option, kubectl uses the namespace recorded in the active context, default most of the time:

kubectl get pods -n atelier          # in a specific namespace
kubectl get pods --all-namespaces    # everywhere; alias: -A

# Change the default namespace of the active context
kubectl config set-context --current --namespace=atelier

That last command saves you from repeating -n atelier fifty times. It has a downside: you no longer see in the command where it applies. So check regularly:

kubectl config view --minify --output 'jsonpath={..namespace}'

Finally, one property to know before learning it the hard way: deleting a namespace deletes everything it contains, in cascade and without confirmation.

kubectl delete namespace atelier   # Pods, Services, Secrets, PVCs: everything goes

Checking That Everything Is in Place

Run these four commands in sequence. If they all respond, your environment is ready for the next lesson:

# 1. The client can reach the API server
kubectl cluster-info

# 2. The node(s) are Ready
kubectl get nodes

# 3. The control plane components are running
kubectl get pods -n kube-system

# 4. An ephemeral Pod can actually start and reach the outside network
kubectl run test --image=busybox:1.36 --rm -it --restart=Never -- \
  wget -qO- https://kubernetes.io/fr/ | head -n 5

The fourth deserves a word: --rm deletes the Pod on exit, --restart=Never creates a standalone Pod rather than a Deployment, and -it attaches the terminal. If it succeeds, the runtime pulls images, the scheduler places Pods, and outbound networking works. Three validations in one command.

Autocompletion and Aliases

Resource names generated by Kubernetes are long and random (mon-api-7d4b8c9f5-xk2mp). Without autocompletion, you will be retyping them by hand all day.

# bash: add to ~/.bashrc
source <(kubectl completion bash)
alias k=kubectl
complete -o default -F __start_kubectl k

# zsh: add to ~/.zshrc
source <(kubectl completion zsh)
alias k=kubectl
# PowerShell: add to your profile ($PROFILE)
kubectl completion powershell | Out-String | Invoke-Expression
Set-Alias -Name k -Value kubectl

Two output options to know now, because you will use them constantly:

# -o wide adds the Pod IP and the node hosting it
kubectl get pods -o wide

# -w (watch) follows changes live: essential during a deployment
kubectl get pods -w

Key Takeaways

  • kubectl is a client independent of the cluster. It obeys the active context in ~/.kube/config, and the most common beginner mistake is talking to a different cluster than the one they think.
  • minikube offers a full cluster with add-ons (ingress, metrics-server); kind is lighter and can simulate multiple nodes. Both work for this course.
  • An image built locally is not visible to the cluster: use kind load docker-image or eval $(minikube docker-env), and declare imagePullPolicy: IfNotPresent.
  • A namespace partitions resources and serves as a basis for quotas, but does not isolate the network without NetworkPolicy. Deleting it destroys all of its contents in cascade.
  • Always verify a deployment against the observed state (kubectl get, kubectl describe), and check the active context before any write command.

Tip: destroy and recreate your local cluster at least once. Knowing you can start over from scratch in two minutes completely changes your relationship with experimentation.

Comments

Comments are powered by GitHub Discussions

Sign in with GitHub to join the discussion

Link copied!