天天看點

apigateway-kong(四)負載均衡理論及實作

  負載均衡(Load balancing)是一種計算機網絡技術,用來在多個計算機(計算機叢集)、網絡連接配接、CPU、磁盤驅動器或其他資源中配置設定負載,以達到最佳化資源使用、最大化吞吐率、最小化響應時間、同時避免過載的目的。

使用帶有負載均衡的多個伺服器元件,取代單一的元件,可以通過備援提高可靠性。負載均衡服務通常是由專用軟體和硬體來完成。

對于網際網路服務,負載均衡器通常是一個軟體程式,這個程式偵聽一個外部端口,網際網路使用者可以通過這個端口來通路服務,而作為負載均衡器的軟體會将使用者的請求轉發給背景内網伺服器,内網伺服器将請求的響應傳回給負載均衡器,負載均衡器再将響應發送到使用者,這樣就向網際網路使用者隐藏了内網結構,阻止了使用者直接通路背景(内網)伺服器,使得伺服器更加安全,可以阻止對核心網絡棧和運作在其它端口服務的攻擊。

當所有背景伺服器出現故障時,有些負載均衡器會提供一些特殊的功能來處理這種情況。例如轉發請求到一個備用的負載均衡器、顯示一條關于服務中斷的消息等。負載均衡器使得 IT 團隊可以顯著提高容錯能力。它可以自動提供大量的容量以處理任何應用程式流量的增加或減少。

  對于核心 api,需要保證搞可靠性,那麼就要對于該 api 有多個 backend service,即實際後端對該 api 有多個服務的節點;那麼最好在 api-gateway 即 kong 這一層實作負載均衡。

  Kong為多個後端服務提供了多種負載平衡請求方式:一種基于DNS的簡單方法,以及一種更加動态的環平衡器,該方法還允許在不需要DNS伺服器的情況下進行服務注冊。

基于DNS的負載均衡

  當使用基于DNS的負載均衡時,後端服務的注冊是在Kong以外完成的,而Kong僅接收來自DNS伺服器的更新。

如果名稱解析為多個IP位址,并且主機名未解析為上遊名稱或名稱,則每個使用包含hostname(而不是IP位址)的host定義的service都将自動使用基于DNS的負載平衡你的DNS hosts檔案。

DNS記錄ttl設定(生存時間)決定資訊重新整理的頻率。當使用0的ttl時,每個請求都将使用自己的DNS查詢來解析。很明顯,這會導緻性能下降,但更新/更改的延遲将非常低。

A記錄

  A記錄包含一個或多個IP位址。是以,當主機名解析為A記錄時,每個後端服務都必須具有自己的IP位址。

由于沒有weight資訊,是以所有條目在負載平衡器中将被視為具有相同的權重,并且平衡器(balancer)将進行簡單的循環。

SRV記錄

  SRV記錄包含所有IP位址的權重(weight)和端口(port)資訊。後端服務可以通過IP位址和端口号的唯一組合來識别。是以,單個IP位址可以在不同的端口上托管同一服務的多個執行個體。

由于weight資訊可用,每個條目将在負載均衡器中獲得自己的權重,并執行權重循環。

同樣,任何給定的端口資訊都将被來自DNS伺服器的端口資訊覆寫。如果服務具有host = myhost.com和port = 123的屬性,并且myhost.com解析為具有127.0.0.1:456的SRV記錄,則該請求将被代理到http://127.0.0.1:456/somepath,因為123端口将被456覆寫。

DNS優先級

DNS解析器将按順序解析以下記錄類型:

  • 上次解析的最後一次成功類型(LAST)
  • CNAME記錄

該順序可通過dns_order配置屬性進行配置。

解析不同記錄類型的順序。 LAST類型表示上次成功查找的類型(用于指定的名稱)。格式是一個(不區分大小寫)逗号分隔的清單。

預設值:LAST,SRV,A,CNAME

