StackGres is operated via CRDs (here is the full CRD Referece). As such, you don’t need to install any separate tool or kubectl
plugin, and it allows you to easily create GitOps workflows. StackGres abstracts away all the internals and complexity of creating a complete Postgres cluster with high availability,
connection pooling, tuned parameters by default, monitoring, and many others; and exposes a very simple, high level interface as part of its CRDs.
The main CRD that drives Postgres cluster creation is called SGCluster. Here we will create a simple one, which will include several values and parameters by default. The next section will create a more advanced cluster, while keeping the simplicity and hiding away the Postgres expertise required.
Create the file simple.yaml
:
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
name: simple
spec:
instances: 2
postgres:
version: 'latest'
pods:
persistentVolume:
size: '5Gi'
The contents of the file should be easily understandable. A StackGres cluster with name simple
will be created in the default
namespace (none specified), with two StackGres instances (which will lead to two pods), using the latest
Postgres version –this is a handy shortcut you may use. StackGres will internally resolve this to the latest version available, and modify your CR to note the specific version used. For each pod, a 5Gi PersistentVolume will be created and attached to the pods.
kubectl apply -f simple.yaml -n stackgres
sgcluster.stackgres.io/simple created
This operation will take some around a minute. At the end, you will be able to see two instances (pods) created on the default namespace. You can track the progress with:
$ kubectl get pods -n stackgres --watch
NAME READY STATUS RESTARTS AGE
simple-0 0/6 Pending 0 5s
simple-0 0/6 Pending 0 6s
simple-0 0/6 Init:0/5 0 6s
simple-0 0/6 Init:1/5 0 26s
...
You can also check the pod creation from the Web Console:
When the process finished, you should see two pods, each with 6 containers:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
simple-0 6/6 Running 0 82s
simple-1 6/6 Running 0 36s
You should be able to see the resolved Postgres version and other details about your StackGres cluster by inspecting the created CR (Custom Resource):
kubectl describe sgcluster simple -n stackgres
Now it’s time to connect to Postgres. In its simplest form, we can do that by running the Postgres command line, psql
, in one of the containers of the pod. StackGres creates within the pods a container called postgres-util
with usual Postgres administration tools, including psql
. Since this container runs in the same pod, it uses Unix Domain Sockets to connect to the Postgres instance, and leverages Postgres peer
authentication method, that will authenticate the user via the operating system username, without requiring any password:
$ kubectl exec -it simple-0 -n stackgres -c postgres-util -- psql
psql (14.0 OnGres Inc.)
Type "help" for help.
postgres=#
Here you have your Postgres database! Feel free to play around with it. You can exit using CTRL+D or \q
.
We can actually create a database and some data. Within the previous Postgres console you can run the following commands
create database hol;
\c hol
create table hol1 as select * from generate_series(1,1000*1000);
\dt+
Now connect to the replica and verify that it is in recovery (i.e. read-only mode) and that it has replicated the database and the data:
$ kubectl exec -it simple-1 -n stackgres -c postgres-util -- psql
(note, we’re connecting to simple-1
, the replica! If simple-1
is not the replica (it may happen), see the next section below to consult with Patroni who is the replica).
postgres=# select pg_is_in_recovery();
pg_is_in_recovery
-------------------
t
(1 row)
postgres=# \c hol
You are now connected to database "hol" as user "postgres".
hol=# \dt+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+----------+-------+-------------
public | hol1 | table | postgres | 35 MB |
(1 row)
StackGres uses internall a software called Patroni to handle high availability and automated failover. It’s handled for you, you shouldn’t even care about it. Feel free to skip this section if you don’t want to know about Patorni. But if you do, you can query Patroni status easily by running patronictl
on patroni
’s container:
$ kubectl exec -it simple-0 -n stackgres -c patroni -- patronictl list
+ Cluster: simple (6979461716096839850) ---+---------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+---------------------+---------+---------+----+-----------+
| simple-0 | 192.168.59.169:7433 | Leader | running | 1 | |
| simple-1 | 192.168.12.150:7433 | Replica | running | 1 | 0 |
+----------+---------------------+---------+---------+----+-----------+
Here you can see the two nodes, that simple-0
is the leader node, the Postgres timeline and the lag. Let’s trigger now a failover. For example, let’s kill the simple-0
pod:
$ kubectl delete pod simple-0 -n stackgres
pod "simple-0" deleted
When killed, two things will happen in parallel:
simple-0
. It will attach the previous disk to this node. This operation may be quite fast (a few seconds).simple-1
) may or may not fail to see the leader renewing its lock. This is due to the timeout that govern when a leader lock expires. If the pod creation operation takes less time than the lock to expire, the new pod will take over the lock quickly enough to “hold” it, and simple-0
will remain the primary. For a few seconds, there was no primary. However, the new simple-0
node will be promoted to a new timeline (2), which will be shown in the Patroni state. If, however, the lock expired before simple-0
was re-created, simple-1
will be elected as the new leader.In any case, the situation should restore back to normal, just with a timeline increased and a potential inversion of the Leader. You may repeat this process as you wish, killing either a primary or replica pod.
$ kubectl exec -it simple-1 -n stackgres -c patroni -- patronictl list
+ Cluster: simple (6979461716096839850) ---+---------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+---------------------+---------+---------+----+-----------+
| simple-0 | 192.168.40.142:7433 | Replica | running | 2 | |
| simple-1 | 192.168.12.150:7433 | Leader | running | 2 | 0 |
+----------+---------------------+---------+---------+----+-----------+
Let’s add a new node to the Postgres cluster. Just edit the simple.yaml
file and change the number of instances from 2
to 3
:
instances: 3
and then apply it:
$ kubectl apply -f simple.yaml -n stackgres
sgcluster.stackgres.io/simple configured
In a few seconds, a third node would have been spinned up:
$ kubectl get pods -n stackgres --watch
NAME READY STATUS RESTARTS AGE
simple-0 6/6 Running 0 55m
simple-1 6/6 Running 0 74m
simple-2 6/6 Running 0 61s
And Patroni should also reflect its status as a new replica:
$ kubectl exec -it simple-0 -n stackgres -c patroni -- patronictl list
+ Cluster: simple (6979461716096839850) ---+---------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+---------------------+---------+---------+----+-----------+
| simple-0 | 192.168.40.142:7433 | Leader | running | 2 | |
| simple-1 | 192.168.12.150:7433 | Replica | running | 2 | 0 |
| simple-2 | 192.168.71.214:7433 | Replica | running | 2 | 0 |
+----------+---------------------+---------+---------+----+-----------+
Let’s now delete the cluster for now, we will create more advanced cluster and configurations in the next section.
$ kubectl delete sgcluster simple -n stackgres
sgcluster.stackgres.io "simple" deleted