Deploying Your First PostgreSQL Clusters on Kubernetes with Cloud Native Postgres

From documentation

CloudNativePG is an open-source operator designed to manage PostgreSQL workloads on any supported Kubernetes cluster running in private, public, hybrid, or multi-cloud environments. CloudNativePG adheres to DevOps principles and concepts such as declarative configuration and immutable infrastructure.

Now let’s try to understand some key concepts used in the above definition

Open source means that the software’s source code is freely available for anyone to view, use, modify, and distribute.

Kubernetes, often called K8s due to the eight letters between “K” and “S,” is an open-source platform that automates the management of Linux containers. It simplifies the manual tasks of deploying and scaling containerized applications. Essentially, Kubernetes allows you to group hosts running Linux containers into clusters and efficiently manage them.

Kubernetes cluster Is a collection of computers (nodes) that work together to run and manage applications in containers. Kubernetes coordinates the deployment, scaling, and management of these applications across the cluster.

Operator Is a custom automation tool that helps manage and run specific applications on Kubernetes. It automates tasks like setup and maintenance, similar to how an administrator would manually manage software, but more efficiently. Just as a PostgreSQL extension adds features to PostgreSQL, a Kubernetes operator automates and manages applications, enhancing the system’s ability to handle specific tasks.

Declarative configuration In this approach, we define the desired state of our system, and the system automatically adjusts to reach that state. We focus on what we want, rather than detailing how to achieve it.

Immutable infrastructure In this approach, once a component (such as a server or container) is deployed, it remains unchanged. If updates are needed, a new instance with the desired changes is created, and the old one is decommissioned.

What Issues Does Cloud Native Postgres Address?

CloudNativePG addresses the challenges of managing PostgreSQL databases in Kubernetes environments by automating deployment, scaling, and high availability. It simplifies:

Management

  • By automating setup and maintenance of PostgreSQL clusters, reducing manual work.

Scaling 

  • Easily scales PostgreSQL clusters up or down based on demand.

High Availability

  • Ensures continuous database operation and failover in case of node failures.

Consistency

  • Handles configuration changes and updates across the cluster consistently.

Setting Up Your First Local Kubernetes Cluster

A Kubernetes cluster consists of a group of computers (nodes) that include a control plane (master) and worker nodes. The control plane oversees and manages container scheduling, while the worker nodes execute the containers. This setup provides an efficient and automated environment for deploying, managing, and scaling containerized applications.

Pre Requirements

First, let’s install the kind utility, a tool designed to create and manage local Kubernetes clusters using Docker containers as nodes. It’s mainly used for testing and developing Kubernetes clusters locally, without requiring a full cloud environment.

Install kind 

curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.24.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
kind --version
kind version 0.24.0

Install docker 

Docker must be installed on our system to use kind because kind sets up a local Kubernetes cluster by running Kubernetes nodes as Docker containers. Both the control plane and worker nodes are essentially Docker containers managed by kind. Since kind depends on Docker to handle these containers, Docker needs to be installed and running for kind to work correctly.

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y

Install kubectl 

After setting up the cluster, we use kubectl to interact with it. It lets you run commands to deploy applications, manage resources, and monitor the cluster.

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg 
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo chmod 644 /etc/apt/sources.list.d/kubernetes.list 
sudo apt-get update
sudo apt-get install -y kubectl

We can now create the local Kubernetes cluster using the following command

sudo kind create cluster --name local-cnpg
Creating cluster "local-cnpg" ...
 ✓ Ensuring node image (kindest/node:v1.31.0) 🖼 
 ✓ Preparing nodes 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
Set kubectl context to "kind-local-cnpg"
You can now use your cluster with:
kubectl cluster-info --context kind-local-cnpg

root@ip-172-31-93-185:~# kubectl cluster-info --context kind-local-cnpg
Kubernetes control plane is running at https://127.0.0.1:44337
CoreDNS is running at https://127.0.0.1:44337/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

When we create a Kubernetes cluster with Kind, it sets up nodes (virtual machines) that need an operating system and essential software to operate properly. Kindest/node: This is a Docker image that provides the essential components for a Kubernetes node.

