Kubernetes Deployment Examples

Kubernetes Dec 29, 2021

In Kubernetes, a Deployment spec is a definition of a Deployment that you would like to exist in the cluster. It represents the state that the Deployment should have.

Writing these manifests manually is a bit of a slog. So sometimes it’s helpful to see what a real manifest looks like, so you can use it as starting point for your own.

And at the end of this article, I’ll show you some time-saving tools to use, so you don’t even need to write a manifest manually if you don’t want to.

So what’s the difference between a Kubernetes manifest and a spec?

A manifest is a JSON or YAML representation of an object that you want to exist in your Kubernetes cluster. Within the manifest, a field called spec describes the state that the object should have.

In this article I’ll show you a few real-world examples, so that you can see what a typical Deployment looks like. Hopefully this will give you a starting point for creating your own Kubernetes Deployment manifests.

Example Deployment YAMLs for Kubernetes

You can write manifests for Kubernetes in either JSON or YAML. I prefer YAML, because it’s less verbose and I don’t need to worry about missing brackets.

However the disavantage of YAML is that you need to pay special attention to your indenting!

How to convert these examples to JSON

Deploying a simple application

This is a simple Pod spec, to begin. This Deployment creates replicas to run the Nginx web server. Each replica (a Pod) has just one container, which runs the Docker image nginx.

This will run the nginx image from Docker Hub.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 8080

Note how the labels in selector.matchLabels match the labels in template.metadata.labels block.

matchLabels is a query that allows the Deployment to find and manage the Pods it creates.

This is a very basic example, and is fine for deploying an off-the-shelf container from Docker Hub. But what happens when you want to provide some runtime configuration to the application?

Configuring your application with environment variables

When you want to provide runtime configuration to your container in Kubernetes, you might use environment variables. Env vars are a well-known way of providing configuration to a container.

You can set env vars directly on a Deployment. But, you don’t have to hardcode their values, especially if you use env vars to set something environment-specific, like a database connection string, or a hostname.

Instead, you can populate the environment variables with values from a ConfigMap or Secret.

This example Deployment shows how to deploy an app which has environment variables taken from a ConfigMap and a Secret:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              value: mydatabase           # an explicit env var value
            - name: POSTGRES_USER
              valueFrom:
                configMapKeyRef:          # populate from a ConfigMap
                  name: postgres-config   # ... with this name
                  key: my.username        # ... and look for this key
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:             # populate from a Secret
                  name: postgres-secret   # ... with this name
                  key: secret.password    # ... and look for this key

Externalising configuration is a really common pattern – one of the 12 Factor App principles – and a great one to follow for most apps. It means that you can set and update the ConfigMap in the cluster separately, without updating the Deployment itself.

Then, once your app is deployed, you might be concerned about how you’re going to upgrade it when you want to roll out a new version.

Configuring zero-downtime upgrades for your app

Zero-downtime upgrades are a really cool feature of Kubernetes. The rolling update strategy allows you to do this.

In a rolling update, Kubernetes will upgrade your Pods to a new container image version, whilst ensuring that at least one instance (replica) of your application is available at all times, so your users don’t experience downtime.

To take advantage of this, you configure a rolling update strategy on the Deployment. You do this by setting strategy.type to RollingUpdate. And you also set some parameters for the update, like whether Kubernetes can increase pods to higher than usual (this is called surging), to allow the upgrade to take place.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-rolling
  labels:
    app: nginx-rolling
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-rolling
  strategy:
    type: RollingUpdate   # Upgrade this application with a rolling strategy
    rollingUpdate:
      maxSurge: 1         # maximum number of pods that can be scheduled
                          # above the desired number of pods (replicas)
      maxUnavailable: 0   # the maximum number of pods that can be unavailable
                          # during the update
  template:
    metadata:
      labels:
        app: nginx-rolling
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 8080

Putting multiple Deployments in the same file

If you want to put more than one Kubernetes manifest (like a Deployment, Service, etc) in a single file, you can separate each entry with three hyphens (---). This is sometimes used if you want to keep all of your resources together; perhaps you want to track changes to a single file in Git.

Here’s an example of what I mean - I’m creating Deployments for Nginx and Postgresql, in the same YAML file. I separate the two manifests with the three hyphens:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  labels:
    app: postgres
spec:
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - image: postgres
        name: postgres
        ports:
        - containerPort: 5432
Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.