Pulumi is an open-source Infrastructure as code (IaC) SDK.
It's possible to use common programming languages to create, deploy and manage infrastructure in the most popular clouds.
This article shows how to use Pulumi to create a Kubernetes infrastructure.
We'll create, build and deploy the Kubernetes Guestbook example using typescript whit Pulumi.
Installation
We'll use NodeJs and npm, so install both:
You have to install Pulumi CLI:
https://www.pulumi.com/docs/get-started/install/
install Pulumi on Windows:
$ choco install pulumi
or on Linux:
$ curl -fsSL https://get.pulumi.com | sh
or on MacOS:
$ brew install pulumi
Create new project
Let's start creating a folder and Pulumi project:
$ mkdir quickstart && cd quickstart
$ pulumi new kubernetes-typescript
After, finish project setup on prompt:
- Set the project name
- Set the project description
- Create a stack (in this case, we will use
dev
)
Change index.ts
file with the below code:
import * as pulumi from "@pulumi/pulumi";
import * as k8sjs from "./config/k8sconfig";
let stack = pulumi.getStack();
let project = pulumi.getProject();
console.log(`Creating Pulumi Project ${project} on Stack ${stack}`)
const config = new pulumi.Config();
const isMinikube = stack == "dev";
const redisImageLeader = "docker.io/redis:6.0.5";
const redisImageReplica = "gcr.io/google_samples/gb-redis-follower:v2";
const frontEndImage = "gcr.io/google_samples/gb-frontend:v5";
const redisLeader = new k8sjs.ServiceDeployment("redis-leader", {
image: redisImageLeader,
ports: [6379],
});
const redisReplica = new k8sjs.ServiceDeployment("redis-replica", {
image: redisImageReplica,
ports: [6379],
});
const frontend = new k8sjs.ServiceDeployment("frontend", {
replicas: 3,
image: frontEndImage,
ports: [80],
allocateIpAddress: true,
isMinikube: isMinikube,
});
export let frontendIp = frontend.ipAddress;
And we will create a folder to components of the configuration of the project:
-
serviceDeployment.ts
import * as k8s from "@pulumi/kubernetes";
import * as k8stypes from "@pulumi/kubernetes/types/input";
import * as pulumi from "@pulumi/pulumi";
import * as serviceDeployArgs from "./serviceDeploymentArgs"
export class ServiceDeployment extends pulumi.ComponentResource {
public readonly deployment: k8s.apps.v1.Deployment;
public readonly service: k8s.core.v1.Service;
public readonly ipAddress?: pulumi.Output<string>;
constructor(name: string, args: serviceDeployArgs.ServiceDeploymentArgs,
opts?: pulumi.ComponentResourceOptions) {
super("k8sjs:service:ServiceDeployment", name, {}, opts);
const labels = { app: name };
const container: k8stypes.core.v1.Container = {
name,
image: args.image,
resources: args.resources || { requests: { cpu: "100m", memory: "100Mi" } },
env: [{ name: "GET_HOSTS_FROM", value: "dns" }],
ports: args.ports && args.ports.map(p => ({ containerPort: p })),
};
this.deployment = new k8s.apps.v1.Deployment(name, {
spec: {
selector: { matchLabels: labels },
replicas: args.replicas || 1,
template: {
metadata: { labels: labels },
spec: { containers: [ container ] },
},
},
}, { parent: this });
this.service = new k8s.core.v1.Service(name, {
metadata: {
name: name,
labels: this.deployment.metadata.labels,
},
spec: {
ports: args.ports && args.ports.map(p => ({ port: p, targetPort: p })),
selector: this.deployment.spec.template.metadata.labels,
type: args.allocateIpAddress ? (args.isMinikube ? "ClusterIP" : "LoadBalancer") : undefined,
},
}, { parent: this });
if (args.allocateIpAddress) {
this.ipAddress = args.isMinikube ?
this.service.spec.clusterIP :
this.service.status.loadBalancer.ingress[0].ip;
}
}
}
-
serviceDeploymentArgs.ts
import * as k8stypes from "@pulumi/kubernetes/types/input";
export interface ServiceDeploymentArgs {
image: string;
resources?: k8stypes.core.v1.ResourceRequirements;
replicas?: number;
ports?: number[];
allocateIpAddress?: boolean;
isMinikube?: boolean;
}
Basically, in index.ts
we define the images of deployments and call serviceDeployment.ts
to create container, deployment, and service in K8s.
Moreover, in the project, we have files with metadata of the project.
The Pulumi.yaml
and Pulumi.dev.yaml
define metadata of configuration of the project, like name, description, and custom properties:
Pulumi.yaml
name: k8s-guestbook
runtime: nodejs
description: Creating Kubernets Gustbook with Pulumi
Pulumi.dev.yaml
config:
k8s-guestbook:isMinikube: "true"
k8s-guestbook:useLoadBalancer: "false"
Deploy to Kubernets
The command to create and update a stack is:
$ pulumi up
Afther this performe updates using prompt, selecting yes:
Previewing update (dev)
View Live: https://app.pulumi.com/acme/k8s-guestbook/dev/previews/9fb88818e4-756d-48ed-baf8-f44774d9bbd0
Type Name Plan Info
+ pulumi:pulumi:Stack k8s-guestbook-dev create 1 message
+ ├─ k8sjs:service:ServiceDeployment redis-leader create
+ │ ├─ kubernetes:apps/v1:Deployment redis-leader create
+ │ └─ kubernetes:core/v1:Service redis-leader create
+ ├─ k8sjs:service:ServiceDeployment frontend create
+ │ ├─ kubernetes:apps/v1:Deployment frontend create
+ │ └─ kubernetes:core/v1:Service frontend create
+ └─ k8sjs:service:ServiceDeployment redis-replica create
+ ├─ kubernetes:apps/v1:Deployment redis-replica create
+ └─ kubernetes:core/v1:Service redis-replica create
Diagnostics:
pulumi:pulumi:Stack (k8s-guestbook-dev):
Creating Pulumi Project k8s-guestbook on Stack dev
Do you want to perform this update? yes
Updating (dev)
View Live: https://app.pulumi.com/acme/k8s-guestbook/dev/updates/23
Type Name Status Info
+ pulumi:pulumi:Stack k8s-guestbook-dev created 1 message
+ ├─ k8sjs:service:ServiceDeployment frontend created
+ │ ├─ kubernetes:apps/v1:Deployment frontend created
+ │ └─ kubernetes:core/v1:Service frontend created
+ ├─ k8sjs:service:ServiceDeployment redis-replica created
+ │ ├─ kubernetes:apps/v1:Deployment redis-replica created
+ │ └─ kubernetes:core/v1:Service redis-replica created
+ └─ k8sjs:service:ServiceDeployment redis-leader created
+ ├─ kubernetes:apps/v1:Deployment redis-leader created
+ └─ kubernetes:core/v1:Service redis-leader created
Diagnostics:
pulumi:pulumi:Stack (k8s-guestbook-dev):
Creating Pulumi Project k8s-guestbook on Stack dev
Outputs:
frontendIp: "10.108.227.133"
Resources:
+ 10 created
Duration: 44s
Checking the results
Inspect your cluster to validate the Guestbook App services:
$ kubectl get services
Minikube does not support type LoadBalancer
; if you are using dev stack, run:
$ kubectl port-forward svc/frontend 8080:80
and open in browser http://localhost:8080
Destroying the Project
To clean up and destroy resources, run commands to destroy and remove your stack/resources:
$ pulumi destroy --yes
$ pulumi stack rm --yes
Links
Pulumi example projects https://github.com/pulumi/examples
Conclusion
Pulumi is a good alternative to use the most common program languages (javascript, typescript, python, C# and Go) to create IaC projects in popular clouds (AWS, Azure, and Google Cloud).
The code of this post is available on Github
Top comments (1)
This is a fantastic post! I would love to feature it on the official Pulumi blog. If that is interesting to you, please email me at matty at pulumi dot com!