FPs

HAProxy 最佳實踐筆記

haproxy_best_practice

這篇筆記內容主要來自Haproxy官方團隊分享的一個幻燈片:《Haproxy best practice》,介紹了一些常規的配置方式和優化手段。

不要盲目使用文中出現的所有技巧。

Haproxy 是如何工作的

關於Haproxy 團隊介紹,以及一些特性介紹直接看前面幾張幻燈片即可。 Haproxy 的主要用作代理請求,工作流程如下:
haproxy_proxy_mode

client 和Haproxy 建立連接,Haproxy 再和對應的後端server 建立連接,然後作爲中間人,轉發請求。 在Haproxy 配置中,對一個代理,會劃分爲2層,frontend(前端) 和backend(後端),這和上文說的Haproxy 的工作流程也是對應的。
配置例(來自幻燈片):

global
  daemon
defaults
  mode http
  timeout client 10s
  timeout connect 4s
  timeout server 30s

frontend fe
  bind 10.0.0.1:80
  bind 10.0.0.1:443 ssl crt ./my.pem
  default_backend be

backend be
  server s1 10.0.0.101:80 check
  server s2 10.0.0.102:80 check

haproxy_frontend_backend

frontend:監聽端口、協議代理定義,HTTP認證,後端選擇等;
backend:監控server,負載均衡,隊列。
可以看出,在frontend 中定義了要綁定的地址和端口,以及證書等,在backend,羅列了後端的IP和端口。不過要把2者合在一起寫,也是可以的,使用listen 即可,如下:

listen http-in
    bind *:80
    server server1 127.0.0.1:8000 maxconn 32
A "frontend" section describes a set of listening sockets accepting client
connections.

A "backend" section describes a set of servers to which the proxy will connect
to forward incoming connections.

A "listen" section defines a complete proxy with its frontend and backend
parts combined in one section. It is generally useful for TCP-only traffic.

摘自 http://www.haproxy.org/download/1.4/doc/configuration.txt

RTF(W|E|L)M

閱讀(Warings/Errors/Log)消息。

  • HAProxy 會向stderr(標準錯誤輸出) 輸出配置錯誤信息;
    • 當配置文件中出現錯誤的時候,warning消息會輸出到stderr。
  • 當開啓日誌記錄的時候,可以發送消息到syslog
  • HAProxy 會提供一條消息,來解釋錯誤,有時候也會提供修復方法。 如下:
[WARNING] 177/011147 (8652) : Setting tune.ssl.default-dh-param to 1024 by default, if your workload permits it
Configuration file is valid

HAProxy 的日誌信息非常豐富,也可以自定義日誌格式,對於線上排查故障非常有用。

硬件推薦

由於HAProxy 的工作機制,需求如下:

  • CPU:相對於多核心,更應該看重速度(也就是主頻更高),和緩存大小。
  • 需要足夠的內存來處理TCP 連接,HAProxy 的開銷以及系統的開銷,千萬不能忽視大量TCP連接帶來的內存開銷。
  • 網卡:intel 是更好的選擇。
  • 硬盤:非必須,除非本地需要日誌記錄。

爲了達到最佳性能,還需要一點調整:

  • 把網卡中斷和內核綁定在core 0 上;
  • HAProxy 綁定在通一塊物理CPU 的下一個核心上。

這個優化很有道理,也非常必要,特別是HAProxy 如果還是工作在單進程模式下,這樣能最大化壓榨性能。

注意:卸載irqbalanceirqbalance 是一個系統服務,可以在多個核心之間自動平衡中斷。
延伸閱讀:褚霸:《深度剖析告诉你irqbalance有用吗?》

避免使用VM 或其他共享資源的雲服務。

Sysctl 調優

最重要的調優:

  • net.ipv4.ip_local_port_range = "1025 65534"
  • net.ipv4.tcp_max_syn_backlog = 100000
  • net.core.netdev_max_backlog = 100000
  • net.core.somaxconn = 65534
  • ipv4.tcp_rmem = "4096 16060 64060"
  • ipv4.tcp_wmem = "4096 16384 262144"

