Skip to content
Joey Wang
Menu

Search

DevOps and Reliability

Troubleshooting pgpool Connection Termination in Kubernetes

Intermittent pgpool connection drops in Kubernetes usually trace to max_connections, idle timeouts, or NetworkPolicy issues, found layer by layer.

· 5 min read

databasedevops #postgresql#kubernetes#debugging#networking

Audio summary

Intermittent connection termination is a common problem when running PostgreSQL behind pgpool in Kubernetes. This is a hands-on guide to debugging it with netcat, kubectl, and psql, testing connectivity at each layer to find where connections actually break.

Common Causes of pgpool Connection Issues

Before troubleshooting, it helps to know the usual culprits:

  • “Sorry, too many clients already”: This error indicates that the number of active connections to the PostgreSQL backend has exceeded the max_connections limit defined in postgresql.conf. This can be caused by application-level connection leaks or inadequate pooling settings in pgpool.conf.
  • Idle Timeouts: pgpool, Kubernetes networking components, or PostgreSQL itself may be configured to terminate idle connections after a certain period. If your application holds connections open without activity for too long, they may be severed. Key parameters to check are idle_in_transaction_session_timeout in postgresql.conf and various timeout settings in pgpool.conf.
  • Authentication Failures: Incorrectly configured authentication methods or missing entries in pool_hba.conf and pool_passwd can lead to connection refusals.
  • Failover and High Availability Problems: In a high-availability setup, issues with pgpool’s health checks, failover commands, or replication synchronization can cause it to terminate connections to backend nodes.
  • Kubernetes Networking: Misconfigured NetworkPolicies, service discovery (DNS) issues, or problems with the container network interface (CNI) can all disrupt communication between application pods, pgpool, and the PostgreSQL backends.

Step 1: Basic Connectivity Check with netcat

The first step is verifying basic TCP connectivity to the pgpool service from within the cluster. netcat is the right tool for this.

We’ll use kubectl run to create a temporary debugging pod. A lightweight image with networking tools like busybox is ideal for this initial test.

  1. Launch a Debugging Pod: Open a terminal with kubectl access to your cluster and run the following command to get an interactive shell in a new pod:

    kubectl run -it --rm --image=busybox netcat-debug -- sh
  2. Test the pgpool Service: From within the netcat-debug pod’s shell, use netcat (nc) to test the connection to your pgpool service. The -vz flags provide verbose output and scan for a listening daemon, while -w3 sets a 3-second timeout.

    nc -vz -w3 <pgpool-service-name>.<namespace>.svc.cluster.local <pgpool-port> && echo $?

    A return code of 0 indicates success.

    • Successful Connection: A successful test means there are no fundamental networking issues (like restrictive NetworkPolicies) preventing your pods from reaching the pgpool service at a TCP level.
    • Failed Connection: A failure suggests a lower-level networking problem. Investigate your cluster’s NetworkPolicies, service definitions, and ensure that the pgpool pods are running and their endpoints are correctly registered with the service.

Step 2: Test PostgreSQL Protocol and Connection Speed with psql

A successful netcat test confirms TCP reachability, not that the PostgreSQL protocol works correctly. Next, use psql to attempt a full connection through pgpool.

  1. Launch a PostgreSQL Client Pod: For this step, using an image with the psql client is necessary. The official postgres image is a good choice.

    kubectl run -it --rm --image=postgres:latest psql-debug -- sh
  2. Attempt a Simple Connection: From the psql-debug pod’s shell, use psql to connect to your pgpool service. The -c "\q" argument will cause psql to connect, execute the quit command, and exit, providing a quick validation of the entire authentication and connection path.

    psql -h <pgpool-service-name>.<namespace> -p <pgpool-port> -U <user> -d <db> -c "\q"
    • Successful Connection: If the command executes without errors, it confirms that pgpool is accepting connections and can communicate with the backend. The problem may lie with the application’s specific behavior.
    • Connection Errors: Errors like “FATAL: password authentication failed” or “FATAL: role ‘<user>’ does not exist,” point to your pgpool or PostgreSQL configuration. Check pool_hba.conf, pool_passwd, and user permissions.
  3. Check Connection Speed: Slow connection establishment can sometimes be misinterpreted as a failure. Use psql’s \timing command to measure how long a simple query takes. This helps diagnose network latency or server-side delays during the connection process.

    From the psql-debug pod shell:

    psql -h <pgpool-service-name>.<namespace> -U <user> -d <db> -c "\timing" -c "SELECT 1"

    The output will show the time taken to execute the query, which includes the round-trip network latency and processing time. Consistently high times (e.g., over a few hundred milliseconds for a simple SELECT 1) could indicate network saturation or high load on the pgpool or PostgreSQL pods.


If the Basic Checks Pass

Simulate Application Load with pgbench

Sometimes, connection problems only manifest under load. The pgbench utility is perfect for simulating multiple concurrent client connections. The following command launches a dedicated pod and drops you into a shell, pre-configured with environment variables to connect to your pgpool service. This is an excellent way to replicate the application’s environment.

kubectl run pgbench-interactive \
  --rm \
  -it \
  --image=postgres:latest \
  --env="PGHOST=pgpool.re" \
  --env="PGPORT=5432" \
  --env="PGDATABASE=postgres" \
  --env="PGUSER=postgres" \
  --env='PGPASSWORD=mypassword' \
  -n re \
  -- /bin/bash

Remember to replace the environment variable values (PGHOST, PGPORT, etc.) with your specific pgpool details.

Once inside the pod’s shell, you can run pgbench to initialize a test environment and run a benchmark.

# Inside the pgbench-interactive pod

# Initialize pgbench tables (-i) with a scale factor (-s)
pgbench -i -s 1

# Run a benchmark for 60 seconds (-T 60) with 10 concurrent clients (-c 10)
pgbench -c 10 -T 60

While this test is running, monitor your pgpool and PostgreSQL logs for errors. If connections drop during the benchmark, it strongly suggests issues with connection handling under load, such as hitting max_connections or exhausting pgpool’s num_init_children.

Inspect pgpool’s Live State

You can connect directly to pgpool’s administrative interface to inspect its state. First, exec into the running pgpool pod.

kubectl exec -it <your-pgpool-pod-name> -n <namespace> -- /bin/bash

Once inside, use psql to connect to the pgpool instance itself (you may need to check your pcp.conf for the port and credentials). Then, run these commands:

  • show pool_nodes;: Displays the status of each backend PostgreSQL node as seen by pgpool. Check if the status is up and the weight is as expected; a down node indicates a health-check failure.
  • show pool_processes;: This shows the active pgpool child processes and their states, which can help identify stuck or idle processes.

Analyze Backend Activity

If you suspect connections are reaching PostgreSQL but are being terminated there, connect directly to the backend PostgreSQL pods (bypassing pgpool) and run the following query:

SELECT pid, datname, usename, application_name, client_addr, state, wait_event, query
FROM pg_stat_activity
WHERE backend_type = 'client backend';

This will show you exactly what queries are running, which connections are idle, and which are idle in transaction. A large number of idle in transaction states can be a red flag, as these hold locks and consume resources, potentially leading to connection slot exhaustion. This is often caused by applications that begin a transaction but do not COMMIT or ROLLBACK it promptly.