DNS警告(DNS caveats)

  • 無論何時重新整理DNS記錄,都會生成一個清單以正确處理權重。盡量保持權重為對方的倍數以保持算法的高效性,例如,17和31的2個權重将導緻具有527個項目的結構,而權重16和32(或其最小的相對對應項1和2)将導緻在隻有3個條目的結構中,尤其是具有非常小(甚至0)ttl值的結構。
  • 在這些情況下,某些域名伺服器不會傳回所有條目(由于UDP資料包的大小)(例如Consul最多傳回3個),給定的Kong節點将隻使用由名稱伺服器提供的少數上遊服務執行個體。在這種情況下,由于名稱伺服器提供的資訊有限,Kong節點實際上不了解某些執行個體,是以上遊執行個體池可能會不一緻地加載。為了緩解這種情況,可以使用不同的名稱伺服器,使用IP位址而不是名稱,或者確定使用足夠的Kong節點來繼續使用所有上遊服務。
  • 當名稱伺服器傳回3 name error時,那麼對于Kong來說這是一個有效的響應。如果這是意外,請首先驗證是否正在查詢正确的name,然後檢查您的nameserver配置。
  • 從DNS記錄(A或SRV)中初始選擇IP位址不是随機的。是以,當使用ttl為0的記錄時,nameserver應該随機記錄條目。

環平衡器(ring-balancer)

  當使用環平衡器時,添加和删除後端服務将由Kong處理,并且不需要DNS更新。kong将擔任服務登記。節點可以通過一個HTTP請求added/deleted,并立即start/stop接收流量。

配置環平衡器是通過上遊和目标實體完成的。

  target:具有後端服務駐留端口号的IP位址或主機名,例如。 “192.168.100.12:80”。每個target都會得到一個額外的權重來訓示它獲得的相對負載。 IP位址可以是IPv4和IPv6兩種格式。

  upstream:可以在路由主機字段中使用的'virtual hostname',例如,上遊命名的weather.v2.service将從具有host = weather.v2.service的服務獲得所有請求

上遊(upstream)

  每個upstream都有自己的環形平衡器。每個upstream可以有許多target條目附加到它,代理到'virtual hostname'的請求将在target上進行負載平衡。環形平衡器具有預定義(pre-defined)數量的槽(number of slots),并且基于目标權重,槽(slots)被配置設定給上遊的目标。

  添加和删​​除目标可以通過

Admin API

上的簡單HTTP請求完成。這個操作相對便宜。改變上遊本身更昂貴,因為例如當槽的數量改變時平衡器将需要重建。

  自動重建平衡器的唯一情況是清理目标曆史記錄時;除此之外,它隻會在改變時重建。

在平衡器内部有(從1到slots)的位置,它們随機分布( randomly distributed)在環上。在運作時需要随機性來調用環平衡器。輪子上的簡單循環(位置)将為目标提供良好的分布式權重循環,同時在插入/删除目标時也具有廉價的操作。

每個目标使用的插槽數量(至少)應該在100個左右,以確定插槽正确分布。例如。對于預期最多8個目标,即使初始設定僅包含2個目标,上遊應至少定義為slot = 800。

這裡的折衷是,插槽數量越多,随機分布越好,但更改更為昂貴(添加/删除目标)

有關添加和操作上遊的詳細資訊,請參閱

目标(target)

  由于上遊保留了更改曆史記錄,目标隻能添加,不能修改或删除。要更改目标,隻需為目标添加一個新條目,然後更改重量值。最後一個條目是将要使用的條目。因為這樣的設定權重= 0将禁用目标,有效地從平衡器中删除它。有關添加和操作目标的詳細資訊,請參閱Admin API參考的目标部分。

  當活動條目比活動條目多10倍時,目标将自動清除。清潔将涉及重建平衡器,是以比添加目标條目更昂貴。

目标也可以具有主機名而不是IP位址,在這種情況下,名稱将被解析,所有找到的條目将被單獨添加到環形平衡器中,例如,添加api.host.com:123且權重= 100。名稱'api.host.com'解析為具有2個IP位址的A記錄。然後這兩個IP位址将被添加為目标,每個獲得weight = 100和端口123.注意:權重用于單個條目,而不是整個!

它是否會解析為SRV記錄,然後DNS記錄中的端口和權重字段将被拾取,并且會否定給定的端口123和權重= 100。

平衡器将遵守DNS記錄的ttl設定和重新查詢,并在平衡器到期時更新。

