Building your SaaS Product Using Kubernetes Operators¶
Omnistrate supports deploying Kubernetes Operators as part of your SaaS Product topology. This enables you to automate infrastructure management and application lifecycle orchestration within Kubernetes clusters. By leveraging Operators, you can turn complex, stateful applications into managed, multi-tenant SaaS Products with minimal effort.
Kubernetes Operators extend the platform’s capabilities by automating complex deployment and operational tasks that would traditionally require manual intervention. This guide will walk you through building a SaaS Product using an existing Kubernetes Operator.
How It Works¶
When you build a SaaS Product from a Kubernetes Operator, Omnistrate automates the entire lifecycle:
- Infrastructure Provisioning: Omnistrate deploys a dedicated or shared Kubernetes cluster in the cloud and region of your choice.
- Operator Installation: The Operator itself is installed into the cluster, typically via a Helm chart dependency that you specify.
- Custom Resource (CR) Instantiation: For each tenant who subscribes to your SaaS Product, Omnistrate creates an instance of your Operator's Custom Resource (CR). The CR is configured using parameters provided by the customer and system-generated values.
- Lifecycle Management: The Operator takes over, provisioning and managing the application components as defined in the CR.
- Readiness and Endpoints: Omnistrate monitors the status of the CR to determine if the SaaS Product instance is ready and exposes the necessary endpoints to the customer.
Prerequisites¶
Before you begin, you should have:
- An existing Kubernetes Operator.
- The Operator packaged as a Helm chart for installation.
- The Custom Resource Definition (CRD) that your Operator manages.
Example: Building a PostgreSQL SaaS With the CNPG Operator¶
In this guide, we will build a managed PostgreSQL SaaS Product using the CloudNativePG (CNPG) Operator. The example is based on the community-contributed PostgreSQL PaaS repository.
We will define our SaaS Product in a spec.yaml
file. This file tells Omnistrate how to install the operator, what kind of database to create for customers, and how to expose it.
Here is the complete spec.yaml
for our PostgreSQL SaaS Product. We will break down each section below.
# yaml-language-server: $schema=https://api.omnistrate.cloud/2022-09-01-00/schema/service-spec-schema.json
name: PostgreSQL Server # Plan Name
deployment:
hostedDeployment:
awsAccountId: "<YOUR_AWS_ACCOUNT_ID>"
awsBootstrapRoleAccountArn: "arn:aws:iam::<YOUR_AWS_ACCOUNT_ID>:role/omnistrate-bootstrap-role"
tenancyType: CUSTOM_TENANCY
features:
INTERNAL:
logs: {} # Omnistrate native
CUSTOMER:
logs: {} # Omnistrate native
services:
- name: CNPG
compute:
instanceTypes:
- apiParam: instanceType
cloudProvider: aws
apiParameters:
- key: instanceType
description: Instance Type
name: Instance Type
type: String
modifiable: true
required: false
export: true
defaultValue: "t3.medium"
- key: postgresqlPassword
description: Default DB Password
name: Password
type: Password
modifiable: false
required: true
export: true
- key: postgresqlUsername
description: Username
name: Default DB Username
type: String
modifiable: false
required: false
export: true
defaultValue: "app"
- key: postgresqlDatabase
description: Default Database Name
name: Default Database Name
type: String
modifiable: false
required: false
export: true
defaultValue: "app"
- key: numberOfInstances
description: Total Number of Instances
name: Total Number of Instances
type: Float64
modifiable: true
required: false
export: true
defaultValue: "1"
limits:
min: 1
- key: storageSize
description: Storage size for PostgreSQL data
name: Storage Size
type: String
modifiable: true
required: false
export: true
defaultValue: "20Gi"
endpointConfiguration:
writer:
host: "$sys.network.externalClusterEndpoint"
ports:
- 5432
primary: true
networkingType: PUBLIC
reader:
host: "reader-{{ $sys.network.externalClusterEndpoint }}"
ports:
- 5432
primary: false
networkingType: PUBLIC
operatorCRDConfiguration:
template: |
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: {{ $sys.id }}
spec:
enablePDB: true
bootstrap:
initdb:
owner: {{ $var.postgresqlUsername }}
database: {{ $var.postgresqlDatabase }}
secret:
name: basic-auth
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 }}
instances: {{ $var.numberOfInstances }}
storage:
resizeInUseVolumes: true
size: {{ $var.storageSize }}
storageClass: gp3
managed:
services:
additional:
- selectorType: ro
serviceTemplate:
metadata:
name: "{{ $sys.id }}-cluster-ro"
annotations:
external-dns.alpha.kubernetes.io/hostname: reader-{{ $sys.network.externalClusterEndpoint }}
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-subnets: "{{ $sys.deploymentCell.publicSubnetIDs[*].id }}"
spec:
type: LoadBalancer
updateStrategy: patch
- selectorType: rw
serviceTemplate:
metadata:
name: "{{ $sys.id }}-cluster-rw"
annotations:
external-dns.alpha.kubernetes.io/hostname: {{ $sys.network.externalClusterEndpoint }}
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-subnets: "{{ $sys.deploymentCell.publicSubnetIDs[*].id }}"
spec:
type: LoadBalancer
updateStrategy: patch
supplementalFiles:
- |
# Basic auth using parameters
apiVersion: v1
kind: Secret
metadata:
name: basic-auth
namespace: {{ $sys.id }}
type: kubernetes.io/basic-auth
data:
username: {{ $func.base64encode($var.postgresqlUsername) }}
password: {{ $func.base64encode($var.postgresqlPassword) }}
readinessConditions:
"$var._crd.status.phase": "Cluster in healthy state"
'$var._crd.status.conditions[?(@.type=="Ready")].status': "True"
outputParameters:
"Postgres Container Image": "$var._crd.status.image"
"Status": "$var._crd.status.phase"
"Topology": "$var._crd.status.topology"
helmChartDependencies:
- chartName: cloudnative-pg
chartVersion: 0.26.0
chartRepoName: cnpg
chartRepoURL: https://cloudnative-pg.github.io/charts
Info
For more detailed information on the pricing, metering, billingProviders configuration, please see End-to-End Billing and Usage Metering.
Anatomy of the Plan Specification¶
Let's break down the key sections of the spec.yaml
.
apiParameters
¶
This section defines the inputs your customers will provide when creating a new PostgreSQL instance. These parameters are then available in the CRD template using the $var
prefix (e.g., {{ $var.postgresqlPassword }}
).
apiParameters:
- key: postgresqlPassword
description: Default DB Password
name: Password
type: Password
required: true
- key: numberOfInstances
description: Total Number of Instances
name: Total Number of Instances
type: Float64
defaultValue: "1"
- key: storageSize
description: Storage size for PostgreSQL data
name: Storage Size
type: String
defaultValue: "20Gi"
helmChartDependencies
¶
This is where you specify the Operator's Helm chart. Omnistrate will install this chart into the Kubernetes cluster before creating any instances of your SaaS Product.
helmChartDependencies:
- chartName: cloudnative-pg
chartVersion: 0.26.0
chartRepoName: cnpg
chartRepoURL: https://cloudnative-pg.github.io/charts
operatorCRDConfiguration
¶
This is the core of the integration. It tells Omnistrate how to interact with your Operator.
-
template
: This is a Go template for the Custom Resource (CR) that the Operator will manage. Here, we define aCluster
resource for the CNPG operator. Notice the use of{{ $var.variableName }}
for customer inputs and{{ $sys.variableName }}
for system-provided values like the instance ID or network details. -
supplementalFiles
: This allows you to create additional Kubernetes resources alongside the main CR. In this example, we create aSecret
to hold the database credentials provided by the user. This secret is then referenced in thebootstrap
section of theCluster
CR. -
readinessConditions
: This tells Omnistrate how to determine if the service instance is ready. It checks thestatus
field of the CR. For CNPG, we wait for thephase
to beCluster in healthy state
. -
outputParameters
: This exposes fields from the CR'sstatus
back to the customer. This is useful for displaying information like the running PostgreSQL version or the current cluster status in the customer portal.
endpointConfiguration
¶
This section defines the connection details that will be shown to your customers. The host
field uses system variables to construct the public DNS endpoint for the writer and reader services created by the CNPG operator.
endpointConfiguration:
writer:
host: "$sys.network.externalClusterEndpoint"
ports:
- 5432
primary: true
networkingType: PUBLIC
reader:
host: "reader-{{ $sys.network.externalClusterEndpoint }}"
ports:
- 5432
primary: false
networkingType: PUBLIC
Registering the SaaS Product¶
Once you have your spec.yaml
file, you can build and register your SaaS Product using the Omnistrate CLI:
This command will:
- Validate your Plan specification.
- Create the SaaS Product and a "PostgreSQL Server" Plan.
- Set up a development environment for you to test.
- Provide you with a URL to a dedicated Customer Portal for your new SaaS Product.
Deploying A PostgreSQL Instance¶
After registering the SaaS Product, you can use the auto-generated Customer Portal to deploy instances of your PostgreSQL SaaS Product. Your customers will be able to:
- Sign in to the portal.
- Choose the "PostgreSQL Server" plan.
- Select a cloud provider and region.
- Configure the parameters you defined in
apiParameters
(like password, storage size). - Click "Create" to deploy their own isolated PostgreSQL cluster.
Omnistrate and the CNPG Operator handle the rest, and the customer will see the connection endpoints once the cluster is ready.
For more details on system parameters and advanced configurations, refer to the Plan Spec guide.