New to KubeDB? Please start here.

Database Snapshots

This tutorial will show you how to take snapshots of a KubeDB managed MySQL database.

Note: The yaml files used in this tutorial are stored in docs/examples/mysql folder in GitHub repository kubedb/cli.

Before You Begin

  • At first, you need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using Minikube.

  • Now, install KubeDB cli on your workstation and KubeDB operator in your cluster following the steps here.

  • StorageClass is required to run KubeDB. Check the available StorageClass in cluster.

  $ kubectl get storageclasses
  NAME                 PROVISIONER                AGE
  standard (default)   k8s.io/minikube-hostpath   4h
  • A MySQL database is needed to take snapshot for this tutorial. To keep things isolated, this tutorial uses a separate namespace called demo throughout this tutorial. Run the following command to prepare your cluster for this tutorial:
  $ kubectl create ns demo
  namespace "demo" created
  
  $ kubectl get ns
  NAME          STATUS    AGE
  demo          Active    1m
  
  $ kubedb create -f https://raw.githubusercontent.com/kubedb/cli/0.9.0-rc.1/docs/examples/mysql/snapshot/demo-1.yaml
  mysql.kubedb.com/mysql-infant created

Instant Backups

You can easily take a snapshot of MySQL database by creating a Snapshot object. When a Snapshot object is created, KubeDB operator will launch a Job that runs the mysql dump command and uploads the output bson file to various cloud providers S3, GCS, Azure, OpenStack Swift and/or locally mounted volumes using osm.

In this tutorial, snapshots will be stored in a Google Cloud Storage (GCS) bucket. To do so, a secret is needed that has the following 2 keys:

Key Description
GOOGLE_PROJECT_ID Required. Google Cloud project ID
GOOGLE_SERVICE_ACCOUNT_JSON_KEY Required. Google Cloud service account json key
$ echo -n '<your-project-id>' > GOOGLE_PROJECT_ID
$ mv downloaded-sa-json.key > GOOGLE_SERVICE_ACCOUNT_JSON_KEY
$ kubectl create secret generic my-snap-secret -n demo \
    --from-file=./GOOGLE_PROJECT_ID \
    --from-file=./GOOGLE_SERVICE_ACCOUNT_JSON_KEY
secret/my-snap-secret created
$ kubectl get secret my-snap-secret -n demo -o yaml
apiVersion: v1
data:
  GOOGLE_PROJECT_ID: PHlvdX....1pZD4=
  GOOGLE_SERVICE_ACCOUNT_JSON_KEY: ewogICJ0eXBlIjogInN...9tIgp9Cg==
kind: Secret
metadata:
  creationTimestamp: 2018-02-09T12:02:08Z
  name: my-snap-secret
  namespace: demo
  resourceVersion: "30349"
  selfLink: /api/v1/namespaces/demo/secrets/my-snap-secret
  uid: 0dccee80-0d91-11e8-9091-08002751ae8c
type: Opaque

To lean how to configure other storage destinations for Snapshots, please visit here. Now, create the Snapshot object.

apiVersion: kubedb.com/v1alpha1
kind: Snapshot
metadata:
  name: snap-mysql-infant
  namespace: demo
  labels:
    kubedb.com/kind: MySQL
spec:
  databaseName: mysql-infant
  storageSecretName: my-snap-secret
  gcs:
    bucket: kubedb
$ kubedb create -f https://raw.githubusercontent.com/kubedb/cli/0.9.0-rc.1/docs/examples/mysql/snapshot/demo-2.yaml
snapshot.kubedb.com/snap-mysql-infant created

$ kubedb get snap -n demo
NAME                DATABASENAME   STATUS    AGE
snap-mysql-infant   mysql-infant   Running   13s
$ kubedb get snap -n demo snap-mysql-infant -o yaml
apiVersion: kubedb.com/v1alpha1
kind: Snapshot
metadata:
  creationTimestamp: 2018-09-27T06:12:37Z
  finalizers:
  - kubedb.com
  generation: 1
  labels:
    kubedb.com/kind: MySQL
    kubedb.com/name: mysql-infant
  name: snap-mysql-infant
  namespace: demo
  resourceVersion: "1754"
  selfLink: /apis/kubedb.com/v1alpha1/namespaces/demo/snapshots/snap-mysql-infant
  uid: 54efc1fe-c21c-11e8-850e-080027517bbf
