ProxySQL har stödt inbyggd klustring sedan v1.4.2. Detta innebär att flera ProxySQL-instanser är klustermedvetna; de är medvetna om varandras tillstånd och kan hantera konfigurationsändringarna automatiskt genom att synkronisera upp till den mest uppdaterade konfigurationen baserat på konfigurationsversion, tidsstämpel och kontrollsumma. Kolla in det här blogginlägget som visar hur du konfigurerar klustringsstöd för ProxySQL och hur du kan förvänta dig att det ska bete sig.
ProxySQL är en decentraliserad proxy, rekommenderad att distribueras närmare applikationen. Det här tillvägagångssättet skalar ganska bra till och med upp till hundratals noder, eftersom det var designat för att enkelt kunna omkonfigureras under körning. För att effektivt hantera flera ProxySQL-noder måste man se till att alla ändringar som görs på en av noderna ska tillämpas på alla noder i farmen. Utan inbyggd klustring måste man manuellt exportera konfigurationerna och importera dem till de andra noderna (även om du kan automatisera detta själv).
I det tidigare blogginlägget har vi täckt ProxySQL-klustring via Kubernetes ConfigMap. Detta tillvägagångssätt är mer eller mindre ganska effektivt med den centraliserade konfigurationsmetoden i ConfigMap. Allt som laddas in i ConfigMap kommer att monteras i pods. Uppdatering av konfigurationen kan göras via versionshantering (ändra innehållet i proxysql.cnf och ladda in det i ConfigMap med ett annat namn) och tryck sedan till poddarna beroende på schemaläggning och uppdateringsstrategi för distributionsmetoden.
Men i en snabbt föränderlig miljö är detta ConfigMap-tillvägagångssätt förmodligen inte den bästa metoden eftersom för att ladda den nya konfigurationen krävs omläggning av pod för att återmontera ConfigMap-volymen och detta kan äventyra ProxySQL-tjänsten som helhet. Till exempel, låt oss säga i vår miljö kräver vår strikta lösenordspolicy att tvinga MySQL-användarlösenordet utgången var 7:e dag, vilket vi skulle behöva uppdatera ProxySQL ConfigMap för det nya lösenordet varje vecka. Som en sidoanteckning kräver MySQL-användare inuti ProxySQL användaren och lösenordet för att matcha det på backend MySQL-servrarna. Det är där vi bör börja använda ProxySQL inbyggt klustringsstöd i Kubernetes för att automatiskt tillämpa konfigurationsändringarna utan krångel med ConfigMap-versionering och omläggning av pod.
I det här blogginlägget visar vi dig hur du kör inbyggd ProxySQL-klustring med huvudlös tjänst på Kubernetes. Vår högnivåarkitektur kan illustreras enligt nedan:
Vi har 3 Galera-noder som körs på barmetallinfrastruktur som distribueras och hanteras av ClusterControl:
- 192.168.0.21
- 192.168.0.22
- 192.168.0.23
Alla våra applikationer körs som pods inom Kubernetes. Tanken är att introducera två ProxySQL-instanser mellan applikationen och vårt databaskluster för att fungera som en omvänd proxy. Applikationer kommer sedan att ansluta till ProxySQL-pods via Kubernetes-tjänsten som kommer att vara belastningsbalanserad och failover över ett antal ProxySQL-repliker.
Följande är en sammanfattning av vår Kubernetes-inställning:
[email protected]:~# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kube1 Ready master 5m v1.15.1 192.168.100.201 <none> Ubuntu 18.04.1 LTS 4.15.0-39-generic docker://18.9.7
kube2 Ready <none> 4m1s v1.15.1 192.168.100.202 <none> Ubuntu 18.04.1 LTS 4.15.0-39-generic docker://18.9.7
kube3 Ready <none> 3m42s v1.15.1 192.168.100.203 <none> Ubuntu 18.04.1 LTS 4.15.0-39-generic docker://18.9.7
ProxySQL-konfiguration via ConfigMap
Låt oss först förbereda vår baskonfiguration som kommer att laddas in i ConfigMap. Skapa en fil som heter proxysql.cnf och lägg till följande rader:
datadir="/var/lib/proxysql"
admin_variables=
{
admin_credentials="proxysql-admin:adminpassw0rd;cluster1:secret1pass"
mysql_ifaces="0.0.0.0:6032"
refresh_interval=2000
cluster_username="cluster1"
cluster_password="secret1pass"
cluster_check_interval_ms=200
cluster_check_status_frequency=100
cluster_mysql_query_rules_save_to_disk=true
cluster_mysql_servers_save_to_disk=true
cluster_mysql_users_save_to_disk=true
cluster_proxysql_servers_save_to_disk=true
cluster_mysql_query_rules_diffs_before_sync=3
cluster_mysql_servers_diffs_before_sync=3
cluster_mysql_users_diffs_before_sync=3
cluster_proxysql_servers_diffs_before_sync=3
}
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="proxysqlpassw0rd"
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="192.168.0.21" , port=3306 , hostgroup=10, max_connections=100 },
{ address="192.168.0.22" , port=3306 , hostgroup=10, max_connections=100 },
{ address="192.168.0.23" , 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=20
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 }
)
proxysql_servers =
(
{ hostname = "proxysql-0.proxysqlcluster", port = 6032, weight = 1 },
{ hostname = "proxysql-1.proxysqlcluster", port = 6032, weight = 1 }
)
Några av ovanstående konfigurationsrader förklaras per avsnitt nedan:
admin_variables
Var uppmärksam på admin_credentials variabel där vi använde icke-standardanvändare som är "proxysql-admin". ProxySQL reserverar standardanvändaren för "admin" endast för lokal anslutning via lokal värd. Därför måste vi använda andra användare för att komma åt ProxySQL-instansen på distans. Annars skulle du få följande felmeddelande:
ERROR 1040 (42000): User 'admin' can only connect locally
Vi har också lagt till kluster_användarnamn och klusterlösenord värde i admin_credentials linje, separerad med semikolon för att tillåta automatisk synkronisering. Alla variabler prefixerade med cluster_* är relaterade till ProxySQL native klustring och är självförklarande.
mysql_galera_hostgroups
Detta är ett nytt direktiv infört för ProxySQL 2.x (vår ProxySQL-avbildning körs på 2.0.5). Om du vill köra på ProxySQL 1.x, ta bort den här delen och använd schemaläggningstabellen istället. Vi har redan förklarat konfigurationsdetaljerna i det här blogginlägget, Hur man kör och konfigurerar ProxySQL 2.0 för MySQL Galera Cluster på Docker under "ProxySQL 2.x Support for Galera Cluster".
mysql_servers
Alla rader är självförklarande, vilket är baserat på tre databasservrar som körs i MySQL Galera Cluster som sammanfattas i följande Topology-skärmdump från ClusterControl:
proxysql_servers
Här definierar vi en lista över ProxySQL-kamrater:
- värdnamn - Peers värdnamn/IP-adress
- port - Peers adminport
- vikt - För närvarande oanvänd, men i färdplanen för framtida förbättringar
- kommentar – kommentarfält i fritt format
I Docker/Kubernetes-miljön finns det flera sätt att upptäcka och länka upp behållarvärdnamn eller IP-adresser och infoga dem i den här tabellen, antingen genom att använda ConfigMap, manuell infogning, via entrypoint.sh-skript, miljövariabler eller på något annat sätt. I Kubernetes, beroende på vilken ReplicationController eller Deployment metod som används, är det lite svårt att gissa poddens lösbara värdnamn i förväg om du inte kör på StatefulSet.
Kolla in den här handledningen om StatefulState pods ordinalindex som ger ett stabilt lösbart värdnamn för de skapade poddarna. Kombinera detta med huvudlös tjänst (förklaras längre ner), det lösbara värdnamnsformatet skulle vara:
{app_name}-{index_number}.{service}
Där {service} är en huvudlös tjänst, vilket förklarar var "proxysql-0.proxysqlcluster" och "proxysql-1.proxysqlcluster" kommer ifrån. Om du vill ha fler än 2 repliker, lägg till fler poster i enlighet med detta genom att lägga till ett stigande indexnummer i förhållande till StatefulSet-applikationens namn.
Nu är vi redo att skjuta in konfigurationsfilen till ConfigMap, som kommer att monteras i varje ProxySQL-pod under driftsättning:
$ kubectl create configmap proxysql-configmap --from-file=proxysql.cnf
Kontrollera om vår ConfigMap är korrekt laddad:
$ kubectl get configmap
NAME DATA AGE
proxysql-configmap 1 7h57m
Skapa ProxySQL-övervakningsanvändare
Nästa steg innan vi startar distributionen är att skapa en ProxySQL-övervakningsanvändare i vårt databaskluster. Eftersom vi kör på Galera-klustret, kör följande satser på en av Galera-noderna:
mysql> CREATE USER 'proxysql'@'%' IDENTIFIED BY 'proxysqlpassw0rd';
mysql> GRANT USAGE ON *.* TO 'proxysql'@'%';
Om du inte har skapat MySQL-användarna (som specificerats under avsnittet mysql_users ovan), måste vi skapa dem också:
mysql> CREATE USER 'wordpress'@'%' IDENTIFIED BY 'passw0rd';
mysql> GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'%';
mysql> CREATE USER 'sbtest'@'%' IDENTIFIED BY 'passw0rd';
mysql> GRANT ALL PRIVILEGES ON sbtest.* TO 'proxysql'@'%';
Det är allt. Vi är nu redo att starta implementeringen.
Distribuera ett StatefulSet
Vi börjar med att skapa två ProxySQL-instanser, eller repliker för redundansändamål med hjälp av StatefulSet.
Låt oss börja med att skapa en textfil som heter proxysql-ss-svc.yml och lägga till följande rader:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: proxysql
labels:
app: proxysql
spec:
replicas: 2
serviceName: proxysqlcluster
selector:
matchLabels:
app: proxysql
tier: frontend
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: proxysql
tier: frontend
spec:
restartPolicy: Always
containers:
- image: severalnines/proxysql:2.0.4
name: proxysql
volumeMounts:
- name: proxysql-config
mountPath: /etc/proxysql.cnf
subPath: proxysql.cnf
ports:
- containerPort: 6033
name: proxysql-mysql
- containerPort: 6032
name: proxysql-admin
volumes:
- name: proxysql-config
configMap:
name: proxysql-configmap
---
apiVersion: v1
kind: Service
metadata:
annotations:
labels:
app: proxysql
tier: frontend
name: proxysql
spec:
ports:
- name: proxysql-mysql
nodePort: 30033
port: 6033
protocol: TCP
targetPort: 6033
- name: proxysql-admin
nodePort: 30032
port: 6032
protocol: TCP
targetPort: 6032
selector:
app: proxysql
tier: frontend
type: NodePort
Det finns två delar av definitionen ovan - StatefulSet och Service. StatefulSet är definitionen av våra pods eller repliker och monteringspunkten för vår ConfigMap-volym, laddad från proxysql-configmap. Nästa avsnitt är tjänstedefinitionen, där vi definierar hur poddarna ska exponeras och dirigeras för det interna eller externa nätverket.
Skapa ProxySQL tillståndsuppsättningen och tjänsten:
$ kubectl create -f proxysql-ss-svc.yml
Verifiera podden och tjänstens tillstånd:
$ kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/proxysql-0 1/1 Running 0 4m46s
pod/proxysql-1 1/1 Running 0 2m59s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10h
service/proxysql NodePort 10.111.240.193 <none> 6033:30033/TCP,6032:30032/TCP 5m28s
Om du tittar på poddens logg skulle du märka att vi blev översvämmade med denna varning:
$ kubectl logs -f proxysql-0
...
2019-08-01 19:06:18 ProxySQL_Cluster.cpp:215:ProxySQL_Cluster_Monitor_thread(): [WARNING] Cluster: unable to connect to peer proxysql-1.proxysqlcluster:6032 . Error: Unknown MySQL server host 'proxysql-1.proxysqlcluster' (0)
Ovanstående betyder helt enkelt att proxysql-0 inte kunde lösa "proxysql-1.proxysqlcluster" och ansluta till det, vilket förväntas eftersom vi inte har skapat vår huvudlösa tjänst för DNS-poster som kommer att behövas för kommunikation mellan ProxySQL.
Kubernetes Headless Service
För att ProxySQL-pods ska kunna lösa det förväntade FQDN och ansluta till det direkt, måste lösningsprocessen kunna slå upp den tilldelade målpoddens IP-adress och inte den virtuella IP-adressen. Det är här huvudlös service kommer in i bilden. När du skapar en huvudlös tjänst genom att sätta "clusterIP=None", konfigureras ingen lastbalansering och ingen kluster-IP (virtuell IP) tilldelas för denna tjänst. Endast DNS konfigureras automatiskt. När du kör en DNS-fråga för huvudlös tjänst får du listan över poddarnas IP-adresser.
Så här ser det ut om vi slår upp de huvudlösa tjänstens DNS-poster för "proxysqlcluster" (i det här exemplet hade vi 3 ProxySQL-instanser):
$ host proxysqlcluster
proxysqlcluster.default.svc.cluster.local has address 10.40.0.2
proxysqlcluster.default.svc.cluster.local has address 10.40.0.3
proxysqlcluster.default.svc.cluster.local has address 10.32.0.2
Medan följande utdata visar DNS-posten för standardtjänsten som heter "proxysql" som löser sig till kluster-IP:
$ host proxysql
proxysql.default.svc.cluster.local has address 10.110.38.154
För att skapa en huvudlös tjänst och koppla den till poddarna måste man definiera ServiceName i StatefulSet-deklarationen, och tjänstdefinitionen måste ha "clusterIP=None" som visas nedan. Skapa en textfil som heter proxysql-headless-svc.yml och lägg till följande rader:
apiVersion: v1
kind: Service
metadata:
name: proxysqlcluster
labels:
app: proxysql
spec:
clusterIP: None
ports:
- port: 6032
name: proxysql-admin
selector:
app: proxysql
Skapa den huvudlösa tjänsten:
$ kubectl create -f proxysql-headless-svc.yml
Bara för verifiering, för närvarande har vi följande tjänster igång:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8h
proxysql NodePort 10.110.38.154 <none> 6033:30033/TCP,6032:30032/TCP 23m
proxysqlcluster ClusterIP None <none> 6032/TCP 4s
Kolla nu in en av våra poddloggar:
$ kubectl logs -f proxysql-0
...
2019-08-01 19:06:19 ProxySQL_Cluster.cpp:215:ProxySQL_Cluster_Monitor_thread(): [WARNING] Cluster: unable to connect to peer proxysql-1.proxysqlcluster:6032 . Error: Unknown MySQL server host 'proxysql-1.proxysqlcluster' (0)
2019-08-01 19:06:19 [INFO] Cluster: detected a new checksum for mysql_query_rules from peer proxysql-1.proxysqlcluster:6032, version 1, epoch 1564686376, checksum 0x3FEC69A5C9D96848 . Not syncing yet ...
2019-08-01 19:06:19 [INFO] Cluster: checksum for mysql_query_rules from peer proxysql-1.proxysqlcluster:6032 matches with local checksum 0x3FEC69A5C9D96848 , we won't sync.
Du skulle märka att Cluster-komponenten kan lösa, ansluta och upptäcka en ny kontrollsumma från den andra peeren, proxysql-1.proxysqlcluster på port 6032 via den huvudlösa tjänsten som kallas "proxysqlcluster". Observera att den här tjänsten endast exponerar port 6032 inom Kubernetes-nätverket, så att den inte kan nås externt.
Vid det här laget är vår distribution nu klar.
Ansluter till ProxySQL
Det finns flera sätt att ansluta till ProxySQL-tjänster. De belastningsbalanserade MySQL-anslutningarna ska skickas till port 6033 från Kubernetes-nätverket och använda port 30033 om klienten ansluter från ett externt nätverk.
För att ansluta till ProxySQL-administratörsgränssnittet från ett externt nätverk kan vi ansluta till porten som definieras under NodePort-avsnittet, 30032 (192.168.100.203 är den primära IP-adressen för värden kube3.local):
$ mysql -uproxysql-admin -padminpassw0rd -h192.168.100.203 -P30032
Använd clusterIP 10.110.38.154 (definierad under "proxysql"-tjänsten) på port 6032 om du vill komma åt den från andra pods i Kubernetes-nätverket.
Utför sedan ProxySQL-konfigurationsändringarna som du vill och ladda dem till runtime:
mysql> INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('newuser','passw0rd',10);
mysql> LOAD MYSQL USERS TO RUNTIME;
Du kommer att märka följande rader i en av poddarna som indikerar att konfigurationssynkroniseringen är klar:
$ kubectl logs -f proxysql-0
...
2019-08-02 03:53:48 [INFO] Cluster: detected a peer proxysql-1.proxysqlcluster:6032 with mysql_users version 2, epoch 1564718027, diff_check 4. Own version: 1, epoch: 1564714803. Proceeding with remote sync
2019-08-02 03:53:48 [INFO] Cluster: detected peer proxysql-1.proxysqlcluster:6032 with mysql_users version 2, epoch 1564718027
2019-08-02 03:53:48 [INFO] Cluster: Fetching MySQL Users from peer proxysql-1.proxysqlcluster:6032 started
2019-08-02 03:53:48 [INFO] Cluster: Fetching MySQL Users from peer proxysql-1.proxysqlcluster:6032 completed
Tänk på att den automatiska synkroniseringen bara sker om det finns en konfigurationsändring i ProxySQL-runtime. Därför är det viktigt att köra "LOAD ... TO RUNTIME"-satsen innan du kan se åtgärden. Glöm inte att spara ProxySQL-ändringarna på disken för beständighet:
mysql> SAVE MYSQL USERS TO DISK;
Begränsning
Observera att det finns en begränsning för denna inställning på grund av att ProxySQL inte stöder att spara/exportera den aktiva konfigurationen till en textkonfigurationsfil som vi senare skulle kunna använda för att ladda in i ConfigMap för beständighet. Det finns en funktionsbegäran för detta. Under tiden kan du trycka på ändringarna till ConfigMap manuellt. Annars, om poddarna raderades av misstag, skulle du förlora din nuvarande konfiguration eftersom de nya podarna skulle vara uppstartade av vad som än definieras i ConfigMap.
Speciellt tack till Sampath Kamineni, som väckte idén med detta blogginlägg och ger insikter om användningsfall och implementering.