sql >> Base de Datos >  >> RDS >> PostgreSQL

Equilibrio de carga de PostgreSQL con HAProxy y Keepalived

Una capa de proxy puede ser muy útil para aumentar la disponibilidad de su base de datos. Puede reducir la cantidad de código en el lado de la aplicación para manejar las fallas de la base de datos y los cambios en la topología de replicación. En esta publicación de blog, discutiremos cómo configurar un HAProxy para que funcione sobre PostgreSQL.

Lo primero es lo primero:HAProxy funciona con bases de datos como un proxy de capa de red. No hay comprensión de la topología subyacente, a veces compleja. Todo lo que hace HAProxy es enviar paquetes por turnos a backends definidos. No inspecciona paquetes ni entiende el protocolo en el que las aplicaciones se comunican con PostgreSQL. Como resultado, no hay forma de que HAProxy implemente la división de lectura/escritura en un solo puerto; requeriría analizar las consultas. Siempre que su aplicación pueda dividir las lecturas de las escrituras y enviarlas a diferentes IP o puertos, puede implementar la división R/W usando dos backends. Echemos un vistazo a cómo se puede hacer.

Configuración de HAProxy

A continuación puede encontrar un ejemplo de dos backends de PostgreSQL configurados en HAProxy.

listen  haproxy_10.0.0.101_3307_rw
        bind *:3307
        mode tcp
        timeout client  10800s
        timeout server  10800s
        tcp-check expect string master\ is\ running
        balance leastconn
        option tcp-check
        option allbackups
        default-server port 9201 inter 2s downinter 5s rise 3 fall 2 slowstart 60s maxconn 64 maxqueue 128 weight 100
        server 10.0.0.101 10.0.0.101:5432 check
        server 10.0.0.102 10.0.0.102:5432 check
        server 10.0.0.103 10.0.0.103:5432 check


listen  haproxy_10.0.0.101_3308_ro
        bind *:3308
        mode tcp
        timeout client  10800s
        timeout server  10800s
        tcp-check expect string is\ running.
        balance leastconn
        option tcp-check
        option allbackups
        default-server port 9201 inter 2s downinter 5s rise 3 fall 2 slowstart 60s maxconn 64 maxqueue 128 weight 100
        server 10.0.0.101 10.0.0.101:5432 check
        server 10.0.0.102 10.0.0.102:5432 check
        server 10.0.0.103 10.0.0.103:5432 check

Como podemos ver, usan los puertos 3307 para escritura y 3308 para lectura. En esta configuración hay tres servidores:una réplica activa y dos en espera. Lo que es importante, tcp-check se usa para rastrear la salud de los nodos. HAProxy se conectará al puerto 9201 y espera ver una cadena devuelta. Los miembros sanos del backend devolverán el contenido esperado, aquellos que no devuelvan la cadena se marcarán como no disponibles.

Configuración de Xinetd

Mientras HAProxy verifica el puerto 9201, algo tiene que escucharlo. Podemos usar xinetd para escuchar allí y ejecutar algunos scripts para nosotros. La configuración de ejemplo de dicho servicio puede verse así:

# default: on
# description: postgreschk
service postgreschk
{
        flags           = REUSE
        socket_type     = stream
        port            = 9201
        wait            = no
        user            = root
        server          = /usr/local/sbin/postgreschk
        log_on_failure  += USERID
        disable         = no
        #only_from       = 0.0.0.0/0
        only_from       = 0.0.0.0/0
        per_source      = UNLIMITED
}

Debes asegurarte de agregar la línea:

postgreschk        9201/tcp

a /etc/services.

Xinetd inicia un script postgreschk, que tiene contenidos como el siguiente:

#!/bin/bash
#
# This script checks if a PostgreSQL server is healthy running on localhost. It will
# return:
# "HTTP/1.x 200 OK\r" (if postgres is running smoothly)
# - OR -
# "HTTP/1.x 500 Internal Server Error\r" (else)
#
# The purpose of this script is make haproxy capable of monitoring PostgreSQL properly
#

export PGHOST='10.0.0.101'
export PGUSER='someuser'
export PGPASSWORD='somepassword'
export PGPORT='5432'
export PGDATABASE='postgres'
export PGCONNECT_TIMEOUT=10

FORCE_FAIL="/dev/shm/proxyoff"

SLAVE_CHECK="SELECT pg_is_in_recovery()"
WRITABLE_CHECK="SHOW transaction_read_only"

return_ok()
{
    echo -e "HTTP/1.1 200 OK\r\n"
    echo -e "Content-Type: text/html\r\n"
    if [ "$1x" == "masterx" ]; then
        echo -e "Content-Length: 56\r\n"
        echo -e "\r\n"
        echo -e "<html><body>PostgreSQL master is running.</body></html>\r\n"
    elif [ "$1x" == "slavex" ]; then
        echo -e "Content-Length: 55\r\n"
        echo -e "\r\n"
        echo -e "<html><body>PostgreSQL slave is running.</body></html>\r\n"
    else
        echo -e "Content-Length: 49\r\n"
        echo -e "\r\n"
        echo -e "<html><body>PostgreSQL is running.</body></html>\r\n"
    fi
    echo -e "\r\n"

    unset PGUSER
    unset PGPASSWORD
    exit 0
}

