Blog articles

Easily build a k8s cluster manifest using the Kubernetes API

May 17, 2023

If you use kubectl to build out your Kubernetes cluster, you are either creating or applying a YAML file called a manifest. Just like shipping manifests have all the information to ensure that a package or set of goods made it from point A to point B, Kubernetes YAML manifests contain all the information necessary to convey or carry your k8s services–such as deployments and pods–into a functioning reality.

While remaining humanly readable, YAML manifests must absolutely be machine-readable and syntactically correct. The proper indentation is critical, as are key words and values. Sure, you can write out your YAML manifests by hand once you've gotten the exact requirements and structure needed for each kind or type of entity being generated. But why not have the Kubernetes API do all that heavy lifting for you? 

With just a single kubectl flag, you can both quickly verify that an existing manifest will work in your cluster and produce a syntactically correct new manifest.

How to Build a Kubernetes Cluster with the Kubernetes API

Turning the Kubernetes API into your debugger using dry-run

Pop quiz! (You never know when one will show up!) Is the following manifest properly written?

apiVersion: apps/v1
kind: Deployment
  name: nginx
    app: nginx
      app: nginx
        app: nginx
      - image: nginx
        name: nginx
        - containerPort: 8080

Buzzzzzz - time's up. And the answer is: Nope! In fact, there are three errors in the manifest. Now, maybe you spotted the problems right away and maybe you still haven't. Either way, it's a lot faster and more accurate to let the Kubernetes API do the work for you. 

The key to turning the Kubernetes API into your debugger is to use the dry-run flag in server mode. Let's say I've cleverly named the above manifest test.yaml. Then, I just SSH into my control plan node and run this kubectl command.

kubectl apply -f test.yaml --dry-run=server

The dry-run flag tells kubectl to run the command as you normally would, but only to validate the file and report the results. In this case, the results returned look like this:

error: error validating "test.yaml": error validating data: [ValidationError(Deployment.metadata): unknown field "label" in io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta, ValidationError(Deployment.spec.selector): unknown field "matchLabel" in io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector, ValidationError(Deployment.spec.template.metadata): unknown field "label" in io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta]; if you choose to ignore these errors, turn validation off with --validate=false

Now, we have our three problems pinpointed. First, the field label in the metadata section is not recognized. Then we have a similar problem with the field matchLabel in the spec:selector section. Finally, we have another unrecognized term label in the template:metadata section. Basically, there are three typos. Even though there is only one value for all of them–app:nginx–each of these singular terms (label and matchLabel) should be plural (labels and matchLabels).

If I make those corrections in the file and rerun the command, kubectl apply -f test.yaml --dry-run=server the Kubernetes API will return this response:

deployment.apps/nginx created (server dry run)

Essentially, the dry-run flag tells the Kubernetes API to go through all the steps of actually creating the deployment, but doesn't store the results even if the manifest is valid.

Cluster magic: kubectl commands to generate a new manifest

You can see that setting the dry-run flag to server allows you to validate and debug an existing manifest. But what if you don't have a manifest yet? Changing that value from server to client (and specifying a couple of other options) will allow you to output syntactically correct YAML and even send it directly to a file. 

For example, let's say you want to generate a manifest for a new pod that uses the latest Nginx image and applies the web label. Furthermore, you want it to be in YAML and stored in a file named pspod.yaml.

kubectl run pspod --image=nginx:latest --labels type=web --dry-run=client -o yaml > pspod.yaml

Everything in this command line, until the --dry-run=client option, is designed to pass arguments into the manifest. And voilà! Here's the pspod.yaml file created:

apiVersion: v1
kind: Pod
  creationTimestamp: null
    type: web
  name: pspod
  - image: nginx:latest
    name: pspod
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

Whether you need a manifest for a deployment, adding a pod, or even a NodePort service that specifies a custom port, rely on the dry-run flag to provide the precise code blocks, copy and paste into a master file and then run the kubectl command–without the flag–to get your custom Kubernetes cluster up and running in no time.

For more information about the Kubernetes API as well as some hands-on experience, check out my course Hands-On with the Kubernetes API.

Joseph Lowery

Joseph is a Senior Training Architect at A Cloud Guru and Pluralsight who got his start in technology from an unlikely source: the stage. He was a theatre director and performer who started using PCs as a prop. That decision led him into programming, web and app development, and finally into the cloud. Since then, he has written books on web and web building tools that have sold over 400k copies worldwide in eleven different languages.