MariaDB
 sql >> Baza danych >  >> RDS >> MariaDB

Natywny klaster ProxySQL z Kubernetes

ProxySQL obsługuje natywne klastrowanie od wersji 1.4.2. Oznacza to, że wiele instancji ProxySQL obsługuje klastry; są świadomi wzajemnego stanu i są w stanie automatycznie obsługiwać zmiany konfiguracji, synchronizując się z najbardziej aktualną konfiguracją na podstawie wersji konfiguracji, znacznika czasu i wartości sumy kontrolnej. Sprawdź ten wpis na blogu, który pokazuje, jak skonfigurować obsługę klastrowania dla ProxySQL i jak możesz się tego spodziewać.

ProxySQL to zdecentralizowany serwer proxy, zalecany do wdrożenia bliżej aplikacji. Takie podejście skaluje się całkiem dobrze nawet do setek węzłów, ponieważ zostało zaprojektowane tak, aby można je było łatwo rekonfigurować w czasie wykonywania. Aby efektywnie zarządzać wieloma węzłami ProxySQL, należy upewnić się, że wszelkie zmiany wykonane na jednym z węzłów powinny zostać zastosowane we wszystkich węzłach w farmie. Bez natywnego klastrowania trzeba ręcznie wyeksportować konfiguracje i zaimportować je do innych węzłów (chociaż można to zautomatyzować samodzielnie).

W poprzednim wpisie w blogu omówiliśmy klastrowanie ProxySQL za pośrednictwem Kubernetes ConfigMap. To podejście jest mniej lub bardziej wydajne dzięki scentralizowanemu podejściu do konfiguracji w programie ConfigMap. Cokolwiek załadowane do ConfigMap zostanie zamontowane w podach. Aktualizację konfiguracji można wykonać za pomocą wersjonowania (zmodyfikuj zawartość proxysql.cnf i załaduj ją do ConfigMap pod inną nazwą), a następnie wypchnij do podów w zależności od planowania metody wdrażania i strategii aktualizacji.

Jednak w szybko zmieniającym się środowisku to podejście ConfigMap prawdopodobnie nie jest najlepszą metodą, ponieważ w celu załadowania nowej konfiguracji wymagane jest ponowne planowanie pod, aby ponownie zainstalować wolumin ConfigMap, co może zagrozić usłudze ProxySQL jako całości. Załóżmy na przykład, że w naszym środowisku nasza ścisła polityka dotycząca haseł wymaga wymuszania wygaśnięcia hasła użytkownika MySQL co 7 dni, co oznacza, że ​​musielibyśmy aktualizować ProxySQL ConfigMap dla nowego hasła co tydzień. Na marginesie, użytkownik MySQL wewnątrz ProxySQL wymaga użytkownika i hasła, aby pasowały do ​​tego na serwerach zaplecza MySQL. Od tego momentu powinniśmy zacząć korzystać z natywnej obsługi klastrów ProxySQL w Kubernetes, aby automatycznie zastosować zmiany konfiguracji bez kłopotów z wersjonowaniem ConfigMap i zmianą harmonogramu pod.

W tym poście na blogu pokażemy, jak uruchomić natywne klastry ProxySQL z usługą headless na Kubernetes. Naszą architekturę wysokiego poziomu można zilustrować poniżej:

Mamy 3 węzły Galera działające na infrastrukturze bare-metal wdrożonej i zarządzanej przez ClusterControl:

  • 192.168.0.21
  • 192.168.0.22
  • 192.168.0.23

Wszystkie nasze aplikacje działają jako pody w Kubernetes. Pomysł polega na wprowadzeniu dwóch instancji ProxySQL pomiędzy aplikacją a naszym klastrem baz danych, aby służyły jako odwrotny serwer proxy. Aplikacje będą następnie łączyć się z podami ProxySQL za pośrednictwem usługi Kubernetes, która będzie równoważyć obciążenie i przełączać awaryjnie w wielu replikach ProxySQL.

Poniżej znajduje się podsumowanie naszej konfiguracji Kubernetes:

[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

Konfiguracja ProxySQL przez ConfigMap

Najpierw przygotujmy naszą podstawową konfigurację, która zostanie załadowana do ConfigMap. Utwórz plik o nazwie proxysql.cnf i dodaj następujące wiersze:

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 }
)

Niektóre z powyższych linii konfiguracyjnych są wyjaśnione w sekcji poniżej:

admin_variables

Zwróć uwagę na admin_credentials zmienna, w której użyliśmy użytkownika innego niż domyślny, którym jest „proxysql-admin”. ProxySQL rezerwuje domyślnego użytkownika „admin” tylko do połączenia lokalnego przez localhost. Dlatego musimy użyć innych użytkowników, aby uzyskać zdalny dostęp do instancji ProxySQL. W przeciwnym razie otrzymasz następujący błąd:

ERROR 1040 (42000): User 'admin' can only connect locally

Dodaliśmy również cluster_username i cluster_password wartość w admin_credentials linii oddzielonej średnikiem, aby umożliwić automatyczną synchronizację. Wszystkie zmienne z prefiksem cluster_* są związane z natywnym klastrowaniem ProxySQL i są oczywiste.

mysql_galera_hostgroups

Jest to nowa dyrektywa wprowadzona dla ProxySQL 2.x (nasz obraz ProxySQL działa na 2.0.5). Jeśli chcesz uruchomić na ProxySQL 1.x, usuń tę część i zamiast tego użyj tabeli harmonogramu. Wyjaśniliśmy już szczegóły konfiguracji w tym poście na blogu, Jak uruchomić i skonfigurować ProxySQL 2.0 dla MySQL Galera Cluster w Docker w sekcji „Obsługa ProxySQL 2.x dla Galera Cluster”.

mysql_servers

Wszystkie wiersze są oczywiste, co jest oparte na trzech serwerach baz danych działających w MySQL Galera Cluster, jak podsumowano na poniższym zrzucie ekranu topologii zaczerpniętym z ClusterControl:

serwery_proxysql

Tutaj definiujemy listę peerów ProxySQL:

  • nazwa hosta — nazwa hosta/adres IP peera
  • port — port administratora peera
  • waga — obecnie nieużywana, ale w planie przyszłych ulepszeń
  • komentarz — pole komentarza w dowolnym formularzu

W środowisku Docker/Kubernetes istnieje wiele sposobów odnajdywania i łączenia nazw hostów lub adresów IP kontenerów i wstawiania ich do tej tabeli za pomocą programu ConfigMap, ręcznego wstawiania, za pomocą skryptu entrypoint.sh, zmiennych środowiskowych lub w inny sposób. W Kubernetes, w zależności od użytej metody ReplicationController lub Deployment, odgadnięcie z góry możliwej do rozwiązania nazwy hosta poda jest nieco trudne, chyba że używasz StatefulSet.

Zapoznaj się z tym samouczkiem dotyczącym indeksu porządkowego pod StatefulState, który zapewnia stabilną, rozpoznawalną nazwę hosta dla utworzonych podów. W połączeniu z usługą bezgłową (wyjaśnioną poniżej), możliwym do rozwiązania formatem nazwy hosta będzie:

{app_name}-{index_number}.{service}

Gdzie {service} jest usługą bezgłową, która wyjaśnia, skąd pochodzą „proxysql-0.proxysqlcluster” i „proxysql-1.proxysqlcluster”. Jeśli chcesz mieć więcej niż 2 repliki, dodaj odpowiednio więcej wpisów, dodając rosnący numer indeksu względem nazwy aplikacji StatefulSet.

Teraz jesteśmy gotowi do wypchnięcia pliku konfiguracyjnego do ConfigMap, który zostanie zamontowany w każdym pod ProxySQL podczas wdrażania:

$ kubectl create configmap proxysql-configmap --from-file=proxysql.cnf

