W pierwszej części otrzymaliśmy działający klaster cmon HA:
[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)
Mamy trzy działające węzły, jeden działa jako lider, a pozostali to obserwatorzy, którzy są dostępni (otrzymują uderzenia serca i odpowiadają na nie). Pozostałym wyzwaniem jest skonfigurowanie dostępu do interfejsu użytkownika w taki sposób, aby zawsze mieć dostęp do interfejsu użytkownika w węźle lidera. W tym poście na blogu przedstawimy jedno z możliwych rozwiązań, które pozwolą Ci to osiągnąć.
Konfigurowanie HAProxy
Ten problem nie jest dla nas nowy. W przypadku każdego klastra replikacji, MySQL lub PostgreSQL, nie ma to znaczenia, istnieje pojedynczy węzeł, do którego powinniśmy wysyłać nasze zapisy. Jednym ze sposobów osiągnięcia tego byłoby użycie HAProxy i dodanie kilku zewnętrznych testów, które testują stan węzła i na tej podstawie zwracają odpowiednie wartości. To w zasadzie to, co zamierzamy wykorzystać do rozwiązania naszego problemu. Użyjemy HAProxy jako dobrze przetestowanego proxy warstwy 4 i połączymy go z kontrolami HTTP warstwy 7, które napiszemy dokładnie dla naszego przypadku użycia. Najpierw zainstalujmy HAProxy. Połączymy go z ClusterControl, ale równie dobrze można go zainstalować w osobnym węźle (najlepiej w węzłach - aby usunąć HAProxy jako pojedynczy punkt awarii).
apt install haproxy
To ustawia HAProxy. Gdy to zrobimy, musimy wprowadzić naszą konfigurację:
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
Możesz chcieć zmienić tutaj niektóre rzeczy, takie jak nazwy węzłów lub backendów, które zawierają tutaj adres IP naszego węzła. Na pewno będziesz chciał zmienić serwery, które zamierzasz uwzględnić w swoim HAProxy.
Najważniejsze bity to:
bind *:81
HAProxy będzie nasłuchiwać na porcie 81.
option httpchk
Włączono sprawdzanie warstwy 7 w węzłach zaplecza.
default-server port 9201 inter 20s downinter 30s rise 2 fall 2 slowstart 60s maxconn 64 maxqueue 128 weight 100
Sprawdzenie warstwy 7 zostanie wykonane na porcie 9201.
Gdy to zrobisz, uruchom HAProxy.
Konfigurowanie xinetd i sprawdzanie skryptu
Zamierzamy użyć xinetd do wykonania sprawdzenia i zwrócenia poprawnych odpowiedzi do HAProxy. Kroki opisane w tym akapicie należy wykonać na wszystkich węzłach klastra cmon HA.
Najpierw zainstaluj xinetd:
[email protected]:~# apt install xinetd
Po wykonaniu tej czynności musimy dodać następujący wiersz:
cmonhachk 9201/tcp
do /etc/services - to pozwoli xinetd otworzyć usługę, która będzie nasłuchiwać na porcie 9201. Następnie musimy dodać sam plik usługi. Powinien znajdować się w /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
}
Na koniec potrzebujemy skryptu sprawdzającego, który jest wywoływany przez xinetd. Zgodnie z definicją w pliku usługi znajduje się on w /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)
Po utworzeniu pliku upewnij się, że jest on wykonywalny:
chmod u+x /usr/local/sbin/cmonhachk.py
Ideą tego skryptu jest testowanie stanu węzłów za pomocą polecenia „s9s controller --list --long”, a następnie sprawdzanie danych wyjściowych odpowiednich dla adresu IP, który może znaleźć na węzeł lokalny. Pozwala to skryptowi określić, czy host, na którym jest wykonywany, jest liderem, czy nie. Jeśli węzeł jest liderem, skrypt zwraca kod „HTTP/1.1 200 OK”, który HAProxy interpretuje jako dostępny i kieruje do niego ruch. W przeciwnym razie zwraca „HTTP/1.1 503 Usługa niedostępna”, co jest traktowane jako węzeł, który nie jest sprawny i ruch nie będzie tam kierowany. W rezultacie, bez względu na to, który węzeł zostanie liderem, HAProxy wykryje go i oznaczy jako dostępny w backendzie:
Może być konieczne ponowne uruchomienie HAProxy i xinetd, aby zastosować zmiany konfiguracji przed wszystkimi części zaczną działać poprawnie.
Posiadanie więcej niż jednego HAProxy zapewnia, że mamy sposób na dostęp do interfejsu użytkownika ClusterControl, nawet jeśli jeden z węzłów HAProxy ulegnie awarii, ale nadal mamy dwie (lub więcej) różne nazwy hosta lub adres IP do połączenia z interfejsem użytkownika ClusterControl. Aby było to wygodniejsze, wdrożymy Keepalived na HAProxy. Będzie monitorował stan usług HAProxy i przypisywał do jednego z nich wirtualny adres IP. Jeśli ten HAProxy stanie się niedostępny, VIP zostanie przeniesiony do innego dostępnego HAProxy. W rezultacie będziemy mieli pojedynczy punkt wejścia (VIP lub powiązana z nim nazwa hosta). Kroki, które tutaj podejmiemy, muszą zostać wykonane na wszystkich węzłach, w których zainstalowano HAProxy.
Najpierw zainstalujmy keepalive:
apt install keepalived
Następnie musimy to skonfigurować. Użyjemy następującego pliku konfiguracyjnego:
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
}
Powinieneś zmodyfikować ten plik w różnych węzłach. Adresy IP muszą być poprawnie skonfigurowane, a priorytety powinny być różne na wszystkich węzłach. Proszę również skonfigurować VIP, który ma sens w Twojej sieci. Możesz także zmienić interfejs - użyliśmy eth1, czyli miejsca, w którym adres IP jest przypisywany na maszynach wirtualnych stworzonych przez Vagrant.
Uruchom Keepalive z tym plikiem konfiguracyjnym i powinieneś być gotowy. Dopóki VIP działa w jednym węźle HAProxy, powinieneś być w stanie użyć go do połączenia z odpowiednim interfejsem użytkownika ClusterControl:
To kończy nasze dwuczęściowe wprowadzenie do klastrów ClusterControl o wysokiej dostępności. Jak powiedzieliśmy na początku, jest to nadal wersja beta, ale czekamy na opinie z twoich testów.