sql >> Database teknologi >  >> RDS >> MariaDB

ProxySQL Native Clustering med Kubernetes

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.


  1. Sådan cachelagres Parsed JSON til offlinebrug

  2. underforespørgsel i FROM skal have et alias

  3. Hvordan oprettes skema i Oracle ved hjælp af SQL Developer?

  4. SQL OG Operator for begyndere