Many MongoDB clusters use storage-level snapshots to provide fast and reliable backups. In this blog post, you’ll learn how to restore such a snapshot from a traditional VM-based sharded MongoDB cluster to a freshly deployed Percona Operator for MongoDB cluster on Kubernetes.
I recently worked with a company running a large four-shard MongoDB Enterprise Server database cluster on VMs on premises that decided to migrate to the Google Cloud Platform. After careful consideration and pros-and-con evaluation, the customer chose to go with Percona Distribution for MongoDB run on Kubernetes – specifically using Percona Operator for MongoDB. There are four main factors that contributed to this decision:
To start realistic compatibility and performance testing, we needed to restore the filesystem snapshot backup that was stored on NetApp volume in GCP into a Google Kubernetes Engine (GKE) cluster running Percona Operator for MongoDB. It’s not a trivial task due to the requirements for restoring a sharded cluster backup into a new cluster. I will show you why and how we did it.
Let’s look at the overall requirements for the cluster where I want to restore the snapshot to:
This seems to be straightforward; however, there are a couple of special considerations to make when it comes to the environment controlled by Percona Operator for MongoDB, specifically:
All of the above makes it impossible to simply adjust the Operator configuration and copy all the files from your snapshot backup in the specific volumes.
The high-level plan consists of the following steps:
The following approach is applicable regardless of what “flavor” of MongoDB your source environment uses. This can be MongoDB Enterprise Server, MongoDB Community Edition, or Percona Server for MongoDB.
In order to deploy Percona Server for MongoDB (PSMDB) on the K8s cluster, follow the documentation. Before you execute the last step (deploying cr.yaml), however, make sure to adjust the following elements of the configuration. This will make the cluster “fit” the one that we took the backup from.
|
1 |
spec:<br> image: percona/percona-server-mongodb:5.0.11-10 |
|
1 |
replsets:<br> - name: shard1<br> size: 3<br> [...]<br> - name: shard2<br> size: 3<br> [...] |
|
1 |
replsets:<br> - name: shard1<br> size: 3<br> configuration: |<br> storage:<br> directoryPerDB: true<br> wiredTiger:<br> engineConfig:<br> directoryForIndexes: true |
Your cluster should be started at this point and it is important that all PersistentVolumes required for it be created. You can check your cluster state with kubectl get psmdb, see all deployed pods with kubectl get pods, or check PVs with kubectl get pv. In this step, you need to mount volumes of all your database nodes to an independent VM as we’ll make changes in MongoDB standalone mode. You need those VMs temporarily to perform the required operations.
|
1 |
# attach disk to an instance<br>gcloud compute instances attach-disk vm_name <br>--disk disk_name <br>--zone=us-central1-a<br><br># check volume on vm<br>sudo lsblk<br><br># create a mountpoint<br>sudo mkdir -p /dir_name (e.g. rs0-primary)<br><br># mount the volume<br>sudo mount -o discard,defaults /dev/sdb /dir_name |
Now, you need to start PSMDB for each volume with data (each primary of each replica set, including Config RS) separately. We will then log in to mongo shell and edit the cluster configuration manually so that when we bring volumes with data back to Kubernetes, it can start successfully.
Execute the steps below for each replica set, including Config RS:
|
1 |
use local;<br>db.dropDatabase(); |
|
1 |
use config;<br><br>db.shards.updateOne(<br> { "_id" : "shard_name" },<br> { $set :<br> { "host" : "shard_name/cluster_name-shard_name-0.cluster_name-shard_name.namespace_name.svc.cluster.local:27017,cluster_name-shard_name-1.cluster_name-shard_name.namespace_name.svc.cluster.local:27017,cluster_name-shard_name-2.cluster_name-shard_name.namespace_name.svc.cluster.local:27017"}<br>}); |
|
1 |
use admin<br>db.system.version.deleteOne( { _id: "minOpTimeRecovery" } ) |
|
1 |
use admin<br><br>db.system.version.updateOne(<br> { "_id" : "shardIdentity" },<br> { $set :<br> { "configsvrConnectionString" : "cfg/cluster_name-cfg-0.cluster_name-cfg.namespace_name.svc.cluster.local:27017,cluster_name-cfg-1.cluster_name-cfg.namespace_name.svc.cluster.local:27017,cluster_name-cfg-2.cluster_name-cfg.namespace_name.svc.cluster.local:27017"}<br> }); |
|
1 |
use admin;<br>//drop user in case they already exist in your backup<br>db.dropUser("userAdmin");<br>db.dropUser("clusterAdmin");<br>db.dropUser("clusterMonitor");<br>db.dropUser("backup");<br>db.dropUser("databaseAdmin");<br><br>//create missing role<br>db.createRole({"role" : "explainRole",<br> "privileges" : [{"resource": {"db" : "","collection" : "system.profile"},<br> "actions" : ["collStats","dbHash","dbStats","find","listCollections","listIndexes"]}],<br> roles: []});<br><br>//create system users<br>db.createUser({ user: "userAdmin",<br> pwd: "userAdmin123456",<br> roles: [ { role: "userAdminAnyDatabase", db: "admin" }]<br> });<br><br>db.createUser({ user: "clusterAdmin",<br> pwd: "clusterAdmin123456",<br> roles: [ { role: "clusterAdmin", db: "admin" }]<br> });<br><br>db.createUser({ user: "clusterMonitor",<br> pwd: "clusterMonitor123456",<br> roles: [{ role: "explainRole", db: "admin" },<br> { role: "read", db: "local" },<br> { role: "clusterMonitor", db: "admin" }]<br> });<br><br>db.createUser({ user: "databaseAdmin",<br> pwd: "databaseAdmin123456",<br> roles: [{ role: "readWriteAnyDatabase", db: "admin" },<br> { role: "readAnyDatabase", db: "admin" },<br> { role: "dbAdminAnyDatabase", db: "admin" },<br> { role: "backup", db: "admin" },<br> { role: "restore", db: "admin" },<br> { role: "clusterMonitor", db: "admin" },]<br> });<br><br>db.createUser({ user: "backup",<br> pwd: "backup123456",<br> roles: [{ role: "backup", db: "admin" },<br> { role: "restore", db: "admin" },<br> { role: "clusterMonitor", db: "admin" }]<br> }); |
|
1 |
mongodb:x:1001:0:Default Application User:/home/mongodb:/sbin/nologin |
|
1 |
cd /your_mountpoint<br>sudo chown -R mongodb:1001 ./ |
|
1 |
sudo umount /dir_name<br><br>gcloud compute instances detach-disk vm_name <br>--disk disk_name <br>--zone=us-central1-a |
You’re ready to get back to K8s. Start the cluster (it will start with previously used volumes). It will be in a pending state because we broke (intentionally) replica sets. Only one pod per Replica Set will start. You must initialize replica sets one by one.
To unpause the PSMDB cluster set spec.pause: false in your cr.yaml file and apply it with kubectl. Then, repeat the steps below for all replica sets, starting with Config RS.
|
1 |
use admin;<br>db.auth("clusterAdmin", "clusterAdmin123456"); |
|
1 |
# version for Config RS<br><br>rs.initiate(<br> {<br> _id: "cfg",<br> members: [<br> { _id: 0, host : "cluster_name-cfg-0.cluster_name-cfg.namespace_name.svc.cluster.local:27017" },<br> { _id: 1, host : "cluster_name-cfg-1.cluster_name-cfg.namespace_name.svc.cluster.local:27017" },<br> { _id: 2, host : "cluster_name-cfg-2.cluster_name-cfg.namespace_name.svc.cluster.local:27017" },<br> ]<br> }<br>);<br><br># version for other RS (shards)<br>rs.initiate(<br> {<br> _id: "shard_name",<br> members: [<br> { _id: 0, host : "cluster_name-shard_name-0.cluster_name-shard_name.namespace_name.svc.cluster.local:27017" },<br> { _id: 1, host : "cluster_name-shard_name-1.cluster_name-shard_name.namespace_name.svc.cluster.local:27017" },<br> { _id: 2, host : "cluster_name-shard_name-2.cluster_name-shard_name.namespace_name.svc.cluster.local:27017" },<br> ]<br> }<br>);<br> |
That’s it! You now successfully restored a snapshot backup into Percona Server for MongoDB deployed on K8s with Percona Operator. To verify that you have successfully done that run kubectl get pods or kubectl get psmdb – the output should be similar to the one below.
|
1 |
$ kubectl get psmdb<br>NAME ENDPOINT STATUS AGE<br>my-cluster-name my-cluster-name-mongos.ns-0.svc.cluster.local ready 156m |
The Percona Kubernetes Operators automate the creation, alteration, or deletion of members in your Percona Distribution for MySQL, MongoDB, or PostgreSQL environment.
Resources
RELATED POSTS