PostgreSQL supports a wide range of use cases. Application developers can benefit from its support for JSON data and SQL. Database administrators can lean on its robust replication and self-healing capabilities.
Kubernetes provides a centralized control plane that represents the state of an application. Whether it’s scaling an instance, upgrading to a new version, or choosing a new primary for failover, the controller performs these tasks.
Pod Deployment
Kubernetes is a powerful platform for automating application deployments. However, running stateful applications like PostgreSQL on it is also often challenging.
A Kubernetes Postgres deployment example must be highly available, which means storing the WAL on disk and having strong read-write performance to maintain availability. Also, it is important to provide dynamic storage provisioning for stateful applications and keep the application data in sync with the central representation of the application state maintained in the Kubernetes API.
In addition, when using a Kubernetes cluster with multiple data centers, you need to use the native features of Kubernetes to implement rapid disaster recovery. This includes relying on object stores that offer an RPO of 0 (Point-in-Time) and on Kubernetes native backup solutions.
Another important aspect to consider is Pod scheduling and scaling. You must be able to handle traffic increases by either increasing CPU and memory or adding more Pod replicas. This flexibility can make a big difference for a business that relies on PostgreSQL as its primary database.
Pod Configuration
Using a storage layer that provides high performance and data locality for PostgreSQL pods is recommended. This is because the database logs are very large and require a lot of disk space. Furthermore, a good container-native layer supports dynamic storage provisioning to comply with stateful services like PostgreSQL requirements.
When setting up a Pod, it is important to set the correct CPU and memory limits to ensure the database does not exceed its resource allocation. Typically, the CPU is expressed in millicores, and the memory is expressed in megabytes or mebibytes. It is also a best practice to set a security limit for the Pod’s security context so that it does not expose sensitive information on the network.
In addition, it is a best practice to keep secrets in a separate file so they cannot be accidentally exposed. This can be done by mounting the secret as a read-only volume in the container instead of in an environment variable. It is important to keep these secrets in a secure location as the security of an application depends on them.
Data Replication
When deploying PostgreSQL, it is important to replicate data to ensure that your database is always available. To accomplish this, you must create a PersistentVolumeClaim resource in the cluster. Then, you will need to mount this PVC on a node.
This will allow your database to be rescheduled if the original Pod fails or if you need to deploy an updated version of the Pod. To prevent data loss, it is also a good idea to use a canary deployment for upgrades and perform data backups before the upgrade.
Another way to protect your data is by encrypting communication over the network. This will help to prevent unwanted traffic from gaining access to the database system. TLS (preferably 1.3, but at least 1.2) will encrypt the connection between your applications and your PostgreSQL server. The open-source PostgreSQL operator from Crunchy Data, called PGO, has built-in support, making enabling TLS on your PostgreSQL database easy.
Rolling Updates
PostgreSQL is a stateful application, meaning its data must persist across pod restarts. This is usually accomplished by mounting a persistent volume to the container using a constant volume resource.
A highly performant persistent storage layer is required, especially given that PostgreSQL writes write-ahead logs (WAL) before writing changes to disk. This allows the database to recover from a data loss incident by replaying the logged WAL files.
Kubernetes supports a wide range of storage options, including network persistent volumes. We recommend using cStor to provide an object storage pool with a read-write-once mount point for your database pods to ensure optimal performance.
Kubernetes offers a variety of pod affinity and anti-affinity settings to control where Pods are scheduled. However, since it’s impossible to predict the performance requirements for a database at deployment time (RTO and RPO), we advise leaving pod affinity and anti-affinity to the Kubernetes scheduler unless there is a need to enforce strict data locality or cluster consistency.
Monitoring
A PostgreSQL database requires a special level of care when running on Kubernetes. It is a key component of the application, and it must be properly configured, with a high availability configuration, and it should be backed up. In addition, it requires a solid troubleshooting and monitoring strategy that is consistent with Kubernetes.
In a highly concurrent environment, connections to the primary server can be too high. When this happens, PostgreSQL can use the CPU more than necessary. To avoid this, it is recommended to use a connection pool. This helps to distribute the workload and prevent the primary server from consuming excessive CPU resources.
The PostgreSQL deadlock_timeout setting determines how long a transaction can wait to acquire a lock. Defaults to one second. It is recommended to increase this value to ensure the database stays on track due to frequent checking for deadlocks.
The db_size metric provides information about the size of the database. This is important because it can help to detect disk bloat and identify the source of slow performance.