ทำไมต้อง HAProxy — เมื่อ Server เดียวไม่พอ
ผมเจอปัญหานี้มาหลายครั้งเว็บไซต์ที่รันบน server เดียวพอ traffic เพิ่มขึ้น CPU ก็ร้อน RAM ก็เต็ม response time ก็ช้าลงจะ scale vertical (เพิ่ม RAM, CPU) ก็มีขีดจำกัดและ downtime ทุกครั้งที่ upgrade คำตอบคือ scale horizontal เพิ่ม server แล้วใช้ Load Balancer กระจาย traffic
HAProxy (High Availability Proxy) เป็น open-source load balancer ที่ได้รับความนิยมมากที่สุดในโลกใช้โดยบริษัทใหญ่ๆเช่น GitHub, Stack Overflow, Reddit, Airbnb และ Instagram มันรองรับ concurrent connections ได้หลายหมื่นถึงหลายแสน connections โดยใช้ memory น้อยมากเพราะเขียนด้วย C และออกแบบมาให้ efficient ตั้งแต่แรก
HAProxy vs Nginx vs Traefik
ทั้ง 3 ตัวทำ load balancing ได้แต่มีจุดเด่นต่างกัน HAProxy เป็น dedicated load balancer ที่ดีที่สุดเรื่อง performance และ advanced routing rules Nginx เป็น web server ที่ทำ reverse proxy และ load balancing ได้ด้วยเหมาะสำหรับใครที่ใช้ Nginx อยู่แล้ว Traefik ออกแบบมาสำหรับ cloud-native environments auto-discover services จาก Docker, Kubernetes ได้ผมใช้ HAProxy สำหรับ production เพราะเรื่อง performance และ reliability ที่ proven มานานกว่า 20 ปี
ติดตั้ง HAProxy LTS
# ติดตั้ง HAProxy 2.8 LTS จาก PPA
apt install -y software-properties-common
add-apt-repository ppa:vbernat/haproxy-2.8
apt update
apt install haproxy
# ตรวจสอบ version
haproxy -v
# HAProxy version 2.8.x LTS
# เปิดใช้งาน
systemctl enable --now haproxy
# ตรวจสอบ config syntax
haproxy -c -f /etc/haproxy/haproxy.cfg
โครงสร้าง Configuration
# /etc/haproxy/haproxy.cfg มี 4 sections หลัก
# 1. global — ตั้งค่า process-level
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
maxconn 100000
# SSL tuning
tune.ssl.default-dh-param 2048
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384
ssl-default-bind-options ssl-min-ver TLSv1.2
# 2. defaults — ค่า default สำหรับทุก frontend/backend
defaults
log global
mode http
option httplog
option dontlognull
option forwardfor
option http-server-close
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
# 3. frontend — รับ traffic จาก client
frontend http_front
bind *:80
bind *:443 ssl crt /etc/haproxy/certs/example.com.pem
http-request redirect scheme https unless { ssl_fc }
default_backend web_servers
# 4. backend — ส่ง traffic ไปยัง servers
backend web_servers
balance roundrobin
option httpchk GET /health
http-check expect status 200
server web1 10.10.10.11:80 check inter 3s fall 3 rise 2
server web2 10.10.10.12:80 check inter 3s fall 3 rise 2
server web3 10.10.10.13:80 check inter 3s fall 3 rise 2
Layer 4 vs Layer 7 Load Balancing
HAProxy รองรับทั้ง Layer 4 (TCP) และ Layer 7 (HTTP) load balancing ซึ่งเป็นจุดเด่นที่ทำให้มันใช้ได้กับทุก protocol
Layer 4 — TCP Load Balancing
# ใช้สำหรับ protocol ที่ไม่ใช่ HTTP เช่น MySQL, PostgreSQL, Redis
frontend mysql_front
mode tcp
bind *:3306
default_backend mysql_servers
backend mysql_servers
mode tcp
balance roundrobin
option mysql-check user haproxy
server mysql1 10.10.10.21:3306 check
server mysql2 10.10.10.22:3306 check backup
# Layer 4 จะไม่ดู HTTP headers เลย
# แค่ forward TCP packets ไปยัง backend
# เหมาะสำหรับ database, mail server, game server
Layer 7 — HTTP Load Balancing
# ใช้สำหรับ HTTP/HTTPS ที่ต้องการ routing ตาม URL, headers, cookies
frontend http_front
mode http
bind *:80
bind *:443 ssl crt /etc/haproxy/certs/
# ACL-based routing
acl is_api path_beg /api
acl is_static path_end .css .js .png .jpg .gif .svg .woff2
acl is_websocket hdr(Upgrade) -i websocket
# Route ตาม ACL
use_backend api_servers if is_api
use_backend static_servers if is_static
use_backend websocket_servers if is_websocket
default_backend web_servers
backend api_servers
mode http
balance leastconn
server api1 10.10.10.31:8080 check
server api2 10.10.10.32:8080 check
backend static_servers
mode http
balance roundrobin
http-response set-header Cache-Control "public, max-age=86400"
server static1 10.10.10.41:80 check
backend web_servers
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
server web1 10.10.10.11:80 check cookie web1
server web2 10.10.10.12:80 check cookie web2
backend websocket_servers
mode http
balance source
timeout tunnel 3600s
server ws1 10.10.10.51:8080 check
server ws2 10.10.10.52:8080 check
SSL Termination และ HTTPS
HAProxy ทำ SSL termination ได้ดีมากรับ HTTPS connection จาก client แล้วส่ง HTTP ธรรมดาไปยัง backend ทำให้ backend servers ไม่ต้องจัดการ SSL เอง
ตั้งค่า SSL Certificate
# รวม certificate และ key เป็น PEM file เดียว
cat /etc/letsencrypt/live/example.com/fullchain.pem \
/etc/letsencrypt/live/example.com/privkey.pem \
> /etc/haproxy/certs/example.com.pem
chmod 600 /etc/haproxy/certs/example.com.pem
# ตั้งค่า frontend
frontend https_front
bind *:443 ssl crt /etc/haproxy/certs/ alpn h2, http/1.1
bind *:80
# Redirect HTTP → HTTPS
http-request redirect scheme https code 301 unless { ssl_fc }
# HSTS header
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
default_backend web_servers
Auto-Renew Let's Encrypt กับ HAProxy
#!/bin/bash
# /usr/local/bin/haproxy-cert-renew.sh
certbot renew --quiet
for domain in /etc/letsencrypt/live/*/; do
domain_name=$(basename "$domain")
cat "fullchain.pem" "privkey.pem" \
> "/etc/haproxy/certs/.pem"
done
systemctl reload haproxy
# Crontab
# 0 3 * * * /usr/local/bin/haproxy-cert-renew.sh
Health Checks และ Failover
Health checks เป็น feature ที่สำคัญที่สุดของ load balancer เพราะถ้า backend server ตาย HAProxy ต้องหยุดส่ง traffic ไปที่ server นั้นทันที
HTTP Health Checks
backend web_servers
mode http
balance roundrobin
# ตรวจสอบด้วย HTTP GET /health
option httpchk GET /health HTTP/1.1\r\nHost:\ example.com
http-check expect status 200
# inter: ตรวจทุกกี่วินาที
# fall: fail กี่ครั้งถึง mark down
# rise: success กี่ครั้งถึง mark up
server web1 10.10.10.11:80 check inter 3s fall 3 rise 2 weight 100
server web2 10.10.10.12:80 check inter 3s fall 3 rise 2 weight 100
server web3 10.10.10.13:80 check inter 3s fall 3 rise 2 weight 50 # server เล็กกว่า
Advanced Health Checks
# ตรวจสอบ MySQL
backend mysql_servers
mode tcp
option mysql-check user haproxy
server mysql1 10.10.10.21:3306 check
# ตรวจสอบ PostgreSQL
backend postgres_servers
mode tcp
option pgsql-check user haproxy
server pg1 10.10.10.21:5432 check
# ตรวจสอบ Redis
backend redis_servers
mode tcp
option tcp-check
tcp-check send PING\r\n
tcp-check expect string +PONG
server redis1 10.10.10.21:6379 check
Roundrobin
กระจาย traffic เท่าๆกันวนไปง่ายที่สุดเหมาะเมื่อ backend servers มี spec เท่ากันถ้า spec ไม่เท่าให้ใช้ weight ช่วย
Leastconn
ส่ง traffic ไปยัง server ที่มี active connections น้อยที่สุดเหมาะสำหรับ long-lived connections เช่น WebSocket, database connections ผมใช้ algorithm นี้สำหรับ API servers เป็นหลัก
Source (IP Hash)
Hash IP ของ client เพื่อให้ client เดิมเข้า server เดิมเสมอเหมาะสำหรับ applications ที่เก็บ session ใน memory ของ server แต่ผมไม่แนะนำวิธีนี้ควรใช้ shared session store (Redis) แทน
backend api_servers
balance leastconn
server api1 10.10.10.31:8080 check maxconn 500
server api2 10.10.10.32:8080 check maxconn 500
# maxconn จำกัด connections สูงสุดต่อ server
# ถ้าเกิน จะ queue ไว้รอ
HAProxy กับ Docker และ Microservices
ในยุค microservices HAProxy ยังคงเป็นตัวเลือกที่ดีสำหรับ ingress load balancer
Docker Compose กับ HAProxy
# docker-compose.yml
services:
haproxy:
image: haproxy:2.8-alpine
ports:
- "80:80"
- "443:443"
- "8404:8404"
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
- ./certs:/etc/haproxy/certs:ro
depends_on:
- web1
- web2
restart: unless-stopped
web1:
image: nginx:alpine
volumes:
- ./html:/usr/share/nginx/html:ro
web2:
image: nginx:alpine
volumes:
- ./html:/usr/share/nginx/html:ro
สำหรับการจัดการ containers ที่ซับซ้อนขึ้นดูบทความ Docker Compose Tutorial ของผม
Dynamic Server Discovery ด้วย DNS
# ใช้ DNS SRV records สำหรับ dynamic backends
backend dynamic_servers
mode http
balance roundrobin
server-template web 1-10 web.service.consul:80 check resolvers consul resolve-prefer ipv4
resolvers consul
nameserver consul 127.0.0.1:8600
accepted_payload_size 8192
hold valid 5s
เปิด Stats Page
# เพิ่มใน haproxy.cfg
frontend stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats admin if LOCALHOST
stats auth admin:SecurePassword123
# เข้าดูที่ http://haproxy-ip:8404/stats
# จะเห็น real-time statistics ของทุก frontend/backend
# รวมถึง session rate, bytes in/out, server health status
ส่ง Metrics ไปยัง Prometheus
# เปิด Prometheus exporter (built-in ตั้งแต่ HAProxy 2.0)
frontend stats
bind *:8404
http-request use-service prometheus-exporter if { path /metrics }
stats enable
stats uri /stats
# เพิ่มใน prometheus.yml
scrape_configs:
- job_name: 'haproxy'
static_configs:
- targets: ['haproxy:8404']
metrics_path: /metrics
ใช้ metrics จาก HAProxy สร้าง dashboard ใน Grafana ได้ Import Dashboard ID: 2428 (HAProxy) จาก Grafana.com
Rate Limiting
# จำกัด request rate ต่อ IP
frontend http_front
bind *:80
# Stick table เก็บ rate per IP
stick-table type ip size 100k expire 30s store http_req_rate(10s)
http-request track-sc0 src
# Block ถ้าเกิน 50 requests ใน 10 วินาที
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 50 }
default_backend web_servers
HAProxy กิน resource เยอะไหม?
น้อยมากครับ HAProxy เขียนด้วย C ที่ optimize มาอย่างดีสำหรับ 10,000 concurrent connections ใช้ RAM ประมาณ 100-200 MB CPU ก็แทบไม่กระทบผมเคย benchmark HAProxy บน server ที่มี 4 cores RAM 8 GB ได้ 200,000 requests per second สำหรับ HTTP keep-alive connections ซึ่งเพียงพอสำหรับเว็บไซต์ส่วนใหญ่
HAProxy ตายแล้ว website ล่มทั้งหมดีไหม?
ถ้ามี HAProxy เครื่องเดียวใช่ครับมันจะเป็น Single Point of Failure ทางแก้คือตั้ง HAProxy 2 เครื่องใช้ Keepalived + Virtual IP (VIP) เมื่อ HAProxy ตัวหลักตาย Keepalived จะย้าย VIP ไปที่ตัว backup ภายใน 1-3 วินาทีผมตั้งแบบนี้สำหรับทุก production deployment
ใช้ HAProxy กับ WireGuard VPN ได้ไหม?
ได้ครับใช้ Layer 4 (TCP/UDP) mode สำหรับ forward WireGuard traffic แต่ WireGuard ใช้ UDP ซึ่ง HAProxy รองรับ UDP ได้ตั้งแต่ version 2.3 ขึ้นไปอีกวิธีคือใช้ HAProxy เป็น load balancer สำหรับ web services ที่อยู่หลัง VPN network
Sticky Sessions คืออะไรควรใช้เมื่อไหร?
Sticky Sessions (หรือ Session Affinity) คือการทำให้ client เดิมเข้า backend server เดิมเสมอโดยใช้ cookie HAProxy จะเพิ่ม cookie (เช่น SERVERID) ให้ client ครั้งถัดไป client ส่ง cookie กลับมา HAProxy ก็จะ route ไปที่ server เดิมใช้เมื่อ application เก็บ session ใน memory ของ server แต่ผมแนะนำให้ใช้ Redis เป็น shared session store แทนจะ scale ได้ดีกว่า
สรุป
HAProxy เป็น load balancer ที่ proven มานานกว่า 20 ปีใช้โดยบริษัทที่มี traffic สูงที่สุดในโลกมันเบาเร็ว reliable และ flexible ไม่ว่าจะเป็น Layer 4 TCP load balancing สำหรับ database หรือ Layer 7 HTTP routing ที่ซับซ้อน HAProxy ทำได้หมด
สำหรับผม HAProxy เป็น component แรกที่ผมวางใน architecture เสมอเมื่อ application ต้องการ high availability ร่วมกับ Keepalived สำหรับ failover, Let's Encrypt สำหรับ SSL, Prometheus + Grafana สำหรับ monitoring ก็ได้ระบบ load balancing ที่สมบูรณ์แบบโดยไม่ต้องเสียค่า license
อ่านเพิ่มเติม: สอนเทรด Forex | XM Signal | IT Hardware | อาชีพ IT