Na primeira parte, acabamos com um cluster cmon HA funcional:
[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)
Temos três nós em funcionamento, um está atuando como líder e os restantes são seguidores, que são acessíveis (eles recebem pulsações e respondem a eles). O desafio restante é configurar o acesso à interface do usuário de uma maneira que nos permita sempre acessar a interface do usuário no nó líder. Neste post do blog, apresentaremos uma das soluções possíveis que permitirão que você faça exatamente isso.
Configurando o HAProxy
Este problema não é novo para nós. Com cada cluster de replicação, MySQL ou PostgreSQL, não importa, há um único nó para onde devemos enviar nossas gravações. Uma maneira de fazer isso seria usar o HAProxy e adicionar algumas verificações externas que testam o estado do nó e, com base nisso, retornam os valores adequados. Isso é basicamente o que vamos usar para resolver nosso problema. Usaremos o HAProxy como um proxy de camada 4 bem testado e o combinaremos com verificações HTTP de camada 7 que escreveremos precisamente para nosso caso de uso. Antes de mais nada, vamos instalar o HAProxy. Vamos colocá-lo com o ClusterControl, mas ele também pode ser instalado em um nó separado (idealmente, nós - para remover o HAProxy como o único ponto de falha).
apt install haproxy
Isso configura o HAProxy. Feito isso, temos que introduzir nossa configuração:
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
Você pode querer mudar algumas coisas aqui como os nomes dos nós ou backend que incluem aqui o IP do nosso nó. Você definitivamente desejará alterar os servidores que incluirá em seu HAProxy.
Os bits mais importantes são:
bind *:81
HAProxy escutará na porta 81.
option httpchk
Ativamos a verificação da camada 7 nos nós de back-end.
default-server port 9201 inter 20s downinter 30s rise 2 fall 2 slowstart 60s maxconn 64 maxqueue 128 weight 100
A verificação da camada 7 será executada na porta 9201.
Uma vez feito isso, inicie o HAProxy.
Configurando o xinetd e o script de verificação
Vamos usar o xinetd para executar a verificação e retornar as respostas corretas ao HAProxy. As etapas descritas neste parágrafo devem ser executadas em todos os nós do cluster cmon HA.
Primeiro, instale o xinetd:
[email protected]:~# apt install xinetd
Uma vez feito isso, temos que adicionar a seguinte linha:
cmonhachk 9201/tcp
para /etc/services - isso permitirá que o xinetd abra um serviço que escutará na porta 9201. Então temos que adicionar o próprio arquivo de serviço. Ele deve estar localizado em /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
}
Finalmente, precisamos do script de verificação que é chamado pelo xinetd. Conforme definido no arquivo de serviço, ele está localizado em /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)
Depois de criar o arquivo, verifique se ele é executável:
chmod u+x /usr/local/sbin/cmonhachk.py
A ideia por trás deste script é que ele testa o status dos nós usando o comando “s9s controller --list --long” e então verifica a saída relevante para o IP que ele pode encontrar no nó local. Isso permite que o script determine se o host no qual é executado é um líder ou não. Se o nó for o líder, o script retorna o código “HTTP/1.1 200 OK”, que o HAProxy interpreta como o nó está disponível e roteia o tráfego para ele. Caso contrário, ele retorna “HTTP/1.1 503 Service Unavailable”, que é tratado como um nó, que não está íntegro e o tráfego não será roteado para lá. Como resultado, não importa qual nó se tornará líder, o HAProxy o detectará e o marcará como disponível no back-end:
Pode ser necessário reiniciar o HAProxy e o xinetd para aplicar as alterações de configuração antes de todos os peças começarão a funcionar corretamente.
Ter mais de um HAProxy garante que tenhamos uma maneira de acessar a interface do usuário do ClusterControl, mesmo que um dos nós do HAProxy falhe, mas ainda temos dois (ou mais) nomes de host ou IP diferentes para conectar à interface do usuário do ClusterControl. Para torná-lo mais confortável, implantaremos o Keepalived em cima do HAProxy. Ele monitorará o estado dos serviços HAProxy e atribuirá o IP virtual a um deles. Se esse HAProxy ficar indisponível, o VIP será movido para outro HAProxy disponível. Como resultado, teremos um único ponto de entrada (VIP ou um hostname associado a ele). As etapas que seguiremos aqui devem ser executadas em todos os nós onde o HAProxy foi instalado.
Primeiro, vamos instalar o keepalived:
apt install keepalived
Depois temos que configurá-lo. Usaremos o seguinte arquivo de configuração:
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
}
Você deve modificar este arquivo em nós diferentes. Os endereços IP devem ser configurados corretamente e a prioridade deve ser diferente em todos os nós. Por favor, configure também o VIP que faça sentido em sua rede. Você também pode querer alterar a interface - usamos eth1, que é onde o IP é atribuído nas máquinas virtuais criadas pelo Vagrant.
Inicie o keepalived com este arquivo de configuração e você deve estar pronto para ir. Enquanto o VIP estiver ativo em um nó HAProxy, você poderá usá-lo para se conectar à interface do usuário do ClusterControl adequada:
Isso conclui nossa introdução em duas partes aos clusters altamente disponíveis do ClusterControl. Como dissemos no início, ainda está em estado beta, mas estamos ansiosos pelo feedback de seus testes.