spec:
  databaseName: mysql-infant
  gcs:
    bucket: kubedb
  storageSecretName: my-snap-secret
status:
  completionTime: 2018-09-27T06:18:41Z
  phase: Succeeded
  startTime: 2018-09-27T06:12:38Z

Here,

  • metadata.labels should include the type of database kubedb.com/kind: MySQL whose snapshot will be taken.
  • spec.databaseName points to the database whose snapshot is taken.
  • spec.storageSecretName points to the Secret containing the credentials for snapshot storage destination.
  • spec.gcs.bucket points to the bucket name used to store the snapshot data.

You can also run the kubedb describe command to see the recent snapshots taken for a database.

$ kubedb describe my -n demo mysql-infant
Name:               mysql-infant
Namespace:          demo
CreationTimestamp:  Thu, 27 Sep 2018 12:12:10 +0600
Labels:             <none>
Annotations:        <none>
Replicas:           1  total
Status:             Running
  StorageType:      Durable
Volume:
  StorageClass:  standard
  Capacity:      50Mi
  Access Modes:  RWO

StatefulSet:          
  Name:               mysql-infant
  CreationTimestamp:  Thu, 27 Sep 2018 12:12:11 +0600
  Labels:               kubedb.com/kind=MySQL
                        kubedb.com/name=mysql-infant
  Annotations:        <none>
  Replicas:           824641842156 desired | 1 total
  Pods Status:        1 Running / 0 Waiting / 0 Succeeded / 0 Failed

Service:        
  Name:         mysql-infant
  Labels:         kubedb.com/kind=MySQL
                  kubedb.com/name=mysql-infant
  Annotations:  <none>
  Type:         ClusterIP
  IP:           10.109.47.223
  Port:         db  3306/TCP
  TargetPort:   db/TCP
  Endpoints:    172.17.0.5:3306

Database Secret:
  Name:         mysql-infant-auth
  Labels:         kubedb.com/kind=MySQL
                  kubedb.com/name=mysql-infant
  Annotations:  <none>
  
Type:  Opaque
  
Data
====
  password:  16 bytes
  user:      4 bytes

Snapshots:
  Name               Bucket     StartTime                        CompletionTime                   Phase
  ----               ------     ---------                        --------------                   -----
  snap-mysql-infant  gs:kubedb  Thu, 27 Sep 2018 12:12:38 +0600  Thu, 27 Sep 2018 12:18:41 +0600  Succeeded

Events:
  Type    Reason              Age   From            Message
  ----    ------              ----  ----            -------
  Normal  Successful          17m   MySQL operator  Successfully created Service
  Normal  Starting            17m   Job Controller  Backup running
  Normal  Successful          14m   MySQL operator  Successfully created StatefulSet
  Normal  Successful          14m   MySQL operator  Successfully created MySQL
  Normal  Successful          14m   MySQL operator  Successfully patched StatefulSet
  Normal  Successful          14m   MySQL operator  Successfully patched MySQL
  Normal  Successful          14m   MySQL operator  Successfully patched StatefulSet
  Normal  Successful          14m   MySQL operator  Successfully patched MySQL
  Normal  SuccessfulSnapshot  11m   Job Controller  Successfully completed snapshot

Once the snapshot Job is complete, you should see the output of the mysql dump command stored in the GCS bucket.

snapshot-console

From the above image, you can see that the snapshot output is stored in a folder called {bucket}/kubedb/{namespace}/{mysql-object}/{snapshot}/.

Restore from Snapshot

You can create a new database from a previously taken Snapshot. Specify the Snapshot name in the spec.init.snapshotSource field of a new MySQL object. See the example mysql-recovered object below:

Note: MySQL mysql-recovered must have same superuser credentials as MySQL mysql-infant.

apiVersion: kubedb.com/v1alpha1
kind: MySQL
metadata:
  name: mysql-recovered
  namespace: demo
