ProxySQL telah mendukung pengelompokan asli sejak v1.4.2. Ini berarti beberapa instance ProxySQL bersifat cluster-aware; mereka mengetahui status satu sama lain dan mampu menangani perubahan konfigurasi secara otomatis dengan menyinkronkan hingga konfigurasi terbaru berdasarkan versi konfigurasi, stempel waktu, dan nilai checksum. Lihat entri blog ini yang menunjukkan cara mengonfigurasi dukungan pengelompokan untuk ProxySQL dan bagaimana Anda dapat mengharapkannya untuk berperilaku.
ProxySQL adalah proxy terdesentralisasi, direkomendasikan untuk digunakan lebih dekat ke aplikasi. Pendekatan ini berskala cukup baik bahkan hingga ratusan node, karena dirancang agar mudah dikonfigurasi ulang saat runtime. Untuk mengelola beberapa node ProxySQL secara efisien, kita harus memastikan perubahan apa pun yang dilakukan pada salah satu node harus diterapkan di semua node di farm. Tanpa pengelompokan asli, seseorang harus mengekspor konfigurasi secara manual dan mengimpornya ke node lain (walaupun, Anda dapat mengotomatiskannya sendiri).
Dalam posting blog sebelumnya, kami telah membahas pengelompokan ProxySQL melalui Kubernetes ConfigMap. Pendekatan ini kurang lebih cukup efisien dengan pendekatan konfigurasi terpusat di ConfigMap. Apa pun yang dimuat ke ConfigMap akan dipasang ke dalam pod. Memperbarui konfigurasi dapat dilakukan melalui pembuatan versi (memodifikasi konten proxysql.cnf dan memuatnya ke ConfigMap dengan nama lain) dan kemudian mendorong ke pod tergantung pada penjadwalan metode Deployment dan strategi pembaruan.
Namun, dalam lingkungan yang berubah dengan cepat, pendekatan ConfigMap ini mungkin bukan metode terbaik karena untuk memuat konfigurasi baru, penjadwalan ulang pod diperlukan untuk me-remount volume ConfigMap dan ini dapat membahayakan layanan ProxySQL secara keseluruhan. Misalnya, katakanlah di lingkungan kita, kebijakan kata sandi ketat kita mengharuskan kedaluwarsa kata sandi pengguna MySQL setiap 7 hari, yang mana kita harus terus memperbarui ProxySQL ConfigMap untuk kata sandi baru setiap minggu. Sebagai catatan tambahan, pengguna MySQL di dalam ProxySQL membutuhkan pengguna dan kata sandi agar sesuai dengan yang ada di server MySQL backend. Di situlah kita harus mulai memanfaatkan dukungan pengelompokan asli ProxySQL di Kubernetes, untuk menerapkan perubahan konfigurasi secara otomatis tanpa kerumitan pembuatan versi ConfigMap dan penjadwalan ulang pod.
Dalam posting blog ini, kami akan menunjukkan cara menjalankan pengelompokan asli ProxySQL dengan layanan tanpa kepala di Kubernetes. Arsitektur tingkat tinggi kami dapat diilustrasikan seperti di bawah ini:
Kami memiliki 3 node Galera yang berjalan pada infrastruktur bare-metal yang diterapkan dan dikelola oleh ClusterControl:
- 192.168.0.21
- 192.168.0.22
- 192.168.0.23
Semua aplikasi kami berjalan sebagai pod di dalam Kubernetes. Idenya adalah untuk memperkenalkan dua instance ProxySQL di antara aplikasi dan cluster database kami untuk berfungsi sebagai proxy terbalik. Aplikasi kemudian akan terhubung ke pod ProxySQL melalui layanan Kubernetes yang akan load balance dan failover di sejumlah replika ProxySQL.
Berikut adalah ringkasan penyiapan Kubernetes kami:
[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
Konfigurasi ProxySQL melalui ConfigMap
Mari kita siapkan konfigurasi dasar kita yang akan dimuat ke ConfigMap. Buat file bernama proxysql.cnf dan tambahkan baris berikut:
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 }
)
Beberapa baris konfigurasi di atas dijelaskan per bagian di bawah ini:
variabel_admin
Perhatikan admin_credentials variabel di mana kami menggunakan pengguna non-default yaitu "proxysql-admin". ProxySQL mencadangkan pengguna "admin" default untuk koneksi lokal hanya melalui localhost. Oleh karena itu, kita harus menggunakan pengguna lain untuk mengakses instance ProxySQL dari jarak jauh. Jika tidak, Anda akan mendapatkan kesalahan berikut:
ERROR 1040 (42000): User 'admin' can only connect locally
Kami juga menambahkan cluster_username dan cluster_password nilai di admin_credentials baris, dipisahkan oleh titik koma untuk memungkinkan sinkronisasi otomatis terjadi. Semua variabel diawali dengan cluster_* terkait dengan pengelompokan asli ProxySQL dan sudah cukup jelas.
mysql_galera_hostgroups
Ini adalah arahan baru yang diperkenalkan untuk ProxySQL 2.x (gambar ProxySQL kami berjalan pada 2.0.5). Jika Anda ingin menjalankan ProxySQL 1.x, hapus bagian ini dan gunakan tabel scheduler sebagai gantinya. Kami sudah menjelaskan detail konfigurasi di posting blog ini, Cara Menjalankan dan Mengonfigurasi ProxySQL 2.0 untuk MySQL Galera Cluster di Docker di bawah "Dukungan ProxySQL 2.x untuk Galera Cluster".
server_mysql
Semua baris cukup jelas, yang didasarkan pada tiga server database yang berjalan di MySQL Galera Cluster sebagaimana dirangkum dalam tangkapan layar Topologi berikut yang diambil dari ClusterControl:
proxysql_servers
Di sini kami mendefinisikan daftar rekan ProxySQL:
- nama host - nama host/alamat IP rekan
- port - port admin rekan
- bobot - Saat ini tidak digunakan, tetapi dalam peta jalan untuk penyempurnaan di masa mendatang
- komentar - Kolom komentar formulir gratis
Di lingkungan Docker/Kubernetes, ada beberapa cara untuk menemukan dan menautkan nama host container atau alamat IP dan memasukkannya ke dalam tabel ini, baik dengan menggunakan ConfigMap, penyisipan manual, melalui skrip entrypoint.sh, variabel lingkungan, atau cara lain. Di Kubernetes, bergantung pada metode ReplicationController atau Deployment yang digunakan, menebak nama host pod yang dapat diselesaikan terlebih dahulu agak rumit kecuali jika Anda menjalankan StatefulSet.
Lihat tutorial ini tentang indeks ordinal pod StatefulState yang menyediakan nama host stabil yang dapat diselesaikan untuk pod yang dibuat. Gabungkan ini dengan layanan tanpa kepala (dijelaskan lebih jauh ke bawah), format nama host yang dapat diselesaikan adalah:
{app_name}-{index_number}.{service}
Di mana {service} adalah layanan tanpa kepala, yang menjelaskan dari mana "proxysql-0.proxysqlcluster" dan "proxysql-1.proxysqlcluster" berasal. Jika Anda ingin memiliki lebih dari 2 replika, tambahkan lebih banyak entri yang sesuai dengan menambahkan nomor indeks menaik relatif terhadap nama aplikasi StatefulSet.
Sekarang kita siap untuk memasukkan file konfigurasi ke ConfigMap, yang akan dipasang ke setiap pod ProxySQL selama penerapan:
$ kubectl create configmap proxysql-configmap --from-file=proxysql.cnf
Verifikasi apakah ConfigMap kami dimuat dengan benar:
$ kubectl get configmap
NAME DATA AGE
proxysql-configmap 1 7h57m
Membuat Pengguna Pemantauan ProxySQL
Langkah selanjutnya sebelum kita memulai penerapan adalah membuat pengguna pemantauan ProxySQL di cluster database kita. Karena kita menjalankan kluster Galera, jalankan pernyataan berikut di salah satu node Galera:
mysql> CREATE USER 'proxysql'@'%' IDENTIFIED BY 'proxysqlpassw0rd';
mysql> GRANT USAGE ON *.* TO 'proxysql'@'%';
Jika Anda belum membuat pengguna MySQL (seperti yang ditentukan di bagian mysql_users di atas), kita juga harus membuatnya:
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'@'%';
Itu dia. Kami sekarang siap untuk memulai penerapan.
Menerapkan StatefulSet
Kita akan mulai dengan membuat dua instance ProxySQL, atau replika untuk tujuan redundansi menggunakan StatefulSet.
Mari kita mulai dengan membuat file teks bernama proxysql-ss-svc.yml dan tambahkan baris berikut:
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
Ada dua bagian dari definisi di atas - StatefulSet dan Service. StatefulSet adalah definisi dari pod atau replika kita dan titik mount untuk volume ConfigMap kita, yang diambil dari proxysql-configmap. Bagian selanjutnya adalah definisi layanan, di mana kita mendefinisikan bagaimana pod harus diekspos dan dirutekan untuk jaringan internal atau eksternal.
Buat set dan layanan stateful ProxySQL:
$ kubectl create -f proxysql-ss-svc.yml
Verifikasi status pod dan layanan:
$ 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
Jika Anda melihat log pod, Anda akan melihat bahwa kami dibanjiri dengan peringatan ini:
$ 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)
Di atas berarti proxysql-0 tidak dapat menyelesaikan "proxysql-1.proxysqlcluster" dan menyambungkannya, yang diharapkan karena kami belum membuat layanan tanpa kepala kami untuk catatan DNS yang akan diperlukan untuk komunikasi antar-ProxySQL.
Layanan Tanpa Kepala Kubernetes
Agar pod ProxySQL dapat menyelesaikan FQDN yang diantisipasi dan terhubung secara langsung, proses penyelesaian harus dapat mencari alamat IP pod target yang ditetapkan dan bukan alamat IP virtual. Di sinilah layanan tanpa kepala muncul. Saat membuat layanan tanpa kepala dengan menyetel "clusterIP=None", tidak ada penyeimbangan beban yang dikonfigurasi dan tidak ada IP cluster (IP virtual) yang dialokasikan untuk layanan ini. Hanya DNS yang dikonfigurasi secara otomatis. Saat Anda menjalankan kueri DNS untuk layanan tanpa kepala, Anda akan mendapatkan daftar alamat IP pod.
Berikut adalah tampilannya jika kita mencari catatan DNS layanan tanpa kepala untuk "proxysqlcluster" (dalam contoh ini kita memiliki 3 instance 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
Sementara output berikut menunjukkan catatan DNS untuk layanan standar yang disebut "proxysql" yang diselesaikan ke clusterIP:
$ host proxysql
proxysql.default.svc.cluster.local has address 10.110.38.154
Untuk membuat layanan tanpa kepala dan melampirkannya ke pod, seseorang harus mendefinisikan ServiceName di dalam deklarasi StatefulSet, dan definisi Layanan harus memiliki "clusterIP=None" seperti yang ditunjukkan di bawah ini. Buat file teks bernama proxysql-headless-svc.yml dan tambahkan baris berikut:
apiVersion: v1
kind: Service
metadata:
name: proxysqlcluster
labels:
app: proxysql
spec:
clusterIP: None
ports:
- port: 6032
name: proxysql-admin
selector:
app: proxysql
Buat layanan tanpa kepala:
$ kubectl create -f proxysql-headless-svc.yml
Hanya untuk verifikasi, pada titik ini, kami menjalankan layanan berikut:
$ 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
Sekarang, periksa salah satu log pod kami:
$ 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.
Anda akan melihat komponen Cluster dapat menyelesaikan, menghubungkan, dan mendeteksi checksum baru dari rekan lainnya, proxysql-1.proxysqlcluster pada port 6032 melalui layanan tanpa kepala yang disebut "proxysqlcluster". Perhatikan bahwa layanan ini hanya mengekspos port 6032 dalam jaringan Kubernetes, sehingga tidak dapat dijangkau secara eksternal.
Pada titik ini, penerapan kami telah selesai.
Menghubungkan ke ProxySQL
Ada beberapa cara untuk terhubung ke layanan ProxySQL. Koneksi MySQL dengan beban seimbang harus dikirim ke port 6033 dari dalam jaringan Kubernetes dan menggunakan port 30033 jika klien terhubung dari jaringan eksternal.
Untuk terhubung ke antarmuka admin ProxySQL dari jaringan eksternal, kita dapat terhubung ke port yang ditentukan di bagian NodePort, 30032 (192.168.100.203 adalah alamat IP utama host kube3.local):
$ mysql -uproxysql-admin -padminpassw0rd -h192.168.100.203 -P30032
Gunakan clusterIP 10.110.38.154 (didefinisikan di bawah layanan "proxysql") pada port 6032 jika Anda ingin mengaksesnya dari pod lain di jaringan Kubernetes.
Kemudian lakukan perubahan konfigurasi ProxySQL sesuai keinginan dan muat ke runtime:
mysql> INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('newuser','passw0rd',10);
mysql> LOAD MYSQL USERS TO RUNTIME;
Anda akan melihat baris berikut di salah satu pod yang menunjukkan sinkronisasi konfigurasi selesai:
$ 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
Perlu diingat bahwa sinkronisasi otomatis hanya terjadi jika ada perubahan konfigurasi pada waktu proses ProxySQL. Oleh karena itu, sangat penting untuk menjalankan pernyataan "LOAD ... TO RUNTIME" sebelum Anda dapat melihat aksinya. Jangan lupa untuk menyimpan perubahan ProxySQL ke dalam disk untuk persistensi:
mysql> SAVE MYSQL USERS TO DISK;
Batasan
Perhatikan bahwa ada batasan untuk pengaturan ini karena ProxySQL tidak mendukung penyimpanan/pengeksporan konfigurasi aktif ke dalam file konfigurasi teks yang dapat kita gunakan nanti untuk memuat ke ConfigMap untuk persistensi. Ada permintaan fitur untuk ini. Sementara itu, Anda dapat mendorong modifikasi ke ConfigMap secara manual. Jika tidak, jika pod terhapus secara tidak sengaja, Anda akan kehilangan konfigurasi saat ini karena pod baru akan di-bootstrap oleh apa pun yang ditentukan dalam ConfigMap.
Terima kasih khusus kepada Sampath Kamineni, yang mencetuskan ide entri blog ini dan memberikan wawasan tentang kasus penggunaan dan implementasi.