Dağıtık mesajlaşma sistemleri (Apache Pulsar, Kafka, RabbitMQ Cluster vb.) söz konusu olduğunda “HA (High Availability)” kelimesi çok hızlı şekilde karmaşık yapılara evrilir, ekstra kontrol katmanları, consensus mekanizmaları, orchestration çözümleri, her şeyi kontrol eden bir “cluster manager”… Bu yaklaşımlar doğru olsa da her ortam için şart değildir.

Benim sahada en sık gördüğüm durum şudur:

İnsanlar HA için çok büyük bir yapı kuruyor ama asıl problem HA deği, doğru failover kararını verememek.

Çünkü HA sadece “yedeklilik” değildir – HA demek hizmetin gerçekten kullanılabilir kalması demektir.

Bu yazımda anlatacağım mimari tam da buraya odaklanıyor:

  • Çok karmaşık cluster topology’si kurmadan,
  • Basit ama kontrollü bir şekilde,
  • Failover’u “servis çalışıyor” gibi yüzeysel sinyallerden çıkarıp,
  • servisin arkasındaki gerçek kapasiteyi ölçerek karar vermek,

Ve bunu iki temel bileşenle yapmak;

  • HAProxy → Load balancing + sağlık bilgisini üreten merkez
  • Keepalived (VRRP) → VIP yönetimi + failover tetikleme

Bu yaklaşım neden önemli? (Sahadaki asıl problem)

Birçok kişi Keepalived’i şu mantıkla kullanır:

  • VIP → MASTER node üzerinde durur
  • MASTER’de HAProxy aktif mi? → Evet → VIP burada kalsın
  • HAProxy down oldu → VIP BACKUP node’a geçsin

Buradaki kritik hata şudur:

HAProxy ayakta olabilir ama arka tarafta broker havuzu ölmüş olabilir.

Yani:

  • keepalived “servis ayakta” zannediyor,
  • ama client tarafında publish/consume başarısız,
  • sistem kullanıcı gözüyle down ama sistemin monitoring’i “green”,

Bu en tehlikeli arızadır: silent failure.

Ben bunun etkisini özellikle messaging sistemlerinde çok gördüm;

  • client timeout’ları başlar,
  • retry storm olur,
  • connection sayıları patlar,
  • broker load artar, daha fazla broker düşer,
  • kısır döngü oluşur,

İşte bu mimari o yüzden güçlü, failover kararını “process var mı?” ile değil capacity var mı? ile veriyor.

Genel mimari tasarım: VIP + HAProxy + HA üzerinden failover

Bu mimarinin ana fikri sade;

  1. Client’lar tek bir adrese bağlanır: Virtual IP (VIP)
  2. VIP arkasında HAProxy vardır ve trafiği broker’lara dağıtır
  3. Broker health bilgisi HAProxy üzerinden toplanır
  4. Keepalived “failover” kararını HAProxy stats verisini okuyup verir

Bu mimariyle şunu başarmış olursunuz:

  • Client config değişmez.
  • Client tarafında “active broker bulma” karmaşası olmaz.
  • Failover kararı ölçülebilir, objektif bir kritere bağlanır.
  • Sistem “half-dead” olduğunda bile otomatik aksiyon alınır.

Keepalived burada ne yapıyor?

Keepalived’in rolü çok net;

  • VIP’i iki node arasında taşımak
  • VRRP ile “kim MASTER kim BACKUP” kararını vermek
  • Failover’u tetiklemek için external script sonuçlarını izlemek

Bu noktada Keepalived’i sıradan bir VIP aracı olmaktan çıkaran şey:

track_script mekanizması ile failover’a servis katmanı zekâsı eklemek.

Sadece HAProxy çalışıyor mu kontrolü yetmez. track_script mantığı

Bu yapıda Keepalived iki ayrı kontrol script’i çalıştırır;

A) chk_haproxy

Bu script HAProxy process’i var mı / port dinliyor mu / basic health kontrol eder.

Bu check “minimum” kontrol. Çünkü HAProxy down olursa zaten VIP taşınmalı.

B) chk_pulsar

İşte burası mimarinin yıldız kısmı.

Bu script HAProxy’nin stats çıktısını okuyarak şu soruyu yanıtlar:

Backend havuzunda kaç broker DOWN durumda?

Yani Keepalived artık şuna bakıyor:

  • “HAProxy yaşıyor mu?” değil,
  • “HAProxy’nin arkasındaki servis gerçekten sağlıklı mı?”

5) HAProxy burada sadece LB değil — doğruluk kaynağı (source of truth)

Bu mimaride HAProxy bir load balancer gibi davranıyor ama aynı zamanda bir “hakem” gibi.

Sahada HA mimarilerinde en büyük problem:

  • health check verisi dağınık olur
  • herkes farklı yerden kontrol eder
  • kimi telnet 6650, kimi curl 8080, kimi “systemctl active”
  • sonuç: çelişkili sinyaller

HAProxy stats ise tek bir noktadan şunu söyler:

  • Hangi broker UP?
  • Hangi broker DOWN?
  • Check rise/fall değerleriyle istikrarlı mı?
  • Backend tamamen mi patladı?

Ben sahada mümkün olduğunca şöyle yapıyorum:

Failover kararında “tek truth kaynağı” seç ve sadece onu okut.

Burada truth kaynağı: HAProxy stats CSV.


6) check_pulsar.sh nasıl çalışır? (Failover’u belirleyen script)

Script’in ana fikri:

  • HAProxy stats CSV çekilir
  • belirli backend filtrelenir
  • $18=="DOWN" olanlar sayılır
  • eşik aşılırsa exit 1

Örnek mantık:

  • 3 broker var
  • MAX_DOWN=1

Bu şu demek:

  • 0 broker DOWN → OK
  • 1 broker DOWN → hala tolere edilebilir
  • 2 broker DOWN → artık kapasite kritik, sistem degrade → FAILOVER

Bu yaklaşım sahada çok kıymetli çünkü:

  • tek broker düşmesi failover’a sebep olmaz
  • false positive azalır
  • failover “gerekli olduğunda” olur

Failover bir çözüm değil, risktir. Gereksiz failover ortamı daha çok bozar.

Bu cümle HA tasarımında çok kritik bir prensip.


Failover kararı nasıl oluşuyor? (Keepalived’in iç mantığı)

Keepalived’te işler şu sırayla olur:

  1. script çalışır.
  2. script exit 0 döndürürse → node sağlıklı sayılır.
  3. script exit 1 döndürürse → node’un priority’si düşürülür.
  4. BACKUP node’un priority’si daha yüksek kalırsa,
  5. VIP diğer tarafa taşınır.

Bu sistemin güzelliği:

  • network bazlı değil servis bazlı HA sağlaması

Ben bunu “VIP failover”dan ziyade şöyle adlandırıyorum:

“Service-aware failover”

Sahada ben olsam ekstra neler eklerdim? (Deneyim + pratik öneri)

Burayı özellikle eklemek istedim çünkü üretimde bu yapı çok iyi çalışsa da bazı noktalarda iyileştirme gerekir.

1) Failover kararına sadece DOWN sayısı değil, latency de eklenebilir

Bazı durumlarda broker UP görünür ama:

  • thread pool doludur,
  • response time uzamıştır,
  • client retry yapar,

Bu yüzden ben genelde şunu severim:

  • DOWN sayısı > threshold → fail,
  • veya response time/queue aşırı artarsa → fail,

HAProxy stats üzerinden ayrıca şu metrikler de değerlendirilebilir:

  • qcur (current queue),
  • scur (current sessions),
  • rate (request rate),

Bunu script’e eklemek kolaydır.

2) “flapping” riskine dikkat

Failover’un en tehlikelisi VIP’in sürekli gidip gelmesi. Bunu önlemek için öneriler:

  • rise/fall değerleri iyi ayarlanmalı
  • Keepalived’de advert_int ve script interval uyumlu olmalı
  • MAX_DOWN agresif seçilmemeli
  • log ve telemetry ile failover sebebi izlenmeli

3 Script timeout / auth / erişim hatası durumları

Benim sahada en çok gördüğüm problem:

HAProxy stats çekilemiyor (curl timeout), script FAIL dönüyor, VIP gereksiz taşınıyor.

