Deploy FerretDB in Kubernetes using KubeDB Managed Postgres

KubeDB – a popular Kubernetes database management solution – now provides support for FerretDB.
FerretDB is an open source document database that adds MongoDB compatibility to PostgreSQL. It lets you use your familiar MongoDB syntax and commands with your data stored in PostgreSQL backend.
Over the past few years, Kubernetes has become a popular option for deploying production-ready databases. Tools like KubeDB simplify the management and automation of database tasks in Kubernetes, including provisioning, monitoring, upgrading, automated scaling and backup, and failure detection.
This blog post will demonstrate how to run and deploy FerretDB in Kubernetes using KubeDB.
Prerequisites
Ensure to have the following set up.
- Kubernetes cluster (Minikube or Docker Desktop's Kubernetes, or any cloud-based service ypu prefer)
 - Get AppsCode License (get cluster ID)
 - Helm
 kubectlmongosh
Get cluster ID
To get the AppsCode license, you need the cluster ID. Run this command to get the cluster ID.
kubectl get ns kube-system -o jsonpath='{.metadata.uid}'
Install KubeDB
Use Helm to install KubeDB:
helm install kubedb oci://ghcr.io/appscode-charts/kubedb \
 --version v2024.2.14 \
 --namespace kubedb --create-namespace \
 --set-file global.license=/path/to/the/license.txt \
 --set global.featureGates.FerretDB=true \
 --wait --burst-limit=10000 --debug
Be sure to include the AppsCode license path for the KubeDB installation.
Verify the installation:
$ kubectl get pods --all-namespaces -l "app.kubernetes.io/instance=kubedb"
NAMESPACE   NAME                                            READY   STATUS    RESTARTS   AGE
kubedb      kubedb-kubedb-autoscaler-5c97c8c7f9-lw64s       1/1     Running   0          11m
kubedb      kubedb-kubedb-ops-manager-7b8fc4d7bf-28qk4      1/1     Running   0          11m
kubedb      kubedb-kubedb-provisioner-6c89ddd5d8-fw24w      1/1     Running   0          11m
kubedb      kubedb-kubedb-webhook-server-6fc6c8b44f-pwdvr   1/1     Running   0          11m
kubedb      kubedb-sidekick-86c64c8f59-gvzd8                1/1     Running   0          11m
KubeDB provides several installed CRD Groups including FerretDB.
Run kubectl get crd -l app.kubernetes.io/name=kubedb command to list them.
Deploy FerretDB with KubeDB Managed PostgreSQL
Create a namespace for all the FerretDB components.
kubectl create namespace ferretdemo
Next, create the FerretDB Custom Resource YAML:
apiVersion: kubedb.com/v1alpha2
kind: FerretDB
metadata:
  name: ferret
  namespace: ferretdemo
spec:
  version: '1.18.0'
  storageType: Durable
  storage:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 1Gi
  backend:
    externallyManaged: false
  terminationPolicy: WipeOut
The YAML file will create a ferret resource in the ferretdemo namespace using KubeDB.
At the moment, KubeDB only supports version FerretDB v1.18.0.
Check here for recent versions
Save the config as ferret.yaml and apply it.
kubectl apply -f ferret.yaml
Once it is applied, the FerretDB resource is deployed with the all its objects.
Get the objects in the ferretdemo namespace:
$ kubectl get all -n ferretdemo
NAME                              READY   STATUS    RESTARTS   AGE
pod/ferret-0                      1/1     Running   0          4m42s
pod/ferret-pg-backend-0           2/2     Running   0          5m15s
pod/ferret-pg-backend-1           2/2     Running   0          5m10s
pod/ferret-pg-backend-arbiter-0   1/1     Running   0          5m1s
NAME                                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
service/ferret                      ClusterIP   10.111.78.151   <none>        27017/TCP                    5m18s
service/ferret-pg-backend           ClusterIP   10.99.57.62     <none>        5432/TCP,2379/TCP            5m18s
service/ferret-pg-backend-pods      ClusterIP   None            <none>        5432/TCP,2380/TCP,2379/TCP   5m18s
service/ferret-pg-backend-standby   ClusterIP   10.108.28.132   <none>        5432/TCP                     5m18s
NAME                                         READY   AGE
statefulset.apps/ferret                      1/1     4m42s
statefulset.apps/ferret-pg-backend           2/2     5m15s
statefulset.apps/ferret-pg-backend-arbiter   1/1     5m1s
NAME                                                   TYPE                  VERSION   AGE
appbinding.appcatalog.appscode.com/ferret              kubedb.com/ferretdb   1.18.0    4m42s
appbinding.appcatalog.appscode.com/ferret-pg-backend   kubedb.com/postgres   13.13     5m1s
NAME                                    VERSION   STATUS   AGE
postgres.kubedb.com/ferret-pg-backend   13.13     Ready    5m18s
To be sure the ferret resource is ready, run this command:
$ kubectl get ferretdb -n ferretdemo ferret
NAME     NAMESPACE    VERSION   STATUS   AGE
ferret   ferretdemo   1.18.0    Ready    9m6s
port-forward the Service
Before port forwarding, list the services created by KubeDB.
$ kubectl get service -n ferretdemo | grep ferret
ferret                      ClusterIP   10.111.78.151   <none>        27017/TCP                    11m
ferret-pg-backend           ClusterIP   10.99.57.62     <none>        5432/TCP,2379/TCP            11m
ferret-pg-backend-pods      ClusterIP   None            <none>        5432/TCP,2380/TCP,2379/TCP   11m
ferret-pg-backend-standby   ClusterIP   10.108.28.132   <none>        5432/TCP                     11m
Next, forward the ferret Service to port 27017 on your local machine.
kubectl port-forward -n ferretdemo svc/ferret 27017
Get FerretDB credentials in Secret
You need the FerretDB credentials before connecting via mongosh.
KubeDB creates and stores the ferret Service credentials as a Secret.
To get the details, run this command:
kubectl get secret -n ferretdemo | grep ferret
Using ferret-pg-backend-auth, get the user credentials.
echo $(kubectl get secret -n ferretdemo ferret-pg-backend-auth -o jsonpath='{.data.username}' | base64 -d)
echo $(kubectl get secret -n ferretdemo ferret-pg-backend-auth -o jsonpath='{.data.password}' | base64 -d)
This will print out the username and password credentials for the instance.
Connect to FerretDB via mongosh
Using the credentials, connect to FerretDB via mongosh using this format:
mongosh mongodb://<username>:<password>@<host>:27017/ferretdb?authMechanism=PLAIN'
Connect via mongosh:
mongosh 'mongodb://postgres:p.i~glw7q9mdbpQ2@localhost:27017/ferretdb?authMechanism=PLAIN'
Current Mongosh Log ID: 662699b8fa65a75337cb3ec7
Connecting to:  mongodb://<credentials>@localhost:27017/ferretdb?authMechanism=PLAIN&directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.2
Using MongoDB:    7.0.42
Using Mongosh:    2.2.2
mongosh 2.2.4 is available for download: https://www.mongodb.com/try/download/shell
For mongosh info see: https://docs.mongodb.com/mongodb-shell/
------
   The server generated these startup warnings when booting
   2024-04-22T17:09:13.234Z: Powered by FerretDB v1.18.0 and PostgreSQL 13.13 on aarch64-unknown-linux-musl, compiled by gcc.
   2024-04-22T17:09:13.235Z: Please star us on GitHub: https://github.com/FerretDB/FerretDB.
   2024-04-22T17:09:13.235Z: The telemetry state is undecided.
   2024-04-22T17:09:13.235Z: Read more about FerretDB telemetry and how to opt out at https://beacon.ferretdb.io.
------
ferretdb>
Let's run some commands in the database.
Start by inserting a document record into a weather collection as shown below.
db.weather.insertMany([
    {
        date: new Date("2024-04-22"),
        location: {
            city: "New York",
            country: "USA",
            coordinates: { lat: 40.7128, lon: -74.0060 }
        },
        weather: {
            temperature: 18,
            conditions: "Cloudy",
            wind_speed: 12,
            humidity: 80
        },
        remarks: "Possible light rain in the evening."
    }
]);
Suppose you want to update the humidity level in New York where the wind speed was more than 10 km/h:
ferretdb> db.weather.updateMany(
...     { "location.city": "New York", "weather.wind_speed": { $gt: 10 } },
...     { $set: { "weather.humidity": 85 } }
... );
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
ferretdb> db.weather.find()
[
  {
    _id: ObjectId('66278976fba61a5fec8bad82'),
    date: ISODate('2024-04-22T00:00:00.000Z'),
    location: {
      city: 'New York',
      country: 'USA',
      coordinates: { lat: 40.7128, lon: -74.006 }
    },
    weather: {
      temperature: 18,
      conditions: 'Cloudy',
      wind_speed: 12,
      humidity: 85
    },
    remarks: 'Possible light rain in the evening.'
  }
]
FerretDB stores the data in the ferret-pg-backend PostgreSQL using mongosh.
Let's exec into the Postgres database to view the record.
% kubectl exec -it -n ferretdemo ferret-pg-backend-0 -- bash -c "psql -d ferretdb"
In Postgres, set the SEARCH_PATH to ferretdb and list the record in the weather_36404793 table.
ferretdb=# set SEARCH_PATH to ferretdb;
SET
ferretdb=# \dt
                     List of relations
  Schema  |            Name             | Type  |  Owner
----------+-----------------------------+-------+----------
 ferretdb | _ferretdb_database_metadata | table | postgres
 ferretdb | weather_36404793            | table | postgres
(2 rows)
ferretdb=# SELECT * FROM weather_36404793;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            _jsonb
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 {"$s": {"p": {"_id": {"t": "objectId"}, "date": {"t": "date"}, "remarks": {"t": "string"}, "weather": {"t": "object", "$s": {"p": {"humidity": {"t": "int"}, "conditions": {"t": "string"}, "wind_speed": {"t": "int"}, "temperature": {"t": "int"}}, "$k": ["temperature", "conditions", "wind_speed", "humidity"]}}, "location": {"t": "object", "$s": {"p": {"city": {"t": "string"}, "country": {"t": "string"}, "coordinates": {"t": "object", "$s": {"p": {"lat": {"t": "double"}, "lon": {"t": "double"}}, "$k": ["lat", "lon"]}}}, "$k": ["city", "country", "coordinates"]}}}, "$k": ["_id", "date", "location", "weather", "remarks"]}, "_id": "66278976fba61a5fec8bad82", "date": 1713744000000, "remarks": "Possible light rain in the evening.", "weather": {"humidity": 85, "conditions": "Cloudy", "wind_speed": 12, "temperature": 18}, "location": {"city": "New York", "country": "USA", "coordinates": {"lat": 40.7128, "lon": -74.006}}}
(1 row)
ferretdb=#
Deploy FerretDB with an externally managed PostgreSQL
So far, we have shown how to set up FerretDB using KubeDB Managed PostgreSQL. However, if you prefer an external PostgreSQL server as your backend, this is entirely possible.
The YAML configuration provided below outlines how to integrate FerretDB with a PostgreSQL instance managed externally.
apiVersion: kubedb.com/v1alpha2
kind: FerretDB
metadata:
  name: ferretdb-external
  namespace: ferretdemo
spec:
  version: "1.18.0"
  authSecret:
    externallyManaged: true
    name: ha-postgres-auth
  storageType: Durable
  storage:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 1Gi
  backend:
    externallyManaged: true
    postgres:
      service:
        name: ha-postgres
        namespace: ferretdemo
        pgPort: 5432
  terminationPolicy: WipeOut
spec.postgres.service contains the service details for the user's external PostgreSQL that exists within the cluster.
spec.authSecret.name refers to the name of the authentication secret for accessing the user's external PostgreSQL database.
Support
This blog post shows you how to deploy a FerretDB instance in Kubernetes using KubeDB. Ensure to experiment and try it out. If you have any questions or just want to contact us, please do so via the FerretDB Slack channel here.
