HAProxy Load Balancer Layer 7 trên CentOS Stream 9: Cài đặt, Cấu hình và Health Check Backend

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

Khi một server không còn đủ dùng

Hồi CentOS 8 EOL, mình phải migrate gấp 5 server sang Rocky Linux trong 1 tuần — đó cũng là lần đầu tiên mình thực sự hiểu tại sao load balancer lại cần thiết đến vậy. Trước đó mình cứ nghĩ “server mạnh là đủ”. Sai hoàn toàn. Phần cứng tốt đến mấy cũng không giúp được gì khi traffic tăng gấp 5 lần chỉ trong vài giờ — hay khi cần deploy mà không thể để downtime dù chỉ 30 giây.

HAProxy (High Availability Proxy) là tool mình quay lại nhiều nhất. Nhẹ, ổn định, free. Nginx hay Apache cũng làm được load balancing, nhưng đó không phải việc chính của chúng — HAProxy được thiết kế từ đầu cho mục đích này, nên xử lý vài chục nghìn request/giây trên hardware tầm trung mà CPU vẫn rảnh.

Layer 7 nghĩa là HAProxy hiểu được nội dung HTTP — nó route traffic dựa trên URL path, header, cookie… Khác với Layer 4 chỉ nhìn vào IP/port, Layer 7 cho phép: /api/* đi server A, /static/* đi server B, hay sticky session theo cookie người dùng. Thực tế hơn: frontend React đi một pool, REST API đi pool khác — tất cả phía sau cùng một IP public.

Cài đặt HAProxy trên CentOS Stream 9

Mô hình lab

  • Load Balancer: 192.168.1.10 — máy CentOS Stream 9 cài HAProxy
  • Backend 1: 192.168.1.21 — Apache hoặc Nginx đang chạy
  • Backend 2: 192.168.1.22 — Apache hoặc Nginx đang chạy

Cài package

CentOS Stream 9 đã có HAProxy trong repo AppStream, cài thẳng bằng dnf:

sudo dnf install -y haproxy
haproxy -v

Chạy haproxy -v để xác nhận — AppStream thường đi với HAProxy 2.4.x, đủ dùng cho phần lớn production workload.

Xử lý SELinux trước khi cấu hình

Đây là chỗ nhiều người bỏ qua rồi than “sao không kết nối được backend”. SELinux mặc định sẽ block HAProxy kết nối đến port tùy ý. Cần bật boolean sau:

# Cho phép HAProxy kết nối đến backend
sudo setsebool -P haproxy_connect_any 1

# Kiểm tra đã bật chưa
getsebool haproxy_connect_any

Nếu backend dùng port không chuẩn như 8080, cần khai báo thêm vào SELinux policy:

sudo semanage port -a -t http_port_t -p tcp 8080

Mở port với firewalld

Port 80 cho client, port 8404 cho Stats monitoring page — mở một lần, reload là xong:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-port=8404/tcp
sudo firewall-cmd --reload

# Xác nhận
sudo firewall-cmd --list-all

Cấu hình HAProxy Layer 7

Viết file cấu hình

Backup file gốc rồi tạo cấu hình mới:

sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak
sudo nano /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    log         /dev/log local0
    log         /dev/log local1 notice
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats

#---------------------------------------------------------------------
# Defaults áp dụng cho tất cả frontend/backend
#---------------------------------------------------------------------
defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

#---------------------------------------------------------------------
# Stats page — http://192.168.1.10:8404/stats
#---------------------------------------------------------------------
frontend stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth admin:StrongPassword123
    stats admin if TRUE

#---------------------------------------------------------------------
# Frontend chính — nhận request từ client
#---------------------------------------------------------------------
frontend http_front
    bind *:80
    default_backend web_servers

    # Layer 7 routing: /api/* đi backend riêng
    acl is_api path_beg /api/
    use_backend api_servers if is_api

#---------------------------------------------------------------------
# Backend web servers
#---------------------------------------------------------------------
backend web_servers
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200

    server web1 192.168.1.21:80 check inter 5s fall 3 rise 2
    server web2 192.168.1.22:80 check inter 5s fall 3 rise 2

#---------------------------------------------------------------------
# Backend API servers
#---------------------------------------------------------------------
backend api_servers
    balance leastconn
    option httpchk GET /api/health
    http-check expect status 200

    server api1 192.168.1.21:8080 check inter 5s fall 3 rise 2
    server api2 192.168.1.22:8080 check inter 5s fall 3 rise 2

Giải thích các tham số quan trọng

  • balance roundrobin: Chia đều request theo vòng tròn. Dùng leastconn nếu muốn gửi đến server ít kết nối nhất — phù hợp với API có xử lý lâu.
  • option httpchk GET /health: HAProxy gửi HTTP GET đến /health để kiểm tra backend còn sống không.
  • inter 5s: Check mỗi 5 giây.
  • fall 3: Fail 3 lần liên tiếp mới đánh dấu DOWN.
  • rise 2: Success 2 lần liên tiếp mới đưa server trở lại UP.
  • option forwardfor: Thêm header X-Forwarded-For để backend biết IP thật của client.

Tạo endpoint /health trên backend

Backend cần trả 200 OK để HAProxy xác nhận còn sống. Với Apache, cách nhanh nhất:

# Chạy trên từng backend server
echo "OK" | sudo tee /var/www/html/health

Khởi động HAProxy

# Validate cấu hình trước
sudo haproxy -c -f /etc/haproxy/haproxy.cfg

# Start và enable
sudo systemctl start haproxy
sudo systemctl enable haproxy

# Kiểm tra trạng thái
sudo systemctl status haproxy

Kiểm tra và Monitoring

Truy cập Stats Page

Truy cập http://192.168.1.10:8404/stats trên trình duyệt, đăng nhập bằng admin / StrongPassword123. Dashboard khá đủ dùng cho monitoring hằng ngày — hiển thị trực tiếp:

  • Trạng thái từng backend (xanh = UP, đỏ = DOWN)
  • Số request, bytes in/out, session hiện tại
  • Response time trung bình và tỉ lệ lỗi

Test load balancing bằng curl

Thay vì đoán, cứ curl thẳng 10 request liên tiếp để thấy HAProxy phân phối thế nào:

for i in {1..10}; do
    curl -s http://192.168.1.10/ -o /dev/null -w "%{http_code} from: %{url_effective}\n"
done

Nếu mỗi backend trả server name khác nhau trong response header, thấy sự phân phối rõ hơn:

for i in {1..6}; do
    curl -si http://192.168.1.10/ | grep -i 'x-served-by\|server:'
done

Kiểm tra trạng thái backend qua socket

Muốn xem chi tiết từng server mà không cần mở browser:

echo "show servers state" | sudo socat stdio /var/lib/haproxy/stats

Xem log real-time

Mở terminal riêng và chạy song song khi test — log HAProxy cực kỳ chi tiết:

sudo journalctl -u haproxy -f

Mỗi dòng log ghi đủ: client IP, request path, response code, thời gian xử lý, và backend nào đã phục vụ request đó.

Thử nghiệm failover

Test thực tế nhất: tắt hẳn một backend, xem HAProxy phản ứng thế nào:

# Trên backend server 192.168.1.21
sudo systemctl stop httpd

# Trên load balancer, theo dõi log
sudo journalctl -u haproxy -f

Trong vòng 15 giây (3 lần check × 5 giây/lần), HAProxy đánh dấu web1 là DOWN và chuyển toàn bộ traffic sang web2. Stats page hiển thị web1 màu đỏ. Khi bật lại httpd, sau 10 giây (2 lần check thành công), web1 tự động trở lại UP.

Bài học từ thực tế

Sau cái vụ migrate CentOS 8 gấp đó, mình rút ra một điều: luôn kiểm tra logic của health check endpoint, không chỉ kiểm tra web server còn sống. Có lần HAProxy báo web1 UP màu xanh lá, nhưng thực ra ứng dụng đang lỗi do database mất kết nối — chỉ là webserver vẫn response 200 cho /health. Từ đó mình thêm logic vào endpoint /health: kiểm tra database ping, cache connection… nếu bất kỳ thứ gì fail thì trả về 500 để HAProxy biết mà tránh server đó.

Tổng kết

Chừng này là đủ để có một load balancer Layer 7 thực chiến trên CentOS Stream 9 — SELinux bật, firewalld đúng chuẩn, không phải tắt thứ gì cho nhanh. HAProxy tự phát hiện backend lỗi và failover, Stats page monitor real-time, ACL Layer 7 xử lý việc route traffic theo URL path.

Muốn đi xa hơn: thêm SSL/TLS termination tại HAProxy với Let’s Encrypt, hoặc cấu hình sticky session cho app stateful. Còn ngay lúc này, khi một backend gặp sự cố, HAProxy tự chuyển traffic sang cái còn lại — người dùng không biết có gì xảy ra.

Share: