Quickstart
This guide walks through deploying CNMSQL - CloudNative for MySQL and a three-instance Percona Server for MySQL cluster in a local Kind environment.
Prerequisites
| Tool | Purpose |
|---|---|
go | Build the operator binary |
docker | Build and load container images |
kubectl | Interact with the Kubernetes cluster |
kind | Local Kubernetes cluster |
make | Run build targets |
cert-manager | Issue TLS certificates for mTLS and MySQL TLS |
Install cert-manager in your cluster if it isn't already present:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
kubectl wait --for=condition=Available deployment/cert-manager-webhook -n cert-manager --timeout=5m
1. Build and Load Images
Build the operator image from source:
make docker-build IMG=cnmsql-controller:dev
Pull the pre-built instance image. Instance images are published from the containers repository:
docker pull ghcr.io/cnmsql/cnmsql-instance:8.4
Load both images into your Kind cluster:
kind load docker-image cnmsql-controller:dev --name cnmsql-test-e2e
kind load docker-image ghcr.io/cnmsql/cnmsql-instance:8.4 --name cnmsql-test-e2e
2. Deploy the Operator
Install the CRDs and deploy the controller manager:
make install
make deploy IMG=cnmsql-controller:dev
Verify the controller is running:
kubectl get pods -n cnmsql-system
You should see a single cnmsql-controller-manager Pod in Running state.
3. Install the CLI Plugin
From a release (no source checkout needed):
curl -sSfL https://github.com/cnmsql/cnmsql/raw/main/hack/install-cnmsql-plugin.sh | sh -s -- -b ~/.local/bin
The script downloads the latest release binary for your platform, verifies its checksum,
and installs the plugin plus a tab-completion shim. Replace ~/.local/bin with any
directory on your PATH (e.g. /usr/local/bin).
From the repo (development):
make install-plugin
Verify the plugin is registered:
kubectl cnmsql version
The plugin is available as kubectl cnmsql. It auto-detects the cluster in the current namespace, so you can omit the cluster name in most commands.
4. Create a Cluster
Apply a minimal three-instance cluster. An initial database app is bootstrapped with an app owner role:
apiVersion: mysql.cnmsql.co/v1alpha1
kind: Cluster
metadata:
name: cluster-sample
spec:
instances: 3
imageName: ghcr.io/cnmsql/cnmsql-instance:8.4
storage:
size: 10Gi
mysql:
binlogFormat: ROW
bootstrap:
initdb:
database: app
owner: app
Wait for the cluster to become ready:
kubectl wait --for=condition=Ready cluster/cluster-sample --timeout=15m
Inspect the topology with the CLI plugin:
kubectl cnmsql status cluster-sample
Expected result:
- Three Pods:
cluster-sample-1,cluster-sample-2,cluster-sample-3 - One Pod labeled
mysql.cnmsql.co/role=primary - Two Pods labeled
mysql.cnmsql.co/role=replica status.readyInstancesis3
5. Connect to the Database
CNMSQL - CloudNative for MySQL creates three role-routed Services automatically:
| Service | Endpoint | Routes to |
|---|---|---|
cluster-sample-rw | Read-write | Current primary |
cluster-sample-ro | Read-only | Ready replicas |
cluster-sample-r | Read | Any ready instance |
Service routing follows the mysql.cnmsql.co/role label and updates automatically after failover — no manual reconfiguration needed.
Application credentials are generated and stored in a Secret. List all Secrets for the cluster:
kubectl get secrets -l mysql.cnmsql.co/cluster=cluster-sample
To test connectivity, launch a temporary MySQL client Pod and connect:
kubectl run mysql-client --rm -it --image=mysql:8.4 --restart=Never -- \
mysql -h cluster-sample-rw -u app -p$(kubectl get secret cluster-sample-app-app -o jsonpath='{.data.password}' | base64 -d) app
6. Scale the Cluster
Scale up to four instances:
kubectl patch cluster cluster-sample --type merge -p '{"spec":{"instances":4}}'
kubectl wait --for=condition=Ready cluster/cluster-sample --timeout=15m
Scale down to one instance:
kubectl patch cluster cluster-sample --type merge -p '{"spec":{"instances":1}}'
Scale-down removes replica Pods (highest ordinal first) but retains their PVCs. Delete retained PVCs only after you're certain the data is no longer needed.
7. Take a Backup
Create an ad-hoc backup via the CLI:
kubectl cnmsql backup cluster-sample
This creates a Backup resource with defaults: XtraBackup, online, backed by the cluster's configured object store. Monitor progress:
kubectl get backup backup-sample -w
kubectl describe backup backup-sample
Once status.phase reaches completed, the backup is ready for restoration.
8. Clean Up
kubectl delete cluster cluster-sample
Deleting a Backup resource does not remove data from the object store. Use your object store's lifecycle policies or delete the objects manually.
Next Steps
- Configure object storage for production backups
- Enable continuous archiving to unlock point-in-time recovery
- Read the operations runbook for day-two commands like switchover and failover
- Explore the API reference for every field and option