調net.ipv4.ip_local_port_range,是因爲HAProxy 需要充當中間人,開N多端口,連接前後端。調大了net.ipv4.tcp_max_syn_backlog,前者決定了三次握手之後,還沒被HAProxy接收的TCP 連接的排隊數量,超過了之後,系統不再接受新的請求,所以得調高這個值。net.core.somaxconn 是系統端口的監聽隊列,也得增大。ipv4.tcp_rmemipv4.tcp_wmem,減小默認值和最大值,防止內存爆了,關於TCP 內存這一塊,可以看陶輝的這篇文章,寫的很好:《高性能网络编程7--tcp连接的内存使用》

如果有長連接,把tcp_slow_start_after_idle 設置爲0,即關閉,防止長連接空閒之後,系統降低擁塞窗口,提升性能。

  • tcp_slow_start_after_idle=0

關於iptables 的調優:

  • net.netfilter.nf_conntrack_max = 131072

如果配置不合理,conntrack 會影響HAProxy 達到高性能。

注意:啓用iptables,及使用連接跟蹤(connection tracking ),會消耗20%的CPU 資源,即使沒任何規則。
如果線上啓用了iptables,特別還是在一些前端流量接入的機器上,例如Nginx,LVS,Haproxy之類,要好好優化一番iptables。

HAProxy的多進程模式

配置示例(摘自幻燈片):

# **DON'T RUN IN PRODUCTION, THERE ARE NO TIMEOUTS**
global
  nbproc 2
  cpu-map 1 1
  cpu-map 2 2
  stats socket /var/run/haproxy/socket_web process 1
  stats socket /var/run/haproxy/socket_mysql process 2

defaults HTTP
  bind-process 1
  mode http

frontend f_web
  bind 192.168.10.1:9000
  default_backend b_web
backend b_web
  server w1 192.168.10.21:8000 check

defaults MYSQL
  bind-process 2
  mode tcp
frontend f_mysql
  bind 192.168.10.1:3306
  default_backend b_mysql
backend b_mysql
   server m1 192.168.10.11:3306 check

好處

一個進程處理一個任務,例如一個進程用於處理HTTP請求,一個用於MySQL,互相不干擾,見上文配置。
多進程可以綁定到多個核心上(cpu-map),擴展性也更好。另外如果HAProxy 需要進行SSL 卸載的話,多進程非常有用,可以更加充分的利用CPU 資源。

壞處