Burada iki yaklaşım var:

  • (A) “Stats çekilemiyorsa failover tetikle” (agresif, riskli)
  • (B) “Stats çekilemiyorsa ayrı alarm üret ama failover etme” (daha güvenli)

Ben genelde şunu yapıyorum:

  • HAProxy down ise zaten chk_haproxy fail eder
  • Stats erişim hatası varsa chk_pulsar FAIL etmek yerine “WARN” yazmalı

Yani script’i şöyle güncellemek iyi olur:

  • stats erişim hatası: exit 0 ama log’a ERROR yaz
  • gerçek DOWN broker sayısı eşiği aşarsa: exit 1

4) Güvenlik: stats endpoint’ini koru

Stats endpoint’i kritik.

Öneriler:

  • mümkünse sadece localhost’tan eriş,
  • veya firewall ile sadece Keepalived node izinli olsun,
  • auth password kesinlikle environment’ta plaintext bırakılmasın,
  • basic auth yerine mTLS bile tercih edilebilir.

Örnek Mimari

Node01 (Datacenter / Site A)

  • Node IP (Keepalived + HAProxy): 192.168.1.10 (MASTER)
  • VIP (Client’ların bağlandığı adres): 192.168.1.11
  • HAProxy Stats Bind: 192.168.1.10:9000
  • Pulsar Broker’lar (Node01 tarafı):
    • 192.168.1.48
    • 192.168.1.49
    • 192.168.1.50

Servis Portları

  • Pulsar Broker: 6650
  • HTTP API: 8080

Node02 (Datacenter / Site B)

  • Node IP (Keepalived + HAProxy): 192.168.1.12 (BACKUP)
  • VIP (Client’ların bağlandığı adres): 192.168.1.11
  • HAProxy Stats Bind: 192.168.1.12:9000
  • Pulsar Broker’lar (Node02 tarafı):
    • 192.168.1.51
    • 192.168.1.52
    • 192.168.1.53

Servis Portları

  • Pulsar Broker: 6650
  • HTTP API: 8080

Node01 (MASTER) – Keepalived

Dosya: /etc/keepalived/keepalived.conf

vrrp_script chk_pulsar {
    script "/usr/local/bin/check_pulsar.sh"
    interval 2
    weight 2
}

vrrp_script chk_haproxy {
    script "/usr/local/bin/check_haproxy.sh"
    interval 2
    weight 2
}

vrrp_instance VI_PULSAR {
    state MASTER
    interface ens160
    virtual_router_id 144
    priority 101
    advert_int 2

    authentication {
        auth_type PASS
        auth_pass cluster_ha_2024
    }

    virtual_ipaddress {
        192.168.1.11/24 dev ens160
    }

    unicast_src_ip 192.168.1.10
    unicast_peer {
        192.168.1.12
    }

    track_script {
        chk_haproxy
        chk_pulsar
    }
}

Node01 – check_pulsar.sh

Dosya: /usr/local/bin/check_pulsar.sh

#!/bin/bash
#
# HAProxy backend health monitor via stats CSV
# Checks if >=2 brokers are DOWN, triggers failover (exit 1)
#
# Author : Kursad GUNES
# Version: 1.0

HAPROXY_STATS_URL="http://192.168.1.10:9000/haproxy?stats;csv"
AUTH="haproxy_admin:secure_pass_2024"
BACKEND="cluster01_backend_pulsar_6650"
MAX_DOWN=1   # fail if more than this many are DOWN
TIMEOUT=2
LOG_FILE="/var/log/check_haproxy_stats_down.log"

down_count=$(
  curl -su "$AUTH" --max-time "$TIMEOUT" "$HAPROXY_STATS_URL" 2>/dev/null \
  | awk -F',' -v backend="$BACKEND" '$1==backend && $2 !~ /BACKEND/ && $18=="DOWN" {c++} END{print c+0}'
)

if [ "$down_count" -gt "$MAX_DOWN" ]; then
  echo "[$(date '+%F %T')] FAILOVER: $down_count servers DOWN" >> "$LOG_FILE"
  exit 1
else
  echo "[$(date '+%F %T')] OK: $down_count servers DOWN" >> "$LOG_FILE"
  exit 0
fi

Not: Script’i çalıştırmadan önce;

chmod +x /usr/local/bin/check_pulsar.sh