spec:
  version: "8.0-v1"
  databaseSecret:
    secretName: mysql-infant-auth
  storage:
    storageClassName: "standard"
    accessModes:
    - ReadWriteOnce
    resources:
      requests:
        storage: 50Mi
  init:
    snapshotSource:
      name: snap-mysql-infant
      namespace: demo
$ kubedb create -f https://raw.githubusercontent.com/kubedb/cli/0.9.0-rc.1/docs/examples/mysql/snapshot/demo-3.yaml
mysql.kubedb.com/mysql-recovered created

Here,

  • spec.init.snapshotSource.name refers to a Snapshot object for a MySQL database in the same namespaces as this new mysql-recovered MySQL object.

Now, wait several seconds. KubeDB operator will create a new StatefulSet. Then KubeDB operator launches a Kubernetes Job to initialize the new database using the data from snap-mysql-infant Snapshot.

$ kubedb get my -n demo
NAME              VERSION   STATUS         AGE
mysql-infant      8.0-v1    Running        27m
mysql-recovered   8.0-v1    Initializing   5m

$ kubedb get my -n demo
NAME              VERSION   STATUS    AGE
mysql-infant      8.0-v1    Running   31m
mysql-recovered   8.0-v1    Running   9m

$ kubedb describe my -n demo mysql-recovered
Name:               mysql-recovered
Namespace:          demo
CreationTimestamp:  Thu, 27 Sep 2018 12:34:07 +0600
Labels:             <none>
Annotations:        kubedb.com/initialized=
Replicas:           1  total
Status:             Running
  StorageType:      Durable
Volume:
  StorageClass:  standard
  Capacity:      50Mi
  Access Modes:  RWO

StatefulSet:          
  Name:               mysql-recovered
  CreationTimestamp:  Thu, 27 Sep 2018 12:34:09 +0600
  Labels:               kubedb.com/kind=MySQL
                        kubedb.com/name=mysql-recovered
  Annotations:        <none>
  Replicas:           824640109500 desired | 1 total
  Pods Status:        1 Running / 0 Waiting / 0 Succeeded / 0 Failed

Service:        
  Name:         mysql-recovered
  Labels:         kubedb.com/kind=MySQL
                  kubedb.com/name=mysql-recovered
  Annotations:  <none>
  Type:         ClusterIP
  IP:           10.99.66.59
  Port:         db  3306/TCP
  TargetPort:   db/TCP
  Endpoints:    172.17.0.6:3306

Database Secret:
  Name:         mysql-infant-auth
  Labels:         kubedb.com/kind=MySQL
                  kubedb.com/name=mysql-infant
  Annotations:  <none>
  
Type:  Opaque
  
Data
====
  password:  16 bytes
  user:      4 bytes

No Snapshots.

Events:
  Type    Reason                Age   From            Message
  ----    ------                ----  ----            -------
  Normal  Successful            9m    MySQL operator  Successfully created Service
  Normal  Successful            9m    MySQL operator  Successfully created MySQL
  Normal  Successful            9m    MySQL operator  Successfully created StatefulSet
  Normal  Initializing          9m    MySQL operator  Initializing from Snapshot: "snap-mysql-infant"
  Normal  Successful            8m    MySQL operator  Successfully patched StatefulSet
  Normal  Successful            8m    MySQL operator  Successfully patched MySQL
  Normal  SuccessfulInitialize  3m    Job Controller  Successfully completed initialization

Cleaning up

To cleanup the Kubernetes resources created by this tutorial, run:

kubectl patch -n demo mysql/mysql-infant mysql/mysql-recovered -p '{"spec":{"terminationPolicy":"WipeOut"}}' --type="merge"
kubectl delete -n demo mysql/mysql-infant mysql/mysql-recovered

kubectl patch -n demo drmn/mysql-infant drmn/mysql-recovered -p '{"spec":{"wipeOut":true}}' --type="merge"
kubectl delete -n demo drmn/mysql-infant drmn/mysql-recovered

kubectl delete ns demo

Next Steps

Subscribe to our free technical newsletter!

Join thousands of subscribers and stay up-to-date on AppsCode.