Haproxy Dynamic Backend IP

18 Likes Comment

Ngần đây tôi có gặp một bài toán về triển khai HAproxy trên một dịch vụ cho khách hàng, khách hàng có một yêu cầu khá là khó đó là phải cấu hình được backend IP với chế độ Dynamic mà trong khi kiến thức của tôi chỉ biết tới statics IP với Haproxy backend. Tôi đã mất 2 ngày để tìm hiểu và cấu hình được đây là bài note lại những gì tôi đã làm để hoàn thành song dự án này

I: Bài toán

  • Khách hàng có số lượng IP backend không cố định lúc thì 10 lúc thì 100 lúc thì 20…. cón số này nhẩy nhanh và nhiều cứ 1 tiếng thay đổi thậm chí khi có event thì nó thay đổi còn nhanh hơn
  • Khách hàng không cố định IP của backend mà để chế độ Dynamic IP thay đổi lung tung may một điều có 1 API để tôi có thể gọi tới tổng hợp được hết IP:PORT của các máy chủ backend

II: Giải Pháp

Bình thường chúng ta thường nghĩ tới việc là viết một file python hoặc script để làm việc lấy IP và thay vào phần server name của HAproxy tuy nhiên tôi có cách khác dùng tới tính năng của HA mà không cần dùng phần mềm hay script can thiệp tránh ảnh hưởng tối đa tới hoạt động của máy chủ trong bài này tôi sẽ chia sẻ cả 2 giải pháp và mã nguồn của chúng

2.1: Giải pháp dùng python can thiệp theo số lượng IP

Đầu tiên tôi cần một đoạn mã làm nhiệm vụ tải toàn bộ các IP backend với định dạng IP:PORT về lưu lại

import requests
import time

def fetch_proxies(url):
    response = requests.get(url)
    proxies = response.text.strip().split("\n")
    return proxies

def clean_proxy(proxy):
    return proxy.replace("\r", "").strip()

def save_proxies(proxies, file_path):
    with open(file_path, 'w') as f:
        for proxy in proxies:
            cleaned_proxy = clean_proxy(proxy)
            f.write(cleaned_proxy + "\n")

def update_proxies(url, file_path, interval=60):
    while True:
        proxies = fetch_proxies(url)
        save_proxies(proxies, file_path)
        print(f"Proxies updated: {len(proxies)} proxies saved.")
        time.sleep(interval)

url = "https://api.localdomain/ip"
file_path = "/etc/haproxy/proxies.txt"
update_proxies(url, file_path, interval=180)  # Cập nhật mỗi 5 phút

Đoạn code trên chúng ta sẽ tải danh sách các ip và lưu vào file proxies.txt trong thư mục /etc/haproxy. Bây giờ chúng ta sẽ dựa vào số dòng của file này ghi được để tạo ra file haproxy.cfg

import os
proxy_file = '/etc/haproxy/proxies.txt'
haproxy_conf = '/etc/haproxy/haproxy.cfg'

# Kiểm tra và xóa tệp haproxy.cfg nếu tồn tại
if os.path.exists(haproxy_conf):
    os.remove(haproxy_conf)
    print(f"Đã xóa tệp {haproxy_conf}")

max_servers = 10000  # Số lượng server tối đa

with open(proxy_file, 'r') as pf, open(haproxy_conf, 'w') as hf:
    hf.write("""
global
  maxconn 1024 
  daemon
  log /dev/log    local0
  log /dev/log    local1 notice

defaults
  mode http
  maxconn 1024
  option  httplog clf
  option  dontlognull
  retries 3
  timeout connect 5s
  timeout client 60s
  timeout server 60s

resolvers mydns
  nameserver dns1 8.8.8.8:53
  nameserver dns2 8.8.4.4:53
  resolve_retries       3
  timeout resolve       1s
  timeout retry         1s
  hold other           30s
  hold refused         30s
  hold nx              30s
  hold timeout         30s
  hold valid          10s

listen stats 
  bind *:10000
  mode            http
  log /dev/log    local0
  log /dev/log    local1 notice
  maxconn 10
  timeout client      100s
  timeout server      100s
  timeout connect      100s
  timeout queue   100s
  stats enable
  stats hide-version
  stats refresh 30s
  stats show-node
  stats uri /

frontend rotating_sv
  mode http
  log /dev/log    local0
  log /dev/log    local1 notice
  bind *:9999
  default_backend backend_sv

backend backend_sv
  mode http
  balance roundrobin
""")

    for i, line in enumerate(pf):
        if i >= max_servers:
            break
        ip_port = line.strip()
        hf.write(f"  server proxy{i+1} {ip_port} check\n")

Bây giờ chúng ta cần thêm 1 file để giám sát nếu file proxies.txt có thay đổi thì cũng cập nhập luôn cấu hình HA

import os
import time