Node01 – HAProxy

Dosya: /etc/haproxy/haproxy.cfg

global
    log /dev/log local0
    log /dev/log local1 notice
    maxconn 20000
    user haproxy
    group haproxy

defaults
    log global
    mode tcp
    option dontlognull
    option tcpka
    timeout connect 10s
    timeout client 1h
    timeout server 1h
    retries 3

frontend cluster01_frontend_pulsar_6650
    bind 0.0.0.0:6650
    mode tcp
    option tcplog
    default_backend cluster01_backend_pulsar_6650

frontend cluster01_frontend_8080
    bind 0.0.0.0:8080
    mode tcp
    option tcplog
    default_backend cluster01_backend_8080

# ON CLUSTER01 NODE: use the haproxy private ip
listen stats
    bind 192.168.1.10:9000
    mode http
    no log
    stats enable
    stats uri /haproxy?stats
    stats refresh 10s
    stats auth haproxy_admin:secure_pass_2024

# ON CLUSTER01 NODE: use the 3 CLUSTER01 brokers
backend cluster01_backend_pulsar_6650
    mode tcp
    balance leastconn
    option tcp-check
    tcp-check connect port 6650
    server b48 192.168.1.48:6650 check inter 3s fall 3 rise 2
    server b49 192.168.1.49:6650 check inter 3s fall 3 rise 2
    server b50 192.168.1.50:6650 check inter 3s fall 3 rise 2

backend cluster01_backend_8080
    mode tcp
    balance leastconn
    option tcp-check
    tcp-check connect port 8080
    server b48 192.168.1.48:8080 check inter 3s fall 3 rise 2
    server b49 192.168.1.49:8080 check inter 3s fall 3 rise 2
    server b50 192.168.1.50:8080 check inter 3s fall 3 rise 2

Node02 (BACKUP) – Keepalived

Dosya: /etc/keepalived/keepalived.conf

vrrp_script chk_pulsar {
    script "/usr/local/bin/check_pulsar.sh"
    interval 2
    weight 2
}

vrrp_script chk_haproxy {
    script "/usr/local/bin/check_haproxy.sh"
    interval 2
    weight 2
}

vrrp_instance VI_PULSAR {
    state BACKUP
    interface ens160
    virtual_router_id 144
    priority 100
    advert_int 2

    authentication {
        auth_type PASS
        auth_pass cluster_ha_2024
    }

    virtual_ipaddress {
        192.168.1.11/24 dev ens160
    }

    unicast_src_ip 192.168.1.12
    unicast_peer {
        192.168.1.10
    }

    track_script {
        chk_haproxy
        chk_pulsar
    }
}

Node02 – check_pulsar.sh

Dosya: /usr/local/bin/check_pulsar.sh

#!/bin/bash
#
# HAProxy backend health monitor via stats CSV
# Checks if >=2 brokers are DOWN, triggers failover (exit 1)
#
# Author : Kursad GUNES
# Version: 1.0

HAPROXY_STATS_URL="http://192.168.1.12:9000/haproxy?stats;csv"
AUTH="haproxy_admin:secure_pass_2024"
BACKEND="cluster02_backend_pulsar_6650"
MAX_DOWN=1   # fail if more than this many are DOWN
TIMEOUT=2
LOG_FILE="/var/log/check_haproxy_stats_down.log"

down_count=$(
  curl -su "$AUTH" --max-time "$TIMEOUT" "$HAPROXY_STATS_URL" 2>/dev/null \
  | awk -F',' -v backend="$BACKEND" '$1==backend && $2 !~ /BACKEND/ && $18=="DOWN" {c++} END{print c+0}'
)

if [ "$down_count" -gt "$MAX_DOWN" ]; then
  echo "[$(date '+%F %T')] FAILOVER: $down_count servers DOWN" >> "$LOG_FILE"
  exit 1
else
  echo "[$(date '+%F %T')] OK: $down_count servers DOWN" >> "$LOG_FILE"
  exit 0
fi

Node02 – HAProxy

Dosya: /etc/haproxy/haproxy.cfg

global
    log /dev/log local0
    log /dev/log local1 notice
    maxconn 20000
    user haproxy
    group haproxy

