Skip to content

Instantly share code, notes, and snippets.

@briceburg
Last active April 12, 2023 20:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save briceburg/babe00e0a924a46f54af95b5aa8b6fa3 to your computer and use it in GitHub Desktop.
Save briceburg/babe00e0a924a46f54af95b5aa8b6fa3 to your computer and use it in GitHub Desktop.
TCP Proxy to a Postgres Database - HAProxy Configuration Example

HAProxy Configuration Example

TCP Proxy to a Postgres Database

Usage

docker compose up -d
docker compose run test

🚀

---
services:
pghost:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: foo
proxy:
build: .
ports:
- 5432:5432
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
test:
image: postgres:15-alpine
entrypoint: ""
command: |
sh -c '
pg_isready --host=proxy --username=postgres
'
profiles:
- test
FROM haproxy:2.7-alpine
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
ARG SOURCE_COMMIT=""
ENV DD_SERVICE="haproxy" DD_ENV="local" DD_VERSION="$SOURCE_COMMIT"
global
log stderr format raw daemon info
defaults
log-format "{\"service\":\"$DD_SERVICE\",\"env\":\"$DD_ENV\",\"version\":\"$DD_VERSION\",\"client_ip\":\"%ci\",\"duration\":%Tt,\"bytes\":%B,\"terminaton_state\":\"%ts\",\"actconn\":%ac,\"beconn\":%bc,\"feconn\":%fc,\"retries\":%rc,\"srv_queue\":%sq,\"backend_queue\":%bq}"
maxconn 512
option dontlognull
retries 3
timeout check 2s
timeout connect 1s
# align client and server timeouts with db server (e.g. match idle_session_timeout value)
timeout client 180m
timeout server 180m
resolvers foo
parse-resolv-conf
frontend health
bind *:8080 # https://github.com/aws/containers-roadmap/issues/1721
mode http
monitor fail if { nbsrv(curly) eq 0 }
monitor-uri /status
frontend larry
bind *:${PROXY_PORT}
default_backend curly
log global
mode tcp
backend curly
server moe ${PROXY_HOST}:${PROXY_PORT} resolvers foo check
@briceburg
Copy link
Author

recent versions of haproxy support environment variable substitution within the haproxy.cfg file. this plays well with docker and helps us provide a portable solution.

we can rewrite the simple, hard-coded example with one that is configurable using environment variables;

haproxy.cfg

global
  log stderr format raw daemon info

defaults
  mode  tcp
  log  global
  option dontlognull
  option redispatch
  retries 3
  maxconn 32

frontend larry
  bind *:${PROXY_PORT}
  default_backend curly

backend curly
  server moe ${PROXY_HOST}:${PROXY_PORT} check

docker-compose.yml

---
services:
  pghost:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: foo
  proxy:
    build: .
    environment:
      PROXY_HOST: pghost
      PROXY_PORT: 5432

@briceburg
Copy link
Author

In the above, the server FQDN is looked up once one startup and cached. In order to respond to PROXY_HOST IP changes, a resolvers section must be used;

haproxy.cfg with runtime DNS lookups

global
  log stderr format raw daemon info

defaults
  mode  tcp
  log  global
  option dontlognull
  option redispatch
  retries 3
  maxconn 32

resolvers foo
  parse-resolv-conf

frontend larry
  bind *:${PROXY_PORT}
  default_backend curly

backend curly
  server moe ${PROXY_HOST}:${PROXY_PORT} resolvers foo check

HAProxy will now log IP changes;

curly/moe changed its IP from 172.16.238.36 to 172.16.238.33 by foo/127.0.0.11.

to simulate this, an extended docker compose file can be used;

  • use the below docker compose file, docker compose up
  • change the IP address of pghost, e.g. from "172.16.238.33" to "172.16.238.34" and run docker compose up pghost -d

docker-compose.yml with set networks and IPs

---
services:
  pghost:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: foo
    networks:
      foo:
        ipv4_address: "172.16.238.33"
  proxy:
    build: .
    environment:
      PROXY_HOST: pghost
      PROXY_PORT: 5432
    networks:
      - foo
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
  test:
    image: postgres:15-alpine
    entrypoint: ""
    networks:
      - foo
    command: |
      sh -c '
        pg_isready --host=proxy --username=postgres
      '
    profiles:
      - test

networks:
  foo:
    ipam:
      driver: default
      config:
        - subnet: "172.16.238.0/24"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment