Nella prima parte, abbiamo ottenuto un cluster HA cmon funzionante:
[email protected]:~# s9s controller --list --long
S VERSION OWNER GROUP NAME IP PORT COMMENT
l 1.7.4.3565 system admins 10.0.0.101 10.0.0.101 9501 Acting as leader.
f 1.7.4.3565 system admins 10.0.0.102 10.0.0.102 9501 Accepting heartbeats.
f 1.7.4.3565 system admins 10.0.0.103 10.0.0.103 9501 Accepting heartbeats.
Total: 3 controller(s)
Abbiamo tre nodi attivi e funzionanti, uno funge da leader e gli altri sono i follower, che sono accessibili (ricevono i battiti del cuore e rispondono ad essi). La sfida rimanente è configurare l'accesso all'interfaccia utente in un modo che ci consenta di accedere sempre all'interfaccia utente sul nodo leader. In questo post del blog presenteremo una delle possibili soluzioni che ti permetteranno di realizzare proprio questo.
Configurazione di HAProxy
Questo problema non è nuovo per noi. Con ogni cluster di replica, MySQL o PostgreSQL, non importa, c'è un singolo nodo a cui inviare le nostre scritture. Un modo per ottenere ciò sarebbe utilizzare HAProxy e aggiungere alcuni controlli esterni che verificano lo stato del nodo e, in base a ciò, restituire valori appropriati. Questo è fondamentalmente ciò che useremo per risolvere il nostro problema. Useremo HAProxy come proxy di livello 4 ben testato e lo combineremo con i controlli HTTP di livello 7 che scriveremo precisamente per il nostro caso d'uso. Per prima cosa, installiamo HAProxy. Lo collocheremo con ClusterControl, ma può anche essere installato su un nodo separato (idealmente, nodi - per rimuovere HAProxy come singolo punto di errore).
apt install haproxy
Questo imposta HAProxy. Una volta fatto, dobbiamo introdurre la nostra configurazione:
global
pidfile /var/run/haproxy.pid
daemon
user haproxy
group haproxy
stats socket /var/run/haproxy.socket user haproxy group haproxy mode 600 level admin
node haproxy_10.0.0.101
description haproxy server
#* Performance Tuning
maxconn 8192
spread-checks 3
quiet
defaults
#log global
mode tcp
option dontlognull
option tcp-smart-accept
option tcp-smart-connect
#option dontlog-normal
retries 3
option redispatch
maxconn 8192
timeout check 10s
timeout queue 3500ms
timeout connect 3500ms
timeout client 10800s
timeout server 10800s
userlist STATSUSERS
group admin users admin
user admin insecure-password admin
user stats insecure-password admin
listen admin_page
bind *:9600
mode http
stats enable
stats refresh 60s
stats uri /
acl AuthOkay_ReadOnly http_auth(STATSUSERS)
acl AuthOkay_Admin http_auth_group(STATSUSERS) admin
stats http-request auth realm admin_page unless AuthOkay_ReadOnly
#stats admin if AuthOkay_Admin
listen haproxy_10.0.0.101_81
bind *:81
mode tcp
tcp-check connect port 80
timeout client 10800s
timeout server 10800s
balance leastconn
option httpchk
# option allbackups
default-server port 9201 inter 20s downinter 30s rise 2 fall 2 slowstart 60s maxconn 64 maxqueue 128 weight 100
server 10.0.0.101 10.0.0.101:443 check
server 10.0.0.102 10.0.0.102:443 check
server 10.0.0.103 10.0.0.103:443 check
Potresti voler cambiare alcune delle cose qui come i nomi dei nodi o del backend che includono qui l'IP del nostro nodo. Sicuramente vorrai cambiare i server che avrai incluso nel tuo HAProxy.
I bit più importanti sono:
bind *:81
HAProxy ascolterà sulla porta 81.
option httpchk
Abbiamo abilitato il controllo del livello 7 sui nodi backend.
default-server port 9201 inter 20s downinter 30s rise 2 fall 2 slowstart 60s maxconn 64 maxqueue 128 weight 100
Il controllo del livello 7 verrà eseguito sulla porta 9201.
Una volta fatto, avvia HAProxy.
Configurazione di xinetd e verifica script
Utilizzeremo xinetd per eseguire il controllo e restituire risposte corrette a HAProxy. I passaggi descritti in questo paragrafo devono essere eseguiti su tutti i nodi cluster HA cmon.
Per prima cosa, installa xinetd:
[email protected]:~# apt install xinetd
Una volta fatto, dobbiamo aggiungere la seguente riga:
cmonhachk 9201/tcp
in /etc/services - questo consentirà a xinetd di aprire un servizio che sarà in ascolto sulla porta 9201. Quindi dobbiamo aggiungere il file di servizio stesso. Dovrebbe trovarsi in /etc/xinetd.d/cmonhachk:
# default: on
# description: cmonhachk
service cmonhachk
{
flags = REUSE
socket_type = stream
port = 9201
wait = no
user = root
server = /usr/local/sbin/cmonhachk.py
log_on_failure += USERID
disable = no
#only_from = 0.0.0.0/0
only_from = 0.0.0.0/0
per_source = UNLIMITED
}
Infine, abbiamo bisogno dello script di controllo chiamato da xinetd. Come definito nel file di servizio, si trova in /usr/local/sbin/cmonhachk.py.
#!/usr/bin/python3.5
import subprocess
import re
import sys
from pathlib import Path
import os
def ret_leader():
leader_str = """HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 48\r\n
\r\n
<html><body>This node is a leader.</body></html>\r\n
\r\n"""
print(leader_str)
def ret_follower():
follower_str = """
HTTP/1.1 503 Service Unavailable\r\n
Content-Type: text/html\r\n
Content-Length: 50\r\n
\r\n
<html><body>This node is a follower.</body></html>\r\n
\r\n"""
print(follower_str)
def ret_unknown():
unknown_str = """
HTTP/1.1 503 Service Unavailable\r\n
Content-Type: text/html\r\n
Content-Length: 59\r\n
\r\n
<html><body>This node is in an unknown state.</body></html>\r\n
\r\n"""
print(unknown_str)
lockfile = "/tmp/cmonhachk_lockfile"
if os.path.exists(lockfile):
print("Lock file {} exists, exiting...".format(lockfile))
sys.exit(1)
Path(lockfile).touch()
try:
with open("/etc/default/cmon", 'r') as f:
lines = f.readlines()
pattern1 = "RPC_BIND_ADDRESSES"
pattern2 = "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
m1 = re.compile(pattern1)
m2 = re.compile(pattern2)
for line in lines:
res1 = m1.match(line)
if res1 is not None:
res2 = m2.findall(line)
i = 0
for r in res2:
if r != "127.0.0.1" and i == 0:
i += 1
hostname = r
command = "s9s controller --list --long | grep {}".format(hostname)
output = subprocess.check_output(command.split())
state = output.splitlines()[1].decode('UTF-8')[0]
if state == "l":
ret_leader()
if state == "f":
ret_follower()
else:
ret_unknown()
finally:
os.remove(lockfile)
Una volta creato il file, assicurati che sia eseguibile:
chmod u+x /usr/local/sbin/cmonhachk.py
L'idea alla base di questo script è che verifica lo stato dei nodi utilizzando il comando "s9s controller --list --long" e quindi controlla l'output relativo all'IP che può trovare sul nodo locale. Ciò consente allo script di determinare se l'host su cui viene eseguito è un leader o meno. Se il nodo è il leader, lo script restituisce il codice "HTTP/1.1 200 OK", che HAProxy interpreta come il nodo è disponibile e indirizza il traffico verso di esso. Altrimenti restituisce "HTTP/1.1 503 Servizio non disponibile", che viene trattato come un nodo, che non è integro e il traffico non verrà instradato lì. Di conseguenza, indipendentemente da quale nodo diventerà leader, HAProxy lo rileverà e lo contrassegnerà come disponibile nel backend:
Potrebbe essere necessario riavviare HAProxy e xinetd per applicare le modifiche alla configurazione prima di le parti inizieranno a funzionare correttamente.
Avere più di un HAProxy ci assicura un modo per accedere all'interfaccia utente di ClusterControl anche se uno dei nodi HAProxy dovesse fallire, ma abbiamo ancora due (o più) nomi host o IP diversi per connettersi all'interfaccia utente di ClusterControl. Per renderlo più comodo, implementeremo Keepalived su HAProxy. Monitorerà lo stato dei servizi HAProxy e assegnerà l'IP virtuale a uno di essi. Se tale HAProxy non è più disponibile, VIP verrà spostato su un altro HAProxy disponibile. Di conseguenza, avremo un unico punto di ingresso (VIP o un hostname ad esso associato). I passaggi che faremo qui devono essere eseguiti su tutti i nodi in cui è stato installato HAProxy.
Per prima cosa, installiamo keepalived:
apt install keepalived
Allora dobbiamo configurarlo. Useremo il seguente file di configurazione:
vrrp_script chk_haproxy {
script "killall -0 haproxy" # verify the pid existance
interval 2 # check every 2 seconds
weight 2 # add 2 points of prio if OK
}
vrrp_instance VI_HAPROXY {
interface eth1 # interface to monitor
state MASTER
virtual_router_id 51 # Assign one ID for this route
priority 102
unicast_src_ip 10.0.0.101
unicast_peer {
10.0.0.102
10.0.0.103
}
virtual_ipaddress {
10.0.0.130 # the virtual IP
}
track_script {
chk_haproxy
}
# notify /usr/local/bin/notify_keepalived.sh
}
Dovresti modificare questo file su nodi diversi. Gli indirizzi IP devono essere configurati correttamente e la priorità deve essere diversa su tutti i nodi. Configura anche VIP che ha senso nella tua rete. Potresti anche voler cambiare l'interfaccia:abbiamo usato eth1, che è dove l'IP viene assegnato alle macchine virtuali create da Vagrant.
Avvia il keepalived con questo file di configurazione e dovresti essere a posto. Finché VIP è attivo su un nodo HAProxy, dovresti essere in grado di usarlo per connetterti all'interfaccia utente ClusterControl corretta:
Questo completa la nostra introduzione in due parti ai cluster a disponibilità elevata di ClusterControl. Come affermato all'inizio, questo è ancora in stato beta, ma non vediamo l'ora di ricevere un feedback dai tuoi test.