ProxySQL har understøttet native clustering siden v1.4.2. Dette betyder, at flere ProxySQL-instanser er klyngebevidste; de er opmærksomme på hinandens tilstand og er i stand til at håndtere konfigurationsændringerne automatisk ved at synkronisere op til den mest opdaterede konfiguration baseret på konfigurationsversion, tidsstempel og kontrolsumværdi. Tjek dette blogindlæg, som viser, hvordan du konfigurerer klyngeunderstøttelse til ProxySQL, og hvordan du kan forvente, at den opfører sig.
ProxySQL er en decentraliseret proxy, der anbefales at blive installeret tættere på applikationen. Denne tilgang skalerer ret godt selv op til hundredvis af noder, da den er designet til at være let rekonfigurerbar under kørsel. For effektivt at administrere flere ProxySQL-noder, skal man sikre sig, at de ændringer, der udføres på en af noderne, skal anvendes på tværs af alle noder i farmen. Uden native clustering skal man manuelt eksportere konfigurationerne og importere dem til de andre noder (omend du kan automatisere dette selv).
I det tidligere blogindlæg har vi dækket ProxySQL-klynger via Kubernetes ConfigMap. Denne tilgang er mere eller mindre ret effektiv med den centraliserede konfigurationstilgang i ConfigMap. Uanset hvad der er indlæst i ConfigMap vil blive monteret i pods. Opdatering af konfigurationen kan udføres via versionering (rediger proxysql.cnf-indholdet og indlæs det i ConfigMap med et andet navn) og skub derefter til pods afhængigt af implementeringsmetodens planlægning og opdateringsstrategi.
Men i et hurtigt skiftende miljø er denne ConfigMap-tilgang sandsynligvis ikke den bedste metode, fordi for at indlæse den nye konfiguration kræves pod-omlægning for at genmontere ConfigMap-volumen, og dette kan bringe ProxySQL-tjenesten som helhed i fare. Lad os f.eks. sige i vores miljø, at vores strenge adgangskodepolitik kræver, at MySQL-brugeradgangskoden udløber hver 7. dag, hvilket vi bliver nødt til at blive ved med at opdatere ProxySQL ConfigMap for den nye adgangskode på en ugentlig basis. Som en sidebemærkning kræver MySQL-bruger inde i ProxySQL bruger og adgangskode for at matche den på backend MySQL-serverne. Det er her, vi bør begynde at bruge ProxySQL native clustering-understøttelse i Kubernetes for automatisk at anvende konfigurationsændringerne uden besværet med ConfigMap-versionering og pod-omlægning.
I dette blogindlæg viser vi dig, hvordan du kører ProxySQL native clustering med hovedløs service på Kubernetes. Vores højniveauarkitektur kan illustreres som nedenfor:
Vi har 3 Galera-knuder, der kører på bare-metal-infrastruktur, implementeret og administreret af ClusterControl:
- 192.168.0.21
- 192.168.0.22
- 192.168.0.23
Vores applikationer kører alle som pods i Kubernetes. Ideen er at introducere to ProxySQL-instanser mellem applikationen og vores databaseklynge for at tjene som en omvendt proxy. Programmer vil derefter oprette forbindelse til ProxySQL-pods via Kubernetes-tjenesten, som vil være belastningsbalanceret og failover på tværs af en række ProxySQL-replikaer.
Følgende er en oversigt over vores Kubernetes-opsætning:
[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
Lad os først forberede vores basiskonfiguration, som vil blive indlæst i ConfigMap. Opret en fil kaldet proxysql.cnf og tilføj følgende linjer:
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 }
)
Nogle af ovenstående konfigurationslinjer er forklaret i afsnittet nedenfor:
admin_variables
Vær opmærksom på admin_credentials variabel, hvor vi brugte ikke-standardbruger, som er "proxysql-admin". ProxySQL reserverer standard "admin"-brugeren kun til lokal forbindelse via localhost. Derfor er vi nødt til at bruge andre brugere til at få adgang til ProxySQL-instansen eksternt. Ellers vil du få følgende fejl:
ERROR 1040 (42000): User 'admin' can only connect locally
Vi har også tilføjet cluster_username og cluster_password værdi i admin_credentials linje, adskilt af et semikolon for at tillade automatisk synkronisering. Alle variabler præfikset med cluster_* er relateret til ProxySQL native clustering og er selvforklarende.
mysql_galera_hostgroups
Dette er et nyt direktiv introduceret til ProxySQL 2.x (vores ProxySQL-billede kører på 2.0.5). Hvis du gerne vil køre på ProxySQL 1.x, skal du fjerne denne del og bruge planlægningstabellen i stedet. Vi har allerede forklaret konfigurationsdetaljerne i dette blogindlæg, Sådan køres og konfigureres ProxySQL 2.0 til MySQL Galera Cluster på Docker under "ProxySQL 2.x Support for Galera Cluster".
mysql_servere
Alle linjer er selvforklarende, hvilket er baseret på tre databaseservere, der kører i MySQL Galera Cluster som opsummeret i følgende Topology-skærmbillede taget fra ClusterControl:
proxysql_servere
Her definerer vi en liste over ProxySQL-peers:
- værtsnavn - Peers værtsnavn/IP-adresse
- port - Peers adminport
- vægt – i øjeblikket ubrugt, men i køreplanen for fremtidige forbedringer
- kommentar – kommentarfelt i fri form
I Docker/Kubernetes-miljøet er der flere måder at opdage og forbinde containerværtsnavne eller IP-adresser på og indsætte dem i denne tabel, enten ved at bruge ConfigMap, manuel indsættelse, via entrypoint.sh scripting, miljøvariabler eller på anden vis. I Kubernetes, afhængigt af den anvendte ReplicationController eller Deployment-metode, er det noget vanskeligt at gætte pod'ens opløselige værtsnavn på forhånd, medmindre du kører på StatefulSet.
Tjek denne tutorial om StatefulState pod-ordinalindeks, som giver et stabilt opløseligt værtsnavn for de oprettede pods. Kombiner dette med hovedløs service (forklaret længere nede), vil det opløselige værtsnavnsformat være:
{app_name}-{index_number}.{service}
Hvor {service} er en hovedløs tjeneste, som forklarer, hvor "proxysql-0.proxysqlcluster" og "proxysql-1.proxysqlcluster" kommer fra. Hvis du vil have mere end 2 replikaer, skal du tilføje flere poster i overensstemmelse hermed ved at tilføje et stigende indeksnummer i forhold til StatefulSet-applikationsnavnet.
Nu er vi klar til at skubbe konfigurationsfilen ind i ConfigMap, som vil blive monteret i hver ProxySQL pod under installationen:
$ kubectl create configmap proxysql-configmap --from-file=proxysql.cnf
Bekræft, om vores ConfigMap er indlæst korrekt:
$ kubectl get configmap
NAME DATA AGE
proxysql-configmap 1 7h57m
Oprettelse af ProxySQL-overvågningsbruger
Det næste trin, før vi starter implementeringen, er at oprette en ProxySQL-overvågningsbruger i vores databaseklynge. Da vi kører på Galera-klyngen, skal du køre følgende sætninger på en af Galera-knuderne:
mysql> CREATE USER 'proxysql'@'%' IDENTIFIED BY 'proxysqlpassw0rd';
mysql> GRANT USAGE ON *.* TO 'proxysql'@'%';
Hvis du ikke har oprettet MySQL-brugerne (som specificeret under mysql_users-sektionen ovenfor), skal vi også oprette dem:
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 er det. Vi er nu klar til at starte implementeringen.
Implementering af et StatefulSet
Vi starter med at oprette to ProxySQL-instanser eller replikaer til redundansformål ved hjælp af StatefulSet.
Lad os starte med at oprette en tekstfil kaldet proxysql-ss-svc.yml og tilføje følgende linjer:
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
Der er to sektioner af ovenstående definition - StatefulSet og Service. StatefulSet er definitionen af vores pods eller replikaer og monteringspunktet for vores ConfigMap-volumen, indlæst fra proxysql-configmap. Det næste afsnit er servicedefinitionen, hvor vi definerer, hvordan pods skal eksponeres og dirigeres til det interne eller eksterne netværk.
Opret ProxySQL stateful sæt og service:
$ kubectl create -f proxysql-ss-svc.yml
Bekræft pod- og tjenestetilstandene:
$ 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
Hvis du ser på podens log, vil du bemærke, at vi blev oversvømmet med denne advarsel:
$ 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)
Ovenstående betyder ganske enkelt, at proxysql-0 ikke var i stand til at løse "proxysql-1.proxysqlcluster" og oprette forbindelse til det, hvilket forventes, da vi ikke har oprettet vores hovedløse service til DNS-poster, der vil være nødvendige for inter-ProxySQL-kommunikation.
Kubernetes Headless Service
For at ProxySQL-pods skal være i stand til at løse den forventede FQDN og oprette forbindelse til den direkte, skal løsningsprocessen være i stand til at slå den tildelte mål-pod-IP-adresse op og ikke den virtuelle IP-adresse. Det er her, hovedløs service kommer ind i billedet. Når du opretter en hovedløs tjeneste ved at indstille "clusterIP=Ingen", konfigureres ingen belastningsbalancering, og ingen klynge-IP (virtuel IP) er allokeret til denne tjeneste. Kun DNS konfigureres automatisk. Når du kører en DNS-forespørgsel til hovedløs tjeneste, får du listen over pods IP-adresser.
Sådan ser det ud, hvis vi slår de hovedløse DNS-poster op for "proxysqlcluster" (i dette eksempel havde vi 3 ProxySQL-forekomster):
$ 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
Mens følgende output viser DNS-posten for standardtjenesten kaldet "proxysql", som løses til clusterIP:
$ host proxysql
proxysql.default.svc.cluster.local has address 10.110.38.154
For at oprette en hovedløs tjeneste og vedhæfte den til pod'erne, skal man definere ServiceName inde i StatefulSet-deklarationen, og Service-definitionen skal have "clusterIP=None" som vist nedenfor. Opret en tekstfil kaldet proxysql-headless-svc.yml og tilføj følgende linjer:
apiVersion: v1
kind: Service
metadata:
name: proxysqlcluster
labels:
app: proxysql
spec:
clusterIP: None
ports:
- port: 6032
name: proxysql-admin
selector:
app: proxysql
Opret den hovedløse tjeneste:
$ kubectl create -f proxysql-headless-svc.yml
Blot til verifikation har vi på dette tidspunkt følgende tjenester kørende:
$ 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
Tjek nu en af vores pods logfiler:
$ 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 vil bemærke, at Cluster-komponenten er i stand til at løse, forbinde og detektere en ny kontrolsum fra den anden peer, proxysql-1.proxysqlcluster på port 6032 via den hovedløse tjeneste kaldet "proxysqlcluster". Bemærk, at denne tjeneste kun afslører port 6032 inden for Kubernetes-netværket, og derfor er den ikke tilgængelig eksternt.
På dette tidspunkt er vores implementering nu færdig.
Opretter forbindelse til ProxySQL
Der er flere måder at oprette forbindelse til ProxySQL-tjenester på. De belastningsbalancerede MySQL-forbindelser skal sendes til port 6033 fra Kubernetes-netværket og bruge port 30033, hvis klienten opretter forbindelse fra et eksternt netværk.
For at oprette forbindelse til ProxySQL-admin-grænsefladen fra et eksternt netværk, kan vi oprette forbindelse til porten defineret under NodePort-sektionen, 30032 (192.168.100.203 er den primære IP-adresse for værten kube3.local):
$ mysql -uproxysql-admin -padminpassw0rd -h192.168.100.203 -P30032
Brug clusterIP 10.110.38.154 (defineret under "proxysql"-tjenesten) på port 6032, hvis du vil have adgang til den fra andre pods i Kubernetes-netværket.
Udfør derefter ProxySQL-konfigurationsændringerne, som du ønsker, og indlæs dem til runtime:
mysql> INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('newuser','passw0rd',10);
mysql> LOAD MYSQL USERS TO RUNTIME;
Du vil bemærke følgende linjer i en af pods, der angiver, at konfigurationssynkroniseringen er fuldført:
$ 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
Husk, at den automatiske synkronisering kun sker, hvis der er en konfigurationsændring i ProxySQL-runtime. Derfor er det vigtigt at køre "LOAD ... TO RUNTIME", før du kan se handlingen. Glem ikke at gemme ProxySQL-ændringerne på disken for persistens:
mysql> SAVE MYSQL USERS TO DISK;
Begrænsning
Bemærk, at der er en begrænsning for denne opsætning på grund af, at ProxySQL ikke understøtter lagring/eksport af den aktive konfiguration til en tekstkonfigurationsfil, som vi senere kunne bruge til at indlæse i ConfigMap for persistens. Der er en funktionsanmodning om dette. I mellemtiden kan du skubbe ændringerne til ConfigMap manuelt. Ellers, hvis pods blev slettet ved et uheld, ville du miste din nuværende konfiguration, fordi de nye pods ville blive bootstrapped af hvad end defineret i ConfigMap.
Særlig tak til Sampath Kamineni, som satte gang i ideen med dette blogindlæg og giver indsigt i brugssituationer og implementering.