Deploy a Stateless Application on Kubernetes
15 minIn this tutorial, you'll run a stateless application on Kubernetes using a Deployment object, authored with Pulumi and TypeScript instead of YAML and the kubectl CLI. This gives you the full power of a familiar programming language combined with immutable infrastructure for a robust, repeatable update experience.
You'll deploy an Nginx container, inspect the resulting Kubernetes resources with kubectl, update the container image to see Pulumi's structured diff, and scale the application by changing a single configuration value.
- How to define a Kubernetes Deployment with Pulumi and TypeScript
- How Pulumi waits for a deployment to converge, unlike kubectl
- How to update a container image and review a structured diff before applying it
- How to scale an application by changing a single configuration value
- The Pulumi CLI
- A Pulumi Cloud account and an access token
- A working Kubernetes cluster (Minikube is an easy way to get started)
- The Kubernetes command line tool kubectl installed
In this tutorial, you’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 you 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.
By the end, you’ll have:
- Created a Deployment using Pulumi that runs Nginx
- Used
pulumito deploy this application - Used
kubectlto list information about the resulting Kubernetes resources - Updated the deployment’s number of replicas using
pulumi - Cleaned up
Create and configure a project#
Normally you would write YAML files to configure your services, and then run kubectl commands to create and manage them. Instead of doing that, you’ll author your program in code and deploy it with pulumi.
To start, create a new Pulumi project from a template:
mkdir k8s-nginx && cd k8s-nginxpulumi new kubernetes-typescriptThis command initializes a fresh project in the newly created k8s-nginx directory.
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 you, giving you the full power of Kubernetes right away.
Deploy the application#
Now you’re ready to deploy your code. To do so, run pulumi up:
pulumi upThe command first shows a complete preview of what will take place, with a confirmation prompt. No changes are 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 detailsSelect “yes” and hit enter. The deployment proceeds, and the output looks 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 createdUpdate duration: 8.127334048s
Permalink: https://app.pulumi.com/joeduffy/k8s-nginx-dev/updates/1Inspect the Kubernetes resources#
Now that you’ve done the deployment, check some state with kubectl:
kubectl describe deployment $(pulumi stack output nginx)Notice that you used the pulumi stack output command to fetch the auto-generated deployment name.
The output will look similar to this:
Name: nginx-deployment-rlefbi4wNamespace: defaultCreationTimestamp: Tue, 30 Aug 2018 18:11:37 -0700Labels: app=nginxAnnotations: deployment.kubernetes.io/revision=1Selector: app=nginxReplicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailableStrategyType: RollingUpdateMinReadySeconds: 0RollingUpdateStrategy: 1 max unavailable, 1 max surgePod 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 NewReplicaSetAvailableOldReplicaSets: <none>NewReplicaSet: nginx-deployment-rlefbi4w-1771418926 (2/2 replicas created)No events.Notice the StrategyType is RollingUpdate with a RollingUpdateStrategy of 1 max unavailable, 1 max surge. This is how Kubernetes rolls out changes to the deployment without downtime, replacing pods gradually.
Now list the pods created by this deployment:
kubectl get pods -l app=nginxThe output will look something like this:
NAME READY STATUS RESTARTS AGEnginx-deployment-rlefbi4w-1771418926-7o5ns 1/1 Running 0 1mnginx-deployment-rlefbi4w-1771418926-r18az 1/1 Running 0 1mUpdate the deployment#
You can update your program by changing the source code and re-running pulumi up. Do two quick updates to see what this looks like. After that, you’ll clean up and you’re done!
First, update your 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. You’ll 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> detailsIf you choose details and hit enter, you’ll 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 you select yes and hit enter to proceed with the update, the deployment is updated in place.
Scale the application#
Next, scale your application by increasing the replica count. Notice this example used the Pulumi configuration system so that the replica count can be easily changed. Simply run:
pulumi config set replicas 4and re-run pulumi up. Pulumi figures out the minimal set of changes to make:
pulumi upThe output from running this command will look like the usual update, with a preview diff, prompt, and details.
Clean up#
From here, feel free to experiment. As soon as you’re done, clean up your stack:
pulumi destroy --yespulumi stack rm --yesAfterwards, query the list of pods to verify that none are remaining:
kubectl get pods -l app=nginxThis should print out something along these lines:
No resources found.</content> </invoke>