Welcome to this guide on how to run immudb SQL and Key-Value Database on Docker / Kubernetes. Immudb can be defined as a relational (SQL) database and a key-value store with a built-in cryptographic proof and verification. This is an open-source product developed by CodeNotary, released under the Apache License 2.0.
In traditional databases, logs and transactions are mutable and you can’t be sure if the data has been manipulated. One of the outstanding features of Immudb is that it is immutable. In other words, it allows one to add and replace data but leaves old data in the disk undeleted. By doing so, data from the past can still be read. It is also lightweight and can handle millions of transactions per second.
Immudb is used in the following areas:
- Storing CI/CD recipes to protect build and deployment pipelines
- Storing public certificates
- Storing log streams (i. e. audit logs) tamperproof
- Recording the location where fish was found aboard fishing trawlers
- Used as additional hash storage for digital objects checksums
- Storing every update to sensitive database fields such as credit card or bank account data of an existing application database
- Storing the last known positions of submarines
Step 1 – Run immudb SQL and Key-Value Database on Docker / Kubernetes
Let’s dive in a learn how to run immudb SQL and Key-Value Database on Docker / Kubernetes.
When immudb is installed, the following defaults are used:
- User: immu
- Group: immu
- Configuration: /etc/immudb
- Data: /var/lib/immudb
- Logs: /var/log/immudb
- Service Port: 3322 (immudb), 3323 (immugw)
- Prometheus Port: 9497
- Web console: 8080
Option 1 – Run immudb SQL and Key-Value Database on Docker
To be able to run the immudb database on docker, you need to have the Docker Engine installed. You can use the below guide to install Docker on your system.
Add your user to the Docker group:
sudo usermod -aG docker $USER
newgrp docker
Ensure the docker service has been started and enabled:
sudo systemctl start docker && sudo systemctl enable docker
Create Persistent Volumes
We will create persistent data paths for the immudb container:
sudo mkdir -p /opt/immudb/data
sudo mkdir /opt/immudb/config
sudo mkdir /opt/immudb/logs
Set the required permissions:
sudo chmod -R 777 /opt/immudb
Configure SELinux on Rhel-based systems as below:
sudo setenforce 0
sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config
Now run the immudb database in a docker container with ports 3322 and 9497 exposed as shown.
docker run -d -it \
--name immudb \
-p 30842:3322 \
-p 30077:9497 \
-p 32641:8080 \
-v /opt/immudb/data:/var/lib/immudb \
-v /opt/immudb/config:/etc/immudb \
-v /opt/immudb/logs:/var/log/immudb \
codenotary/immudb:latest
Sample execution:
Unable to find image 'codenotary/immudb:latest' locally
latest: Pulling from codenotary/immudb
b94a3daa6831: Pull complete
4a67363f400e: Pull complete
256bed11b5e1: Pull complete
fd6bfecfcf6f: Pull complete
0c76a5976060: Pull complete
Digest: sha256:9f5f1716237825cec75a2ac71f5afaf31b057a0db31f0b412513ba5cec30b53d
Status: Downloaded newer image for codenotary/immudb:latest
23bdedc141d56019c240889e4b8025624bb9edafedbd45d7e60af300e12837d2
Check if the container is running:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
23bdedc141d5 codenotary/immudb:latest "/usr/sbin/immudb" 35 seconds ago Up 34 seconds (healthy) 5432/tcp, 0.0.0.0:30842->3322/tcp, :::30842->3322/tcp, 0.0.0.0:32641->8080/tcp, :::32641->8080/tcp, 0.0.0.0:30077->9497/tcp, :::30077->9497/tcp immudb
Option 2 – Run immudb SQL and Key-Value Database on Kubernetes
You first need to have a Kubernetes cluster step up. The below guides can be used to set up a Kubernetes cluster.
- Install Kubernetes Cluster on Rocky Linux 8 with Kubeadm & CRI-O
- Install Kubernetes Cluster on Ubuntu using K3s
- Deploy Kubernetes Cluster on Linux With k0s
- Run Kubernetes on Debian with Minikube
Install kubectl with the command:
curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin
To access the cluster using kubectl, export the certificate:
# For k0s
export KUBECONFIG=/var/lib/k0s/pki/admin.conf
#1. Create a StorageClass
To be able to create a Persistent Volume for immudb, we will begin by creating a storage class:
vim storageClass.yml
Add the lines below to the file:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: my-local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
Apply the manifest:
$ kubectl create -f storageClass.yml
storageclass.storage.k8s.io/my-local-storage created
#2. Create Local Persistent Volume
Using, the created storage class, we will create a PV:
vim local-pv.yml
The file will contain the lines below:
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-local-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: my-local-storage
local:
path: /opt/immudb
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node1
This requires the path available on the specified node(node1). Create the path as shown:
sudo mkdir /opt/immudb
sudo chcon -Rt svirt_sandbox_file_t /opt/immudb
sudo chmod 777 /opt/immudb
Now apply your manifest:
kubectl create -f local-pv.yml
#3. Create a Persistent Volume Claim
Create a PVC using the storageClass:
vim local-pvc.yml
Paste the lines below:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
# This name uniquely identifies the PVC. This is used in deployment.
name: immudb-pvc-claim
spec:
# Read more about access modes here: http://kubernetes.io/docs/user-guide/persistent-volumes/#access-modes
storageClassName: my-local-storage
accessModes:
# The volume is mounted as read-write by Multiple nodes
- ReadWriteMany
resources:
# This is the request for storage. Should be available in the cluster.
requests:
storage: 5Gi
Apply the manifest:
kubectl create -f local-pvc.yml
Check if the PV is available:
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
my-local-pv 5Gi RWX Retain Available my-local-storage 20s
#4. Create the Immudb Pod
We will create the Immudb pod and use the created PVC to persist its data.
vim immudb-pod.yml
The file will have the lines:
apiVersion: apps/v1
kind: Deployment
metadata:
name: immudb
spec:
selector:
matchLabels:
app: immudb
template:
metadata:
labels:
app: immudb
spec:
containers:
- name: immudb
image: codenotary/immudb:latest
volumeMounts:
- name: data
mountPath: /var/lib/immudb
ports:
- containerPort: 3322
- containerPort: 9497
- containerPort: 8080
volumes:
- name: data
persistentVolumeClaim:
claimName: immudb-pvc-claim
Apply the manifest:
kubectl create -f immudb-pod.yml
Verify if the pod is running:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
immudb-67c7df5747-wz8sc 1/1 Running 0 9s
Also, the PV should be bound:
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
my-local-pv 5Gi RWX Retain Bound default/immudb-pvc-claim my-local-storage 11m
Now create the service file:
vim immudb-svc.yml
We will set the service type as LoadBalancer, though you can still use NodePort as per your preferences.
apiVersion: v1
kind: Service
metadata:
# This name uniquely identifies the service
name: immudb-service
spec:
type: LoadBalancer
ports:
- name: immudb
port: 3322
targetPort: 3322
protocol: TCP
- name: prometheus
port: 9497
targetPort: 9497
protocol: TCP
- name: web
port: 8080
targetPort: 8080
protocol: TCP
selector:
# Looks for labels `app:immudb` in the namespace and applies the spec
app: immudb
Create the service:
kubectl create -f immudb-svc.yml
Verify if the service is running:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
immudb-service LoadBalancer 10.108.175.107 <pending> 3322:30842/TCP,9497:30077/TCP,8080:32641/TCP 4s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 25h
Step 2 – Accessing the Immudb Web Console
The immudb web console is available on port 8080, proceed and access it via HTTP using the exposed port. In this guide, we have exposed the services as below:
- Web console 8080 -> 32641
- Immudb 3322 -> 30842
- Prometheus 9497 -> 30077
Allow these ports through the firewall:
##For UFW
sudo ufw allow 30842
sudo ufw allow 30077
sudo ufw allow 32641
##For Firewalld
sudo firewall-cmd --add-port=30842/tcp --permanent
sudo firewall-cmd --add-port=30077/tcp --permanent
sudo firewall-cmd --add-port=32641/tcp --permanent
sudo firewall-cmd --reload
Now access the Immudb web console using the URL http://IP_address:32641
The default login credentials are:
Username: immudb
Password: immudb
Here, you can easily manage your database. To create a database, navigate to the Databases tab.
Create a database and provide the db name.
You can also run queries.
Step 3 – Install Immuclient on Linux
This is a client tool used to manage the Immudb. You can easily create, update users/databases, perform backups and restores e.t.c
The latest release is available on Github. Download the latest release file as below:
VER=$(curl -s https://api.github.com/repos/codenotary/immudb/releases/latest|grep tag_name|cut -d '"' -f 4|sed 's/v//')
wget https://github.com/codenotary/immudb/releases/download/v${VER}/immuclient-v${VER}-linux-amd64
Rename the file as shown and make it executable:
mv immuclient-v${VER}-linux-amd64 immuclient
chmod +x immuclient
Now the client with the command:
$ ./immuclient -a 192.168.205.11 -p 30842
immuclient>login immudb
Password: immudb
Successfully logged in
immudb user has the default password: please change it to ensure proper security
immuclient>
Remember to provide the Immudb’s IP address and port appopriately.
Obtaining help:
immuclient> help
login Login using the specified username and password args: username
logout
use Select database args: databasename
safeget Get and verify item having the specified key args: key
get Get item having the specified key args: key
gettx Return a tx by id
....
Step 4 – Using Immuclient on Linux
The Immuclient can be used to run queries. Some of the common queries are:
- Create Tables to an existing database (the default database is defaultdb)
immuclient>use testdb
Now using testdb
immuclient> exec CREATE TABLE people(id INTEGER, name VARCHAR, salary INTEGER, PRIMARY KEY id);
- Insert data in the table:
immuclient> exec UPSERT INTO people(id, name, salary) VALUES (1, 'Klinsmann', 10000);
Updated rows: 1
immuclient> exec UPSERT INTO people(id, name, salary) VALUES (2, 'Mutai', 30000);
Updated rows: 1
- View the tables
immuclient> query SELECT id, name, salary FROM people;
+--------------------+----------------------+------------------------+
| (TESTDB PEOPLE ID) | (TESTDB PEOPLE NAME) | (TESTDB PEOPLE SALARY) |
+--------------------+----------------------+------------------------+
| 1 | "Klinsmann" | 10000 |
| 2 | "Mutai" | 30000 |
+--------------------+----------------------+------------------------+
- Overwrite values. For example the value for Klinsmann
immuclient> exec UPSERT INTO people(id, name, salary) VALUES (1, 'Klinsmann', 20000);
Updated rows: 1
immuclient> query SELECT id, name, salary FROM people;
+--------------------+----------------------+------------------------+
| (TESTDB PEOPLE ID) | (TESTDB PEOPLE NAME) | (TESTDB PEOPLE SALARY) |
+--------------------+----------------------+------------------------+
| 1 | "Klinsmann" | 20000 |
| 2 | "Mutai" | 30000 |
+--------------------+----------------------+------------------------+
Time Travel on Immudb
One of the amazing features associated with immudb is preserving history. Let us see if this is true. We edited the salary for Klinsmann to 20000. This has been written as the current value.
immuclient> query SELECT id, name, salary FROM people WHERE name='Klinsmann';
+--------------------+----------------------+------------------------+
| (TESTDB PEOPLE ID) | (TESTDB PEOPLE NAME) | (TESTDB PEOPLE SALARY) |
+--------------------+----------------------+------------------------+
| 1 | "Klinsmann" | 20000 |
+--------------------+----------------------+------------------------+
Get the current transaction ID:
immuclient> current
database: testdb
txID: 5
hash: 29e345077eaaf204a03cf8ef39326cc19f49bafd492594f0bcfe34b6c47fcdc4
Get the value before the update:
immuclient> query SELECT id, name, salary FROM people BEFORE TX 5 WHERE name='Klinsmann';
Sample Output:
You can also get values before anything was inserted into the table:
immuclient> query SELECT id, name, salary FROM people BEFORE TX 2 WHERE name='Klinsmann';
+--------------------+----------------------+------------------------+
| (TESTDB PEOPLE ID) | (TESTDB PEOPLE NAME) | (TESTDB PEOPLE SALARY) |
+--------------------+----------------------+------------------------+
+--------------------+----------------------+------------------------+
Using the information, you can perform comparisons and see how data has changed over time:
immuclient> query SELECT peoplenow.id, peoplenow.name, peoplethen.salary, peoplenow.salary FROM people BEFORE TX 5 AS peoplethen INNER JOIN people AS peoplenow ON peoplenow.id=peoplethen.id;
Sample Output:
Logout and exit:
immuclient>logout
Successfully logged out
immuclient>exit
Step 5 – Using immudb SDKs
SDKs make it comfortable to communicate with the server using your preferred programming language. There are several SDKs. They include;
- Go
- Java
- Node
- Js
- Ruby
- Python
Here, I will demonstrate how to use Python. Install the immudb client for Python as below:
Install Python and PIP:
##On Debian/Ubuntu
sudo apt install python3 python3-pip git libsasl2-dev libldap2-dev libssl-dev libxml2-dev libxslt1-dev libxmlsec1-dev libffi-dev pkg-config apt-transport-https virtualenv python3-venv build-essential libmariadb-dev python3-flask -y
##On RHEL/CentOS/Rocky Linux 8/Alma Linux 8
sudo yum install python3 python3-pip git python3-devel
sudo yum install epel-release
sudo yum group install "Development Tools"
Use PIP to install the immudb client for Python.
sudo pip3 install --upgrade pip
sudo pip3 install immudb-py
Now create a sample Python script:
vim setup.py
Add the following lines replacing them appropriately. Create the connection to port 3322, or the port to which the service has been exposed(30842):
#!/usr/bin/env python
from immudb.client import ImmudbClient
ic = ImmudbClient("192.168.205.4:30842")
ic.login(username="immudb", password="immudb")
key = "Hello".encode('utf8')
value = "Immutable World!".encode('utf8')
# set a key/value pair
ic.set(key, value)
# reads back the value
readback = ic.get(key)
saved_value = readback.value.decode('utf8')
print("Hello", saved_value)
Run the script:
# python3 setup.py
Hello Immutable World!
The end!
Books For Learning Kubernetes Administration:
Closing Remarks
At this point, you can use the installed immudb SQL and Key-Value Database on Docker / Kubernetes to store your sensitive data. I hope this was helpful.
See more: