Skip to content

Commit

Permalink
Example for Kanister integration with MongoDB 4.0.12 (#274)
Browse files Browse the repository at this point in the history
Signed-off-by: Prasad Ghangal <prasad.ghangal@gmail.com>
  • Loading branch information
PrasadG193 authored and tdmanv committed Sep 13, 2019
1 parent 82d4e0e commit 9d29b50
Show file tree
Hide file tree
Showing 4 changed files with 292 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ dockers:
- 'examples/helm/kanister/kanister-mongodb-replicaset/image/90forceyes'
- 'examples/helm/kanister/kanister-mongodb-replicaset/image/config'
- 'examples/helm/kanister/kanister-mongodb-replicaset/image/gbl_env.sh'
- image_templates:
- 'kanisterio/mongodb:{{ .Tag }}'
dockerfile: 'docker/mongodb/Dockerfile'

snapshot:
name_template: '{{ .Tag }}'
Expand Down
6 changes: 6 additions & 0 deletions docker/mongodb/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM bitnami/mongodb:4.0.12-debian-9-r22

MAINTAINER "Tom Manville <tom@kasten.io>"

# Install restic to take backups
COPY --from=restic/restic:0.9.5 /usr/bin/restic /usr/local/bin/restic
181 changes: 181 additions & 0 deletions examples/stable/mongodb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# MongoDB

[MongoDB](https://www.mongodb.com/) is a cross-platform document-oriented database. Classified as a NoSQL database, MongoDB eschews the traditional table-based relational database structure in favor of JSON-like documents with dynamic schemas, making the integration of data in certain types of applications easier and faster.

## Prerequisites

* Kubernetes 1.9+
* Kubernetes beta APIs enabled only if `podDisruptionBudget` is enabled
* PV support on the underlying infrastructure
* Kanister controller version 0.21.0 installed in your cluster
* Kanctl CLI installed (https://docs.kanister.io/tooling.html#kanctl)

## Chart Details

We will be using stable [mongodb](https://github.com/helm/charts/tree/master/stable/mongodb) chart from official helm repo which bootstraps a [MongoDB](https://github.com/bitnami/bitnami-docker-mongodb) deployment on a [Kubernetes](http://kubernetes.io) cluster in replication mode using the [Helm](https://helm.sh) package manager.

## Installing the Chart

To install the chart with the release name `my-release`:

```bash
$ helm repo add stable https://kubernetes-charts.storage.googleapis.com/
$ helm repo update

$ helm install stable/mongodb --name my-release --namespace mongo-test \
--set replicaSet.enabled=true \
--set image.repository=kanisterio/mongodb \
--set image.tag=0.21.0
```

The command deploys MongoDB on the Kubernetes cluster in the mongo-test namespace


## Integrating with Kanister

If you have deployed mongodb application with other name than `my-release` and namespace other than `mongo-test`, you need to modify the commands used below to use the correct name and namespace

### Create Profile
Create Profile CR if not created already

```bash
$ kanctl create profile s3compliant --access-key <aws-access-key-id> \
--secret-key <aws-secret-key> \
--bucket <s3-bucket-name> --region <region-name> \
--namespace mongo-test
```

**NOTE:**

The command will configure a location where artifacts resulting from Kanister data operations such as backup should go. This is stored as a profiles.cr.kanister.io CustomResource (CR) which is then referenced in Kanister ActionSets. Every ActionSet requires a Profile reference to complete the action. This CR (profiles.cr.kanister.io) can be shared between Kanister-enabled application instances.


### Create Blueprint
Create Blueprint in the same namespace as the controller

```bash
$ kubectl create -f ./mongodb-blueprint.yaml -n kasten-io
```

Once MongoDB is running, you can populate it with some data. Let's add a collection called "restaurants" to a test database:

```bash
# Connect to MongoDB primary pod
$ kubectl exec -ti my-release-mongodb-primary-0 -n mongo-test -- bash

# From inside the shell, use the mongo CLI to insert some data into the test database
$ mongo admin --authenticationDatabase admin -u root -p $MONGODB_ROOT_PASSWORD --quiet --eval "db.restaurants.insert({'name' : 'Roys', 'cuisine' : 'Hawaiian', 'id' : '8675309'})"
WriteResult({ "nInserted" : 1 })

# View the restaurants data in the test database
$ mongo admin --authenticationDatabase admin -u root -p $MONGODB_ROOT_PASSWORD --quiet --eval "db.restaurants.find()"
{ "_id" : ObjectId("5d778d49bd622241df3bc133"), "name" : "Roys", "cuisine" : "Hawaiian", "id" : "8675309" }
```
## Protect the Application
You can now take a backup of the MongoDB data using an ActionSet defining backup for this application. Create an ActionSet in the same namespace as the controller.
```bash
$ kubectl get profile -n mongo-test
NAME AGE
s3-profile-sph7s 2h
$ kanctl create actionset --action backup --namespace kasten-io --blueprint mongodb-blueprint --statefulset mongo-test/my-release-mongodb-primary --profile mongo-test/s3-profile-sph7s
actionset backup-llfb8 created
$ kubectl --namespace kasten-io get actionsets.cr.kanister.io
NAME AGE
backup-llfb8 2h
# View the status of the actionset
$ kubectl --namespace kasten-io describe actionset backup-llfb8
```
### Disaster strikes!
Let's say someone with fat fingers accidentally deleted the restaurants collection using the following command in mongodb primary pod:
```bash
# Drop the restaurants collection
$ mongo admin --authenticationDatabase admin -u root -p $MONGODB_ROOT_PASSWORD --quiet --eval "db.restaurants.drop()"
true
```
If you try to access this data in the database, you should see that it is no longer there:
```bash
$ mongo admin --authenticationDatabase admin -u root -p $MONGODB_ROOT_PASSWORD --quiet --eval "db.restaurants.find()"
# No entries should be found in the restaurants collection
```
### Restore the Application
To restore the missing data, you should use the backup that you created before. An easy way to do this is to leverage `kanctl`, a command-line tool that helps create ActionSets that depend on other ActionSets:
**NOTE:**
As a part of restore operation in MongoDB ReplicaSet, we are deleting data from the Secondary replicas to allow MongoDB to use `Initial Sync` for updating Secondaries as documented [here](https://docs.mongodb.com/manual/tutorial/restore-replica-set-from-backup/#update-secondaries-using-initial-sync)
```bash
$ kanctl --namespace kasten-io create actionset --action restore --from "backup-llfb8"
actionset restore-backup-llfb8-64gqm created
# View the status of the ActionSet
kubectl --namespace kasten-io describe actionset restore-backup-llfb8-64gqm
```
You should now see that the data has been successfully restored to MongoDB!
```bash
$ mongo admin --authenticationDatabase admin -u root -p $MONGODB_ROOT_PASSWORD --quiet --eval "db.restaurants.find()"
{ "_id" : ObjectId("5d778f987799be09da2f8ade"), "name" : "Roys", "cuisine" : "Hawaiian", "id" : "8675309" }
```
### Delete the Artifacts
The artifacts created by the backup action can be cleaned up using the following command:
```bash
$ kanctl --namespace kasten-io create actionset --action delete --from "backup-llfb8"
actionset "delete-backup-llfb8-k9ncm" created
# View the status of the ActionSet
$ kubectl --namespace kasten-io describe actionset delete-backup-llfb8-k9ncm
```
### Troubleshooting
If you run into any issues with the above commands, you can check the logs of the controller using:
```bash
$ kubectl --namespace kasten-io logs -l app=kanister-operator
```
you can also check events of the actionset
```bash
$ kubectl describe actionset restore-backup-llfb8-64gqm -n kasten-io
```
## Uninstalling the Chart
To uninstall/delete the `my-release` deployment:
```bash
$ helm delete my-release
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
To completely remove the release include the `--purge` flag.
Delete Blueprint and Profile CR
```bash
$ kubectl delete blueprints.cr.kanister.io mongodb-blueprint -n kasten-io
$ kubectl get profiles.cr.kanister.io -n mongo-test
NAME AGE
s3-profile-sph7s 2h
$ kubectl delete profiles.cr.kanister.io s3-profile-sph7s -n mongo-test
```
102 changes: 102 additions & 0 deletions examples/stable/mongodb/mongodb-blueprint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
apiVersion: cr.kanister.io/v1alpha1
kind: Blueprint
metadata:
name: mongodb-blueprint
actions:
backup:
type: StatefulSet
outputArtifacts:
backupInfo:
keyValue:
backupIdentifier: "{{ .Phases.backupSnapshots.Output.BackupAllInfo }}"
phases:
- func: BackupDataAll
name: backupSnapshots
args:
namespace: "{{ .StatefulSet.Namespace }}"
container: "mongodb-primary"
includePath: /bitnami/mongodb
backupArtifactPrefix: "{{ .Profile.Location.Bucket }}/mongodb-backups/{{ .StatefulSet.Name }}/rs_backup"
restore:
type: StatefulSet
phases:
# Scale down mongodb replicas
- func: ScaleWorkload
name: shutdownSecondary
args:
namespace: "{{ .StatefulSet.Namespace }}"
name: "{{ .Object.metadata.labels.release }}-mongodb-secondary"
kind: StatefulSet
replicas: 0
- func: ScaleWorkload
name: shutdownPrimary
args:
namespace: "{{ .StatefulSet.Namespace }}"
name: "{{ .Object.metadata.labels.release }}-mongodb-primary"
kind: StatefulSet
replicas: 0
- func: ScaleWorkload
name: shutdownArbiter
args:
namespace: "{{ .StatefulSet.Namespace }}"
name: "{{ .Object.metadata.labels.release }}-mongodb-arbiter"
kind: StatefulSet
replicas: 0

# Restore data to primary db
- func: RestoreDataAll
name: restorePrimary
args:
namespace: "{{ .StatefulSet.Namespace }}"
image: kanisterio/kanister-tools:0.21.0
backupArtifactPrefix: "{{ .Profile.Location.Bucket }}/mongodb-backups/{{ .StatefulSet.Name }}/rs_backup"
backupInfo: "{{ .ArtifactsIn.backupInfo.KeyValue.backupIdentifier }}"

# Delete data from secondary replicas to perform initial sync
# https://docs.mongodb.com/manual/tutorial/restore-replica-set-from-backup/#update-secondaries-using-initial-sync
- func: PrepareData
name: syncSecondary
args:
namespace: "{{ .StatefulSet.Namespace }}"
image: busybox
volumes:
datadir-my-release-mongodb-secondary-0: "/mongo/secondary"
command:
- sh
- -c
- |
rm -rf /mongo/secondary/data/db/*
# Restart mongo replicas
- func: ScaleWorkload
name: startPrimary
args:
namespace: "{{ .StatefulSet.Namespace }}"
name: "{{ .StatefulSet.Name }}"
kind: StatefulSet
replicas: 1
- func: ScaleWorkload
name: startSecondary
args:
namespace: "{{ .StatefulSet.Namespace }}"
name: "{{ .Object.metadata.labels.release }}-mongodb-secondary"
kind: StatefulSet
replicas: 1
- func: ScaleWorkload
name: startArbiter
args:
namespace: "{{ .StatefulSet.Namespace }}"
name: "{{ .Object.metadata.labels.release }}-mongodb-arbiter"
kind: StatefulSet
replicas: 1

delete:
type: StatefulSet
phases:
- func: DeleteDataAll
name: deleteSnapshots
args:
namespace: "{{ .StatefulSet.Namespace }}"
backupArtifactPrefix: "{{ .Profile.Location.Bucket }}/mongodb-backups/{{ .StatefulSet.Name }}/rs_backup"
backupInfo: "{{ .ArtifactsIn.backupInfo.KeyValue.backupIdentifier }}"
reclaimSpace: true

0 comments on commit 9d29b50

Please sign in to comment.