def watch_file(file_path, callback, interval=60):
    """ Watch file modification and call callback if file is modified """
    last_mtime = os.path.getmtime(file_path)
    while True:
        time.sleep(interval)
        mtime = os.path.getmtime(file_path)
        if mtime != last_mtime:
            last_mtime = mtime
            callback()

def reload_haproxy():
    """ Reload HAProxy using systemctl """
    os.system("python3 new_ip_update.py")
    os.system("systemctl reload haproxy")
    print("HAProxy reloaded")

file_path = "/etc/haproxy/proxies.txt"
watch_file(file_path, reload_haproxy, interval=180)

Trong đoạn code trên cứ 180s bạn có thể thay đổi thời gian cho phù hợp thì hệ thống sẽ check file proxies.txt một lần và nếu có thay đổi nó sẽ tạo ra file haproxy.cfg mới cùng với việc reload lại cấu hình như vậy việc cấu hình Dynamic Backend IP cho HA đã song, bạn muốn cập nhập hay chậm thì thay đổi thời gian check của các file và thời gian chờ tải lại file là song

2: Giải pháp cấu hình HAproxy nhanh gọn

Sau khi tìm hiểu các giải pháp để hạn chế tối đa việc tôi phải chạy tools bên ngoài thì tôi nhận thấy phần server backend ngoài việc nó phải là dạng IP thì nó cũng nhận dạng là domain và phân giải DNS thông qua cấu hình resolvers dns trên HA từ đó làm IP backend và truy cập điều này làm tôi nảy ra ý tưởng điều gì sẽ xảy ra nếu domain của tôi trỏ tới nhiều IP cùng một lúc?

service.localdomain.com  IN A 10.0.0.1
                         A 10.0.0.2
                         A 10.0.0.3
                         A 10.0.0.4
                         A 10.0.0.5
                         A 10.0.0.6
                         A ........ 

Điều này có được là do bản ghi A của domain là không giới hạn bạn trỏ bao nhiêu tùy thích, Và tôi nhận thấy cách này hoàn toàn hoạt động được với cấu hình haproxy như sau

global
  maxconn 1024 
  daemon
  log /dev/log    local0
  log /dev/log    local1 notice
  #pidfile <%= pid_file %>

defaults
  mode http
  maxconn 1024
  option  httplog clf
  option  dontlognull
  retries 3
  timeout connect 5s
  timeout client 60s
  timeout server 60s

resolvers mydns
  nameserver dns1 127.0.0.1:53
  nameserver dns2 127.0.0.1:53
  resolve_retries       3
  timeout resolve       1s
  timeout retry         1s
  hold other           30s
  hold refused         30s
  hold nx              30s
  hold timeout         30s
  hold valid          10s

listen stats 
  bind *:10000
  mode            http
  log /dev/log    local0
  log /dev/log    local1 notice
  maxconn 10
  timeout client      100s
  timeout server      100s
  timeout connect      100s
  timeout queue   100s
  stats enable
  stats hide-version
  stats refresh 30s
  stats show-node
  stats uri /


frontend rotating_sv
  mode http
  log /dev/log    local0
  log /dev/log    local1 notice
  bind *:9999
  default_backend backend_sv

backend backend_sv
  mode http
  balance roundrobin
  server a001 172.183.241.1:8080 check
  server sv service.localdomain.com check resolvers mydns

Tôi nhận thấy lúc này HA hoạt động bình thường nghĩa là nó đã chạy theo một đường nào đó vào backend giờ tôi cần nhân nó ra và nhận nhiều IP nhất có thể thì tôi cấu hình server-template cho nó

global
  maxconn 1024 
  daemon
  log /dev/log    local0
  log /dev/log    local1 notice
  #pidfile <%= pid_file %>

defaults
  mode http
  maxconn 1024
  option  httplog clf
  option  dontlognull
  retries 3
  timeout connect 5s
  timeout client 60s
  timeout server 60s

resolvers mydns
  nameserver dns1 127.0.0.1:53
  nameserver dns2 127.0.0.1:53
  accepted_payload_size 8192
  resolve_retries       3
  timeout resolve       1s
  timeout retry         1s
  hold other           30s
  hold refused         30s
  hold nx              30s
  hold timeout         30s
  hold valid          10s

listen stats 
  bind *:10000
  mode            http
  log /dev/log    local0
  log /dev/log    local1 notice
  maxconn 10
  timeout client      100s
  timeout server      100s
  timeout connect      100s
  timeout queue   100s
  stats enable
  stats hide-version
  stats refresh 30s
  stats show-node
  stats uri /


frontend rotating_sv
  mode http
  log /dev/log    local0
  log /dev/log    local1 notice
  bind *:9999
  default_backend backend_sv

backend backend_sv
  mode http
  balance roundrobin
  server-template sv 1-300 sv service.localdomain.com:8080 check resolvers mydns init-addr none

