SaaS Multi-Tenant example
This example demonstrates how to create a multi-tenant SaaS application using Kro ResourceGraphDefinitions. It creates isolated tenant environments with dedicated applications, following a hierarchical structure of ResourceGraphDefinitions.
Overview
This example creates a multi-tenant architecture with:
- TenantEnvironment: Creates isolated namespaces with resource quotas and NetworkPolicy
- TenantApplication: Deploys applications within tenant environments
- Tenant: Orchestrates tenant environments and applications together
The example uses a hierarchical approach where each ResourceGraphDefinition builds upon standard Kubernetes resources.
Architecture
Getting Started
Apply the ResourceGraphDefinitions and instance in the following order:
Create ResourceGraphDefinition
Apply the tenant environment RGD:
kubectl apply -f tenant-environment-rgd.yaml
Apply the tenant application RGD:
kubectl apply -f tenant-application-rgd.yaml
Apply the main tenant RGD:
kubectl apply -f tenant-rgd.yaml
Check all RGDs are in Active state:
kubectl get rgd
Expected result:
NAME APIVERSION KIND STATE AGE
tenantenvironment.kro.run v1alpha1 TenantEnvironment Active 2m
tenantapplication.kro.run v1alpha1 TenantApplication Active 1m
tenant.kro.run v1alpha1 Tenant Active 30s
Create Tenant Instance
Apply the tenant instance:
kubectl apply -f tenant-instance.yaml
Check tenant instance status:
kubectl get tenant
Expected result:
NAME STATE SYNCED AGE
tenant001 ACTIVE True 5m
Clean Up
Remove resources in reverse order:
Remove Tenant Instance:
kubectl delete -f tenant-instance-tmpl.yaml
Remove ResourceGraphDefinitions:
kubectl delete -f tenant-rgd.yaml
kubectl delete -f tenant-application-rgd.yaml
kubectl delete -f tenant-environment-rgd.yaml
ResourceGraphDefinition
apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
name: tenantenvironment.kro.run
spec:
schema:
apiVersion: v1alpha1
kind: TenantEnvironment
spec:
tenantId: string
resources:
- id: tenantNamespace
template:
apiVersion: v1
kind: Namespace
metadata:
name: ${schema.spec.tenantId}
labels:
name: ${schema.spec.tenantId}
- id: tenantQuota
template:
apiVersion: v1
kind: ResourceQuota
metadata:
name: ${schema.spec.tenantId}-quota
namespace: ${schema.spec.tenantId}
labels:
saas/tenant-id: ${schema.spec.tenantId}
spec:
hard:
requests.cpu: "1"
requests.memory: "1Gi"
limits.cpu: "2"
limits.memory: "2Gi"
- id: tenantNetworkpolicy
template:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: ${schema.spec.tenantId}-isolation
namespace: ${schema.spec.tenantId}
labels:
saas/tenant-id: ${schema.spec.tenantId}
spec:
podSelector:
matchLabels:
ingress:
- from:
- podSelector: {}