1. Packages
  2. Kubernetes
  3. How-to Guides
  4. Kubernetes Stateless App Deployment
Kubernetes v4.11.0 published on Thursday, Apr 18, 2024 by Pulumi

Kubernetes Stateless App Deployment

kubernetes logo
Kubernetes v4.11.0 published on Thursday, Apr 18, 2024 by Pulumi

    In this tutorial, we’ll run an application using a Kubernetes Deployment object. This results in an automatically scaled out container running inside of a cluster.

    This example is authored using Pulumi’s programming model in TypeScript instead of YAML and using the pulumi CLI for deployment rather than kubectl. This gives us the full power of familiar languages, combined with immutable infrastructure, delivering a robust and repeatable update experience.

    This example is based on this Kubernetes tutorial, and its full code is available on GitHub.

    Objectives

    • Create a Deployment using Pulumi that runs Nginx
    • Use pulumi to deploy this application
    • Use kubectl to list information about the resulting Kubernetes resources
    • Update the deployment’s number of replicas using pulumi
    • Clean up

    Before you begin

    You need to have the Pulumi CLI and a working Kubernetes cluster. Minikube is an easy way to get started.

    1. Install Pulumi
    2. Connect Pulumi to a Kubernetes Cluster

    Creating and Running the Application

    Normally we would write YAML files to configure them, and then run kubectl commands to create and manage the services. Instead of doing that, we will author our program in code and deploy it with pulumi.

    To start, we’ll need to create a project and stack (a deployment target) for our new project:

    Create and Configure a Project

    1. To create a new Pulumi project, let’s use a template:

      $ mkdir k8s-nginx && cd k8s-nginx
      $ pulumi new kubernetes-typescript
      

      This command will initialize a fresh project in the k8s-nginx newly-created directory.

    2. Next, replace the minimal contents of the template’s index.ts file with the code for the deployment:

      import * as pulumi from "@pulumi/pulumi";
      import * as k8s from "@pulumi/kubernetes";
      
      let config = new pulumi.Config();
      
      let nginxLabels = { app: "nginx" };
      let nginxDeployment = new k8s.apps.v1.Deployment("nginx-deployment", {
          spec: {
              selector: { matchLabels: nginxLabels },
              replicas: config.getNumber("replicas") || 2,
              template: {
                  metadata: { labels: nginxLabels },
                  spec: {
                      containers: [{
                          name: "nginx",
                          image: "nginx:1.7.9",
                          ports: [{ containerPort: 80 }]
                      }],
                  },
              },
          },
      });
      
      export let nginx = nginxDeployment.metadata.apply(md => md.name);
      

      This code simply creates a Kubernetes Deployment object. The entire Kubernetes object model is available to us, giving us the full power of Kubernetes right away.

    Deploying

    1. Now we’re ready to deploy our code. To do so, simply run pulumi up:

      $ pulumi up
      

      The command will first show us a complete preview of what will take place, with a confirmation prompt. No changes will have been made yet. It should look something like this:

       Previewing update of stack 'k8s-nginx-dev'
       Previewing changes:
      
           Type                           Name                     Plan       Info
       +   pulumi:pulumi:Stack            k8s-nginx-k8s-nginx-dev  create
       +   └─ kubernetes:apps:Deployment  nginx-deployment         create
      
       info: 2 changes previewed:
           + 2 resources to create
      
       Do you want to perform this update?
       > yes
       no
       details
      

      Let’s select “yes” and hit enter. The deployment will proceed, and the output will look like this:

       Updating stack 'k8s-nginx-dev'
       Performing changes:
      
           Type                           Name                     Status      Info
       +   pulumi:pulumi:Stack            k8s-nginx-k8s-nginx-dev  created
       +   └─ kubernetes:apps:Deployment  nginx-deployment         created
      
       ---outputs:---
       nginx: "nginx-deployment-rlefbi4w"
      
       info: 2 changes performed:
           + 2 resources created
       Update duration: 8.127334048s
      
       Permalink: https://app.pulumi.com/joeduffy/k8s-nginx-dev/updates/1
      

      Note that Pulumi will wait until the deployment has succeeded (or failed), and it will print detailed status outputs as the deployment happens. This is in contrast to kubectl which returns immediately. The Pulumi CLI’s approach ensures that you have more robust deployments that converge as expected.

    2. Now that we’ve done the deployment, let’s check some state with kubectl:

      $ kubectl describe deployment $(pulumi stack output nginx)
      

      Notice that we used the pulumi stack output command to fetch the auto-generated deployment name.

      The output will look similar to this:

      Name:     nginx-deployment-rlefbi4w
      Namespace:    default
      CreationTimestamp:  Tue, 30 Aug 2018 18:11:37 -0700
      Labels:     app=nginx
      Annotations:    deployment.kubernetes.io/revision=1
      Selector:   app=nginx
      Replicas:   2 desired | 2 updated | 2 total | 2 available | 0 unavailable
      StrategyType:   RollingUpdate
      MinReadySeconds:  0
      RollingUpdateStrategy:  1 max unavailable, 1 max surge
      Pod Template:
        Labels:       app=nginx
        Containers:
         nginx:
          Image:              nginx:1.7.9
          Port:               80/TCP
          Environment:        <none>
          Mounts:             <none>
        Volumes:              <none>
      Conditions:
        Type          Status  Reason
        ----          ------  ------
        Available     True    MinimumReplicasAvailable
        Progressing   True    NewReplicaSetAvailable
      OldReplicaSets:   <none>
      NewReplicaSet:    nginx-deployment-rlefbi4w-1771418926 (2/2 replicas created)
      No events.
      

      Let’s also list the pods created by this deployment:

      $ kubectl get pods -l app=nginx
      

      The output will look something like this:

      NAME                                        READY     STATUS    RESTARTS   AGE
      nginx-deployment-rlefbi4w-1771418926-7o5ns   1/1       Running   0          1m
      nginx-deployment-rlefbi4w-1771418926-r18az   1/1       Running   0          1m
      

    Updating the Deployment

    You can update your program by changing the source code and re-running pulumi up. Let’s do two quick updates to see what this looks like. After that, we’ll clean up and we’re done!

    1. Let’s update our version of Nginx from 1.7 to 1.8. Simply replace the line

                          image: "nginx:1.7.9",
      

      with

                          image: "nginx:1.8",
      

      and re-run pulumi up. We will see a preview that indicates just the spec changed:

       Previewing update of stack 'k8s-nginx-dev'
       Previewing changes:
      
           Type                           Name                     Plan          Info
       *   pulumi:pulumi:Stack            k8s-nginx-k8s-nginx-dev  no change
       ~   └─ kubernetes:apps:Deployment  nginx-deployment         update        changes: ~ spec
      
           ---outputs:---
           nginx: "nginx-deployment-rlefbi4w"
      
       info: 1 change previewed:
           ~ 1 resource to update
           1 resource unchanged
      
       Do you want to perform this update?
       yes
       no
       > details
      

      If we choose details and hit enter we will see a full diff of the changes:

       * pulumi:pulumi:Stack: (same)
           [urn=urn:pulumi:k8s-nginx-dev::k8s-nginx::pulumi:pulumi:Stack::k8s-nginx-k8s-nginx-dev]
           ---outputs:---
           nginx: "nginx-deployment-rlefbi4w"
           ~ kubernetes:apps/v1:Deployment: (update)
               [id=default/nginx-deployment-rlefbi4w]
               [urn=urn:pulumi:k8s-nginx-dev::k8s-nginx::kubernetes:apps/v1:Deployment::nginx-deployment]
           ~ spec      : {
               ~ template: {
                   ~ spec    : {
                       ~ containers: [
                           ~ [0]: {
                                   ~ image: "nginx:1.7.9" => "nginx:1.8"
                                   }
                           ]
                       }
                   }
               }
      

      If we select yes and hit enter to proceed with the update, the deployment will be updated in place.

    2. Next, let’s scale our application by increasing the replica count. You may have noticed this example used the Pulumi configuration system so that the replica count can be easily changed. Simply run

      $ pulumi config set replicas 4
      

      and re-run pulumi up. Pulumi will figure out the minimal set of changes to make:

      $ pulumi up
      

      The output from running this command will look like the usual update, with a preview diff, prompt, and details.

    Cleaning Up

    From here, feel free to experiment. As soon as you’re done, let’s clean up our stack:

    $ pulumi destroy --yes
    $ pulumi stack rm --yes
    

    Afterwards, query the list of pods to verify that none are remaining:

    $ kubectl get pods -l app=nginx
    

    This should print out something along these lines:

    No resources found.
    
    kubernetes logo
    Kubernetes v4.11.0 published on Thursday, Apr 18, 2024 by Pulumi