ProxySQL поддържа собствени клъстери от v1.4.2. Това означава, че множество екземпляри на ProxySQL са наясно с клъстери; те са наясно със състоянието на всеки друг и могат да се справят автоматично с промените в конфигурацията, като синхронизират до най-актуалната конфигурация въз основа на версията на конфигурацията, времевата марка и стойността на контролната сума. Вижте тази публикация в блога, която демонстрира как да конфигурирате поддръжка на клъстери за ProxySQL и как бихте очаквали да се държи.
ProxySQL е децентрализирано прокси, което се препоръчва да се внедри по-близо до приложението. Този подход се мащабира доста добре дори до стотици възли, тъй като е проектиран да бъде лесно преконфигурируем по време на изпълнение. За ефективно управление на множество възли на ProxySQL, трябва да се уверите, че всички промени, извършени на един от възлите, трябва да се прилагат към всички възли във фермата. Без собствено клъстериране, човек трябва ръчно да експортира конфигурациите и да ги импортира в другите възли (макар че бихте могли да автоматизирате това сами).
В предишната публикация в блога разгледахме клъстерирането на ProxySQL чрез Kubernetes ConfigMap. Този подход е повече или по-малко доста ефективен с подхода за централизирано конфигуриране в ConfigMap. Каквото и да е заредено в ConfigMap, ще бъде монтирано в pods. Актуализирането на конфигурацията може да се извърши чрез управление на версиите (променете съдържанието на proxysql.cnf и го заредете в ConfigMap с друго име) и след това натиснете към модулите в зависимост от планирането на метода на разгръщане и стратегията за актуализиране.
Въпреки това, в бързо променяща се среда, този подход на ConfigMap вероятно не е най-добрият метод, тъй като за да се зареди новата конфигурация, е необходимо разсрочване на модула за повторно монтиране на тома ConfigMap и това може да застраши услугата ProxySQL като цяло. Например, да кажем, че в нашата среда нашата строга политика за пароли изисква принудително изтичане на потребителската парола за MySQL на всеки 7 дни, което ще трябва да актуализираме ProxySQL ConfigMap за новата парола на седмична база. Като странична бележка, потребителят на MySQL вътре в ProxySQL изисква потребител и парола, които да съвпадат с тази на сървърите на сървъра на MySQL. Това е мястото, където трябва да започнем да използваме поддръжката на ProxySQL за клъстериране в Kubernetes, за да приложим автоматично промените в конфигурацията, без да се притеснявате от версията на ConfigMap и пренасрочването на модула.
В тази публикация в блога ще ви покажем как да стартирате ProxySQL роден клъстер с безглавна услуга на Kubernetes. Нашата архитектура на високо ниво може да бъде илюстрирана по-долу:
Имаме 3 възела на Galera, работещи върху гола метална инфраструктура, разгърната и управлявана от ClusterControl:
- 192.168.0.21
- 192.168.0.22
- 192.168.0.23
Всички наши приложения работят като модули в Kubernetes. Идеята е да се въведат две копия на ProxySQL между приложението и нашия клъстер от база данни, които да служат като обратен прокси. След това приложенията ще се свържат с ProxySQL модули чрез услугата Kubernetes, която ще бъде балансирана на натоварването и ще бъде преодоляна при отказ в редица реплики на ProxySQL.
Следното е обобщение на нашата настройка на 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
Конфигуриране на ProxySQL чрез ConfigMap
Нека първо подготвим нашата базова конфигурация, която ще бъде заредена в ConfigMap. Създайте файл, наречен proxysql.cnf, и добавете следните редове:
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 }
)
Някои от горните конфигурационни редове са обяснени в раздел по-долу:
административни_променливи
Обърнете внимание на admin_credentials променлива, където използвахме потребител, който не е по подразбиране, който е "proxysql-admin". ProxySQL запазва потребителя по подразбиране "admin" за локална връзка само чрез localhost. Следователно трябва да използваме други потребители за отдалечен достъп до екземпляра на ProxySQL. В противен случай ще получите следната грешка:
ERROR 1040 (42000): User 'admin' can only connect locally
Добавихме и cluster_username и cluster_password стойност в admin_credentials ред, разделени с точка и запетая, за да се позволи автоматично синхронизиране. Всички променливи с префикс с cluster_* са свързани с родния клъстер на ProxySQL и се разбират сами.
mysql_galera_hostgroups
Това е нова директива, въведена за ProxySQL 2.x (нашето ProxySQL изображение работи на 2.0.5). Ако искате да работите на ProxySQL 1.x, премахнете тази част и вместо това използвайте таблицата на планировчика. Вече обяснихме подробностите за конфигурацията в тази публикация в блога, Как да стартирате и конфигурирате ProxySQL 2.0 за MySQL Galera Cluster на Docker под „Поддръжка на ProxySQL 2.x за Galera Cluster“.
mysql_servers
Всички редове се обясняват сами, което се основава на три сървъра на бази данни, работещи в MySQL Galera Cluster, както е обобщено в следната екранна снимка на топологията, взета от ClusterControl:
proxysql_servers
Тук ние дефинираме списък с партньори на ProxySQL:
- име на хост – име на хост/IP адрес на партньор
- порт – Администраторски порт на Peer
- тегло – в момента не се използва, но в пътната карта за бъдещи подобрения
- коментар – поле за коментар в свободна форма
В средата на Docker/Kubernetes има множество начини да откриете и свържете имена на хостове или IP адреси на контейнери и да ги вмъкнете в тази таблица, или чрез използване на ConfigMap, ръчно вмъкване, чрез скриптове на entrypoint.sh, променливи на средата или по някакъв друг начин. В Kubernetes, в зависимост от използвания ReplicationController или метод за разгръщане, предварителното отгатване на разрешаваемото име на хост на модула е малко трудно, освен ако не работите на StatefulSet.
Вижте този урок за порядковия индекс на StatefulState, който предоставя стабилно разрешимо име на хост за създадените модули. Комбинирайте това с услугата без глава (обяснено по-долу), форматът на име на хост ще бъде:
{app_name}-{index_number}.{service}
Където {service} е услуга без глава, която обяснява откъде идват "proxysql-0.proxysqlcluster" и "proxysql-1.proxysqlcluster". Ако искате да имате повече от 2 реплики, добавете съответно повече записи, като добавите нарастващ индексен номер спрямо името на приложението StatefulSet.
Сега сме готови да натиснем конфигурационния файл в ConfigMap, който ще бъде монтиран във всеки ProxySQL модул по време на внедряването:
$ kubectl create configmap proxysql-configmap --from-file=proxysql.cnf
Проверете дали нашата ConfigMap е заредена правилно:
$ kubectl get configmap
NAME DATA AGE
proxysql-configmap 1 7h57m
Създаване на потребител за наблюдение на ProxySQL
Следващата стъпка, преди да започнем внедряването, е да създадем потребител за мониторинг на ProxySQL в нашия клъстер от база данни. Тъй като работим в клъстера Galera, изпълнете следните оператори на един от възлите на Galera:
mysql> CREATE USER 'proxysql'@'%' IDENTIFIED BY 'proxysqlpassw0rd';
mysql> GRANT USAGE ON *.* TO 'proxysql'@'%';
Ако не сте създали потребителите на MySQL (както е посочено в секцията mysql_users по-горе), ние също трябва да ги създадем:
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'@'%';
Това е. Вече сме готови да започнем внедряването.
Разгръщане на StatefulSet
Ще започнем със създаване на две копия на ProxySQL или реплики за целите на излишъка, използвайки StatefulSet.
Нека започнем със създаване на текстов файл, наречен proxysql-ss-svc.yml и добавяне на следните редове:
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
Има два раздела от горната дефиниция - StatefulSet и Service. StatefulSet е дефиницията на нашите модули или реплики и точката на монтиране за нашия том ConfigMap, заредена от proxysql-configmap. Следващият раздел е дефиницията на услугата, където дефинираме как модулите трябва да бъдат изложени и маршрутизирани за вътрешната или външната мрежа.
Създайте ProxySQL набор и услуга с проследяване на състоянието:
$ kubectl create -f proxysql-ss-svc.yml
Проверете състоянието на модула и услугата:
$ 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
Ако погледнете дневника на модула, ще забележите, че сме наводнени с това предупреждение:
$ 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)
Горното просто означава, че proxysql-0 не е успял да разреши "proxysql-1.proxysqlcluster" и да се свърже с него, което се очаква, тъй като не сме създали нашата безглавна услуга за DNS записи, които ще са необходими за комуникация между ProxySQL.
Услуга без глава Kubernetes
За да могат ProxySQL модулите да разрешат очакваното FQDN и да се свържат директно с него, процесът на разрешаване трябва да може да търси присвоения IP адрес на целеви модул, а не виртуалния IP адрес. Тук се появява услугата без глава. Когато създавате услуга без глава чрез настройка на "clusterIP=None", не се конфигурира балансиране на натоварването и не се разпределя IP на клъстер (виртуален IP) за тази услуга. Само DNS се конфигурира автоматично. Когато стартирате DNS заявка за безглавна услуга, ще получите списък с IP адресите на pods.
Ето как изглежда, ако потърсим DNS записите на услугата без глава за "proxysqlcluster" (в този пример имахме 3 екземпляра на 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
Докато следният изход показва DNS записа за стандартната услуга, наречена "proxysql", която се разрешава до clusterIP:
$ host proxysql
proxysql.default.svc.cluster.local has address 10.110.38.154
За да създадете услуга без глава и да я прикачите към pods, трябва да дефинирате ServiceName в декларацията StatefulSet, а дефиницията на услугата трябва да има "clusterIP=None", както е показано по-долу. Създайте текстов файл, наречен proxysql-headless-svc.yml и добавете следните редове:
apiVersion: v1
kind: Service
metadata:
name: proxysqlcluster
labels:
app: proxysql
spec:
clusterIP: None
ports:
- port: 6032
name: proxysql-admin
selector:
app: proxysql
Създайте услугата без глава:
$ kubectl create -f proxysql-headless-svc.yml
Само за потвърждение, в този момент имаме работещи следните услуги:
$ 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
Сега вижте един от регистрационните файлове на нашия модул:
$ 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.
Ще забележите, че компонентът Cluster е в състояние да разрешава, свързва и открива нова контролна сума от другия партньор, proxysql-1.proxysqlcluster на порт 6032 чрез безглавната услуга, наречена "proxysqlcluster". Имайте предвид, че тази услуга разкрива порт 6032 само в мрежата на Kubernetes, поради което е недостъпен отвън.
На този етап внедряването ни вече е завършено.
Свързване с ProxySQL
Има няколко начина за свързване с ProxySQL услуги. Балансираните по натоварване MySQL връзки трябва да се изпращат до порт 6033 от мрежата на Kubernetes и да използват порт 30033, ако клиентът се свързва от външна мрежа.
За да се свържем с администраторския интерфейс на ProxySQL от външна мрежа, можем да се свържем с порта, дефиниран в раздел NodePort, 30032 (192.168.100.203 е основният IP адрес на хост kube3.local):
$ mysql -uproxysql-admin -padminpassw0rd -h192.168.100.203 -P30032
Използвайте clusterIP 10.110.38.154 (дефиниран в услугата „proxysql“) на порт 6032, ако искате да получите достъп до него от други модули в мрежата на Kubernetes.
След това извършете промените в конфигурацията на ProxySQL, както желаете, и ги заредете във времето за изпълнение:
mysql> INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('newuser','passw0rd',10);
mysql> LOAD MYSQL USERS TO RUNTIME;
Ще забележите следните редове в един от модулите, които показват, че синхронизирането на конфигурацията е завършено:
$ 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
Имайте предвид, че автоматичното синхронизиране се случва само ако има промяна в конфигурацията в времето за изпълнение на ProxySQL. Ето защо е жизненоважно да изпълните оператор "LOAD ... TO RUNTIME", преди да можете да видите действието. Не забравяйте да запазите промените в ProxySQL на диска за постоянство:
mysql> SAVE MYSQL USERS TO DISK;
Ограничение
Имайте предвид, че има ограничение за тази настройка, тъй като ProxySQL не поддържа запазване/експортиране на активната конфигурация в текстов конфигурационен файл, който бихме могли да използваме по-късно за зареждане в ConfigMap за постоянство. Има заявка за функция за това. Междувременно можете да натиснете ръчно модификациите в ConfigMap. В противен случай, ако модулите бъдат изтрити случайно, ще загубите текущата си конфигурация, защото новите модули ще бъдат заредени от всичко, дефинирано в ConfigMap.
Специални благодарности на Sampath Kamineni, който предизвика идеята за тази публикация в блога и предостави информация за случаите на използване и прилагането.