defaults
    log global
    mode tcp
    option dontlognull
    option tcpka
    timeout connect 10s
    timeout client 1h
    timeout server 1h
    retries 3

frontend cluster02_frontend_pulsar_6650
    bind 0.0.0.0:6650
    mode tcp
    option tcplog
    default_backend cluster02_backend_pulsar_6650

frontend cluster02_frontend_8080
    bind 0.0.0.0:8080
    mode tcp
    option tcplog
    default_backend cluster02_backend_8080

# ON CLUSTER02 NODE: use the haproxy private ip
listen stats
    bind 192.168.1.12:9000
    mode http
    no log
    stats enable
    stats uri /haproxy?stats
    stats refresh 10s
    stats auth haproxy_admin:secure_pass_2024

# ON CLUSTER02 NODE: use the 3 CLUSTER02 brokers
backend cluster02_backend_pulsar_6650
    mode tcp
    balance leastconn
    option tcp-check
    tcp-check connect port 6650
    server b51 192.168.1.51:6650 check inter 3s fall 3 rise 2
    server b52 192.168.1.52:6650 check inter 3s fall 3 rise 2
    server b53 192.168.1.53:6650 check inter 3s fall 3 rise 2

backend cluster02_backend_8080
    mode tcp
    balance leastconn
    option tcp-check
    tcp-check connect port 8080
    server b51 192.168.1.51:8080 check inter 3s fall 3 rise 2
    server b52 192.168.1.52:8080 check inter 3s fall 3 rise 2
    server b53 192.168.1.53:8080 check inter 3s fall 3 rise 2

(İsteğe bağlı ama öneririm) chk_haproxy.sh örneği

Eğer elinde yoksa, Keepalived’in chk_haproxy script’i için pratik bir örnek bırakıyorum:

Dosya: /usr/local/bin/check_haproxy.sh

#!/bin/bash
systemctl is-active --quiet haproxy
exit $?
chmod +x /usr/local/bin/check_haproxy.sh

Keepalived (Production) – Node01 (MASTER)

/etc/keepalived/keepalived.conf

global_defs {
    router_id PULSAR_HA_NODE01
    enable_script_security
    script_user root
}

vrrp_script chk_haproxy {
    script "/usr/local/bin/check_haproxy.sh"
    interval 2
    timeout 2
    fall 2
    rise 2
    weight -20
}

vrrp_script chk_pulsar {
    script "/usr/local/bin/check_pulsar.sh"
    interval 2
    timeout 3
    fall 3
    rise 3
    weight -30
}

vrrp_instance VI_PULSAR {
    state MASTER
    interface ens160
    virtual_router_id 144
    priority 101
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass cluster_ha_2024
    }

    virtual_ipaddress {
        192.168.1.11/24 dev ens160
    }

    unicast_src_ip 192.168.1.10
    unicast_peer {
        192.168.1.12
    }

    track_script {
        chk_haproxy
        chk_pulsar
    }

    # Flapping azaltmak için:
    # VIP bir kere Node02'ye geçtiyse hemen geri kapma
    # (İstersen bunu kaldırabiliriz; bazı ortamlarda preempt istenir.)
    nopreempt

    # (opsiyonel) Bildirim scriptleri
    notify_master "/usr/local/bin/vrrp_notify.sh MASTER"
    notify_backup "/usr/local/bin/vrrp_notify.sh BACKUP"
    notify_fault  "/usr/local/bin/vrrp_notify.sh FAULT"
}

Neden böyle?

  • weight -20/-30: problem varsa priority düşer (failover tetiklenir)
  • fall/rise: tek seferlik anlık hatalarda VIP zıplamasını engeller
  • nopreempt: VIP taşındıktan sonra hemen geri gelmesini engeller (çok faydalı)

Keepalived (Production) – Node02 (BACKUP)

/etc/keepalived/keepalived.conf

global_defs {
    router_id PULSAR_HA_NODE02
    enable_script_security
    script_user root
}

vrrp_script chk_haproxy {
    script "/usr/local/bin/check_haproxy.sh"
    interval 2
    timeout 2
    fall 2
    rise 2
    weight -20
}

