In the last part, we successfully deployed our first application using kubectl create deployment
. This command is an example of an imperative approach. We gave Kubernetes a direct order: "Go create this."
This is great for quick tests, but it has serious limitations in the real world:
- It's not repeatable: How do you create the exact same deployment on another cluster? You have to remember the exact command and flags.
- It's not version-controlled: How do you track changes to your application's configuration over time? A command history is not a substitute for Git.
- It's not collaborative: How does a teammate review the changes you want to make to a deployment before they go live?
To solve these problems, Kubernetes is designed to work with a declarative model. Instead of giving orders, we describe the desired end state in a file, and Kubernetes works to make it happen. These files are called manifests, and they are written in YAML.
The Declarative Way: Infrastructure as Code
A YAML manifest is like a blueprint. Instead of telling a builder "lay this brick, then that one," you hand them a complete blueprint and say, "build this." The builder (Kubernetes) then figures out the necessary steps.
The benefits are immense:
- Version Control: Your YAML files live in Git. You can see every change, who made it, and when. You can use pull requests to review infrastructure changes just like code.
- Source of Truth: The manifest in Git is the single source of truth for what should be running in your cluster.
- Repeatability: You can apply the same manifest to your local, staging, and production clusters to ensure consistency.
Anatomy of a Kubernetes Manifest
Let's convert the hello-nginx
deployment we created imperatively into a declarative YAML manifest. A Kubernetes YAML file has four essential top-level fields:
-
apiVersion
: Which Kubernetes API to use. For Deployments, this isapps/v1
. -
kind
: The type of resource this manifest describes. For us, it'sDeployment
. -
metadata
: Data about the resource, like itsname
. -
spec
: The most important part. This is where we describe the desired state of the resource.
Let's clean up our old deployment before we recreate it declaratively.
kubectl delete deployment hello-nginx
Now, create a new file named deployment.yaml
and add the following content. This manifest describes the exact same state we created with our previous kubectl
command.
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-web-server
image: nginx
Breaking Down the spec
This spec
(specification) section is the heart of the manifest. Let's look at its three key parts:
replicas: 1
: This is simple and clear. We are telling the Deployment that the desired state is to have exactly one Pod running.selector
: This section tells the Deployment how to find the Pods it's supposed to manage. ThematchLabels
field says, "any Pod that has the labelapp: nginx
belongs to me." Labels are simple key-value pairs that we can attach to any Kubernetes object to organize them.-
template
: This is the blueprint for the Pods that the Deployment will create. It's essentially a Pod manifest embedded inside the Deployment manifest.-
metadata
: This is the metadata for the Pods. Note thelabels
field. We give the Pods the labelapp: nginx
, which perfectly matches the Deployment'sselector
. This is how the two are linked. -
spec
: This is the specification for the Pods. It describes what should be inside them. Here we define a list ofcontainers
.-
name
: A name for our container. -
image
: The container image to run, which isnginx
.
-
-
Applying the Manifest
Now, instead of kubectl create
, we use kubectl apply
. The apply
command is declarative. It reads the file and makes the cluster match the state described in the file.
Run this command in your terminal:
kubectl apply -f deployment.yaml
The -f
flag stands for "filename." You should see the output deployment.apps/hello-nginx created
.
Now, verify that everything is running just as before:
kubectl get deployments
# NAME READY UP-TO-DATE AVAILABLE AGE
# hello-nginx 1/1 1 1 10s
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# hello-nginx-65868b4b7-vwbnq 1/1 Running 0 10s
The result is identical, but now our infrastructure is defined in code.
What happens if you run kubectl apply
again? You'll see deployment.apps/hello-nginx unchanged
. Kubernetes is smart enough to see that the desired state in the file already matches the actual state in the cluster, so it does nothing.
If you were to change replicas: 1
to replicas: 3
in the file and re-run kubectl apply
, Kubernetes would see the difference and create two new Pods. This is the power of the declarative model.
What's Next
We have now learned the proper, modern way to define our applications in Kubernetes. From now on, we will be working with YAML files to describe our desired state.
However, we still have the same problem as before: our application is running, but it's not accessible from the outside world. We have the blueprint for the house, but we still need the street sign.
In the next part, we will finally solve this. We will create another YAML manifest, this time for a Service
object, to expose our hello-nginx
deployment and access it from a web browser.
Top comments (0)