每個進程有自己的內存區域,帶來的一些限制:

  • 開啓debug 模式時,多進程會退出,只啓動單個進程;
  • frontentd 和 對應的backend 必須運行在通一個進程上;
  • 和Peers 衝突;(peers 是一個在haproxy 節點之間同步server 列表的功能,具體看haproxy 配置peers 一節
  • 存儲在單個進程內存區域內的信息無法共享給其他進程
    • stick table + tracked counters
    • statistics(統計信息)
    • server's maxconn (queue management)
    • connection rate
  • 每個HAProxy 進程自行進行監控檢查:
  • 每個service 需要被每個進程檢查;
  • 一個service 在不同進程中,某一刻的狀態可能不同;
  • 簡單來說,就是後端的監控檢查無法共享,導致資源浪費和狀態不一致。
  • 管理一個多進程的配置更加複雜。

Logging(日誌)

Haproxy 的日誌非常詳細(雖然很簡短),也非常重要,生產環境中如果條件允許,應該一致打開日誌記錄。另外日誌格式可以自定義(log-format)。 另外也可以對不同的frontend 指定日誌記錄,將傳輸日誌和事件日誌分開,只記錄error等等,具體見幻燈片。

Timeout(超時)

一些timeout解釋:

  • timeout client: 客戶端不活躍的超時時間
  • timeout connect: 和服務端建立TCP 連接的超時時間
  • timeout server:
    • TCP 模式:服務端不活躍的超時時間
    • HTTP 模式:服務端處理響應內容的時間(返回504 狀態碼)
  • timeout client-fin: 客戶端處於FIN_WAIT 狀態的最長時間
  • timeout server-fin: 服務端處於FIN_WAIT 狀態的最長時間
  • HTTP 模式中最要的一些timeout:
    • timouet http-request:客戶端發送整個請求到服務端的用時(抵抗slowlowris-live 攻擊)
    • timeout http-keep-alive:http-keep-alive 時,保持連接,等待下個請求的超時時間
    • timeout tunnel:tunnel 模式和websockets 下,連接不活躍的超時時間
  • timeout queue:一個連接在隊列中能待的最長時間
  • timeout tarpit:how long the tarpitted connection is maintained(暫時不太理解這個狀態)

一些超時配置示例,來源幻燈片:

HTTP 服務:

defaults HTTP
  mode http
  timeout http-request 10s
  timeout client 20s
  timeout connect 4s
  timeout server 30s
  timeout http-keep-alive 4s
  # for websockets:
  timeout tunnel 2m
  timeout client-fin 1s
  timeout server-fin 1s

TCP 長連接的服務,例如POP、IMAP 之類:

defaults HTTP
  mode http
  timeout client  1m
  timeout connect 4s
  timeout server  1m
  timeout client-fin 1s
  timeout server-fin 1s

Fetch

可以用來獲取請求和響應的一些數據,也就是在haproxy 配置中可以使用一些變量,這些變量代表具體請求的一些內容,然後用它們來編寫規則,更多細節參看幻燈片,以及官方這個文檔:Fetching data samples

ACL

可以有匿名或者命名的ACL 規則,使用acl 來給規則起名字:

acl api_path path_beg -i /api/
use_backend bk_api if api_path

匿名的規則,需要用大括號包起來:

use_backend bk_api if { path_beg -i /api/ }

多個ACL 規則可以共用一個名字,關係上爲邏輯或,即匹配任何一條規則即可:

acl myapi path_beg -i /api/
acl myapi hdr_beg(Host) -i api.
use_backend bk_api if myapi

等價餘:

acl api_path path_beg -i /api/
acl api_vhost hdr_beg(Host) -i api.
use_backend bk_api if api_path || api_vhost

HTTP 規則

在HTTP 層,HAProxy 支持規則的定義,可以做下列事情:

  • 允許或禁止某些請求或響應
  • 轉發流量
  • 篡改header 或者URL
  • 捕獲請求內容
  • 更新ACL 規則或map 內容等

在HTTP 規則中可以使用上文提到的fetch 變量,編寫複雜的規則,例如:

http-request deny unless { req.hdr(Host) -i www.mydomain.com }
http-request redirect location /%[req.hdr(Host)]%[path] if { path_beg -i /api/ }

服務和應用保護

HAProxy 有請求隊列,可以弊民後端服務被打掛,給server 配置合適的maxconn ,即可把後端服務收到的請求控制住。
HAProxy 團隊有另外一份幻燈片,提供了更多HAProxy防DDoS的經驗分享:《Presentation packetshield, solution de protection contre les DDoS réseau》

另外可以對不同性能的後端,設置不同的maxconn,轉發不同的請求,分流壓力:

frontend f_myapp
  use_backend b_light if { path_beg /api/ /foo/ /bar/ }
  use_backend b_heavy if { path_beg /search /massivefoo /heavybar }
backend b_light
  server s1 server1:80 maxconn 300
backend b_heavy
  server s1 server1:80 maxconn 10

統計頁面

HAProxy 內部會維護很多計數器,用做統計與展示。統計頁面可以配置成一個web 頁面,也可以是輸出CSV格式的UNIX 的套接字(UNIX socket)。配置示例:

listen stats
  bind-process 1
  bind :9010
  stats enable
  stats uri /
  stats auth demo:demo
  stats realm Demo
  stats admin if TRUE

如果開了多進程(nbproc > 1),我們上文提到的,HAProxy 進程直接的內存內容是不共享的,所以維護者各自的統計計數器,所以需要爲每個進程創建一個狀態頁面或者UNIX socket。
最後提供一個用於Falcon Agent 的HAProxy監控採集腳本:30_haproxy_stats.sh

This article is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
If you reprint it, please indicate the source: http://fangpeishi.com/haproxy_best_practice_notes.html