Skip to content

Helm charts topology

In the Getting Started guide, we designed a SaaS service that deploys Redis Clusters using a Helm chart. However, in that example, we only deployed a single component as part of the overall SaaS deployment.

Omnistrate supports deploying multiple Helm charts as part of a single deployment. Your SaaS service may require multiple components to be deployed, and each component may have its own Helm chart. For eg: a Redis Cluster Helm chart and a Postgres Helm chart.

In this guide, we will show you how to deploy multiple Helm charts as part of a single deployment. We will use the example of deploying a Redis Cluster and a Postgres database as part of a single deployment.

Add the Helm Chart Configuration

First, we need to update the Service Plan specification to include the Helm chart configurations for the Postgres Database. We will use the Bitnami chart to do so. We will also hide this component from your customers from a provisioning point of view and make it an internal component. We will also add some customization parameters that will be exposed to your customers.

  - name: Postgres Database
    internal: false
    compute:
      instanceTypes:
        - apiParam: postgresInstanceType
          cloudProvider: aws
        - apiParam: postgresInstanceType
          cloudProvider: gcp
    network:
      ports:
        - 5432
    helmChartConfiguration:
      chartName: postgresql
      chartVersion: 15.5.36
      chartRepoName: bitnami
      chartRepoURL: https://charts.bitnami.com/bitnami
      chartValues:
        auth:
          database: $var.postgresDatabase
          username: $var.postgresUsername
          password: $var.postgresPassword
        primary:
          persistence:
            enabled: false
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 1000m
              memory: 1024Mi
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                  - matchExpressions:
                    - key: omnistrate.com/managed-by
                      operator: In
                      values:
                      - omnistrate
                    - key: topology.kubernetes.io/region
                      operator: In
                      values:
                      - $sys.deploymentCell.region
                    - key: node.kubernetes.io/instance-type
                      operator: In
                      values:
                      - $sys.compute.node.instanceType
                    - key: omnistrate.com/resource
                      operator: In
                      values:
                      - $sys.deployment.resourceID
            podAntiAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                  - key: omnistrate.com/schedule-mode
                    operator: In
                    values:
                    - exclusive
                namespaceSelector: {}
                topologyKey: kubernetes.io/hostname
        readReplicas:
          replicaCount: 0
    apiParameters:
      - key: postgresUsername
        description: Postgres Username
        name: Postgres Username
        type: String
        modifiable: true
        required: true
        export: true
      - key: postgresPassword
        description: Postgres Password
        name: Postgres Password
        type: String
        modifiable: true
        required: true
        export: true
      - key: postgresDatabase
        description: Postgres Database
        name: Postgres Database
        type: String
        modifiable: true
        required: true
        export: true
      - key: postgresInstanceType
        description: Postgres Instance Type
        name: Postgres Instance Type
        type: String
        modifiable: true
        required: true
        export: true

We will then set the Postgres component as a dependency on the Redis component. This will ensure that the Postgres component is deployed before the Redis component.

services:
  - name: Redis Cluster
    dependsOn:
    - Postgres Database
    apiParameters:
      ...
      - key: postgresUsername
        description: Postgres Username
        name: Postgres Username
        type: String
        modifiable: false
        required: false
        export: true
        defaultValue: "postgres"
        parameterDependencyMap:
          Postgres Database: postgresUsername
      - key: postgresPassword
        description: Postgres Password
        name: Postgres Password
        type: Password
        modifiable: false
        required: true
        export: true
        parameterDependencyMap:
          Postgres Database: postgresPassword
      - key: postgresDatabase
        description: Postgres Database
        name: Postgres Database
        type: String
        modifiable: false
        required: false
        export: true
        defaultValue: "postgres"
        parameterDependencyMap:
          Postgres Database: postgresDatabase
      - key: postgresInstanceType
        description: Postgres Instance Type
        name: Postgres Instance Type
        type: String
        modifiable: true
        required: false
        export: true
        defaultValue: "t4g.small"
        parameterDependencyMap:
          Postgres Database: postgresInstanceType
You will also notice we copied over the new API parameters from the Postgres component to the Redis component. The platform requires any dependent component to provide the necessary parameters to the child component as a pass-through. This is done by setting the parameterDependencyMap field in the API parameters and mapping them to the dependent component and the relevant API parameter defined on the dependent component.

Final specification

name: Redis Server # Service Plan Name
deployment:
  hostedDeployment:
    AwsAccountId: "<AWS_ID>"
    AwsBootstrapRoleAccountArn: arn:aws:iam::<AWS_ID>:role/omnistrate-bootstrap-role

