Run Metis on top of StackGres

In this how-to guide, we’ll walk you through the whole process of setting up Metis on a Kubernetes cluster using StackGres. This guide is also available as a video

This post has been published with explicit permission from Metis. It’s a copy of an article originally published on Metis Data’s website on February 12, 2024.

Setting Up Metis On Top Of StackGres

We’ll need to install the Kubernetes cluster, then install StackGres, then configure the StackGres cluster, and then configure Metis. Instructions below are for Amazon Linux 2 x86_64 but will work the same way on other operating systems like Mac OS and Windows (with slight syntax changes for PowerShell or other shells), and on ARM64 architecture.

Installing Kubernetes With Minikube

Before installing StackGres, you will need a running Kubernetes cluster and the usual command line tools kubectl and Helm. Please refer to the respective installation pages if you don’t have these tools. As for Kubernetes, if you don’t have one you can try easily with minikube. It can be installed like this:

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

This should give you a running single-node cluster in seconds (depending on your Internet connection speed). You can install it in other operating systems the way it’s described in the docs.

Keep in mind that minikube uses Docker behind the scenes. It works natively in Linux, and with a virtual machine in MacOS and Windows. Refer to installation instructions for Docker Desktop to learn how to install it on your machine.

You can now create the minikube cluster with:

This should give you the output similar to this:

* minikube v1.32.0 on Amazon 2
* Automatically selected the docker driver. Other choices: none, ssh
* Using Docker driver with root privileges
* Starting control plane node minikube in cluster minikube
* Pulling base image ...
* Creating docker container (CPUs=2, Memory=3900MB) ...
* Preparing Kubernetes v1.28.3 on Docker 24.0.7 ...
  - Generating certificates and keys ...
  - Booting up control plane ...
  - Configuring RBAC rules ...
* Configuring bridge CNI (Container Networking Interface) ...
* Verifying Kubernetes components...
  - Using image gcr.io/k8s-minikube/storage-provisioner:v5
* Enabled addons: storage-provisioner, default-storageclass
* Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

When you’re done with the examples, you can easily delete the cluster with:

Let’s now install StackGres.

Installing StackGres

The best way to install StackGres is through the official Helm chart. You can install it in this way:

curl -LO https://get.helm.sh/helm-v3.14.0-linux-amd64.tar.gz
tar -zxvf helm-v3.14.0-linux-amd64.tar.gz
sudo mv linux-amd64/helm /usr/local/bin/helm

Follow this page to install Helm on your machine.

For our particular setup, we use the following Helm commands:

helm repo add stackgres-charts https://stackgres.io/downloads/stackgres-k8s/stackgres/helm/
helm install --create-namespace --namespace stackgres stackgres-operator stackgres-charts/stackgres-operator

To confirm that the operator is running while also waiting for setup to complete, run the following commands:

kubectl wait -n stackgres deployment -l group=stackgres.io --for=condition=Available
kubectl get pods -n stackgres -l group=stackgres.io

As you run the first kubectl command, it should wait for the successful deployment, and the second command will list the pods running in the stackgres namespace.

NAME                                READY   STATUS    RESTARTS        AGE
stackgres-operator-57bff75d-xlnfp   1/1     Running   1 (2m36s ago)   2m58s

Creating a StackGres Cluster

Here, we’ll create an SGCluster configured to fit Metis requirements. We’re going to use some resources created inline. Metis requires the pg_stat_statements extension, a separate user, and permissions to monitor databases.

Let’s create a password for the user that will get initialized based on the SQL command:

kubectl -n stackgres create secret generic metis-user-password-secret --from-literal=metis-create-user-sql="create user metis password 'admin123'"

Let’s now create a script that will create a database named metis and a user metis:

cat << EOF | kubectl apply -f -
apiVersion: stackgres.io/v1
kind: SGScript
metadata:
  namespace: stackgres
  name: cluster-scripts
spec:
  scripts:
  - name: create-metis-user
    scriptFrom:
      secretKeyRef:
        name: metis-user-password-secret
        key: metis-create-user-sql
  - name: grant-monitor-to-metis
    script: |
            GRANT pg_monitor TO metis
  - name: create-metis-database
    script: |
            CREATE DATABASE metis OWNER metis
  - name: grant-conntent-to-metis-database
    script: |
            GRANT CONNECT ON DATABASE metis TO metis
  - name: create-pgstatstatements-extension
    database: metis
    script: |
            CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
EOF

We can see the script has five parts. First, we create the user with a password and store the password in the Kubernetes secret. Next, we give pg_monitor permissions to the user. Next, we create the database. Then, we let the user connect to the database. Finally, we create the pg_stat_statements extension in the metis database.

We are now ready to create the Postgres cluster:

cat << EOF | kubectl apply -f -
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
  namespace: stackgres
  name: cluster
spec:
  postgres:
    version: '15.0'
  instances: 1
  pods:
    persistentVolume:
      size: '5Gi'
  managedSql:
    scripts:
    - sgScript: cluster-scripts
