HAProxy

去年因為需要建置 OpenShift 容器平台而接觸到了 HAProxy,才知道為什麼 AWS 上會有分 Application Load balancer 等不同類型的負載平衡器。

簡單的說,HAProxy 的負載平衡模式分為兩種,一種是 HTTP mode,一種是 TCP mode;也就是 Layer 7 跟 Layer 4。建置 OpenShift 容器平台時,是使用 TCP mode ,Layer 4 的模式,當 HAProxy 收到連線請求時,是直接看哪一個 backend node 可以用,就直接左手給右手,連線到該台 backend node。那 HAProxy 也可以設定為 HTTP mode ,在這種模式下,就像是 nginx/apache 的 reverse proxy ,可以在 frontend 加上憑證跟做 VirtualHost。

安裝很簡單,Ubuntu 跟 RHEL 都有提供

sudo apt install -y haproxy
sudo yum install -y haproxy

安裝以後,設定檔位置是在 /etc/haproxy.cfg

主要需要調整的是 frontend 跟 backend

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 10s
        timeout client  60s
        timeout server  60s
        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

frontend http_bind
        bind *:80
        mode tcp
        option tcplog
        default_backend router_http


frontend https_bind
        bind *:443
        mode tcp
        option tcplog
        default_backend router_https

backend router_http
        mode tcp
        server router_server router:80

backend router_https
        mode tcp
        server router_server router:443

frontend 決定 haproxy 要 listen 哪一個 port,backend 決定是由哪個 node 來處理連線,很容易理解。(聽同事說,這跟 F5 的設定也很接近。)

在 RHEL 會比較容易遇到 HAProxy 有問題,主因是 SELinux ,在啟動 HAProxy 前,需要先使用 setsebool 來設定:

getsebool -a | grep haproxy_connect_any
setsebool haproxy_connect_any on

跟 HAProxy 相關的 SELinux 設定可以用 man 8 haproxy_selinux 來查詢。

參考資料

HAProxy的VirtualHost

Apache / Nginx 很常用到的情境就是同個 Port 會有不同的站台,這時會使用到 VirtualHost 。

那 HAProxy 可以這樣做嗎?利用 Google 找了一下,發現是可以,主要是使用 ACL 的設定。

關鍵的設定是寫在 frontend 裡 (設定摘錄自 HAProxy – route by domain name | Sean McGary)

frontend http-in
        bind *:80

        # Define hosts
        acl host_bacon req.hdr(host) -i ilovebacon.com
        acl host_milkshakes req.hdr(host) -i bobsmilkshakes.com

        ## figure out which one to use
        use_backend bacon_cluster if host_bacon
        use_backend milshake_cluster if host_milkshakes

利用 acl + hdr(host) 來判斷 HTTP header 裡的 Host ,接著再使用 use_backend … if … 來決定使用哪一個 backend。

查到以後,我試了好一陣子,都試不出來,後來才搞懂,當使用這個來判斷 header 時,mode 必須要改為 http,如果 mode 是 tcp,那麼是沒作用的。同理,當要用到 https 上時,也會不行,因為連線的內容被加密,HAProxy 無法解讀內容,也就無法得知 header。

HAProxy 的 backend 裡可以改寫 request 的 header,方法如下

backend ocp-apps-http
    mode http
    http-request del-header Host
    http-request set-header Host petclinic.apps.example.com
    server node1 petclinic.apps.example.com:80

透過這個,就可以達到類似 reverse proxy 的效果。

今天試下來,大致對 Apache / Nginx / HAProxy 的定位有了一定程度的了解。HAProxy 有兩個 mode:tcp / http,tcp 適用於 SSL pass-through 跟純 TCP 傳輸的情況,這時是由 backend 來處理 SSL 憑證;http 可以視為跟 Apache / Nginx 相似的情況,也可以掛 SSL 憑證。

簡單的說,Apache / Nginx 並不能完全取代 HAProxy,反之亦然。