vrrp_script chk_pulsar {
    script "/usr/local/bin/check_pulsar.sh"
    interval 2
    timeout 3
    fall 3
    rise 3
    weight -30
}

vrrp_instance VI_PULSAR {
    state BACKUP
    interface ens160
    virtual_router_id 144
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass cluster_ha_2024
    }

    virtual_ipaddress {
        192.168.1.11/24 dev ens160
    }

    unicast_src_ip 192.168.1.12
    unicast_peer {
        192.168.1.10
    }

    track_script {
        chk_haproxy
        chk_pulsar
    }

    nopreempt

    notify_master "/usr/local/bin/vrrp_notify.sh MASTER"
    notify_backup "/usr/local/bin/vrrp_notify.sh BACKUP"
    notify_fault  "/usr/local/bin/vrrp_notify.sh FAULT"
}

chk_haproxy.sh (Production)

/usr/local/bin/check_haproxy.sh

#!/bin/bash
set -euo pipefail

# HAProxy aktif mi?
if systemctl is-active --quiet haproxy; then
  exit 0
fi

exit 1

chmod +x /usr/local/bin/check_haproxy.sh

check_pulsar.sh (Production) – False Positive azaltılmış sürüm

Bu sürümde kritik farklar:

Stats endpoint’e ulaşılamıyorsa hemen failover ettirmiyoruz (en yaygın üretim kazası budur)
“Ardışık” kötü sonuç sayısını tutuyoruz (FAIL_STREAK)
Aynı anda iki kez çalışmayı engelliyoruz (flock)
Loglar daha okunur

Node01 için

/usr/local/bin/check_pulsar.sh

#!/bin/bash
set -euo pipefail

HAPROXY_STATS_URL="http://192.168.1.10:9000/haproxy?stats;csv"
AUTH="haproxy_admin:secure_pass_2024"
BACKEND="cluster01_backend_pulsar_6650"

MAX_DOWN=1          # 2 broker down olursa kritik (3 broker içinde)
TIMEOUT=2
LOG_FILE="/var/log/check_haproxy_stats_down.log"

# Üretimde çok işe yarar: ardışık hata olmadan failover yapma
STATE_DIR="/run/pulsar-ha"
STATE_FILE="${STATE_DIR}/pulsar_fail_streak"
FAIL_STREAK_TRIGGER=3   # 3 kez üst üste kötü -> failover
LOCK_FILE="/run/lock/check_pulsar.lock"

mkdir -p "$STATE_DIR"

log() {
  echo "[$(date '+%F %T')] $*" >> "$LOG_FILE"
}

# Aynı anda paralel çalışmayı engelle
exec 200>"$LOCK_FILE"
flock -n 200 || exit 0

# Stats çek
csv="$(curl -su "$AUTH" --max-time "$TIMEOUT" -fsS "$HAPROXY_STATS_URL" 2>/dev/null || true)"

# Stats erişilemiyorsa: burada iki yaklaşım var.
# Üretimde genelde "failover etme, logla" daha güvenli.
if [[ -z "${csv}" ]]; then
  log "WARN: Stats unreachable or empty. No failover (safe mode)."
  exit 0
fi

down_count="$(echo "$csv" \
  | awk -F',' -v backend="$BACKEND" '$1==backend && $2 !~ /BACKEND/ && $18=="DOWN" {c++} END{print c+0}')"

# Fail streak oku
streak=0
if [[ -f "$STATE_FILE" ]]; then
  streak="$(cat "$STATE_FILE" 2>/dev/null || echo 0)"
fi

if [[ "$down_count" -gt "$MAX_DOWN" ]]; then
  streak=$((streak + 1))
  echo "$streak" > "$STATE_FILE"
  log "BAD: backend=$BACKEND down_count=$down_count streak=$streak"

  if [[ "$streak" -ge "$FAIL_STREAK_TRIGGER" ]]; then
    log "FAILOVER: threshold exceeded. down_count=$down_count (MAX_DOWN=$MAX_DOWN) streak=$streak"
    exit 1
  fi

  # Henüz streak dolmadı, failover tetikleme
  exit 0
else
  # Sağlıklıysa streak sıfırla
  echo "0" > "$STATE_FILE"
  log "OK: backend=$BACKEND down_count=$down_count"
  exit 0
fi