We can verify that the command has created a Docker container on our system, which serves as the primary node (control-plane).

docker ps -a
CONTAINER ID   IMAGE                  COMMAND                  CREATED         STATUS         PORTS                       NAMES
702cfaffaca4   kindest/node:v1.31.0   "/usr/local/bin/entr…"   3 minutes ago   Up 2 minutes   127.0.0.1:41739->6443/tcp   local-cnpg-control-plane

Installing the Cloud Native Postgres

Now, we’ll install Cloud Native Postgres on our deployed cluster. To do this, we applied a YAML configuration file to the Kubernetes cluster. This file manages the creation and updating of various resources, such as namespaces, Custom Resource Definitions (CRDs), service accounts, roles, and deployments. The term “serverside-applied” indicates that these changes were made directly on the server side of the Kubernetes cluster.

kubectl apply --server-side -f   https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.24.0.yaml
namespace/cnpg-system serverside-applied
customresourcedefinition.apiextensions.k8s.io/backups.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/clusterimagecatalogs.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/clusters.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/imagecatalogs.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/poolers.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/scheduledbackups.postgresql.cnpg.io serverside-applied
serviceaccount/cnpg-manager serverside-applied
clusterrole.rbac.authorization.k8s.io/cnpg-manager serverside-applied
clusterrolebinding.rbac.authorization.k8s.io/cnpg-manager-rolebinding serverside-applied
configmap/cnpg-default-monitoring serverside-applied
service/cnpg-webhook-service serverside-applied
deployment.apps/cnpg-controller-manager serverside-applied
mutatingwebhookconfiguration.admissionregistration.k8s.io/cnpg-mutating-webhook-configuration serverside-applied
validatingwebhookconfiguration.admissionregistration.k8s.io/cnpg-validating-webhook-configuration serverside-applied

After successfully installing Cloud Native Postgres, we can now proceed to deploy the PostgreSQL cluster using the CNPG operator.

Creating the 3-node PostgreSQL cluster

Create a local file named configure-PostgreSQL.yml and add the following content:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-example
spec:
  instances: 3
  storage:
    size: 3Gi

apiVersion: postgresql.cnpg.io/v1: This defines the API version provided by the CNPG operator. The postgresql.cnpg.io is a custom API group created by the CNPG operator when installed in your Kubernetes cluster, and /v1 specifies the API version.

kind: Cluster: This indicates that the resource being created is a Cluster object, which is a custom resource type managed by the CNPG operator. The operator recognizes and manages this type to deploy a PostgreSQL cluster.

These fields tell the CNPG operator how to interpret and manage the configuration.

When you apply this YAML file with kubectl, the CNPG operator in your Kubernetes cluster will

  • Create the Necessary Pods: Deploy 3 PostgreSQL instances as defined in the spec section.
  • Manage the Storage: Allocate 3Gi of storage for each instance as specified.
  • Ensure High Availability and Manage Lifecycle: Handle replication, failover, updates, and other aspects of running the PostgreSQL cluster.

If the CNPG operator is not installed, the custom resource definitions (CRDs) like Cluster in apiVersion: postgresql.cnpg.io/v1 won’t be recognized, and the cluster creation will not occur.

Apply the configuration file to set up the 3-node PostgreSQL cluster

kubectl apply -f configure-PostgreSQL.yml 
cluster.postgresql.cnpg.io/cluster-example created

Get information about the pods 

kubectl get pods 
NAME                READY   STATUS    RESTARTS   AGE
cluster-example-1   1/1     Running   0          2m30s
cluster-example-2   1/1     Running   0          2m9s
cluster-example-3   1/1     Running   0          108s

To see the containers running within your Kubernetes pods, you should use the following Kubernetes commands which give more details about the containers inside each pod.

kubectl describe pod cluster-example-1

We can also access the running pod and connect to PostgreSQL from within it.

kubectl exec -it cluster-example-1 -- /bin/bash
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
postgres@cluster-example-1:/$ 
postgres@cluster-example-1:/$ psql
psql (16.4 (Debian 16.4-1.pgdg110+1))
Type "help" for help.
postgres=# 