例外情況:當DNS記錄的ttl = 0時,主機名将被添加為具有指定權重的單個目标。在對該目标的每個代理請求時,它将再次查詢名稱伺服器。

平衡算法

  預設情況下,環平衡器将使用權重循環方案。另一種方法是使用基于散列的算法。散列的輸入可以是none,consumer,ip或header。如果設定為none,則将使用權重循環方案,并且散列将被禁用。

有兩種選擇,一種primary 和 一種回退(fallback),以防primary失敗(例如,如果primary設定為consumer,但沒有consumer通過驗證)

不同的散列選項:

  • none:不要使用散列,而是使用weighted-round-robin(預設)。
  • consumer:使用消費者ID作為散列輸入。如果沒有消費者ID可用(如果使用外部身份驗證,如ldap),此選項将回退到憑證ID。
  • ip:遠端(始發)IP位址将用作輸入。在使用此設定時,檢視确定真實IP的配置設定。
  • header:使用指定的标題(在hash_on_header或hash_fallback_header字段中)作為散列的輸入。

雜湊演算法基于'一緻哈希'(或'ketama原理'),它確定當平衡器通過改變目标(添加,移除,失敗或改變權重)而被修改時,隻有最小數量的哈希損失發生。這将最大化上遊緩存命中。

有關确切設定的更多資訊,請參閱Admin API參考的上遊upstream部分。

平衡警告(Balancing caveats)

  環平衡器設計為既可以在單個節點上工作,也可以在群集中工作。對于權重循環算法沒有太大的差別,但是當使用基于散列的算法時,重要的是所有節點建構完全相同的環平衡器以確定它們都工作一緻。要做到這一點,平衡器必須以确定性的方式建構。

  不要在平衡器中使用主機名稱,因為平衡器可能會/會慢慢發生分歧,因為DNS ttl隻有第二精度,更新取決于實際請求名稱的時間。最重要的是一些域名伺服器沒有傳回所有條目的問題,這加劇了這個問題。是以,在Kong群集中使用哈希方法時,隻能通過IP位址添加目标實體,而不能通過名稱添加target實體。

  當選擇你的散列輸入時,確定輸入具有足夠的方差以得到散布良好的散列。哈希将使用CRC-32摘要進行計算。例如,如果您的系統有成千上萬的使用者,但隻有少數使用者(每個平台定義了3個使用者:Web,iOS和Android),那麼挑選consumer散列輸入是不夠的,通過設定使用遠端IP位址對于ip的哈希将提供更多的輸入差異,進而更好地配置設定哈希輸出

藍綠部署(Blue-Green Deployments)

  使用環形平衡器,可以輕松地為一項服務策劃一個藍綠色部署。切換目标基礎架構隻需要服務上的PATCH請求,即可更改其主機值。

設定“blue”環境,運作version 1 of the address service:

# create an upstream
$ curl -X POST http://kong:8001/upstreams \
    --data "name=address.v1.service"

# add two targets to the upstream
$ curl -X POST http://kong:8001/upstreams/address.v1.service/targets \
    --data "target=192.168.34.15:80"
    --data "weight=100"
$ curl -X POST http://kong:8001/upstreams/address.v1.service/targets \
    --data "target=192.168.34.16:80"
    --data "weight=50"

# create a Service targeting the Blue upstream
$ curl -X POST http://kong:8001/services/ \
    --data "name=address-service" \
    --data "host=address.v1.service" \
    --data "path=/address"

# finally, add a Route as an entry-point into the Service
$ curl -X POST http://kong:8001/services/address-service/routes/ \
    --data "hosts[]=address.mydomain.com"      

在部署version 2 of the address service之前,請設定“green”環境:

# create a new Green upstream for address service v2
$ curl -X POST http://kong:8001/upstreams \
    --data "name=address.v2.service"

# add targets to the upstream
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
    --data "target=192.168.34.17:80"
    --data "weight=100"
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
    --data "target=192.168.34.18:80"
    --data "weight=100"      

要激活blue/green開關,現在隻需要更新服務:

# Switch the Service from Blue to Green upstream, v1 -> v2
$ curl -X PATCH http://kong:8001/services/address-service \
    --data "host=address.v2.service"      

