DEV Community

Vinicius Souza
Vinicius Souza

Posted on • Edited on

Creating Kubernetes Guestbook App With Pulumi

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.

Image description

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
Enter fullscreen mode Exit fullscreen mode

or on Linux:

$ curl -fsSL https://get.pulumi.com | sh
Enter fullscreen mode Exit fullscreen mode

or on MacOS:

$ brew install pulumi
Enter fullscreen mode Exit fullscreen mode

Create new project

Let's start creating a folder and Pulumi project:

$ mkdir quickstart && cd quickstart
$ pulumi new kubernetes-typescript
Enter fullscreen mode Exit fullscreen mode

After, finish project setup on prompt:

  1. Set the project name
  2. Set the project description
  3. Create a stack (in this case, we will use dev)

Setup prompt in VSCode

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;

Enter fullscreen mode Exit fullscreen mode

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;
        }
    }
}

Enter fullscreen mode Exit fullscreen mode
  • 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;
}

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Pulumi.dev.yaml

config:
  k8s-guestbook:isMinikube: "true"
  k8s-guestbook:useLoadBalancer: "false"
Enter fullscreen mode Exit fullscreen mode

Deploy to Kubernets

The command to create and update a stack is:

$ pulumi up
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Checking the results

Inspect your cluster to validate the Guestbook App services:

$ kubectl get services
Enter fullscreen mode Exit fullscreen mode

Minikube does not support type LoadBalancer; if you are using dev stack, run:

$ kubectl port-forward svc/frontend 8080:80
Enter fullscreen mode Exit fullscreen mode

and open in browser http://localhost:8080

Result Guestbook in browser

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
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
mattstratton profile image
Matty Stratton

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!