- What is a logical backup?
- Storage types
BackupCRRestoreCR- Bootstrap new
MariaDBinstances - Backup and restore specific databases
- Extra options
- Staging area
- Important considerations and limitations
- Migrations using logical backups
- Minio reference installation
- Reference
- Troubleshooting
A logical backup is a backup that contains the logical structure of the database, such as tables, indexes, and data, rather than the physical storage format. It is created using mariadb-dump, which generates SQL statements that can be used to recreate the database schema and populate it with data.
Logical backups serve not just as a source of restoration, but also enable data mobility between MariaDB instances. These backups are called "logical" because they are independent from the MariaDB topology, as they only contain DDLs and INSERT statements to populate data.
Although logical backups are a great fit for data mobility and migrations, they are not as efficient as physical backups for large databases. For this reason, physical backups are the recommended method for backing up MariaDB databases, especially in production environments.
Currently, the following storage types are supported:
- S3 compatible storage: Store backups in a S3 compatible storage, such as AWS S3 or Minio.
- PVCs: Use the available StorageClasses in your Kubernetes cluster to provision a PVC dedicated to store the backup files.
- Kubernetes volumes: Use any of the volume types supported natively by Kubernetes.
Our recommendation is to store the backups externally in a S3 compatible storage. Minio makes this incredibly easy, take a look at our Minio reference installation to quickly spin up an instance.
You can take a one-time backup of your MariaDB instance by declaring the following resource:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
mariaDbRef:
name: mariadb
storage:
persistentVolumeClaim:
resources:
requests:
storage: 100Mi
accessModes:
- ReadWriteOnceThis will use the default StorageClass to provision a PVC that would hold the backup files, but ideally you should use a S3 compatible storage:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
mariaDbRef:
name: mariadb
storage:
s3:
bucket: backups
prefix: mariadb
endpoint: minio.minio.svc.cluster.local:9000
region: us-east-1
accessKeyIdSecretKeyRef:
name: minio
key: access-key-id
secretAccessKeySecretKeyRef:
name: minio
key: secret-access-key
tls:
enabled: true
caSecretKeyRef:
name: minio-ca
key: tls.crtBy providing the authentication details and the TLS configuration via references to Secret keys, this example will store the backups in a local Minio instance.
Alternatively you can use dynamic credentials from an EKS Service Account using EKS Pod Identity or IRSA:
apiVersion: v1
kind: ServiceAccount
metadata:
name: mariadb-backup
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<<account_id>>:role/my-role-irsaapiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
mariaDbRef:
name: mariadb
serviceAccountName: mariadb-backup
storage:
s3:
bucket: backups
prefix: mariadb
endpoint: s3.us-east-1.amazonaws.com
region: us-east-1
tls:
enabled: trueBy leaving out the accessKeyIdSecretKeyRef and secretAccessKeySecretKeyRef credentials and pointing to the correct serviceAccountName, the backup Job will use the dynamic credentials from EKS.
To minimize the Recovery Point Objective (RPO) and mitigate the risk of data loss, it is recommended to perform backups regularly. You can do so by providing a spec.schedule in your Backup resource:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
mariaDbRef:
name: mariadb
schedule:
cron: "*/1 * * * *"
suspend: falseThis resource gets reconciled into a CronJob that periodically takes the backups.
It is important to note that regularly scheduled Backups complement very well the target recovery time feature detailed below.
Given that the backups can consume a substantial amount of storage, it is crucial to define your retention policy by providing the spec.maxRetention field in your Backup resource:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
mariaDbRef:
name: mariadb
maxRetention: 720h # 30 daysYou are able to compress backups by providing the compression algorithm you want to use in the spec.compression field:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
mariaDbRef:
name: mariadb
compression: gzipCurrently the following compression algorithms are supported:
bzip2: Good compression ratio, but slower compression/decompression speed compared to gzip.gzip: Good compression/decompression speed, but worse compression ratio compared to bzip2.none: No compression.
compression is defaulted to none by the operator.
You can enable server-side encryption using your own encryption key (SSE-C) by providing a reference to a Secret containing a 32-byte (256-bit) key encoded in base64:
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: ssec-key
stringData:
# 32-byte key encoded in base64 (use: openssl rand -base64 32)
customer-key: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
mariaDbRef:
name: mariadb
storage:
s3:
bucket: backups
prefix: mariadb
endpoint: minio.minio.svc.cluster.local:9000
region: us-east-1
accessKeyIdSecretKeyRef:
name: minio
key: access-key-id
secretAccessKeySecretKeyRef:
name: minio
key: secret-access-key
tls:
enabled: true
caSecretKeyRef:
name: minio-ca
key: tls.crt
ssec:
customerKeySecretKeyRef:
name: ssec-key
key: customer-keyImportant
When using SSE-C, you are responsible for managing and securely storing the encryption key. If you lose the key, you will not be able to decrypt your backups. Ensure you have proper key management procedures in place.
Note
When restoring from SSE-C encrypted backups, the same key must be provided in the Restore CR or bootstrapFrom configuration.
You can easily restore a Backup in your MariaDB instance by creating the following resource:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Restore
metadata:
name: restore
spec:
mariaDbRef:
name: mariadb
backupRef:
name: backupThis will trigger a Job that will mount the same storage as the Backup and apply the dump to your MariaDB database.
Nevertheless, the Restore resource doesn't necessarily need to specify a spec.backupRef, you can point to other storage source that contains backup files, for example a S3 bucket:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Restore
metadata:
name: restore
spec:
mariaDbRef:
name: mariadb
s3:
bucket: backups
prefix: mariadb
endpoint: minio.minio.svc.cluster.local:9000
region: us-east-1
accessKeyIdSecretKeyRef:
name: minio
key: access-key-id
secretAccessKeySecretKeyRef:
name: minio
key: secret-access-key
tls:
enabled: true
caSecretKeyRef:
name: minio-ca
key: tls.crtIf you have multiple backups available, specially after configuring a scheduled Backup, the operator is able to infer which backup to restore based on the spec.targetRecoveryTime field.
apiVersion: k8s.mariadb.com/v1alpha1
kind: Restore
metadata:
name: restore
spec:
mariaDbRef:
name: mariadb
backupRef:
name: backup
targetRecoveryTime: 2023-12-19T09:00:00ZThe operator will look for the closest backup available and utilize it to restore your MariaDB instance. Only backups strictly before or at targetRecoveryTime will be matched.
By default, spec.targetRecoveryTime will be set to the current time, which means that the latest available backup will be used.
To minimize your Recovery Time Objective (RTO) and to switfly spin up new clusters from existing Backups, you can provide a Restore source directly in the MariaDB object via the spec.bootstrapFrom field:
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: mariadb-from-backup
spec:
storage:
size: 1Gi
bootstrapFrom:
backupRef:
name: backup
targetRecoveryTime: 2023-12-19T09:00:00ZAs in the Restore resource, you don't strictly need to specify a reference to a Backup, you can provide other storage types that contain backup files:
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: mariadb-from-backup
spec:
storage:
size: 1Gi
bootstrapFrom:
s3:
bucket: backups
prefix: mariadb
endpoint: minio.minio.svc.cluster.local:9000
accessKeyIdSecretKeyRef:
name: minio
key: access-key-id
secretAccessKeySecretKeyRef:
name: minio
key: secret-access-key
tls:
enabled: true
caSecretKeyRef:
name: minio-ca
key: tls.crt
targetRecoveryTime: 2023-12-19T09:00:00ZUnder the hood, the operator creates a Restore object just after the MariaDB resource becomes ready. The advantage of using spec.bootstrapFrom over a standalone Restore is that the MariaDB is bootstrap-aware and this will allow the operator to hold primary switchover/failover operations until the restoration is finished.
By default, all the logical databases are backed up when a Backup is created, but you may also select specific databases by providing the databases field:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
mariaDbRef:
name: mariadb
databases:
- db1
- db2
- db3When it comes to restore, all the databases available in the backup will be restored, but you may also choose a single database to be restored via the database field available in the Restore resource:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Restore
metadata:
name: restore
spec:
mariaDbRef:
name: mariadb
backupRef:
name: backup
database: db1There are a couple of points to consider here:
- The referred database (
db1in the example) must previously exist for theRestoreto succeed. - The
mariadbCLI invoked by the operator under the hood only supports selecting a single database to restore via the--one-databaseoption, restoration of multiple specific databases is not supported.
Not all the flags supported by mariadb-dump and mariadb have their counterpart field in the Backup and Restore CRs respectively, but you may pass extra options by using the args field. For example, setting the --verbose flag can be helpful to track the progress of backup and restore operations:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
mariaDbRef:
name: mariadb
args:
- --verboseapiVersion: k8s.mariadb.com/v1alpha1
kind: Restore
metadata:
name: restore
spec:
mariaDbRef:
name: mariadb
backupRef:
name: backup
args:
- --verboseRefer to the mariadb-dump and mariadb CLI options in the reference section.
Note
S3 is the only storage type that requires a staging area.
When using S3 storage for backups, a staging area is used for keeping the external backups while they are being processed. By default, this staging area is an emptyDir volume, which means that the backups are temporarily stored in the node's local storage where the Backup/Restore Job is scheduled. In production environments, large backups may lead to issues if the node doesn't have sufficient space, potentially causing the backup/restore process to fail.
To overcome this limitation, you are able to define your own staging area by setting the stagingStorage field to both the Backup and Restore CRs:
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
storage:
s3:
...
stagingStorage:
persistentVolumeClaim:
resources:
requests:
storage: 10Gi
accessModes:
- ReadWriteOnceapiVersion: k8s.mariadb.com/v1alpha1
kind: Restore
metadata:
name: restore
spec:
s3:
...
stagingStorage:
persistentVolumeClaim:
resources:
requests:
storage: 10Gi
accessModes:
- ReadWriteOnceIn the examples above, a PVC with the default StorageClass will be used as staging area. Refer to the API reference for more configuration options.
Similarly, you may also use a custom staging area when bootstrapping from backup:
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: mariadb
spec:
bootstrapFrom:
s3:
...
stagingStorage:
persistentVolumeClaim:
resources:
requests:
storage: 10Gi
accessModes:
- ReadWriteOnceWhen restoring a backup, the root credentials specified through the spec.rootPasswordSecretKeyRef field in the MariaDB resource must match the ones in the backup. These credentials are utilized by the liveness and readiness probes, and if they are invalid, the probes will fail, causing your MariaDB Pods to restart after the backup restoration.
Restoring large backups can consume significant compute resources and may cause Restore Jobs to become stuck due to insufficient resources. To prevent this, you can define the compute resources allocated to the Job:
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: mariadb
spec:
storage:
size: 1Gi
bootstrapFrom:
restoreJob:
args:
- --verbose
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 1GiGalera only replicates the tables with InnoDB engine:
Something that does not include mysql.global_priv, the table used to store users and grants, which uses the MyISAM engine. This basically means that a Galera instance with mysql.global_priv populated will not replicate this data to an empty Galera instance. However, DDL statements (CREATE USER, ALTER USER ...) will be replicated.
Taking this into account, if we think now about a restore scenario where:
- The backup file includes a
DROP TABLEstatement for themysql.global_privtable. - The backup has some
INSERTstatements for themysql.global_privtable. - The Galera cluster has 3 nodes:
galera-0,galera-1andgalera-2. - The backup is restored in
galera-0.
This is what will happen under the scenes while restoring the backup:
- The
DROP TABLEstatement is a DDL so it will be executed ingalera-0,galera-1andgalera-2. - The
INSERTstatements are not DDLs, so they will only be applied togalera-0. - This results in the
galera-1andgalera-2not having themysql.global_privtable.
After the backup is fully restored, the liveness and readiness probes will kick in, they will succeed in galera-0, but they will fail in galera-1 and galera-2, as they rely in the root credentials available in mysql.global_priv, resulting in the galera-1 and galera-2 getting restarted.
To address this issue, when backing up MariaDB instances with Galera enabled, the mysql.global_priv table will be excluded from backups by using the --ignore-table option with mariadb-dump. This prevents the replication of the DROP TABLE statement for the mysql.global_priv table. You can opt-out from this feature by setting spec.ignoreGlobalPriv=false in the Backup resource.
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
spec:
mariaDbRef:
name: mariadb
ignoreGlobalPriv: falseAlso, to avoid situations where mysql.global_priv is unreplicated, all the entries in that table must be managed via DDLs. This is the recommended approach suggested in the Galera docs. There are a couple of ways that we can guarantee this:
- Use the
rootPasswordSecretKeyRef,usernameandpasswordSecretKeyReffields of theMariaDBCR to create the root and initial user respectively. This fields will be translated into DDLs by the image entrypoint. - Rely on the
UserandGrantCRs to create additional users and grants. Refer to the SQL resource documentation for further detail.
Galera is not compatible with the LOCK TABLES statement:
For this reason, the operator automatically adds the --skip-add-locks option to the Backup to overcome this limitation.
You can leverage logical backups to bring your external MariaDB data into a new MariaDB instance running in Kubernetes. Follow this runbook for doing so:
- Take a logical backup of your external MariaDB using one of the commands below:
mariadb-dump --user=${MARIADB_USER} --password=${MARIADB_PASSWORD} --host=${MARIADB_HOST} --single-transaction --events --routines --all-databases > backup.2024-08-26T12:24:34Z.sqlImportant
If you are using Galera or planning to migrate to a Galera instance, make sure you understand the Galera backup limitations and use the following command instead:
mariadb-dump --user=${MARIADB_USER} --password=${MARIADB_PASSWORD} --host=${MARIADB_HOST} --single-transaction --events --routines --all-databases --skip-add-locks --ignore-table=mysql.global_priv > backup.2024-08-26T12:24:34Z.sql-
Ensure that your backup file is named in the following format:
backup.2024-08-26T12:24:34Z.sql. If the file name does not follow this format, it will be ignored by the operator. -
Upload the backup file to one of the supported storage types. We recommend using S3.
-
Create your
MariaDBresource declaring that you want to bootstrap from the previous backup and providing a root passwordSecretthat matches the backup:
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: mariadb-galera
spec:
rootPasswordSecretKeyRef:
name: mariadb
key: root-password
replicas: 3
galera:
enabled: true
storage:
size: 1Gi
bootstrapFrom:
s3:
bucket: backups
prefix: mariadb
endpoint: minio.minio.svc.cluster.local:9000
accessKeyIdSecretKeyRef:
name: minio
key: access-key-id
secretAccessKeySecretKeyRef:
name: minio
key: secret-access-key
tls:
enabled: true
caSecretKeyRef:
name: minio-ca
key: tls.crt
targetRecoveryTime: 2024-08-26T12:24:34Z- If you are using Galera in your new instance, migrate your previous users and grants to use the
UserandGrantCRs. Refer to the SQL resource documentation for further detail.
Databa mobility between MariaDB instances with different topologies is possible with logical backups. However, there are a couple of technical details that you need to be aware of in the following scenarios:
This should be fully compatible, no issues have been detected.
There are a couple of limitations regarding the backups in Galera, please make sure you read the Galera backup limitations section before proceeding.
To overcome this limitations, the Backup in the standalone/replicated instance needs to be taken with spec.ignoreGlobalPriv=true. In the following example, we are backing up a standalone MariaDB (single instance):
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup-standalone
spec:
mariaDbRef:
name: mariadb-standalone
ignoreGlobalPriv: trueOnce the previous Backup is completed, we will be able bootstrap a new Galera instance from it:
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: mariadb-galera
spec:
replicas: 3
galera:
enabled: true
storage:
size: 1Gi
bootstrapFrom:
backupRef:
name: backup-standaloneThe easiest way to get a S3 compatible storage is Minio. You can install it by using their helm chart, or, if you are looking for a production-grade deployment, take a look at their operator.
In our case, we have have configured a Minio instance for development purposes, you can easily install it by running:
make cluster
make install-minio
make net # to access the console via a MetalLB LoadBalancer: https://minio-console:9001As an alternative, you can also use play.min.io using these credentials.
Please make sure you understand the Galera backup limitations.
After doing so, ensure that your backup does not contain a DROP TABLE mysql.global_priv; statement, as it will make your liveness and readiness probes to fail after the backup restoration.