TCP Proxy to a Postgres Database
docker compose up -d
docker compose run test🚀
| --- | |
| services: | |
| pghost: | |
| image: postgres:15-alpine | |
| environment: | |
| POSTGRES_PASSWORD: foo | |
| proxy: | |
| build: . | |
| environment: | |
| PROXY_HOST: pghost | |
| PROXY_PORT: 5432 | |
| 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.9-alpine | |
| COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg | |
| COPY healthcheck.sh /usr/local/bin/healthcheck.sh | |
| HEALTHCHECK CMD ["/usr/local/bin/healthcheck.sh"] | |
| ARG HEALTHCHECK_PORT="8080" | |
| ARG METRICS_PORT="8405" | |
| # align client and server timeouts with underlying server (e.g. match databaseidle_session_timeout value) | |
| # WARNING: Ports Must be > 1024 in AWS Fargate -- https://github.com/aws/containers-roadmap/issues/1721 | |
| ENV \ | |
| CLIENT_TIMEOUT="180m" \ | |
| HEALTHCHECK_PORT="${HEALTHCHECK_PORT}" \ | |
| MAX_CONNECTIONS="1024" \ | |
| METRICS_PORT="${METRICS_PORT}" \ | |
| PROXY_HOST="replace-me" \ | |
| PROXY_PORT="replace-me" \ | |
| SERVER_TIMEOUT="180m" |
| global | |
| maxconn 24000 | |
| log stderr format raw daemon info | |
| defaults | |
| # log-format "{\"client_ip\":\"%ci\",\"duration\":%Tt,\"bytes\":%B,\"terminaton_state\":\"%ts\",\"actconn\":%ac,\"beconn\":%bc,\"feconn\":%fc,\"retries\":%rc,\"srv_queue\":%sq,\"backend_queue\":%bq}" | |
| option tcplog | |
| option dontlognull | |
| retries 3 | |
| timeout check 2s | |
| timeout connect 1s | |
| # align client and server timeouts with underlying server (e.g. match databaseidle_session_timeout value) | |
| timeout client "$CLIENT_TIMEOUT" | |
| timeout server "$SERVER_TIMEOUT" | |
| maxconn "$MAX_CONNECTIONS" | |
| resolvers dns | |
| parse-resolv-conf | |
| frontend healthcheck | |
| bind *:"$HEALTHCHECK_PORT" | |
| mode http | |
| monitor fail if { nbsrv(host) eq 0 } | |
| monitor-uri /status | |
| maxconn 16 | |
| frontend metrics | |
| bind *:"$METRICS_PORT" | |
| mode http | |
| http-request use-service prometheus-exporter if { path /metrics } | |
| no log | |
| maxconn 16 | |
| frontend main | |
| bind *:"$PROXY_PORT" | |
| default_backend host | |
| log global | |
| mode tcp | |
| backend host | |
| server h1 "$PROXY_HOST":"$PROXY_PORT" resolvers dns check maxconn "$MAX_CONNECTIONS" |
| #!/usr/bin/env sh | |
| wget -SO - "http://127.0.0.1:${HEALTHCHECK_PORT}/status" > /dev/null 2>&1 || exit 1 |
In the above, the server FQDN is looked up once one startup and cached. In order to respond to PROXY_HOST IP changes, a
resolverssection must be used;haproxy.cfg with runtime DNS lookups
HAProxy will now log IP changes;
to simulate this, an extended docker compose file can be used;
docker compose uppghost, e.g. from "172.16.238.33" to "172.16.238.34" and rundocker compose up pghost -ddocker-compose.yml with set networks and IPs