Sprawdź, czy nasza ConfigMap jest poprawnie załadowana:

$ kubectl get configmap
NAME                 DATA   AGE
proxysql-configmap   1      7h57m

Tworzenie użytkownika monitorującego ProxySQL

Kolejnym krokiem przed rozpoczęciem wdrożenia jest utworzenie użytkownika monitorującego ProxySQL w naszym klastrze baz danych. Ponieważ działamy w klastrze Galera, uruchom następujące instrukcje na jednym z węzłów Galera:

mysql> CREATE USER 'proxysql'@'%' IDENTIFIED BY 'proxysqlpassw0rd';
mysql> GRANT USAGE ON *.* TO 'proxysql'@'%';

Jeśli nie utworzyłeś użytkowników MySQL (jak określono w sekcji mysql_users powyżej), musimy ich również utworzyć:

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'@'%';

Otóż ​​to. Jesteśmy teraz gotowi do rozpoczęcia wdrażania.

Wdrażanie StatefulSet

Zaczniemy od utworzenia dwóch instancji ProxySQL lub replik dla celów redundancji przy użyciu StatefulSet.

Zacznijmy od utworzenia pliku tekstowego o nazwie proxysql-ss-svc.yml i dodaj następujące wiersze:

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

W powyższej definicji istnieją dwie sekcje — StatefulSet i Service. StatefulSet to definicja naszych podów lub replik oraz punkt instalacji dla naszego woluminu ConfigMap, ładowany z proxysql-configmap. Następna sekcja to definicja usługi, w której definiujemy, w jaki sposób pody powinny być eksponowane i kierowane dla sieci wewnętrznej lub zewnętrznej.

Utwórz zestaw stanowy i usługę ProxySQL:

$ kubectl create -f proxysql-ss-svc.yml

Sprawdź stan poda i usługi:

$ 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

Jeśli spojrzysz na dziennik pod, zauważysz, że zostaliśmy zalani tym ostrzeżeniem:

$ 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)

Powyższe oznacza po prostu, że proxysql-0 nie było w stanie rozwiązać „proxysql-1.proxysqlcluster” i połączyć się z nim, czego można się spodziewać, ponieważ nie stworzyliśmy naszej usługi headless dla rekordów DNS, które będą potrzebne do komunikacji między serwerami ProxySQL.

Usługa bezgłowa Kubernetes

Aby pody ProxySQL mogły rozpoznać oczekiwaną nazwę FQDN i połączyć się z nią bezpośrednio, proces rozpoznawania musi mieć możliwość wyszukania przypisanego docelowego adresu IP poda, a nie wirtualnego adresu IP. I tu pojawia się bezgłowa służba. Podczas tworzenia usługi bezgłowej przez ustawienie „clusterIP=Brak”, nie jest konfigurowane równoważenie obciążenia i żaden adres IP klastra (wirtualny adres IP) nie jest przydzielany dla tej usługi. Tylko DNS jest konfigurowany automatycznie. Po uruchomieniu zapytania DNS dla usługi bezgłowej otrzymasz listę adresów IP podów.

Oto, jak to wygląda, jeśli wyszukamy rekordy DNS usługi bezgłowej dla „proxysqlcluster” (w tym przykładzie mieliśmy 3 instancje ProxySQL):

$ 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

Podczas gdy poniższe dane wyjściowe pokazują rekord DNS dla standardowej usługi o nazwie „proxysql”, który jest tłumaczony na adres IP klastra:

$ host proxysql
proxysql.default.svc.cluster.local has address 10.110.38.154

Aby utworzyć usługę bezgłową i dołączyć ją do podów, należy zdefiniować ServiceName wewnątrz deklaracji StatefulSet, a definicja usługi musi mieć "clusterIP=None", jak pokazano poniżej. Utwórz plik tekstowy o nazwie proxysql-headless-svc.yml i dodaj następujące wiersze:

apiVersion: v1
kind: Service
metadata:
  name: proxysqlcluster
  labels:
    app: proxysql
spec:
  clusterIP: None
  ports:
  - port: 6032
    name: proxysql-admin
  selector:
    app: proxysql

Utwórz usługę bezgłową:

$ kubectl create -f proxysql-headless-svc.yml

Tylko dla weryfikacji, w tym momencie mamy uruchomione następujące usługi:

$ 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

Teraz sprawdź jeden z naszych dzienników pod:

$ 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.

Można zauważyć, że komponent Cluster jest w stanie rozwiązać, połączyć i wykryć nową sumę kontrolną z drugiego peera, proxysql-1.proxysqlcluster na porcie 6032 za pośrednictwem usługi bezgłowej o nazwie „proxysqlcluster”. Pamiętaj, że ta usługa ujawnia port 6032 tylko w sieci Kubernetes, dlatego jest nieosiągalny zewnętrznie.

W tym momencie nasze wdrożenie jest już zakończone.

Łączenie z ProxySQL

Istnieje kilka sposobów łączenia się z usługami ProxySQL. Połączenia MySQL ze zrównoważonym obciążeniem powinny być wysyłane na port 6033 z sieci Kubernetes i używać portu 30033, jeśli klient łączy się z sieci zewnętrznej.

Aby połączyć się z interfejsem administratora ProxySQL z sieci zewnętrznej, możemy połączyć się z portem zdefiniowanym w sekcji NodePort, 30032 (192.168.100.203 to podstawowy adres IP hosta kube3.local):

$ mysql -uproxysql-admin -padminpassw0rd -h192.168.100.203 -P30032

Użyj klastraIP 10.110.38.154 (zdefiniowanego w usłudze "proxysql") na porcie 6032, jeśli chcesz uzyskać do niego dostęp z innych podów w sieci Kubernetes.

Następnie dokonaj zmian w konfiguracji ProxySQL, jak chcesz i załaduj je do środowiska wykonawczego:

mysql> INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('newuser','passw0rd',10);
mysql> LOAD MYSQL USERS TO RUNTIME;

W jednym z zasobników zauważysz następujące wiersze wskazujące, że synchronizacja konfiguracji została zakończona:

$ 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

Należy pamiętać, że automatyczna synchronizacja ma miejsce tylko w przypadku zmiany konfiguracji w środowisku wykonawczym ProxySQL. Dlatego ważne jest, aby uruchomić instrukcję „LOAD ... TO RUNTIME”, zanim zobaczysz akcję. Nie zapomnij zapisać zmian ProxySQL na dysku w celu zachowania trwałości:

mysql> SAVE MYSQL USERS TO DISK;

Ograniczenia

Należy zauważyć, że istnieje ograniczenie tej konfiguracji, ponieważ ProxySQL nie obsługuje zapisywania/eksportowania aktywnej konfiguracji do tekstowego pliku konfiguracyjnego, którego moglibyśmy później użyć do załadowania do programu ConfigMap w celu zachowania trwałości. Jest prośba o nową funkcję. Tymczasem możesz ręcznie przekazać modyfikacje do ConfigMap. W przeciwnym razie, jeśli zasobniki zostałyby przypadkowo usunięte, utracisz bieżącą konfigurację, ponieważ nowe zasobniki zostaną załadowane przez wszystko, co zostało zdefiniowane w ConfigMap.

Specjalne podziękowania dla Sampath Kamineni, który wpadł na pomysł tego wpisu na blogu i zapewnił wgląd w przypadki użycia i implementację.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wskazówki dotyczące przechowywania kopii zapasowych MariaDB w chmurze

  2. Jak działa LOG() w MariaDB

  3. Przewodnik po replikacji strumieniowej MySQL Galera Cluster:część druga

  4. Napraw „BŁĄD 1250 (42000):Tabela „…” z jednego z SELECTów nie może być użyta w klauzuli ORDER” w MariaDB

  5. Jak formatować liczby w MariaDB