Node02 için (sadece URL + BACKEND değişiyor)

/usr/local/bin/check_pulsar.sh

#!/bin/bash
set -euo pipefail

HAPROXY_STATS_URL="http://192.168.1.12:9000/haproxy?stats;csv"
AUTH="haproxy_admin:secure_pass_2024"
BACKEND="cluster02_backend_pulsar_6650"

MAX_DOWN=1
TIMEOUT=2
LOG_FILE="/var/log/check_haproxy_stats_down.log"

STATE_DIR="/run/pulsar-ha"
STATE_FILE="${STATE_DIR}/pulsar_fail_streak"
FAIL_STREAK_TRIGGER=3
LOCK_FILE="/run/lock/check_pulsar.lock"

mkdir -p "$STATE_DIR"

log() {
  echo "[$(date '+%F %T')] $*" >> "$LOG_FILE"
}

exec 200>"$LOCK_FILE"
flock -n 200 || exit 0

csv="$(curl -su "$AUTH" --max-time "$TIMEOUT" -fsS "$HAPROXY_STATS_URL" 2>/dev/null || true)"
if [[ -z "${csv}" ]]; then
  log "WARN: Stats unreachable or empty. No failover (safe mode)."
  exit 0
fi

down_count="$(echo "$csv" \
  | awk -F',' -v backend="$BACKEND" '$1==backend && $2 !~ /BACKEND/ && $18=="DOWN" {c++} END{print c+0}')"

streak=0
if [[ -f "$STATE_FILE" ]]; then
  streak="$(cat "$STATE_FILE" 2>/dev/null || echo 0)"
fi

if [[ "$down_count" -gt "$MAX_DOWN" ]]; then
  streak=$((streak + 1))
  echo "$streak" > "$STATE_FILE"
  log "BAD: backend=$BACKEND down_count=$down_count streak=$streak"

  if [[ "$streak" -ge "$FAIL_STREAK_TRIGGER" ]]; then
    log "FAILOVER: threshold exceeded. down_count=$down_count (MAX_DOWN=$MAX_DOWN) streak=$streak"
    exit 1
  fi

  exit 0
else
  echo "0" > "$STATE_FILE"
  log "OK: backend=$BACKEND down_count=$down_count"
  exit 0
fi

chmod +x /usr/local/bin/check_pulsar.sh

VRRP notify script (Opsiyonel ama çok faydalı)

VIP geçişlerini loglamak için:

/usr/local/bin/vrrp_notify.sh

#!/bin/bash
STATE="${1:-UNKNOWN}"
LOGGER_TAG="keepalived-vrrp"

logger -t "$LOGGER_TAG" "VRRP state changed to: $STATE on $(hostname -s)"
exit 0

chmod +x /usr/local/bin/vrrp_notify.sh

HAProxy Stats Güvenliği (Production önerisi)

Şu an stats 192.168.1.10:9000 ve 192.168.1.12:9000 üzerinden erişilebilir. Üretimde ideal olan:

Seçenek A (en iyisi): Stats sadece localhost

HAProxy’de:

listen stats
    bind 127.0.0.1:9000

Ama burada script’in de http://127.0.0.1:9000/... kullanması gerekir.

Seçenek B: ACL ile sadece node IP’lerine izin

listen stats
    bind 192.168.1.10:9000
    mode http
    acl allowed src 192.168.1.10 192.168.1.12
    http-request deny if !allowed
    stats enable
    stats uri /haproxy?stats
    stats auth haproxy_admin:secure_pass_2024

Uygulama / Test Komutları (Hızlı doğrulama)

Keepalived durum

systemctl status keepalived
ip a | grep 192.168.1.11

HAProxy stats test

curl -su haproxy_admin:secure_pass_2024 "http://192.168.1.10:9000/haproxy?stats;csv" | head

Script test

/usr/local/bin/check_pulsar.sh; echo $?
tail -n 50 /var/log/check_haproxy_stats_down.log

Failover simülasyonu

  • Node01 tarafında 2 broker’ı DOWN durumuna düşür (servisi durdur / portu kapat)
  • log’da FAILOVER: satırlarını gör
  • VIP’nin Node02’ye geçtiğini doğrula

İlginizi Çekebilir