Load Balance ile Mysql Replikasyon

Bu yazıda HaProxy kullanarak Mysql veritabanı için master-slave yapısını docker ile kurarak slave veritabanları için bir yük dağıtımı yapacağız.

Load Balance Nedir?

Bir hizmete gelen ağ trafiğini belirli kurallar çerçevesinde yöneten veya dağıtan yazılımlardır. Bu içerikte bahsedilecek olan load balance için HaProxy kullanılmıştır. Temel amacı birden fazla sunucu arasında yük dağıtımı yaparak performansı ve sistem sürdürülebilirliğini arttırmaktır. Buradan ilgili dökümanlara ve daha fazla detaya ulaşabilirsiniz.

Mysql Master-Slave Nedir?

Mysql veritabanı sisteminde bulunan bir ana (master) veritabanı sunucusunun değişikliklerini, bir veya daha fazla ikincil (slave) veritabanı sunucusuna otomatik olarak replike etme işlemidir. Bu replikasyon, veritabanı sunucularını yedekleme, yük dengeleme ve yüksek erişilebilirlik gibi senaryolarda kullanılabilir. Temelde yüksek erişebilirlik ve performans konusunda oldukça faydalı bir yöntemdir. Yazma işlemleri için master kullanılırken okuma işlemleri için slave kullanılır. Buradan konunun tüm ayrıntılarını okuyabilirsiniz.

Şimdi ise örneğimize bakalım. Docker ile hazırlanmış örneğe buradan ulaşabilirsiniz.

Öncelikle bir master database e ihtiyacımız olacak. İlgili örnekte docker-compose.yaml da mysql_master adıyla tanımlanmış bir mysql veritabanı bulunuyor. Ardından mysql_slave_1 ve mysql_slave_2 adında slave olarak konumlandırılacak veritabanı bulunuyor. Bir adet HaProxy ve örneği test etmek için PHP tanımlamaları yapılmıştır. Ben örnek üzerinde docker için bir network oluşturup her birine IP tanımlaması yaptım fakat host name ilede bu işlemler yapılabilirdi.

Proje içerisinde bash .build.sh diyerek konteynırlar ayağa kaldırılabilir ve master slave ayarları sağlanır. Bash script içersinde her çalıştırıldığında mysql datalarının silineceği bir komut bulunmaktadır.

PHP ile test etmek için pdo_mysql eklentisini kurdum. Bu sayede veritabanına bağlantı kuracağız.

Proje içerisinde bulunan master klasörü altında conf dizini bulunmaktadır. İçerisinde bulunan conf.cnf dosyasında
master için tanımlamalar bulunmaktadır.

[mysqld]

skip-name-resolve # bağlantı sırasında DNS çözümlemesi yapmamasını belirten bir seçenektir. 
default_authentication_plugin = mysql_native_password #eski tip şifreleme yöntemini kullanmasını sağlar.

server-id = 1 #Mysql replikasyonu için bir sunucu kimliği belirtir.
log_bin = 1 #binlog'un etkinleştirildiğini belirtir.
binlog_format = ROW #binary log formatını belirtir
binlog_do_db = app #belirli bir veritabanının binlog'a yazılmasını sağlar

Diğer slave veritabanları içinde aynı konfigurasyonlar bulunur fakat tek fark server-id değeridir.

Ayrıca her bir veritabanı için oluşturulan bağlantı bilgileri .env dosyası içerisinde bulunur. Her birinin kendine ait bağlantı bilgileri kendi veritabanları için geçerli olacak şekilde bulunmaktadır.

conf dizini altında HaProxy’e ait konfigurasyon bulunmaktadır. Bu konfigürasyonda;

listen mysql-slaves #mysql-slaves grubu 
    mode 	tcp 
    option	tcpka
    bind		*:3306 #haproxy ye slaveler için baglanılacak port bilgisi
    option	mysql-check	user	haproxy_check #ha-proxy için oluşturulmuş yetkisiz mysql kullanıcısı
    balance	roundrobin #dengeleme yöntemi
    server	slave1		10.10.0.3:3306	check #slave 1 in adresi
    server  slave2      10.10.0.4:3306  check #slave 2 nin adresi

