Talking to the cluster
kubectl is an HTTP client
Before creating anything it helps to know what you're talking to, and with which tool.
kubectl is the command line you use to work with a cluster, and it's important to understand what it is not: it is not Kubernetes, it is not an agent, it has no intelligence of its own and it doesn't talk to the nodes. It is a binary that translates what you type into HTTP calls against the API server and formats the response so it can be read.
All the work happens from the dev-machine tab, against a real Kubernetes cluster.
💡 You can open the Explorer tab at any time to see visually what is going on inside the cluster.
Step 1: the syntax, which is always the same
Start by checking which version you have in front of you:
kubectl version
You'll see two: the one of the client you just ran and the one of the server it talks to. They are separate programs and may not match.
From there on, every command has the same shape:
kubectl [verb] [object] [options]
kubectl get pods, kubectl delete pod web, kubectl scale deployment web. If you know that shape, the rest of the course is vocabulary.
Step 2: see the HTTP request with your own eyes
You don't have to take it on faith that kubectl is an HTTP client: you can see it. Every command accepts --v, with values from 0 to 9, which shows the request data. That verbose output goes to standard error, so to save it you have to redirect both:
kubectl get pods -A --v=7 > ~/traza.txt 2>&1
Open it and look for the request line:
grep -i "GET" ~/traza.txt -A4
"Request" verb="GET" url="https://127.0.0.1:6443/api/v1/pods?limit=500"
"Response" status="200 OK" milliseconds=4
"Request" verb="GET" url="https://172.16.0.2:6443/api/v1/pods?limit=500" headers=<
Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json;as=Table;v=v1beta1;g=meta.k8s.io,application/json
User-Agent: kubectl/v1.36.3 (linux/amd64) kubernetes/0f29094
>
"Response" status="200 OK" milliseconds=52
A GET request to a URL, with its headers and its response code. Nothing more.
Three consequences follow, and it helps to have them clear from the start:
- Everything
kubectldoes, any other client can do: a script withcurl, a program in Go, Argo CD or the operator you'll write in the Extensibility module. The API is the product;kubectlis a convenient way to use it, not the only one. - When something is slow or fails,
--v=8tells you which side the problem is on, because you see the request, the response code and the time. - The permissions aren't
kubectl's: they're yours, and they are checked in the API server. That is what the Security module is about.
Step 3: which types exist, and how they are abbreviated
The cluster can tell you which object types it understands, without looking in any documentation:
kubectl api-resources
Notice the columns: the name of the type, its short names, whether it lives inside a Namespace or belongs to the whole cluster, and which API group it belongs to. That list grows: in the Extensibility module a type you invented yourself will show up in it.
The short names are what you type day to day, and they save hundreds of keystrokes. To look up a specific one:
kubectl api-resources | grep persistentvolumeclaims
Step 4: don't memorize fields
A Pod manifest accepts more than a hundred fields and nobody knows them all. You don't need to: the documentation lives inside the cluster, and it matches exactly the version you have in front of you.
kubectl explain pod.spec
It returns the description of every field at that level. You can go as far down the tree as you like:
kubectl explain pod.spec.containers
And with --recursive it unfolds the whole thing at once:
kubectl explain pod.spec --recursive | head -40
It works the same with types that didn't come out of the box, like the Gateway from the Networking module or the Promocion from the Extensibility one. If at any point in the course you're left wondering what a field accepts, this is the answer.
Summary
kubectltranslates commands into HTTP requests against the API server:--v=7shows you.- The syntax is always
kubectl [verb] [object] [options]. kubectl api-resourcestells you which types exist and how they're abbreviated.kubectl explaintells you which fields each one accepts, in your cluster's version.
In the next unit, the other half: the YAML file that describes what you want, and which cluster your commands end up at.
The file, and which cluster you're talking to
You can work with kubectl in two ways, and both show up throughout the course:
- The imperative mode is commands that say what to do:
kubectl run web --image=...,kubectl create namespace tienda,kubectl scale deployment web --replicas=5. - The declarative mode is files that describe how you want things to be, sent to the cluster with
kubectl apply -f. The file can be read, reviewed, versioned in Git and applied again.
The criterion for choosing is simple: the imperative is for finding out what's going on; the declarative, for leaving a record of what you want. An urgent fix done with a command works just as well, but it doesn't get written down.
Step 1: almost no Kubernetes error is a Kubernetes error
Those files are written in YAML, and it's worth spending two minutes on it, because almost every beginner's error with Kubernetes is actually a YAML error.
There are only two rules to retain. The first: indentation decides the structure, and only with spaces; a tab is a syntax error. The second: a dash at the start of a line creates a list item.
apiVersion: v1 # key: value
kind: Pod
metadata: # a block, with its keys inside
name: web
labels:
app: web # three levels: metadata, labels, app
spec:
containers: # a list...
- name: web # ...with one item, which is another block
image: ghcr.io/iximiuz/labs/nginx:alpine
That dash matters more than it seems: when you get to the NetworkPolicies in the Networking module, one dash too many or too few will turn a rule from "both conditions must be met" into "meeting one is enough".
And then there are the types. Values are text, numbers or booleans, and YAML guesses which. It almost always guesses right, and the two times it doesn't, it hurts:
- A value like
NO,on,offoryesis read as a boolean. The classic case is Norway's country code,NO, which ends up turned intofalse. - A version number is read as a decimal number, and that is where a whole afternoon gets lost.
Run into it now, while it's cheap. Write this file:
cat > ~/tipos.yaml <<'YAML'
apiVersion: v1
kind: ConfigMap
metadata:
name: tipos
namespace: tienda
data:
version: 1.10
YAML
It's a ConfigMap, the object that stores configuration and that you'll see in depth in the Configuration module. Only one thing about it matters here: the data block only accepts text. Apply it and see what happens:
kubectl apply -f ~/tipos.yaml
It isn't created. The cluster complains that where it expected a string it received a number: you wrote 1.10, but YAML didn't see a version, it saw a decimal (and in a decimal the trailing zero means nothing, so it isn't even 1.10 anymore, it's 1.1). Fix it by putting quotes around the value in the file and apply it again.
💡 The practical rule: if the value is a string and there's the slightest doubt, quote it. Ports, replicas and booleans go without quotes.
Notice that you haven't typed -n at any point: the manifest carries its Namespace inside, in metadata. When it doesn't (or when the command is one of those that don't read files, like kubectl get) you do have to say it, and that is what the last step of this lesson is about.
And before applying anything, this validates a file without touching the cluster:
kubectl apply -f ~/tipos.yaml --dry-run=client
Step 2: let the command write the file for you
There is a third way that combines the two modes: asking an imperative command to, instead of touching the cluster, write the declarative file it would have applied. You get it by adding -o yaml to the --dry-run=client from before:
kubectl create namespace pruebas --dry-run=client -o yaml > ~/pruebas.yaml
Open it in the IDE tab:
cat ~/pruebas.yaml
You have the manifest, and nothing has been created in the cluster:
kubectl get namespace pruebas
Error from server (NotFound): namespaces "pruebas" not found
From there you edit the file and apply it whenever you want. It is the fastest way there is to start a manifest, and it avoids the classic mistake of copying one from the internet without knowing what half of its lines do. In the Working with the cluster module this becomes a complete way of working.
💡 Change client to server and the validation is done by the API server: it also checks the fields that depend on the cluster and goes through the admission controllers, without ever storing anything.
Step 3: which cluster you're talking to
What's left is to answer where kubectl gets the cluster's address from, and who you are.
It reads it from a file called kubeconfig, usually at ~/.kube/config, which can hold several clusters at once. Each combination of cluster, user and default Namespace is called a context, and only one is active at a time. Every command you run goes against the active context.
kubectl config current-context
kubectl config get-contexts
In the NAMESPACE column you'll see it's empty right now: with nothing set, the default Namespace is used.
⚠️ The most expensive mistake in this profession is running in production a command you thought was going to the test cluster. kubectl config current-context before touching anything costs one second. Many administrators end up putting the active context in their terminal prompt for this very reason.
The cluster in this course already comes with the book's two Namespaces created. Check it:
kubectl get namespaces
There they are: tienda, where the application lives, and plataforma, for the pieces that don't fit inside it. The whole course happens in tienda, and since the Namespace is part of the context you can set it once and stop typing -n tienda in every command:
kubectl config set-context --current --namespace=tienda
Look at it again and compare with before:
kubectl config get-contexts
In the rest of the course's lessons this step is already done for you, and you'll see the -n flag only where it really matters: when a command crosses Namespaces or when the tool demands it.
Summary
- Imperative mode to find out; declarative mode to leave a record.
- YAML: indentation is the structure, the dash is a list, and without quotes
1.10stops being a version. --dry-run=clientvalidates without touching anything; with-o yamlit also writes the manifest for you.- The kubeconfig stores contexts (cluster, user and Namespace), and only one is active.
You now know how to talk to the cluster. In the next lesson you ask it for something: your first Pod.
- Previous lesson
- Challenge: build the store image
- Next lesson
- Your first Pod