Get details about a specific cluster (cluster-example) within a Kubernetes environment.

kubectl get cluster cluster-example
NAME              AGE     INSTANCES   READY   STATUS                     PRIMARY
cluster-example   6m54s   3           3       Cluster in healthy state   cluster-example-1

CNPG Plugin

A CNPG plugin is a specialized extension that enhances the functionality of the CNPG operator. It offers additional features or integrates with external systems, enabling you to customize and improve your PostgreSQL deployments in Kubernetes.

Install CNPG using the installation script

curl -sSfL \
https://github.com/cloudnative-pg/cloudnative-pg/raw/main/hack/install-cnpg-plugin.sh | \
  sudo sh -s -- -b /usr/local/bin
cloudnative-pg/cloudnative-pg info checking GitHub for latest tag
cloudnative-pg/cloudnative-pg info found version: 1.24.0 for v1.24.0/linux/x86_64
cloudnative-pg/cloudnative-pg info installed /usr/local/bin/kubectl-cnpg

Display the current status and health of the PostgreSQL cluster

kubectl cnpg status cluster-example
Cluster Summary
Name:                cluster-example
Namespace:           default
System ID:           7409746140750782489
PostgreSQL Image:    ghcr.io/cloudnative-pg/postgresql:16.4
Primary instance:    cluster-example-1
Primary start time:  2024-09-01 18:39:32 +0000 UTC (uptime 12h30m6s)
Status:              Cluster in healthy state 
Instances:           3
Ready instances:     3
Current Write LSN:   0/8008870 (Timeline: 1 - WAL File: 000000010000000000000008)

Certificates Status
Certificate Name             Expiration Date                Days Left Until Expiration
----------------             ---------------                --------------------------
cluster-example-ca           2024-11-30 18:34:05 +0000 UTC  89.48
cluster-example-replication  2024-11-30 18:34:05 +0000 UTC  89.48
cluster-example-server       2024-11-30 18:34:05 +0000 UTC  89.48

Continuous Backup status
Not configured

Physical backups
No running physical backups found

Streaming Replication status
Replication Slots Enabled

Name               Sent LSN   Write LSN  Flush LSN  Replay LSN  Write Lag  Flush Lag  Replay Lag  State      Sync State  Sync Priority  Replication Slot
----               --------   ---------  ---------  ----------  ---------  ---------  ----------  -----      ----------  -------------  ----------------
cluster-example-2  0/8008870  0/8008870  0/8008870  0/8008870   00:00:00   00:00:00   00:00:00    streaming  async       0              active
cluster-example-3  0/8008870  0/8008870  0/8008870  0/8008870   00:00:00   00:00:00   00:00:00    streaming  async       0              active

Unmanaged Replication Slot Status
No unmanaged replication slots found

Managed roles status
No roles managed

Tablespaces status
No managed tablespaces

Pod Disruption Budgets status
Name                     Role     Expected Pods  Current Healthy  Minimum Desired Healthy  Disruptions Allowed
----                     ----     -------------  ---------------  -----------------------  -------------------
cluster-example          replica  2              2                1                        1
cluster-example-primary  primary  1              1                1                        0

Instances status
Name               Database Size  Current LSN  Replication role  Status  QoS         Manager Version  Node
----               -------------  -----------  ----------------  ------  ---         ---------------  ----
cluster-example-1  29 MB          0/8008870    Primary           OK      BestEffort  1.24.0           local-cnpg-control-plane
cluster-example-2  29 MB          0/8008870    Standby (async)   OK      BestEffort  1.24.0           local-cnpg-control-plane
cluster-example-3  29 MB          0/8008870    Standby (async)   OK      BestEffort  1.24.0           local-cnpg-control-plane

In conclusion, Cloud Native Postgres offers a flexible solution for managing PostgreSQL clusters in Kubernetes, making it ideal for modern database environments. In upcoming blogs, we will explore how to use Cloud Native Postgres for production systems.

Leave A Comment