ProxySQL to inteligentny i wydajny serwer proxy SQL, który obsługuje MySQL, MariaDB i ClickHouse. Ostatnio ProxySQL 2.0 stał się GA i zawiera nowe ekscytujące funkcje, takie jak spójne odczyty GTID, frontend SSL, natywna obsługa Galera i MySQL Group Replication.
Stosunkowo łatwo jest uruchomić ProxySQL jako kontener Docker. Pisaliśmy wcześniej o tym, jak uruchomić ProxySQL na Kubernetes jako kontener pomocniczy lub jako usługę Kubernetes, która jest oparta na ProxySQL 1.x. W tym poście na blogu zamierzamy użyć nowej wersji ProxySQL 2.x, która wykorzystuje inne podejście do konfiguracji klastra Galera.
Obraz dokera ProxySQL 2.x
Wydaliśmy nowy kontener obrazu ProxySQL 2.0 Docker, który jest dostępny w Docker Hub. README zawiera szereg przykładów konfiguracji, szczególnie dla Galera i MySQL Replication, przed i po v2.x. Linie konfiguracyjne można zdefiniować w pliku tekstowym i zmapować na ścieżkę kontenera w /etc/proxysql.cnf w celu załadowania do usługi ProxySQL.
Znacznik obrazu „najnowszy” nadal wskazuje na 1.x, dopóki ProxySQL 2.0 oficjalnie nie stanie się GA (nie widzieliśmy jeszcze żadnego oficjalnego bloga/artykułu dotyczącego wydania od zespołu ProxySQL). Oznacza to, że za każdym razem, gdy zainstalujesz obraz ProxySQL przy użyciu najnowszego tagu firmy Manynines, nadal otrzymasz z nim wersję 1.x. Zwróć uwagę, że nowe przykładowe konfiguracje włączają również statystyki internetowe ProxySQL (wprowadzone w wersji 1.4.4, ale wciąż w wersji beta) - prosty pulpit nawigacyjny, który podsumowuje ogólną konfigurację i stan samego ProxySQL.
Obsługa ProxySQL 2.x dla klastra Galera
Porozmawiajmy bardziej szczegółowo o natywnej obsłudze Galera Cluster. Nowa tabela mysql_galera_hostgroups składa się z następujących pól:
- writer_hostgroup : Identyfikator grupy hostów, która będzie zawierać wszystkich członków, którzy są pisarzami (read_only=0).
- backup_writer_hostgroup : Jeśli klaster działa w trybie wielu zapisów (tj. istnieje wiele węzłów z parametrem tylko do odczytu=0) i parametr max_writers jest ustawiony na mniejszą liczbę niż całkowita liczba węzłów, dodatkowe węzły są przenoszone do tej grupy hostów zapisujących kopie zapasowe.
- reader_hostgroup : Identyfikator grupy hostów, która będzie zawierać wszystkich członków będących czytelnikami (tj. węzły, które mają tylko do odczytu=1)
- offline_hostgroup : Gdy monitorowanie ProxySQL określi, że host jest w trybie OFFLINE, host zostanie przeniesiony do grupy hostów_offline.
- aktywny : wartość logiczna (0 lub 1) aktywująca grupę hostów
- max_writers : Kontroluje maksymalną dozwoloną liczbę węzłów w grupie hostów zapisujących, jak wspomniano wcześniej, dodatkowe węzły zostaną przeniesione do grupy backup_writer_hostgroup.
- writer_is_also_reader : Gdy 1, węzeł w writer_hostgroup zostanie również umieszczony w reader_hostgroup tak, aby był używany do odczytów. Gdy jest ustawiony na 2, węzły z grupy backup_writer_hostgroup zostaną umieszczone w grupie reader_hostgroup zamiast węzłów w grupie writer_hostgroup.
- max_transactions_behind : określa maksymalną liczbę zestawów zapisu, które węzeł w klastrze może umieścić w kolejce, zanim węzeł zostanie ODCIĄGNIĘTY, aby zapobiec nieaktualnym odczytom (jest to określane przez zapytanie o zmienną Galera wsrep_local_recv_queue).
- komentarz : Pole tekstowe, które można wykorzystać do dowolnych celów zdefiniowanych przez użytkownika
Oto przykładowa konfiguracja dla mysql_galera_hostgroups w formacie tabeli:
Admin> select * from mysql_galera_hostgroups\G
*************************** 1. row ***************************
writer_hostgroup: 10
backup_writer_hostgroup: 20
reader_hostgroup: 30
offline_hostgroup: 9999
active: 1
max_writers: 1
writer_is_also_reader: 2
max_transactions_behind: 20
comment:
ProxySQL przeprowadza kontrole stanu Galera, monitorując następujące stany/zmienne MySQL:
- tylko do odczytu - Jeśli WŁĄCZONE, ProxySQL zgrupuje zdefiniowanego hosta w reader_hostgroup, chyba że writer_is_also_reader ma wartość 1.
- wsrep_desync - Jeśli WŁĄCZONE, ProxySQL oznaczy węzeł jako niedostępny, przenosząc go do grupy hostów_offline.
- wsrep_reject_queries - Jeśli ta zmienna jest WŁĄCZONA, ProxySQL oznaczy węzeł jako niedostępny, przenosząc go do grupy hostów_offline (przydatne w niektórych sytuacjach konserwacyjnych).
- wsrep_sst_donor_rejects_queries - Jeśli ta zmienna jest WŁĄCZONA, ProxySQL oznaczy węzeł jako niedostępny, podczas gdy węzeł Galera służy jako dawca SST, przenosząc go do grupy hostów_offline.
- wsrep_local_state - Jeśli ten status zwróci inny niż 4 (4 oznacza zsynchronizowany), ProxySQL oznaczy węzeł jako niedostępny i przeniesie go do grupy hostów_offline.
- wsrep_local_recv_queue - Jeśli ten status jest wyższy niż max_transactions_behind, węzeł zostanie odrzucony.
- wsrep_cluster_status - Jeśli ten status zwróci inny niż podstawowy, ProxySQL oznaczy węzeł jako niedostępny i przeniesie go do grupy hostów_offline.
To powiedziawszy, łącząc te nowe parametry w mysql_galera_hostgroups z mysql_query_rules, ProxySQL 2.x ma elastyczność, aby dopasować się do znacznie większej liczby przypadków użycia Galera. Na przykład, można mieć grupy hostów z jednym zapisem, z wieloma zapisami i wieloma czytnikami zdefiniowane jako docelowa grupa hostów reguły zapytania, z możliwością ograniczenia liczby zapisujących i dokładniejszej kontroli nad zachowaniem nieaktualnych odczytów.
Porównajmy to z ProxySQL 1.x, gdzie użytkownik musiał jawnie zdefiniować harmonogram, aby wywołać zewnętrzny skrypt w celu przeprowadzenia kontroli stanu zaplecza i aktualizacji stanu serwerów bazy danych. Wymaga to pewnego dostosowania skryptu (użytkownik musi zaktualizować użytkownika/hasło/port administratora ProxySQL) oraz zależy od dodatkowego narzędzia (klienta MySQL) do połączenia z interfejsem administratora ProxySQL.
Oto przykładowa konfiguracja harmonogramu skryptów kontroli stanu Galera w formacie tabeli dla ProxySQL 1.x:
Admin> select * from scheduler\G
*************************** 1. row ***************************
id: 1
active: 1
interval_ms: 2000
filename: /usr/share/proxysql/tools/proxysql_galera_checker.sh
arg1: 10
arg2: 20
arg3: 1
arg4: 1
arg5: /var/lib/proxysql/proxysql_galera_checker.log
comment:
Poza tym, ponieważ wątek harmonogramu ProxySQL wykonuje niezależnie dowolny skrypt, dostępnych jest wiele wersji skryptów sprawdzających kondycję. Wszystkie instancje ProxySQL wdrożone przez ClusterControl używają domyślnego skryptu dostarczonego przez pakiet instalacyjny ProxySQL.
W ProxySQL 2.x zmienne max_writers i writer_is_also_reader mogą określać, w jaki sposób ProxySQL dynamicznie grupuje serwery MySQL zaplecza i ma bezpośredni wpływ na dystrybucję połączeń i routing zapytań. Rozważmy na przykład następujące serwery zaplecza MySQL:
Admin> select hostgroup_id, hostname, status, weight from mysql_servers;
+--------------+--------------+--------+--------+
| hostgroup_id | hostname | status | weight |
+--------------+--------------+--------+--------+
| 10 | DB1 | ONLINE | 1 |
| 10 | DB2 | ONLINE | 1 |
| 10 | DB3 | ONLINE | 1 |
+--------------+--------------+--------+--------+
Wraz z następującą definicją grup hostów Galera:
Admin> select * from mysql_galera_hostgroups\G
*************************** 1. row ***************************
writer_hostgroup: 10
backup_writer_hostgroup: 20
reader_hostgroup: 30
offline_hostgroup: 9999
active: 1
max_writers: 1
writer_is_also_reader: 2
max_transactions_behind: 20
comment:
Biorąc pod uwagę, że wszystkie hosty są uruchomione i działają, ProxySQL najprawdopodobniej pogrupuje hosty w następujący sposób:
Spójrzmy na nie jeden po drugim:
Konfiguracja | Opis |
---|---|
writer_is_also_reader=0 |
|
writer_is_also_reader=1 |
|
writer_is_also_reader=2 |
|
W tej konfiguracji można mieć różne możliwości wyboru miejsca docelowego grupy hostów, aby zaspokoić określone obciążenia. Zapisy „Hotspot” można skonfigurować tak, aby przechodziły tylko do jednego serwera w celu zmniejszenia konfliktów z wieloma serwerami głównymi, zapisy nie powodujące konfliktu mogą być równomiernie rozprowadzane na innych serwerach głównych, większość odczytów może być dystrybuowana równomiernie na wszystkich serwerach MySQL lub serwerach nie zapisujących, odczyty krytyczne mogą być przekazywane do najbardziej aktualnych serwerów, a odczyty analityczne mogą być przekazywane do repliki podrzędnej.
Wdrożenie ProxySQL w klastrze Galera
W tym przykładzie załóżmy, że mamy już trzywęzłowy klaster Galera wdrożony przez ClusterControl, jak pokazano na poniższym diagramie:
Nasze aplikacje Wordpress działają na platformie Docker, podczas gdy baza danych Wordpress jest hostowana na naszym klastrze Galera działającym na serwerach bare-metal. Zdecydowaliśmy się uruchomić kontener ProxySQL obok naszych kontenerów Wordpress, aby mieć lepszą kontrolę nad routingiem zapytań do bazy danych Wordpress i w pełni wykorzystać naszą infrastrukturę klastra bazy danych. Ponieważ współczynnik odczytu i zapisu wynosi około 80%-20%, chcemy skonfigurować ProxySQL tak, aby:
- Przesyłaj wszystkie zapisy do jednego węzła Galera (mniej konfliktów, skup się na zapisie)
- Zrównoważ wszystkie odczyty do pozostałych dwóch węzłów Galera (lepsza dystrybucja dla większości obciążenia)
Najpierw utwórz plik konfiguracyjny ProxySQL na hoście Docker, abyśmy mogli zmapować go do naszego kontenera:
$ mkdir /root/proxysql-docker
$ vim /root/proxysql-docker/proxysql.cnf
Następnie skopiuj następujące wiersze (omówimy wiersze konfiguracji poniżej):
datadir="/var/lib/proxysql"
admin_variables=
{
admin_credentials="admin:admin"
mysql_ifaces="0.0.0.0:6032"
refresh_interval=2000
web_enabled=true
web_port=6080
stats_credentials="stats:admin"
}
mysql_variables=
{
threads=4
max_connections=2048
default_query_delay=0
default_query_timeout=36000000
have_compress=true
poll_timeout=2000
interfaces="0.0.0.0:6033;/tmp/proxysql.sock"
default_schema="information_schema"
stacksize=1048576
server_version="5.1.30"
connect_timeout_server=10000
monitor_history=60000
monitor_connect_interval=200000
monitor_ping_interval=200000
ping_interval_server_msec=10000
ping_timeout_server=200
commands_stats=true
sessions_sort=true
monitor_username="proxysql"
monitor_password="proxysqlpassword"
monitor_galera_healthcheck_interval=2000
monitor_galera_healthcheck_timeout=800
}
mysql_galera_hostgroups =
(
{
writer_hostgroup=10
backup_writer_hostgroup=20
reader_hostgroup=30
offline_hostgroup=9999
max_writers=1
writer_is_also_reader=1
max_transactions_behind=30
active=1
}
)
mysql_servers =
(
{ address="db1.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
{ address="db2.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
{ address="db3.cluster.local" , port=3306 , hostgroup=10, max_connections=100 }
)
mysql_query_rules =
(
{
rule_id=100
active=1
match_pattern="^SELECT .* FOR UPDATE"
destination_hostgroup=10
apply=1
},
{
rule_id=200
active=1
match_pattern="^SELECT .*"
destination_hostgroup=30
apply=1
},
{
rule_id=300
active=1
match_pattern=".*"
destination_hostgroup=10
apply=1
}
)
mysql_users =
(
{ username = "wordpress", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 },
{ username = "sbtest", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 }
)
Teraz odwiedźmy niektóre z większości sekcji konfiguracyjnych. Po pierwsze, definiujemy konfigurację grup hostów Galera jak poniżej:
mysql_galera_hostgroups =
(
{
writer_hostgroup=10
backup_writer_hostgroup=20
reader_hostgroup=30
offline_hostgroup=9999
max_writers=1
writer_is_also_reader=1
max_transactions_behind=30
active=1
}
)
Hostgroup 10 będzie to writer_hostgroup, hostgroup 20 dla backup_writer i hostgroup 30 dla czytnika. Ustawiamy max_writers na 1, więc możemy mieć grupę hostów z jednym zapisem dla grupy hostów 10, do której powinny być wysyłane wszystkie zapisy. Następnie definiujemy writer_is_also_reader na 1, co sprawi, że wszystkie węzły Galera jako czytnik będą odpowiednie dla zapytań, które mogą być równomiernie rozłożone na wszystkie węzły. Hostgroup 9999 jest zarezerwowana dla offline_hostgroup, jeśli ProxySQL wykryje niedziałające węzły Galera.
Następnie konfigurujemy nasze serwery MySQL z domyślną grupą hostów 10:
mysql_servers =
(
{ address="db1.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
{ address="db2.cluster.local" , port=3306 , hostgroup=10, max_connections=100 },
{ address="db3.cluster.local" , port=3306 , hostgroup=10, max_connections=100 }
)
W powyższych konfiguracjach ProxySQL "zobaczy" nasze grupy hostów jak poniżej:
Następnie definiujemy routing zapytań poprzez reguły zapytań. Zgodnie z naszymi wymaganiami wszystkie odczyty powinny być wysyłane do wszystkich węzłów Galera z wyjątkiem programu zapisującego (grupa hostów 20), a wszystko inne jest przekazywane do grupy hostów 10 dla pojedynczego programu zapisującego:
mysql_query_rules =
(
{
rule_id=100
active=1
match_pattern="^SELECT .* FOR UPDATE"
destination_hostgroup=10
apply=1
},
{
rule_id=200
active=1
match_pattern="^SELECT .*"
destination_hostgroup=20
apply=1
},
{
rule_id=300
active=1
match_pattern=".*"
destination_hostgroup=10
apply=1
}
)
Na koniec definiujemy użytkowników MySQL, którzy będą przepuszczani przez ProxySQL:
mysql_users =
(
{ username = "wordpress", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 },
{ username = "sbtest", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 }
)
Ustawiamy transaction_persistent na 0, aby wszystkie połączenia przychodzące od tych użytkowników respektowały reguły zapytań dla routingu odczytu i zapisu. W przeciwnym razie połączenia trafiłyby na jedną grupę hostów, co niweczy cel równoważenia obciążenia. Nie zapomnij najpierw utworzyć tych użytkowników na wszystkich serwerach MySQL. W przypadku użytkownika ClusterControl możesz użyć funkcji Zarządzaj -> Schematy i użytkownicy, aby utworzyć tych użytkowników.
Jesteśmy teraz gotowi do uruchomienia naszego kontenera. Zamierzamy zmapować plik konfiguracyjny ProxySQL jako montowanie powiązania podczas uruchamiania kontenera ProxySQL. Zatem polecenie uruchomienia będzie wyglądało następująco:
$ docker run -d \
--name proxysql2 \
--hostname proxysql2 \
--publish 6033:6033 \
--publish 6032:6032 \
--publish 6080:6080 \
--restart=unless-stopped \
-v /root/proxysql/proxysql.cnf:/etc/proxysql.cnf \
severalnines/proxysql:2.0
Na koniec zmień bazę danych Wordpress wskazującą na port kontenera ProxySQL 6033, na przykład:
$ docker run -d \
--name wordpress \
--publish 80:80 \
--restart=unless-stopped \
-e WORDPRESS_DB_HOST=proxysql2:6033 \
-e WORDPRESS_DB_USER=wordpress \
-e WORDPRESS_DB_HOST=passw0rd \
wordpress
W tym momencie nasza architektura wygląda mniej więcej tak:
Jeśli chcesz, aby kontener ProxySQL był trwały, zmapuj /var/lib/proxysql/ na wolumin Docker lub montuj powiązanie, na przykład:
$ docker run -d \
--name proxysql2 \
--hostname proxysql2 \
--publish 6033:6033 \
--publish 6032:6032 \
--publish 6080:6080 \
--restart=unless-stopped \
-v /root/proxysql/proxysql.cnf:/etc/proxysql.cnf \
-v proxysql-volume:/var/lib/proxysql \
severalnines/proxysql:2.0
Należy pamiętać, że uruchomienie z pamięcią trwałą, jak powyżej, spowoduje, że nasz /root/proxysql/proxysql.cnf stanie się przestarzały przy drugim ponownym uruchomieniu. Wynika to z wielowarstwowej konfiguracji ProxySQL, w której jeśli istnieje /var/lib/proxysql/proxysql.db, ProxySQL pominie ładowanie opcji z pliku konfiguracyjnego i załaduje to, co jest w bazie danych SQLite (chyba że uruchomisz usługę proxysql z opcją --initial flaga). To powiedziawszy, następne zarządzanie konfiguracją ProxySQL musi być wykonane przez konsolę administracyjną ProxySQL na porcie 6032, zamiast używać pliku konfiguracyjnego.
Monitorowanie
Dziennik procesu ProxySQL domyślnie loguje się do sysloga i można go wyświetlić za pomocą standardowego polecenia docker:
$ docker ps
$ docker logs proxysql2
Aby zweryfikować bieżącą grupę hostów, wykonaj zapytanie do tabeli runtime_mysql_servers:
$ docker exec -it proxysql2 mysql -uadmin -padmin -h127.0.0.1 -P6032 --prompt='Admin> '
Admin> select hostgroup_id,hostname,status from runtime_mysql_servers;
+--------------+--------------+--------+
| hostgroup_id | hostname | status |
+--------------+--------------+--------+
| 10 | 192.168.0.21 | ONLINE |
| 30 | 192.168.0.21 | ONLINE |
| 30 | 192.168.0.22 | ONLINE |
| 30 | 192.168.0.23 | ONLINE |
| 20 | 192.168.0.22 | ONLINE |
| 20 | 192.168.0.23 | ONLINE |
+--------------+--------------+--------+
Jeśli wybrany program zapisujący ulegnie awarii, zostanie on przeniesiony do grupy hostów offline (HID 9999):
Admin> select hostgroup_id,hostname,status from runtime_mysql_servers;
+--------------+--------------+--------+
| hostgroup_id | hostname | status |
+--------------+--------------+--------+
| 10 | 192.168.0.22 | ONLINE |
| 9999 | 192.168.0.21 | ONLINE |
| 30 | 192.168.0.22 | ONLINE |
| 30 | 192.168.0.23 | ONLINE |
| 20 | 192.168.0.23 | ONLINE |
+--------------+--------------+--------+
Powyższe zmiany topologii można zilustrować na poniższym diagramie:
Włączyliśmy również interfejs użytkownika statystyk internetowych z admin-web_enabled=true. Aby uzyskać dostęp do interfejsu internetowego, po prostu przejdź do hosta platformy Docker w porcie 6080, na przykład:http://192.168.0.200:8060 i zostaniesz poproszony o wyskakujące okienko z nazwą użytkownika/hasłem. Wprowadź poświadczenia zdefiniowane w admin-stats_credentials i powinieneś zobaczyć następującą stronę:
Monitorując tabelę puli połączeń MySQL, możemy uzyskać przegląd dystrybucji połączeń dla wszystkich grup hostów:
Admin> select hostgroup, srv_host, status, ConnUsed, MaxConnUsed, Queries from stats.stats_mysql_connection_pool order by srv_host;
+-----------+--------------+--------+----------+-------------+---------+
| hostgroup | srv_host | status | ConnUsed | MaxConnUsed | Queries |
+-----------+--------------+--------+----------+-------------+---------+
| 20 | 192.168.0.23 | ONLINE | 5 | 24 | 11458 |
| 30 | 192.168.0.23 | ONLINE | 0 | 0 | 0 |
| 20 | 192.168.0.22 | ONLINE | 2 | 24 | 11485 |
| 30 | 192.168.0.22 | ONLINE | 0 | 0 | 0 |
| 10 | 192.168.0.21 | ONLINE | 32 | 32 | 9746 |
| 30 | 192.168.0.21 | ONLINE | 0 | 0 | 0 |
+-----------+--------------+--------+----------+-------------+---------+
Powyższe dane wyjściowe pokazują, że grupa hostów 30 niczego nie przetwarza, ponieważ nasze reguły zapytań nie mają tej grupy hostów skonfigurowanej jako docelowa grupa hostów.
Statystyki związane z węzłami Galera można przeglądać w tabeli mysql_server_galera_log:
Admin> select * from mysql_server_galera_log order by time_start_us desc limit 3\G
*************************** 1. row ***************************
hostname: 192.168.0.23
port: 3306
time_start_us: 1552992553332489
success_time_us: 2045
primary_partition: YES
read_only: NO
wsrep_local_recv_queue: 0
wsrep_local_state: 4
wsrep_desync: NO
wsrep_reject_queries: NO
wsrep_sst_donor_rejects_queries: NO
error: NULL
*************************** 2. row ***************************
hostname: 192.168.0.22
port: 3306
time_start_us: 1552992553329653
success_time_us: 2799
primary_partition: YES
read_only: NO
wsrep_local_recv_queue: 0
wsrep_local_state: 4
wsrep_desync: NO
wsrep_reject_queries: NO
wsrep_sst_donor_rejects_queries: NO
error: NULL
*************************** 3. row ***************************
hostname: 192.168.0.21
port: 3306
time_start_us: 1552992553329013
success_time_us: 2715
primary_partition: YES
read_only: NO
wsrep_local_recv_queue: 0
wsrep_local_state: 4
wsrep_desync: NO
wsrep_reject_queries: NO
wsrep_sst_donor_rejects_queries: NO
error: NULL
Zestaw wyników zwraca powiązany stan zmiennej/stanu MySQL dla każdego węzła Galera dla określonego znacznika czasu. W tej konfiguracji skonfigurowaliśmy kontrolę stanu Galera tak, aby była uruchamiana co 2 sekundy (monitor_galera_healthcheck_interval=2000). W związku z tym maksymalny czas przełączania awaryjnego wynosiłby około 2 sekund, jeśli w klastrze nastąpi zmiana topologii.
Referencje
- Natywna obsługa Galera ProxySQL
- Rozwiązanie HA i klastrowanie:ProxySQL jako inteligentny router dla Galera i replikacji grupowej
- Obraz dockera ProxySQL firmy Severalnines
- Jak monitorować ProxySQL za pomocą Prometheus i ClusterControl