PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

PostgreSQL-Lastausgleich mit HAProxy und Keepalived

Eine Proxyebene kann sehr nützlich sein, um die Verfügbarkeit Ihrer Datenbankschicht zu erhöhen. Es kann die Codemenge auf der Anwendungsseite reduzieren, um Datenbankfehler und Änderungen der Replikationstopologie zu handhaben. In diesem Blogpost werden wir diskutieren, wie man einen HAProxy einrichtet, um auf PostgreSQL zu arbeiten.

Das Wichtigste zuerst – HAProxy arbeitet mit Datenbanken als Netzwerkschicht-Proxy. Es gibt kein Verständnis für die zugrunde liegende, manchmal komplexe Topologie. Alles, was HAProxy tut, ist, Pakete im Round-Robin-Verfahren an definierte Backends zu senden. Es untersucht weder Pakete noch versteht es das Protokoll, in dem Anwendungen mit PostgreSQL kommunizieren. Infolgedessen gibt es für den HAProxy keine Möglichkeit, eine Lese-/Schreibaufteilung auf einem einzelnen Port zu implementieren – dies würde das Analysieren von Abfragen erfordern. Solange Ihre Anwendung Lese- und Schreibvorgänge aufteilen und an verschiedene IPs oder Ports senden kann, können Sie die R/W-Aufteilung mit zwei Backends implementieren. Schauen wir uns an, wie es gemacht werden kann.

HAProxy-Konfiguration

Unten finden Sie ein Beispiel für zwei in HAProxy konfigurierte PostgreSQL-Backends.

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

Wie wir sehen können, verwenden sie die Ports 3307 zum Schreiben und 3308 zum Lesen. In diesem Setup gibt es drei Server – eine aktive und zwei Standby-Replikate. Was wichtig ist, tcp-check wird verwendet, um den Zustand der Knoten zu verfolgen. HAProxy stellt eine Verbindung zu Port 9201 her und erwartet, dass eine Zeichenfolge zurückgegeben wird. Gesunde Mitglieder des Backends geben den erwarteten Inhalt zurück, diejenigen, die den String nicht zurückgeben, werden als nicht verfügbar markiert.

Xinetd-Setup

Da HAProxy Port 9201 überprüft, muss etwas darauf lauschen. Wir können xinetd verwenden, um dort zu lauschen und einige Skripte für uns auszuführen. Eine Beispielkonfiguration eines solchen Dienstes kann wie folgt aussehen:

# 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
}

Sie müssen sicherstellen, dass Sie die Zeile hinzufügen:

postgreschk        9201/tcp

zu /etc/services.

Xinetd startet ein Postgreschk-Skript, das folgenden Inhalt hat:

#!/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";

Die Logik des Skripts geht wie folgt. Es gibt zwei Abfragen, die verwendet werden, um den Status des Knotens zu ermitteln.

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

Der erste prüft, ob sich PostgreSQL in der Wiederherstellung befindet – er ist „false“ für den aktiven Server und „true“ für Standby-Server. Die zweite prüft, ob sich PostgreSQL im schreibgeschützten Modus befindet. Der aktive Server gibt „off“ zurück, während Standby-Server „on“ zurückgeben. Basierend auf den Ergebnissen ruft das Skript die Funktion return_ok() mit einem richtigen Parameter auf („Master“ oder „Slave“, je nachdem, was erkannt wurde). Wenn die Abfragen fehlgeschlagen sind, wird eine „return_fail“-Funktion ausgeführt.

Die Return_ok-Funktion gibt eine Zeichenfolge basierend auf dem übergebenen Argument zurück. Wenn der Host ein aktiver Server ist, gibt das Skript „PostgreSQL-Master läuft“ zurück. Wenn es sich um einen Standby handelt, lautet die zurückgegebene Zeichenfolge:„PostgreSQL-Slave läuft“. Wenn der Status nicht eindeutig ist, wird zurückgegeben:„PostgreSQL läuft“. Hier endet die Schleife. HAProxy überprüft den Status, indem es sich mit xinetd verbindet. Letzteres startet ein Skript, das dann einen String zurückliefert, den HAProxy parst.

Wie Sie sich vielleicht erinnern, erwartet HAProxy die folgenden Zeichenfolgen:

tcp-check expect string master\ is\ running

für das Write-Backend und

tcp-check expect string is\ running.

für das schreibgeschützte Backend. Dadurch wird der aktive Server zum einzigen verfügbaren Host im Schreib-Backend, während im Lese-Backend sowohl aktive als auch Standby-Server verwendet werden können.

PostgreSQL und HAProxy in ClusterControl

Das obige Setup ist nicht komplex, aber es dauert einige Zeit, es einzurichten. ClusterControl kann verwendet werden, um all dies für Sie einzurichten.

Im Drop-down-Menü des Cluster-Jobs haben Sie die Möglichkeit, einen Load Balancer hinzuzufügen. Dann wird eine Option zum Bereitstellen von HAProxy angezeigt. Sie müssen angeben, wo Sie es installieren möchten, und einige Entscheidungen treffen:aus den Repositories, die Sie auf dem Host konfiguriert haben, oder der neuesten Version, die aus dem Quellcode kompiliert wurde. Sie müssen auch konfigurieren, welche Knoten im Cluster Sie zu HAProxy hinzufügen möchten.

Sobald die HAProxy-Instanz bereitgestellt ist, können Sie auf der Registerkarte „Knoten“ auf einige Statistiken zugreifen:

Wie wir sehen können, ist für das R/W-Backend nur ein Host (aktiver Server) als aktiv markiert. Für das schreibgeschützte Backend sind alle Knoten aktiv.

Laden Sie noch heute das Whitepaper PostgreSQL-Verwaltung und -Automatisierung mit ClusterControl herunterErfahren Sie, was Sie wissen müssen, um PostgreSQL bereitzustellen, zu überwachen, zu verwalten und zu skalierenLaden Sie das Whitepaper herunter

Keepalived

HAProxy sitzt zwischen Ihren Anwendungen und Datenbankinstanzen und spielt somit eine zentrale Rolle. Es kann leider auch zu einem Single Point of Failure werden, bei einem Ausfall gibt es keinen Weg zu den Datenbanken. Um eine solche Situation zu vermeiden, können Sie mehrere HAProxy-Instanzen bereitstellen. Aber dann ist die Frage, wie man entscheidet, mit welchem ​​Proxy-Host man sich verbindet. Wenn Sie HAProxy von ClusterControl bereitgestellt haben, ist es so einfach wie das Ausführen eines weiteren „Load Balancer hinzufügen“-Jobs, diesmal mit der Bereitstellung von Keepalived.

Wie wir im obigen Screenshot sehen können, können Sie bis zu drei HAProxy-Hosts auswählen und Keepalived wird auf ihnen bereitgestellt und überwacht ihren Zustand. Einem von ihnen wird eine virtuelle IP (VIP) zugewiesen. Ihre Anwendung sollte diese VIP verwenden, um eine Verbindung zur Datenbank herzustellen. Wenn der „aktive“ HAProxy nicht mehr verfügbar ist, wird VIP auf einen anderen Host verschoben.

Wie wir gesehen haben, ist es recht einfach, einen vollständigen Hochverfügbarkeits-Stack für PostgreSQL bereitzustellen. Probieren Sie es aus und lassen Sie uns wissen, wenn Sie Feedback haben.