sql >> Databasteknik >  >> RDS >> MariaDB

ProxySQL Native Clustering med Kubernetes

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.


  1. Ta bort data från en MySQL-databas

  2. Installera SQL Server 2016

  3. ORACLE Hur man använder spolen med dynamisk spolplacering

  4. Formatera ett tal som valuta i SQLite