DEV Community

Akash for MechCloud Academy

Posted on

Part 5: Speaking Kubernetes: A Beginner's Guide to YAML Manifests

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:

  1. apiVersion: Which Kubernetes API to use. For Deployments, this is apps/v1.
  2. kind: The type of resource this manifest describes. For us, it's Deployment.
  3. metadata: Data about the resource, like its name.
  4. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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. The matchLabels field says, "any Pod that has the label app: 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 the labels field. We give the Pods the label app: nginx, which perfectly matches the Deployment's selector. 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 of containers.
      • name: A name for our container.
      • image: The container image to run, which is nginx.

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)