Manage your database schema with Atlas using Crossplane
This guide walks you through managing your database schema with Atlas on Kubernetes using Crossplane, a powerful control plane framework for managing infrastructure as code.
Why Crossplane?
Using Crossplane to manage the Atlas Operator provides several advantages:
- Infrastructure as Code: All resources are defined declaratively in YAML
- Version Control: Track changes to your operator configuration in Git
- Composition: Combine multiple resources into reusable templates
- Multi-Cluster: Use Crossplane's composition features to deploy across multiple clusters
- Unified API: Manage databases, operators, and applications using the same Kubernetes API
For more information about Crossplane, visit the official documentation.
Prerequisites
Before you begin, ensure you have:
- A Kubernetes cluster (this guide uses minikube)
kubectlinstalled and configured- Crossplane installed on your cluster
Step 1: Install Crossplane Providers
If you already have the Crossplane Helm and Kubernetes providers installed, you can skip this step and proceed directly to Step 2.
Crossplane uses providers to interact with different APIs. For Atlas Operator, you'll need:
- Helm Provider: To install the operator as a Helm chart
- Kubernetes Provider: To create AtlasSchema and AtlasMigration resources
Install the Helm Provider
Create a file named provider-helm.yaml:
apiVersion: pkg.crossplane.io/v1beta1
kind: DeploymentRuntimeConfig
metadata:
name: helm-runtime-config
spec:
serviceAccountTemplate:
metadata:
name: provider-helm-sa
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-helm
spec:
package: xpkg.upbound.io/crossplane-contrib/provider-helm:v0.19.0
runtimeConfigRef:
name: helm-runtime-config
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: provider-helm-sa
namespace: crossplane-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: provider-helm-admin-binding
subjects:
- kind: ServiceAccount
name: provider-helm-sa
namespace: crossplane-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
Apply it:
kubectl apply -f provider-helm.yaml
Configure the Helm provider with provider-helm-config.yaml:
apiVersion: helm.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
name: default
spec:
credentials:
source: InjectedIdentity
Apply it:
kubectl apply -f provider-helm-config.yaml
Install the Kubernetes Provider
Create a file named provider-k8s.yaml:
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-kubernetes
spec:
package: xpkg.crossplane.io/crossplane-contrib/provider-kubernetes:v1.0.0
Apply it:
kubectl apply -f provider-k8s.yaml
Configure the Kubernetes provider with provider-k8s-config.yaml:
apiVersion: kubernetes.m.crossplane.io/v1alpha1
kind: ProviderConfig
metadata:
name: provider-kubernetes
spec:
credentials:
source: InjectedIdentity
Apply it:
kubectl apply -f provider-k8s-config.yaml
Verify Provider Installation
Wait for both providers to become healthy:
kubectl get providers.pkg.crossplane.io
You should see output similar to:
NAME INSTALLED HEALTHY PACKAGE
provider-helm True True xpkg.upbound.io/crossplane-contrib/provider-helm:v0.19.0
provider-kubernetes True True xpkg.crossplane.io/crossplane-contrib/provider-kubernetes:v1.0.0
Step 2: Install Atlas Operator via Crossplane
Now that the providers are ready, create a Helm Release resource to install the Atlas Operator.
Create a file named atlas-operator-release.yaml:
apiVersion: helm.crossplane.io/v1beta1
kind: Release
metadata:
name: crossplane-atlas-operator
spec:
forProvider:
chart:
name: atlas-operator
repository: oci://ghcr.io/ariga/charts
namespace: atlas-operator-system
providerConfigRef:
name: default
Apply it:
kubectl apply -f atlas-operator-release.yaml
Verify the Installation
Check the Helm release status:
kubectl get release.helm.crossplane.io crossplane-atlas-operator
You should see:
NAME CHART VERSION SYNCED READY STATE REVISION
crossplane-atlas-operator atlas-operator 0.7.16 True True deployed 1
Verify the operator pod is running:
kubectl get pods -n atlas-operator-system
Expected output:
NAME READY STATUS RESTARTS AGE
crossplane-atlas-operator-64d5d6887b-xxxxx 1/1 Running 0 30s
Check that the Atlas CRDs are installed:
kubectl get crd | grep atlas
You should see:
atlasmigrations.db.atlasgo.io
atlasschemas.db.atlasgo.io
Configure RBAC for Kubernetes Provider
Before creating AtlasSchema resources, you need to grant the Kubernetes provider permission to manage them.
First, get the service account name used by the Kubernetes provider:
kubectl get sa -n crossplane-system | grep provider-kubernetes
Create a file named k8s-provider-rbac.yaml, replacing <SA_NAME> with the actual service account name from the previous command:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: atlas-schema-manager
rules:
- apiGroups: ['db.atlasgo.io']
resources: ['atlasschemas', 'atlasmigrations']
verbs: ['*']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: provider-kubernetes-atlas-binding
subjects:
- kind: ServiceAccount
name: <SA_NAME>
namespace: crossplane-system
roleRef:
kind: ClusterRole
name: atlas-schema-manager
apiGroup: rbac.authorization.k8s.io
Apply it:
kubectl apply -f k8s-provider-rbac.yaml
Step 3: Create an AtlasSchema Resource
With the operator running, you can now create AtlasSchema resources using Crossplane's Kubernetes provider.
An AtlasSchema resource represents the desired state of your database schema. When you create one, you define the schema you want (using SQL or HCL), and the Atlas Operator ensures your database matches that definition. The operator automatically calculates the diff between your desired schema and the current database state, then applies the necessary migrations.
Since we're using Crossplane, we wrap the AtlasSchema in a Crossplane Object resource. This allows Crossplane to manage the lifecycle of the AtlasSchema and provides additional benefits like drift detection and reconciliation.
First, create database credentials. For this example, we'll use PostgreSQL:
kubectl create secret generic postgres-credentials \
--from-literal=url="postgres://postgres:pass@postgres.default:5432/postgres?sslmode=disable&search_path=public"
Replace the connection string with your actual database credentials.
Create a file named atlas-schema.yaml:
apiVersion: kubernetes.m.crossplane.io/v1alpha1
kind: Object
metadata:
name: atlas-schema-pg
namespace: default
spec:
forProvider:
manifest:
apiVersion: db.atlasgo.io/v1alpha1
kind: AtlasSchema
metadata:
name: atlas-schema-pg
namespace: default
spec:
urlFrom:
secretKeyRef:
key: url
name: postgres-credentials
schema:
sql: |
create table t1 (
id int
);
providerConfigRef:
kind: ProviderConfig
name: provider-kubernetes
Apply it:
kubectl apply -f atlas-schema.yaml
Once applied, the Atlas Operator detects the new AtlasSchema resource and begins reconciling. It connects to your database using the provided credentials, inspects the current schema, and applies the necessary changes to reach the desired state defined in the schema.sql field. In this case, it will create the t1 table with an id column.
Verify the Schema Application
Check the Crossplane Object status:
kubectl get object.kubernetes.m.crossplane.io atlas-schema-pg
Expected output:
NAME KIND PROVIDERCONFIG SYNCED READY AGE
atlas-schema-pg AtlasSchema provider-kubernetes True True 30s
Check the AtlasSchema resource directly:
kubectl get atlasschema atlas-schema-pg
You should see:
NAMESPACE NAME READY REASON
default atlas-schema-pg True Applied
Get detailed status:
kubectl get atlasschema atlas-schema-pg -o jsonpath='{.status.conditions[?(@.type=="Ready")].message}'
This will show the apply response with details about the schema changes.
Next Steps
Now that you have the Atlas Operator running via Crossplane, you can:
- Learn more about AtlasSchema resources
- Explore AtlasMigration resources for versioned migrations
- Set up project-based configuration
- Configure ad-hoc approvals for schema changes