DEV Community

Cover image for Deploying Redis on GCP with Helm, Kubernetes, and Pulumi
Rodrigo Burgos
Rodrigo Burgos

Posted on

Deploying Redis on GCP with Helm, Kubernetes, and Pulumi

In this tutorial, you’ll learn how to deploy a highly available Redis instance on Google Kubernetes Engine (GKE) using Pulumi, Helm, and Kubernetes. We'll cover:

  1. Prerequisites

  2. Setting up GCP & GKE via Pulumi

  3. Deploying Redis with the Bitnami Helm chart

  4. Exposing Redis with a LoadBalancer

  5. Validating the Deployment

1. Prerequisites

Before you begin, make sure you have:

  • A GCP project with billing enabled.

  • gcloud CLI installed and authenticated.

  • Pulumi CLI installed.

  • A local Kubernetes context (you'll dynamically generate one via Pulumi).

  • Helm installed.

  • Go ≥1.18 (or your preferred Pulumi language runtime).

2. Setting up GCP & GKE via Pulumi

Create a new Pulumi project (e.g. pulumi new go), then install the GCP and Kubernetes providers:

go get github.com/pulumi/pulumi-gcp/sdk/v6/go/gcp/container
go get github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes
Enter fullscreen mode Exit fullscreen mode

Next, implement a provider package to:

  1. Provision a GKE cluster

  2. Generate a kubeconfig

  3. Instantiate a Kubernetes provider

// provider/provider.go
package provider

import (
  "fmt"
  "os/exec"
  "strings"

  "github.com/pulumi/pulumi-gcp/sdk/v6/go/gcp/container"
  k8s "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes"
  "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func SetupProvider(ctx *pulumi.Context) (*k8s.Provider, error) {
  // Provision a GKE cluster
  cluster, err := container.NewCluster(ctx, "my-gke-cluster", &container.ClusterArgs{
    Location:         pulumi.String("<YOUR_REGION>"),
    InitialNodeCount: pulumi.Int(1),
    NodeConfig: &container.ClusterNodeConfigArgs{
      MachineType: pulumi.String("e2-standard-2"),
      OauthScopes: pulumi.StringArray{
        pulumi.String("https://www.googleapis.com/auth/cloud-platform"),
      },
    },
  })
  if err != nil {
    return nil, fmt.Errorf("creating GKE cluster: %w", err)
  }

  // Build kubeconfig dynamically
  kubeconfig := pulumi.All(cluster.Name, cluster.Endpoint, cluster.MasterAuth).ApplyT(
    func(args []interface{}) (string, error) {
      name := args[0].(string)
      endpoint := args[1].(string)
      auth := args[2].(container.ClusterMasterAuth)

      // Retrieve a short-lived access token
      out, err := exec.Command("gcloud", "auth", "print-access-token").Output()
      if err != nil {
        return "", fmt.Errorf("gcloud auth: %w", err)
      }
      token := strings.TrimSpace(string(out))

      return fmt.Sprintf(`
apiVersion: v1
kind: Config
clusters:
- name: %s
  cluster:
    certificate-authority-data: %s
    server: https://%s
contexts:
- name: %s
  context:
    cluster: %s
    user: %s
current-context: %s
users:
- name: %s
  user:
    token: %s
`, name, *auth.ClusterCaCertificate, endpoint,
        name, name, name, name, name, token), nil
    }).(pulumi.StringOutput)

  // Create the Pulumi Kubernetes provider
  k8sProvider, err := k8s.NewProvider(ctx, "k8s", &k8s.ProviderArgs{
    Kubeconfig: kubeconfig,
  })
  if err != nil {
    return nil, fmt.Errorf("creating Kubernetes provider: %w", err)
  }

  return k8sProvider, nil
}

Enter fullscreen mode Exit fullscreen mode

Finally, call this from your main.go:

// main.go
package main

import (
  "iac_staging/provider"
  "iac_staging/redis"

  "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
  pulumi.Run(func(ctx *pulumi.Context) error {
    // Setup GKE + Kubernetes provider
    k8sProvider, err := provider.SetupProvider(ctx)
    if err != nil {
      return err
    }

    // Deploy Redis
    if err := redis.Install(ctx, k8sProvider); err != nil {
      return err
    }

    return nil
  })
}

Enter fullscreen mode Exit fullscreen mode

3. Deploying Redis with the Bitnami Helm Chart

Create a redis package that uses Pulumi’s Helm integration to install the Bitnami Redis chart:

// redis/redis.go
package redis

import (
  "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3"
  k8s "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes"
  "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func Install(ctx *pulumi.Context, provider *k8s.Provider) error {
  // Replace <YOUR_REDIS_PASSWORD> with a Pulumi secret or config
  redisPassword := pulumi.String("<YOUR_REDIS_PASSWORD>")

  _, err := helm.NewChart(ctx, "redis", helm.ChartArgs{
    Chart:     pulumi.String("redis"),
    Version:   pulumi.String("17.3.11"),
    Namespace: pulumi.String("default"),
    FetchArgs: helm.FetchArgs{
      Repo: pulumi.String("https://charts.bitnami.com/bitnami"),
    },
    Values: pulumi.Map{
      "auth": pulumi.Map{
        "enabled":  pulumi.Bool(true),
        "password": redisPassword,
      },
      "master": pulumi.Map{
        "service": pulumi.Map{
          // Type LoadBalancer will provision a GCP Network LB
          "type": pulumi.String("LoadBalancer"),
        },
        "resources": pulumi.Map{
          "requests": pulumi.Map{
            "cpu":    pulumi.String("100m"),
            "memory": pulumi.String("128Mi"),
          },
          "limits": pulumi.Map{
            "cpu":    pulumi.String("250m"),
            "memory": pulumi.String("256Mi"),
          },
        },
      },
      "replica": pulumi.Map{
        "replicaCount": pulumi.Int(1),
        "resources": pulumi.Map{
          "requests": pulumi.Map{
            "cpu":    pulumi.String("100m"),
            "memory": pulumi.String("128Mi"),
          },
          "limits": pulumi.Map{
            "cpu":    pulumi.String("250m"),
            "memory": pulumi.String("256Mi"),
          },
        },
      },
    },
  }, pulumi.Provider(provider))
  return err
}

Enter fullscreen mode Exit fullscreen mode

4. Exposing Redis with a LoadBalancer

By setting the service.type to LoadBalancer, GKE will automatically provision a GCP Network Load Balancer with an external IP. You can retrieve this IP with:

kubectl --context $(pulumi stack output kubeconfig) \
  get svc redis-master --namespace default -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
Enter fullscreen mode Exit fullscreen mode

Tip: You can also configure a static IP in GCP and annotate the chart to use it by adding:

master:
  service:
    annotations:
      networking.gke.io/load-balancer-type: "External"
    loadBalancerIP: "YOUR_RESERVED_STATIC_IP"

Enter fullscreen mode Exit fullscreen mode

5. Validating the Deployment

  1. Obtain the External IP:
EXTERNAL_IP=$(kubectl get svc redis-master \
  --namespace default \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "Redis external IP: $EXTERNAL_IP"
Enter fullscreen mode Exit fullscreen mode
  1. Test Connectivity with redis-cli:
redis-cli -h $EXTERNAL_IP -a $REDIS_PASSWORD PING
# Expected: PONG
Enter fullscreen mode Exit fullscreen mode

Conclusion
You’ve now:

  • Provisioned a GKE cluster on GCP with Pulumi.

  • Installed Redis using the Bitnami Helm chart via Pulumi.

  • Exposed Redis publicly (or privately, if you choose a different service.type).

  • Validated the deployment with redis-cli.

This approach lets you manage your infrastructure and application in a single Pulumi program, ensuring repeatable, version-controlled deployments.

Feel free to drop any questions or improvements in the comments!

Top comments (0)