EOF

It should take a few seconds to a few minutes for the cluster to be up and running:

kubectl get pods -n stackgres
NAME       READY   STATUS    RESTARTS AGE
cluster-0  6/6     Running   0        74s

Likewise, a database named metis must exist and be owned by the same user:

kubectl -n stackgres exec -it cluster-0 -c postgres-util -- psql -l metis
    	List of databases
   Name	|  Owner   | Encoding | Collate |  Ctype  | ICU Locale | Locale Provider |   Access privileges
--------+----------+----------+---------+---------+------------+-----------------+--------------------
 metis 	| metis    | UTF8     | C.UTF-8 | C.UTF-8 |            | libc            | =Tc/metis
 ...

Deploying Metis

We can now add monitoring of the database to Metis. Go to Metis and click Deploy:

Metis Deploy

Select Postgres and click Next:

Metis Select Vendor

Metis instructs us how to configure the user and grant necessary permissions. We already did that. Click Next.

Metis Host Connection

Metis asks for the connection string now:

Metis Configuration

Let’s extract the server name:

kubectl get svc -n stackgres
NAME                TYPE          CLUSTER-IP      EXTERNAL-IP                           PORT(S)             AGE
cluster             ClusterIP     10.99.23.208    <none>                                5432/TCP,5433/TCP   2m34s
cluster-config      ClusterIP     None            <none>                                <none>              2m34s
cluster-primary     ExternalName  <none>          cluster.stackgres.svc.cluster.local   <none>              2m34s
cluster-replicas    ClusterIP     10.103.149.23   <none>                                5432/TCP,5433/TCP   2m34s
cluster-rest        ClusterIP     10.104.238.166  <none>                                8008/TCP            2m34s
stackgres-operator  ClusterIP     10.108.192.220  <none>                                443/TCP             34m
stackgres-restapi   ClusterIP     10.103.146.192  <none>                                443/TCP             33m

The service name is cluster.stackgres.svc.cluster.local. The final URL should be like this:

postgresql://metis:admin123@cluster.stackgres.svc.cluster.local:5432/postgres?sslmode=disable

Metis Connection String

Click Next. Finally, click on Helm and copy the script to deploy Metis.

Metis Helm

Extend the script with --namespace stackgres and run it. Your API key will be different as its a specific API key to be used with your project:

helm repo add metis https://charts.metisdata.io
helm install mmc metis/mmc-chart --namespace stackgres --set API_KEY=YOURAPIKEY --set-json CONNECTION_STRING='[{ "uri":"postgresql://metis:admin123@cluster.stackgres.svc.cluster.local:5432/metis?sslmode=disable"  }]'

When you execute the script, you should get the following:

NAME: mmc
LAST DEPLOYED: Tue Jan 23 11:24:14 2024
NAMESPACE: stackgres
STATUS: deployed
REVISION: 1
TEST SUITE: None

We can verify that it works with:

kubectl get pods -n stackgres
NAME                             	READY   STATUS	RESTARTS  	AGE
cluster-0                        	6/6 	Running   0         	25m
mmc-deployment-58dbb76fcc-vnnd5  	1/1 	Running   0         	13s

You can now go to Metis Monitoring and see that the new host has been added:

Metis Monitoring

When you click on the host, you should see the properties and databases:

Metis Database

You can also click on database metis and get all the details:

Metis Observability

Notice that many charts are empty as it’s a completely new database.

Monitoring Live Database Activity

Let’s now add some data and run some queries in the database.

kubectl -n stackgres exec -it cluster-0 -c postgres-util -- psql --username=metis --dbname=metis -c "CREATE TABLE orders(region VARCHAR(100), amount INT, product VARCHAR(100))"
kubectl -n stackgres exec -it cluster-0 -c postgres-util -- psql --username=metis --dbname=metis -c "INSERT INTO orders(region, amount, product)VALUES ('EU', 10, 'Product1'), ('EU', 20, 'Product2'), ('US', 1, 'XYZ'), ('JP', 10, 'ABC')"
kubectl -n stackgres exec -it cluster-0 -c postgres-util -- psql --username=metis --dbname=metis -c "SELECT region, product, (SELECT SUM(amount) AS total_sales FROM orders AS o2 WHERE o2.region = o.region GROUP BY region)
FROM orders AS o
WHERE region IN (
    SELECT region
    FROM orders
    GROUP BY region
    HAVING SUM(amount) > (
   	 SELECT SUM(amount)
   	 FROM orders
    ) / 10
)"

We create table orders, insert some data, and then run a query that extracts the rows with some aggregation. This is the output:

region  | product  | total_sales
--------+----------+-------------
 EU     | Product1 |        30
 EU     | Product2 |        30
 JP     | ABC      |        10
(3 rows)

You can now go to Metis and check the Table Sizes widget. It shows the orders table:

Metis Table Sizes

You can also go to Top Queries widget and notice that new queries are flowing to the database:

Metis Top Queries

Metis captures the live activity of your database and can suggest how to improve things.