return_fail()
{
    echo -e "HTTP/1.1 503 Service Unavailable\r\n"
    echo -e "Content-Type: text/html\r\n"
    echo -e "Content-Length: 48\r\n"
    echo -e "\r\n"
    echo -e "<html><body>PostgreSQL is *down*.</body></html>\r\n"
    echo -e "\r\n"

    unset PGUSER
    unset PGPASSWORD
    exit 1
}

if [ -f "$FORCE_FAIL" ]; then
    return_fail;
fi

# check if in recovery mode (that means it is a 'slave')
SLAVE=$(psql -qt -c "$SLAVE_CHECK" 2>/dev/null)
if [ $? -ne 0 ]; then
    return_fail;
elif echo $SLAVE | egrep -i "(t|true|on|1)" 2>/dev/null >/dev/null; then
    return_ok "slave"
fi

# check if writable (then we consider it as a 'master')
READONLY=$(psql -qt -c "$WRITABLE_CHECK" 2>/dev/null)
if [ $? -ne 0 ]; then
    return_fail;
elif echo $READONLY | egrep -i "(f|false|off|0)" 2>/dev/null >/dev/null; then
    return_ok "master"
fi

return_ok "none";

La lógica del script es la siguiente. Hay dos consultas que se utilizan para detectar el estado del nodo.

SLAVE_CHECK="SELECT pg_is_in_recovery()"
WRITABLE_CHECK="SHOW transaction_read_only"

El primero verifica si PostgreSQL está en recuperación:será "falso" para el servidor activo y "verdadero" para los servidores en espera. El segundo verifica si PostgreSQL está en modo de solo lectura. El servidor activo volverá a estar "apagado", mientras que los servidores en espera volverán a estar "encendidos". Según los resultados, el script llama a la función return_ok() con un parámetro correcto ('maestro' o 'esclavo', según lo que se haya detectado). Si las consultas fallan, se ejecutará una función 'return_fail'.

La función Return_ok devuelve una cadena basada en el argumento que se le pasó. Si el host es un servidor activo, el script devolverá "El maestro de PostgreSQL se está ejecutando". Si es un modo de espera, la cadena devuelta será:"El esclavo de PostgreSQL se está ejecutando". Si el estado no está claro, devolverá:"PostgreSQL se está ejecutando". Aquí es donde termina el bucle. HAProxy comprueba el estado conectándose a xinetd. Este último inicia un script, que luego devuelve una cadena que HAProxy analiza.

Como recordará, HAProxy espera las siguientes cadenas:

tcp-check expect string master\ is\ running

para el backend de escritura y

tcp-check expect string is\ running.

para el backend de solo lectura. Esto hace que el servidor activo sea el único host disponible en el backend de escritura, mientras que en el backend de lectura se pueden usar tanto servidores activos como en espera.

PostgreSQL y HAProxy en ClusterControl

La configuración anterior no es compleja, pero lleva algún tiempo configurarla. Se puede usar ClusterControl para configurar todo esto por usted.

En el menú desplegable del trabajo del clúster, tiene la opción de agregar un balanceador de carga. Luego aparece una opción para implementar HAProxy. Debe completar dónde desea instalarlo y tomar algunas decisiones:desde los repositorios que ha configurado en el host o la última versión, compilada a partir del código fuente. También deberá configurar qué nodos del clúster desea agregar a HAProxy.

Una vez que se implementa la instancia de HAProxy, puede acceder a algunas estadísticas en la pestaña "Nodos":

Como podemos ver, para el backend R/W, solo un host (servidor activo) está marcado como activo. Para el backend de solo lectura, todos los nodos están activos.

Descargue el documento técnico hoy Administración y automatización de PostgreSQL con ClusterControlObtenga información sobre lo que necesita saber para implementar, monitorear, administrar y escalar PostgreSQLDescargar el documento técnico

Mantener vivo

HAProxy se ubicará entre sus aplicaciones y las instancias de la base de datos, por lo que desempeñará un papel central. Desafortunadamente, también puede convertirse en un punto único de falla; si falla, no habrá ruta a las bases de datos. Para evitar tal situación, puede implementar varias instancias de HAProxy. Pero entonces la pregunta es:cómo decidir a qué host proxy conectarse. Si implementó HAProxy desde ClusterControl, es tan simple como ejecutar otro trabajo de "Agregar balanceador de carga", esta vez implementando Keepalived.

Como podemos ver en la captura de pantalla anterior, puede seleccionar hasta tres hosts HAProxy y Keepalived se implementará encima de ellos, monitoreando su estado. A uno de ellos se le asignará una IP Virtual (VIP). Su aplicación debe usar este VIP para conectarse a la base de datos. Si el HAProxy "activo" deja de estar disponible, el VIP se moverá a otro host.

Como hemos visto, es bastante fácil implementar una pila completa de alta disponibilidad para PostgreSQL. Pruébelo y háganos saber si tiene algún comentario.