In the last post we’ve created a very simple three node PostgreSQL cluster using CloudNativePG and this was really easy to do. While this is great to get started quickly, you usually want to have more control on how PostgreSQL is initialized and configured. In this post we’ll look at bootstrapping the cluster and what options you have for controlling it, when you want to start with an empty cluster.
What we got in the last post was mostly a default configuration. Lets start from scratch by deleting the existing cluster:
minicube@micro-minicube:~> kubectl get cluster -A
NAMESPACE NAME AGE INSTANCES READY STATUS PRIMARY
default cluster-example 26h 3 3 Cluster in healthy state cluster-example-1
minicube@micro-minicube:~> kubectl delete cluster cluster-example
cluster.postgresql.cnpg.io "cluster-example" deleted
minicube@micro-minicube:~> kubectl get cluster -A
No resources found
minicube@micro-minicube:~> kubectl get pods
No resources found in default namespace.
minicube@micro-minicube:~>
Instead of using the minimal configuration from the last post, we’ll now use this one to describe our cluster:
minicube@micro-minicube:~> cat pg.yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: my-pg-cluster
spec:
instances: 3
bootstrap:
initdb:
database: db1
owner: db1
dataChecksums: true
walSegmentSize: 32
storage:
size: 1Gi
This is what changed (all under the “initdb” section):
- The name of the cluster
- We want a new database “db1” to be created and this database should be owned by a new user called “db1”
- We want data checksum to be enabled
- Finally we want to change the WAL segment size from the default of 16MB to 32MB
Once we apply this, we of course get the same picture as before for the pods and the services:
minicube@micro-minicube:~> kubectl apply -f pg.yaml
cluster.postgresql.cnpg.io/my-pg-cluster created
minicube@micro-minicube:~> kubectl get pods
NAME READY STATUS RESTARTS AGE
my-pg-cluster-1 1/1 Running 0 119s
my-pg-cluster-2 1/1 Running 0 97s
my-pg-cluster-3 1/1 Running 0 78s
Lets take one of these pods, start a shell and check what was created:
minicube@micro-minicube:~> kubectl exec my-pg-cluster-2 -i -t -- /bin/bash
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
postgres@my-pg-cluster-2:/$ psql
psql (16.2 (Debian 16.2-1.pgdg110+2))
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
-----------+----------+----------+-----------------+---------+-------+------------+-----------+-----------------------
db1 | db1 | UTF8 | libc | C | C | | |
postgres | postgres | UTF8 | libc | C | C | | |
template0 | postgres | UTF8 | libc | C | C | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | libc | C | C | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
(4 rows)
postgres=#
We got the database and the database is owned by the new user. What we also can see is, the we got “C” for the “Collate” and “Ctype”. This is probably not what you want, so you also need to change this in the initdb section of the cluster definition:
minicube@micro-minicube:~> cat pg.yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: my-pg-cluster
spec:
instances: 3
bootstrap:
initdb:
database: db1
owner: db1
dataChecksums: true
walSegmentSize: 32
localeCollate: 'en_US.utf8'
localeCType: 'en_US.utf8'
storage:
size: 1Gi
Destroy, re-create and check again:
minicube@micro-minicube:~> kubectl delete -f pg.yaml
cluster.postgresql.cnpg.io "my-pg-cluster" deleted
minicube@micro-minicube:~> kubectl apply -f pg.yaml
cluster.postgresql.cnpg.io/my-pg-cluster created
minicube@micro-minicube:~> kubectl get pods
NAME READY STATUS RESTARTS AGE
my-pg-cluster-1 1/1 Running 0 55m
my-pg-cluster-2 1/1 Running 0 54m
my-pg-cluster-3 1/1 Running 0 54m
minicube@micro-minicube:~> kubectl exec my-pg-cluster-1 -i -t -- /bin/bash
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
postgres@my-pg-cluster-1:/$ psql
psql (16.2 (Debian 16.2-1.pgdg110+2))
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
-----------+----------+----------+-----------------+------------+------------+------------+-----------+-----------------------
db1 | db1 | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
postgres | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
template0 | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
(4 rows)
postgres=#
Now we are fine and the size of the WAL segments is as well as we wanted to have it:
postgres=# show wal_segment_size;
wal_segment_size
------------------
32MB
(1 row)
Data checksum are enabled:
postgres=# \! pg_controldata | grep checksum
Data page checksum version: 1
We could have changed the encoding, but as the default anyway is “UTF8”, there is no reason to touch this usually.
Another option you have, is to execute SQL commands or whole scripts (via ConfigMaps). A simple example is this one:
minicube@micro-minicube:~> cat pg.yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: my-pg-cluster
spec:
instances: 3
bootstrap:
initdb:
database: db1
owner: db1
dataChecksums: true
walSegmentSize: 32
localeCollate: 'en_US.utf8'
localeCType: 'en_US.utf8'
postInitSQL:
- create user db2
- create database db2 with owner = db2
storage:
size: 1Gi
minicube@micro-minicube:~> kubectl delete -f pg.yaml
cluster.postgresql.cnpg.io "my-pg-cluster" deleted
minicube@micro-minicube:~> kubectl apply -f pg.yaml
cluster.postgresql.cnpg.io/my-pg-cluster created
minicube@micro-minicube:~> kubectl exec my-pg-cluster-1 -i -t -- /bin/bash
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
postgres@my-pg-cluster-1:/$ psql
psql (16.2 (Debian 16.2-1.pgdg110+2))
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
-----------+----------+----------+-----------------+------------+------------+------------+-----------+-----------------------
db1 | db1 | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
db2 | db2 | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
postgres | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
template0 | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
(5 rows)
postgres=#
All this is documented, of course. In the next post we’ll look at how we can configure PostgreSQL, because bootstrapping is one task, but configuring PostgreSQL for your needs is another task you definitely should look at.