Với server-template thì lúc này hệ thống sẽ tạo ra mặc định 300 sv backend và lấy IP từ phân giải domain service.localdomain.com để lấy IP và set vào backend thứ duy nhất tôi cần làm đó là điều chỉnh cái DNS của domain đó trên local bằng cách thay đổi file host của OS theo cái API Lấy được vậy là song, HA sẽ tự động cập nhập IP ta có thể set thời gian cho nó mà không lo chết hay phải reload lại HA nữa đỡ cực hơn rất nhiều việc phải sinh lại cấu hình của haproxy liên tục.

Giải pháp này có một lưu ý đó là bạn không nên dùng máy chủ DNS bên ngoài vì bạn sẽ phải truy vấn nhiều có thể dẫn tới các vấn đề về cập nhập hãy dùng local thôi và set file host là nhanh nhất thay đổi nó cũng nhanh.

Update 1: Nếu trong trường hợp các port của backend là khác nhau thì chúng ta có một giải pháp là sử dụng bản ghi dns SRV trong loại bản ghi này thì chúng ta có thể khai bào được cả IP và PORT bạn cần thay đổi domain trong cấu hình HA và máy chủ DNS và bản ghi SRV của mình thành domain _myservice._tcp.localdomain.com ví dụ cấu hình sẽ thành

global
  maxconn 1024 
  daemon
  log /dev/log    local0
  log /dev/log    local1 notice
  #pidfile <%= pid_file %>

defaults
  mode http
  maxconn 1024
  option  httplog clf
  option  dontlognull
  retries 3
  timeout connect 5s
  timeout client 60s
  timeout server 60s

resolvers mydns
  nameserver dns1 127.0.0.1:53
  nameserver dns2 127.0.0.1:53
  accepted_payload_size 8192
  resolve_retries       3
  timeout resolve       1s
  timeout retry         1s
  hold other           30s
  hold refused         30s
  hold nx              30s
  hold timeout         30s
  hold valid          10s

listen stats 
  bind *:10000
  mode            http
  log /dev/log    local0
  log /dev/log    local1 notice
  maxconn 10
  timeout client      100s
  timeout server      100s
  timeout connect      100s
  timeout queue   100s
  stats enable
  stats hide-version
  stats refresh 30s
  stats show-node
  stats uri /


frontend rotating_sv
  mode http
  log /dev/log    local0
  log /dev/log    local1 notice
  bind *:9999
  default_backend backend_sv

backend backend_sv
  mode http
  balance roundrobin
  server-template sv 1-300 sv _myservice._tcp.localdomain.com check resolvers mydns init-addr none

Điều này làm được là do bản ghi SRV có cấu trúc

_myservice._tcp.localdomain.com 10 1 8080 10.10.1.1
_myservice._tcp.localdomain.com 20 1 9090 10.10.1.2
_myservice._tcp.localdomain.com 10 1 8080 10.10.1.3
_myservice._tcp.localdomain.com 20 1 9090 10.10.1.4

và khi chúng ta kiểm tra dns trên máy chủ

dig @127.0.0.1 -p 53 SRV _myservice._tcp.localdomain.com

Kết quả chúng ta sẽ nhận được

;; QUESTION SECTION:
;_myservice._tcp.localdomain.com. IN
;; ANSWER SECTION:
_myservice._tcp.localdomain.com. 0 IN     SRV     0 0 8080 host1.
_myservice._tcp.localdomain.com. 0 IN     SRV     0 0 9090 host2.
_myservice._tcp.localdomain.com. 0 IN     SRV     0 0 8080 host3.
_myservice._tcp.localdomain.com. 0 IN     SRV     0 0 9090 host4.

;; ADDITIONAL SECTION:
host1.                  0       IN      A       10.10.1.1
host2.                  0       IN      A       10.10.1.2
host3.                  0       IN      A       10.10.1.3
host4.                  0       IN      A       10.10.1.4

Từ đó cấu hình này có thể được đẩy vào HAproxy một cách đơn giản

0 0 votes
Article Rating
Avatar

About the Author: Vô Ưu

Xin chào! Tôi là Vouu, người sáng lập blog này. Tôi là một người yêu thích. Tôi có một công việc ban ngày với tư cách là một chuyên viên bơm vá săm xe các loại, và trang web này là một trong những hoạt động yêu thích của tôi, đặc biệt là trong những lúc rảnh rỗi. Hy vọng bạn thích sử dụng trang web này, và nó sẽ mang lại cho bạn nhiều điều bổ ích. Phản hồi từ người đọc sẽ truyền cảm hứng cho tôi để tôi có thể viết được nhiều hơn nữa !. Bạn chỉ cần để lại nhận xét bên dưới nếu bạn thấy bài viết này hữu ích. Chúc bạn ngày mới tốt lành!
Subscribe
Notify of
guest
0 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x