listen mysql-masters
    mode 	tcp
    option	tcpka
    bind		*:3307
    option	mysql-check	user	haproxy_check
    balance	roundrobin
    server	master1		10.10.0.6:3306	check

tanımlamaları yapılmıştır. Bu tanımlamalar ile konteynırlar ayağa kalktığında HaProxy hizmeti kendisine tanımlamış olan server bilgilerini dinleyerek yine bind etiketi ile tanımlanmış olan portlara gelen istekleri roundrobin olarak dengeleyecektir.

Bash scripti içerisinde mysql user ve master-slave gerçekleştirmek için komutlar bulunmaktadır.

#!/bin/bash


# her seferinde baştan bir kurulum sağlar
rm -rf ./storage/master/data/*
rm -rf ./storage/slave_1/data/*
rm -rf ./storage/slave_2/data/*

docker compose up -d
#mysql_master erişilebilir hale gelene kadar bekle
until docker exec mysql_master sh -c 'export MYSQL_PWD=password; mysql -u root -e ";"'
do
    echo "Waiting for mysql_master database connection..."
    sleep 4
done

#mysql_slave erişilebilir hal gelene kadar bekle
until docker exec mysql_slave_1 sh -c 'export MYSQL_PWD=password; mysql -u root -e ";"'
do
    echo "Waiting for mysql_slave_1 database connection..."
    sleep 4
done
until docker exec mysql_slave_2 sh -c 'export MYSQL_PWD=password; mysql -u root -e ";"'
do
    echo "Waiting for mysql_slave_2 database connection..."
    sleep 4
done

#mysql_master içerisinde slave_user_1 oluşturulur ve slave olarak tanımlanarak slave veritabanı gösterilir

docker exec mysql_master sh -c "mysql -u root -ppassword -e 'CREATE USER \"slave_user_1\"@\"%\" IDENTIFIED BY \"password\"; GRANT REPLICATION SLAVE ON *.* TO \"slave_user_1\"@\"%\"; FLUSH PRIVILEGES;'"

#mysql_master içerisinde slave_user_1 oluşturulur ve slave olarak tanımlanarak slave veritabanı gösterilir

docker exec mysql_master sh -c "mysql -u root -ppassword -e 'CREATE USER \"slave_user_2\"@\"%\" IDENTIFIED BY \"password\"; GRANT REPLICATION SLAVE ON *.* TO \"slave_user_2\"@\"%\"; FLUSH PRIVILEGES;'"

#master log dizinleri alınır
MS_STATUS=`docker exec mysql_master sh -c 'mysql -u root -ppassword -e "SHOW MASTER STATUS"'`
CURRENT_LOG=`echo $MS_STATUS | awk '{print $6}'`
CURRENT_POS=`echo $MS_STATUS | awk '{print $7}'`

#slaveler için master tanımlaması yapılır ve slaveler başlatılır

start_slave_stmt="CHANGE MASTER TO MASTER_HOST='mysql_master',MASTER_USER='slave_user_1',MASTER_PASSWORD='password',MASTER_LOG_FILE='$CURRENT_LOG',MASTER_LOG_POS=$CURRENT_POS; START SLAVE;"
start_slave_cmd='mysql -u root -ppassword -e "'
start_slave_cmd+="$start_slave_stmt"
start_slave_cmd+='"'
docker exec mysql_slave_1 sh -c "$start_slave_cmd"

start_slave_stmt="CHANGE MASTER TO MASTER_HOST='mysql_master',MASTER_USER='slave_user_2',MASTER_PASSWORD='password',MASTER_LOG_FILE='$CURRENT_LOG',MASTER_LOG_POS=$CURRENT_POS; START SLAVE;"
start_slave_cmd='mysql -u root -ppassword -e "'
start_slave_cmd+="$start_slave_stmt"
start_slave_cmd+='"'

docker exec mysql_slave_2 sh -c "$start_slave_cmd"

#Haproxy için yetkisiz bu kullanıcı oluşturulur bu kullanıcı mysqlin ayakta olup olmadığını kontrol eder. 

docker exec mysql_master sh -c "mysql -u root -ppassword -e 'CREATE USER '\''haproxy_check'\''@'\''%'\'"

Sadece master üzerinden HaProxy için kullanıcı oluşturduk fakat master-slave bağlantıları gerçekleştiği için aslında diğer slave veritabanları içinde bu kullanıcı var olmuş oldu.

Artık bunu test edebiliriz. Tarayıcı üzerinden http://localhost:90 adresine giderek slavelere atılan server id sorgusunun her bir istekte değiştiğini ve slavelere tanımlı olan 2 ve 3 id sayısını göreceksiniz.

<?php

$masterConection = new PDO("mysql:host=haproxy;port=3307;dbname=app", "root", "password");
$masterConection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$slaveConnection = new PDO("mysql:host=haproxy;port=3306;dbname=app", "root", "password");
$slaveConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$query = "SHOW VARIABLES LIKE 'server_id'";

$masterStmt = $masterConection->prepare($query);
$masterStmt->execute();

$slaveStmt = $slaveConnection->prepare($query);
$slaveStmt->execute();

$masterResult = $masterStmt->fetchAll(PDO::FETCH_ASSOC);
$slaveResult = $slaveStmt->fetchAll(PDO::FETCH_ASSOC);

var_dump("master: ", $masterResult);
var_dump("slave: ", $slaveResult);

Burada bağlantıyı aslında HaProxy’e kuruyorum fakat HaProxy bu bağlantıyı benim için kendisine tanımlı olan serverlara iletiyor.

Avantajları

  • Yük Dengeleme: HAProxy, gelen Mysql trafiğini birden fazla Mysql sunucusuna yönlendirerek yükü dengeler. Bu, taleplerin eşit bir şekilde paylaşılmasını sağlar, böylece her bir Mysql sunucusu üzerindeki yük dengelenir. Bu, performansı artırabilir ve tek bir sunucunun üzerindeki yükü azaltabilir.
  • Yüksek Kullanılabilirlik: HAProxy, Mysql sunucuları arasında otomatik olarak geçiş yaparak yüksek kullanılabilirlik sağlar. Bir Mysql sunucusu başarısız olduğunda, HAProxy otomatik olarak çalışan diğer sunucuya yönlendirme yapabilir. Bu, sistemdeki kesinti süresini minimize eder.
  • Tek Giriş Noktası: HAProxy, istemcilere karşı tek bir giriş noktası sunarak, uygulama geliştiricilerinin ve yöneticilerin tek bir bağlantı noktası üzerinden Mysql sunucularına erişmelerini sağlar. Bu, konfigürasyonu ve erişimi yönetmeyi kolaylaştırır.
  • Performans ve Ölçeklenebilirlik: Yük dengeleme sayesinde, HAProxy ile yapılandırılmış bir Mysql master-slave yapı, artan taleplere daha etkili bir şekilde yanıt verebilir. Yeni slave sunucular eklemek, sistemi ölçeklendirmek için daha kolaydır.

Dezavantajları

  • Ek Karmaşıklık: Yük dengeleyici ek bir bileşen ekler ve bu da yapıyı daha karmaşık hale getirebilir. Yapılandırma, yönetim ve izleme gereksinimleri daha fazla olabilir.
  • Ekstra Arıza Olasılığı: Eğer HAProxy sunucusu arızalanırsa, tüm trafik etkilenebilir. Bu durumda, yedek HAProxy sunucuları veya yük dengeleyiciler arasında yük dengeleme yapılması düşünülebilir.
  • SSL/TLS Sertifikası Yönetimi: Eğer Mysql trafiği üzerinde SSL/TLS kullanılıyorsa, HAProxy’nin SSL/TLS sertifikası yönetimi ile ilgilenmesi gerekebilir. Bu, ek konfigürasyon ve güncelleme ihtiyacı doğurabilir.
  • Bağlantı Gecikmeleri: Yük dengeleyici, Mysql sunucuları arasında talepleri yönlendirmek için ek bir aşama ekler. Bu, bağlantı gecikmelerine neden olabilir. Bu durum, uygulama performansı için kritikse dikkate alınmalıdır.