services:
  - name: Redis Cluster
    dependsOn:
    - Postgres Database
    compute:
      instanceTypes:
        - apiParam: instanceType
          cloudProvider: aws
        - apiParam: instanceType
          cloudProvider: gcp
    network:
      ports:
        - 6379
    helmChartConfiguration:
      chartName: redis
      chartVersion: 19.6.2
      chartRepoName: bitnami
      chartRepoURL: https://charts.bitnami.com/bitnami
      chartValues:
        master:
          podLabels:
            omnistrate.com/schedule-mode: exclusive
          persistence:
            enabled: false
          service:
            type: LoadBalancer
            annotations:
              external-dns.alpha.kubernetes.io/hostname: $sys.network.externalClusterEndpoint

          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 150m
              memory: 256Mi
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                  - matchExpressions:
                    - key: omnistrate.com/managed-by
                      operator: In
                      values:
                      - omnistrate
                    - key: topology.kubernetes.io/region
                      operator: In
                      values:
                      - $sys.deploymentCell.region
                    - key: node.kubernetes.io/instance-type
                      operator: In
                      values:
                      - $sys.compute.node.instanceType
                    - key: omnistrate.com/resource
                      operator: In
                      values:
                      - $sys.deployment.resourceID
            podAntiAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                  - key: omnistrate.com/schedule-mode
                    operator: In
                    values:
                    - exclusive
                namespaceSelector: {}
                topologyKey: kubernetes.io/hostname
        replica:
          podLabels:
            omnistrate.com/schedule-mode: exclusive
          persistence:
            enabled: false
          replicaCount: $var.replicas
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 150m
              memory: 256Mi
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                  - matchExpressions:
                    - key: omnistrate.com/managed-by
                      operator: In
                      values:
                      - omnistrate
                    - key: topology.kubernetes.io/region
                      operator: In
                      values:
                      - $sys.deploymentCell.region
                    - key: node.kubernetes.io/instance-type
                      operator: In
                      values:
                      - $sys.compute.node.instanceType
                    - key: omnistrate.com/resource
                      operator: In
                      values:
                      - $sys.deployment.resourceID
            podAntiAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                  - key: omnistrate.com/schedule-mode
                    operator: In
                    values:
                    - exclusive
                namespaceSelector: {}
                topologyKey: kubernetes.io/hostname
    apiParameters:
      - key: replicas
        description: Number of Replicas
        name: Replica Count
        type: Float64
        modifiable: true
        required: false
        export: true
        defaultValue: "1"
      - key: instanceType
        description: Instance Type
        name: Instance Type
        type: String
        modifiable: true
        required: false
        export: true
        defaultValue: "t4g.small"
      - key: postgresUsername
        description: Postgres Username
        name: Postgres Username
        type: String
        modifiable: false
        required: false
        export: true
        defaultValue: "postgres"
        parameterDependencyMap:
          Postgres Database: postgresUsername
      - key: postgresPassword
        description: Postgres Password
        name: Postgres Password
        type: Password
        modifiable: false
        required: true
        export: true
        parameterDependencyMap:
          Postgres Database: postgresPassword
      - key: postgresDatabase
        description: Postgres Database
        name: Postgres Database
        type: String
        modifiable: false
        required: false
        export: true
        defaultValue: "postgres"
        parameterDependencyMap:
          Postgres Database: postgresDatabase
      - key: postgresInstanceType
        description: Postgres Instance Type
        name: Postgres Instance Type
        type: String
        modifiable: true
        required: false
        export: true
        defaultValue: "t4g.small"
        parameterDependencyMap:
          Postgres Database: postgresInstanceType
  - name: Postgres Database
    internal: false
    compute:
      instanceTypes:
        - apiParam: postgresInstanceType
          cloudProvider: aws
        - apiParam: postgresInstanceType
          cloudProvider: gcp
    network:
      ports:
        - 5432
    helmChartConfiguration:
      chartName: postgresql
      chartVersion: 15.5.36
      chartRepoName: bitnami
      chartRepoURL: https://charts.bitnami.com/bitnami
      chartValues:
        auth:
          database: $var.postgresDatabase
          username: $var.postgresUsername
          password: $var.postgresPassword
        primary:
          persistence:
            enabled: false
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 1000m
              memory: 1024Mi
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                  - matchExpressions:
                    - key: omnistrate.com/managed-by
                      operator: In
                      values:
                      - omnistrate
                    - key: topology.kubernetes.io/region
                      operator: In
                      values:
                      - $sys.deploymentCell.region
                    - key: node.kubernetes.io/instance-type
                      operator: In
                      values:
                      - $sys.compute.node.instanceType
                    - key: omnistrate.com/resource
                      operator: In
                      values:
                      - $sys.deployment.resourceID
            podAntiAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                  - key: omnistrate.com/schedule-mode
                    operator: In
                    values:
                    - exclusive
                namespaceSelector: {}
                topologyKey: kubernetes.io/hostname
        readReplicas:
          replicaCount: 0
    apiParameters:
      - key: postgresUsername
        description: Postgres Username
        name: Postgres Username
        type: String
        modifiable: true
        required: true
        export: true
      - key: postgresPassword
        description: Postgres Password
        name: Postgres Password
        type: String
        modifiable: true
        required: true
        export: true
      - key: postgresDatabase
        description: Postgres Database
        name: Postgres Database
        type: String
        modifiable: true
        required: true
        export: true
      - key: postgresInstanceType
        description: Postgres Instance Type
        name: Postgres Instance Type
        type: String
        modifiable: true
        required: true
        export: true

Apply changes to the service

For this we will run the same command that was used to setup the service the first time.

omnistrate-ctl build -f spec.yaml --name 'RedisHelm' --release-as-preferred --spec-type ServicePlanSpec

# Example output shown below
 Successfully built service
Check the service plan result at: https://omnistrate.cloud/product-tier?serviceId=s-dEhutaDa2X&environmentId=se-92smpU2YAm
Access your SaaS offer at: https://saasportal.instance-w6vidhd14.hc-pelsk80ph.us-east-2.aws.f2e0a955bb84.cloud/service-plans?serviceId=s-dEhutaDa2X&environmentId=se-92smpU2YAm

Deploying a Redis Cluster through your dedicated Customer Portal

Now, your customers can deploy Redis Clusters alongwith the Postgres Helm chart with the desired instance type and number of replicas through the dedicated customer portal. The portal will use the REST API parameters to customize the deployment based on the customer's requirements.

Redis+Postgres

The deployed cluster details

Cluster Details

The Kubernetes Dashboard view

K8s Dashboard