主機頭設定為address.mydomain.com的傳入請求現在由Kong代理到新目标; 1/2的請求将轉到http://192.168.34.17:80/address(權重= 100),另一半将轉到http://192.168.34.18:80/address(權重= 100 )。

與往常一樣,通過Kong Admin API進行的更改是動态的,并且會立即生效。不需要重新加載或重新啟動,并且沒有進行中的請求将被丢棄。 

金絲雀版本(Canary Releases)

  使用環形平衡器,可以精确調整目标重量,進而實作平穩.。

使用一個非常簡單的2 target示例:

# first target at 1000
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
    --data "target=192.168.34.17:80"
    --data "weight=1000"

# second target at 0
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
    --data "target=192.168.34.18:80"
    --data "weight=0"      

通過重複請求,但每次改變權重,流量将緩慢路由到另一個target。例如,将其設定為10%:

# first target at 900
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
    --data "target=192.168.34.17:80"
    --data "weight=900"

# second target at 100
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
    --data "target=192.168.34.18:80"
    --data "weight=100"      

通過Kong Admin API進行的更改是動态的,并會立即生效。不需要重新加載或重新啟動,并且沒有進行中的請求将被丢棄。

KONG負載均衡實作

  實際在操作過程中,采用的是 kong 的 Ring-balancer 做負載均衡。

  使用 Kong Community Edition(社群版 v0.13)來搭建一個負載均衡器,由于 Kong 是基于 Openresty 的,而 Openresty 又是 Nginx 的二次封裝,所有很多配置項和 Nginx 類似。

來看一個較為典型的 Nginx 負載均衡配置:

upstream hello {  
    server localhost:3000 weight=100;  
    server localhost:3001 weight=50;  
}  
  
server {  
    listen  80;  
    location /hello {  
        proxy_pass http://hello;  
    }  
}       

  nginx 監聽來自本地 80 端口的請求,如果路徑與 /hello 比對,便将請求原封不動的轉發到名稱為 hello 的upstream,而該 upstream 我們配置了一個負載均衡器,會路由到本地的 3000 端口和 3001 端口。

接下來便可以針對 Kong 進行負載均衡的配置了。

配置 upstream 和 target

建立一個名稱 hello 的 upstream

curl -X POST http://localhost:8001/upstreams --data "name=hello"        

為 hello 添加兩個負載均衡節點

curl -X POST http://localhost:8001/upstreams/hello/targets --data "target=localhost:3000" --data "weight=100"       
curl -X POST http://localhost:8001/upstreams/hello/targets --data "target=localhost:3001" --data "weight=50"       

如上的配置對應了 Nginx 的配置:

upstream hello {  
    server localhost:3000 weight=100;  
    server localhost:3001 weight=50;  
}        

配置 service 和 route

  使用 Kong v0.13之前版本的使用者可能會接觸過 api 這個概念,但是在 Kong v0.13.0 中,已經被廢除了,取而代之的是 service 和 route 的配置。

配置一個 service

curl -X POST http://localhost:8001/services --data "name=hello" --data "host=hello"       

host 的值便對應了 upstream 的名稱,配置成功後會傳回生成的 service 的 id,傳回結果:8695cc65-16c1-43b1-95a1-5d30d0a50409。

為上面的 service 配置路由資訊

curl -X POST http://localhost:8001/routes --data "paths[]=/hello" --data "service.id=8695cc65-16c1-43b1-95a1-5d30d0a50409"      

請求路徑包含 /hello 的請求都會被轉移到對應的 service 進行處理。

如上的配置便對應了Nginx的配置:

location /hello {  
    proxy_pass http://hello;  
}        

測試 Kong 的負載均衡

curl http://localhost:8000/hello/hi       

因為複雜均衡的原因,需要多測試幾次,多次 curl 之後結果如下:

3000  
3000  
3000  
3000  
3000  
3000  
3001  
3001  
3001  
3000  
3001       

reference:

https://getkong.org/docs/0.13.x/loadbalancing/ https://getkong.org/docs/0.13.x/configuration/

作者:

zhoujie

出處:

http://www.cnblogs.com/zhoujie/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,不然我擔心部落格園找你算賬

如果您覺得本文對你有幫助,請豎起您的大拇指右下角點推薦,也可以關注我

繼續閱讀