一次 Keepalived 高可用的事故,讓我重學了一遍它!

語言: CN / TW / HK

原文首發:

你好,我是悟空。

前言

上次我們遇到了一個 MySQL 故障的事故,這次我又遇到了另外一個奇葩的問題:

Keepalived 高可用元件的虛擬 IP 持續漂移,導致 MySQL 主從不斷切換,進而導致 MySQL 主從資料同步失敗。

雖然沒能重現 Keepalived 的這個問題,但是我深入研究了下 Keepalived 的原理以及針對核心配置引數做了大量實驗。悟空帶著大家一起看下 Keepalived 到底是如何運轉的,以及為什麼它能做到高可用。

原理講解分為上、中、下三篇:

上篇涉及以下知識點:

  • Keepalived 如何提供資料流量轉發。
  • Keepalived 選舉的原理。
  • Keepalived 的負載均衡演算法。

中篇涉及以下知識點:

  • Keepalived 的路由規則。
  • Keepalived 如何監控服務的。
  • Keepalived 如何進行故障切換。
  • Keepalived 的架構剖析。

下篇設計以下知識點:

  • Keepalived 配置詳解
  • Keepalived 實戰部署

一、Keepalived 和 LVS 概述

1.1 Keepalived 概述

談到 Keepalived,給人的印象就是用在高可用架構中,保證某個服務不故障,其實它還有很多其他的功能。Keepalived 是 Linux 系統下的一個比較輕量級的高可用解決方案,這個輕量級是相對於 Heartbeat 等元件的。雖然 Heartbeat 功能完善、專業性強,但是安裝部署就沒有 Keepalived 簡單,Keepalived 只需要一個配置檔案即可。企業中大多選擇 Keepalived 作為高可用元件。

1.2 LVS 概述

Keepalived 最開始是由 Alexandre Cassen 使用 C 語言編寫的開源軟體專案,專案的目的主要是簡化 LVS 專案的配置並增強 LVS 的穩定性。簡單來說,Keepalived 就是對 LVS 的擴充套件增強。

LVS(Linux Virtual Server)翻譯過來就是 Linux 虛擬伺服器,由章文嵩博士主導開發的開源負載專案,目前 LVS 已經被整合到 Linux 核心模組中。

LVS 主要用在負載均衡方面,比如 Web 客戶端想要訪問後端服務,Web 請求會先經過 LVS 排程器,排程器根據預設的演算法決定如何分發給後端的所有伺服器。

1.3 LVS 基本原理

LVS 的基本原理如下圖所示:

LVS基本原理

LVS 的核心功能就是提供負載均衡,負載均衡技術有多種:

  • 基於 DNS 域名輪流解析方案。
  • 基於客戶端排程訪問方案。
  • 基於應用層系統的排程方案。
  • 基於 IP 地址的排程方案。

而效率最高的是基於 IP 地址的排程方案。其實就是將請求轉發給對應的 IP 地址 + 埠號,它的效率是非常高的,LVS 的 IP 負載均衡技術是通過 IPVS 模組來實現的,IPVS 是 LVS 集群系統的核心軟體。

LVS 負載均衡器會虛擬化一個IP(VIP),對於客戶端來說,它事先只知道這個 VIP 的,客戶端就將請求傳送給 VIP,然後 LVS 負載均衡器會將請求轉發給後端伺服器中的一個,這些伺服器都稱為 Real Server(真實伺服器)。轉發的規則是通過設定 LVS 的負載均衡演算法來的,比如隨機分配、按照權重分配等。

後端伺服器的提供的功能要求是一致的,不論轉發到哪臺伺服器,最終得到的結果是一致的,所以對於客戶端來說,它並不關心有多少個後端伺服器在提供服務,它只關心訪問的 VIP 是多少。

那麼後端服務處理完請求後,如何將資料返回給客戶端呢?根據 LVS 的不同模式,會選擇不同的方式將資料返回給客戶端。LVS 的工作模式有三種:NAT 模式、TUN 模式、DR 模式。這個後面講到路由機制再來細說。

二、Keepalived 流量轉發原理

Keepalived 為 Linux 系統提供了負載均衡和高可用能力。負載均衡的能力來自 Linux 核心中的 LVS 專案模組 IPVS(IP Virtual Server)。

Keepalived 執行在 Linux 系統中,它會啟動核心中的 LVS 服務來建立虛擬伺服器。比如我們在兩臺伺服器上都啟動了一個 Keepalived 服務,然後 LVS 會虛擬化出來一個 IP(VIP),但是隻有一個 Keepalived 會接管這個 VIP,就是說客戶端的請求只會到 Master Keepalived 節點上。這樣流量就只會到一臺 keepalived 上了,然後 keepalived 可以配置幾臺真實的服務 IP 地址和埠,通過負載排程演算法將流量分攤到這些服務上。對於另外一臺 Backup Keepalived 節點,它是待機狀態,沒有流量接入的。

三、Keepalived 如何進行選主的

那麼上面的兩個 Keepalived 服務是如何選出其中一個作為 Master 節點的呢?

我們一般都是執行在兩臺主備伺服器或一主多備的伺服器上。而這多臺伺服器都是遵循 VRRP 的。

3.1 VRRP 協議

VRRP 的全稱為 Virtual Router Redundancy Protoco,虛擬路由冗餘協議。它是一種容錯協議,為了解決區域網中單點路由故障的問題。比如之前我們都是一個路由器進行路由轉發,如果這個路由器故障了,那麼整個路由轉發的鏈路就斷了,服務就不可用了。

VRRP 協議主要的功能:

  • 虛擬路由器和虛擬 IP。
  • Master 廣播 ARP 報文。
  • Backup 選舉新的 Master。

現在我們配置多臺路由器(一主多備),每臺路由器都有一個自己的 IP 地址,它們組成一個路由器組,其中有一個作為 Master,其他作為 Backup。然後這些路由器會虛擬出單個路由,擁有自己的 IP 地址,也就是 Virtual IP,簡稱 VIP。

客戶端訪問這個虛擬的 IP 地址就可以了,當主路由器故障了,備份路由器通過選舉機制選出一個新的主路由器,繼續向客戶端提供路由服務,實現了路由功能的高可用。

路由器開啟 VRRP 功能後,根據優先順序配置進行選舉,優先順序高的會成為主(Master)路由器,另外的則會成為備(Backup)路由器。

Master 路由器定期傳送 VRRP 通知報文給 Backup 路由器,告訴它們我是在正常工作的,你們不用競選新的 Master 路由器。

關於 Master 和 Backup 通訊的原理其實很簡單,就是一個 心跳機制 ,不過這個和 Eureka 的心跳機制不一樣,Eureka 是客戶端定期向 Eureka 註冊中心傳送心跳,而 Keepalived 則是 Master 定期向 Backup 傳送心跳機制,而 Backup 路由器它有一個定時監測通知的任務,如果在這個時間段內未收到通知,則認為 Mater 故障了,然後通過優先順序進行選舉,選舉出新的 Master 後,就定期傳送 VRRP 通知報文給 Backup 路由器。(Eureka 心跳機制: 唐太宗把微服務的“心跳機制”玩到了極致!

通過這個 VRRP 協議,可以提高系統的可用性,避免因單點故障導致的服務不可用問題,同時在路由器故障時,無需手動修改網路連線資訊以訪問新的 Master 路由器。如下圖所示,Backup 切換為了 Master。

關於選舉的配置主要依賴 vrrp_instance 和 vrrp_script 欄位。

3.2 vrrp_instance 配置

對於 Keepalived 的選主有三個重要引數:

  • state:可選值為 MASTER、BACKUP。
  • priority:節點的優先順序,可選值為 [1-255]。
  • nopreempt:不搶佔模式,如果配置,則當優先順序高時,會將自己設定為 Master。
vrrp_instance VI_1 {
    # 節點為 BACKUP
    state BACKUP
    # 優先順序為 100
    priority 100   
    # 不搶佔模式
    nopreempt
}

當一臺設定為 master,另外一臺設定為 BACKUP,當 MASTER 故障後,BACKUP 會成為新的 MASTER,而當老的 MASTER 恢復後,又會搶佔成為新的 MASTER,接管 VIP 的流量,導致不必要的主備切換。為了避免這種主備切換,我們可以將兩臺 Keepalived 都設定為 BACKUP,且高優先順序的那臺 Keepalived 設定為不搶佔 nopreempt。

3.2 vrrp_script 配置

而優先順序 priority 它是可以增減的,通過 vrrp_script 來配置:

vrrp_script restart_mysql {
   # 監測和重啟 mysql 容器,如果 MySQL 服務正常或 MySQL 失敗
   script "/usr/local/keepalived/restart_mysql.sh"  
   interval 5
   weight -20
}

這個是定時執行指令碼的配置,script 配置會監測 mysql 服務是否不正常。這是一個自定義的指令碼,可以自己寫返回值。這裡我寫的邏輯是如果 MySQL 服務正常則返回 0,不正常則返回 1。

當 weight 為正數

當指令碼返回 0 時(服務正常),則增加優先順序=priority + weight;否則,保持設定的 priority 值。

切換策略:

  • 如果 MASTER 節點的 vrrp_script 指令碼檢測失敗時,如果 MASTER 節點的 priority 值小於 BACKUP 節點 weight + priority,則發生主備切換。
  • 如果 MASTER 節點的 vrrp_script 指令碼檢測成功時,如果 MASTER 節點的 priority 值大於 BACKUP 節點 weight + priority,則不發生主備切換。

當 weight 為負數

當指令碼返回非 0 時(服務異常),則優先順序=priority - |weight|;否則,保持設定的 priority 值。

切換策略:

  • 如果 MASTER 節點的 vrrp_script 指令碼檢測失敗時,如果 MASTER 節點的 priority - |weight| 值小於 BACKUP 節點 priority 值,則發生主備切換。
  • 如果 MASTER 節點的 vrrp_script 指令碼檢測成功時,如果 MASTER 節點的 priority 值大於 BACKUP 節點 priority 值,則不發生主備切換。

注意:增加或減少優先順序的範圍為 [1,254]。

舉例說明:

兩臺 Keepalived 的 state 都配置成 BACKUP,其中一臺伺服器 node1 的 Keepalived 的優先順序設定為 100,不搶佔模式,另外一臺 node2 的優先順序設定為 90,搶佔模式。

node1 節點配置的優先順序高,它成為 Master 節點,當 Master 節點監控的 MySQL 服務發生故障後,會降低優先順序,從 100 降低到 80。另外一臺優先順序為 90,收到優先順序比自己低的 ARP 廣播時,就會變成新的 Master 節點。而 node1 節點會成為 BACKUP 節點,當 node1 監控到 MySQL 服務恢復後,優先順序變為配置的 priority 100,但是也不會搶佔。

如下圖所示:雖然 node1 上的 keepalived 重啟 mysql 成功了,優先順序也恢復成了 100,但是並沒有變為 master,還是維持 backup 狀態。

而 node2 還是 master 節點,定時向 node 1 傳送 vrrp 通知,如下圖所示:

如果 node2 的 mysql 宕機了,那麼它的優先順序會從 90 降低到 70,即使這樣,也不會出現主備切換,因為我們配置的策略就是 node1 不會搶佔。如果要在這種情況下切換到 node1,就只能將 node2 的 keepalived 主動停掉,故障轉移中篇會講到。

四、Keepalived 的負載均衡機制

4.1 轉發機制

要理解 Keepalived 的負載均衡機制,必須瞭解 IPVS,也就是 IP Virtual Server,IP 虛擬伺服器。

IPVS 模組是 Keepalived 引入的一個第三方模組,目的是解決單 IP 多伺服器的工作環境,通過 IPVS 可以實現基於 IP 的負載均衡叢集。IPVS 預設包含在 LVS 軟體中,而 LVS 又是包含在 Linux 系統中。所以 Keepalived 在 Linux 系統上可以直接利用 LVS 的功能。LVS 的作用就是虛擬出一個 IP,也就是 VIP,客戶端請求先到達 VIP,然後從伺服器叢集中選擇一個伺服器節點,將流量轉發給這個節點,由這個節點處理請求。

如圖所示:

  • Keepalived 是執行在使用者空間的 LVS 路由(LVS Router)程序,作為 MASTER 角色 Keepalived 稱為 Active Router,BACKUP 角色的 Keepalived 稱為 SLAVE Router。只有 Active Router 是工作的,其他 Router 是 Stand By (待機狀態)。
  • Active Router 和 Backup Router 之間是通過 VRRP 協議進行主備切換的。
  • Active Router 會啟動核心中 LVS 服務以建立虛擬伺服器,虛擬伺服器有一個虛擬 IP(VIP),比如下圖中的 VIP 為 192.168.56.88。
  • Active Router 還會設定 IPVS TABLES(伺服器列表),記錄了後端伺服器的地址及服務執行狀態。負載均衡就從伺服器列表選擇一個可用的服務進行轉發。
  • 這些後端服務是配置在 Keepalived 的 virtual_server 配置項裡面的,如下所示,配置了三個 real_server,分別對應了三臺後端伺服器。
virtual_server 192.168.56.88 80 { 
   delay_loop 6 
   lb_algo rr 
   lb kind NAT 
   protocol tcp
   # 伺服器 1
   real_server 192.168.56.11 80 { 
      TCP_CHECK { 
      connect timeout 10 
   }
   # 伺服器 2
   real_server 192.168.56.12 80 { 
      TCP_CHECK { 
      connect timeout 10 
   }
   # 伺服器 3
   real_server 192.168.56.13 80 { 
      TCP_CHECK { 
      connect timeout 10 
   }

4.2 負載排程演算法

配置中有一個欄位 lb_algo,這個就是負載排程演算法,可以配置成 rr、wrr、lc、wlc、sh、dh 等。常用的是 rr 和 wrr。

  • rr,就是 Round-Robin,輪詢演算法, 每個伺服器平等的,依次被排程。
  • wrr,就是 Weighted Round-Robin,加權輪詢排程演算法,加權值較大的,會被轉發更多的請求。比如有的伺服器硬體能力較弱,則可以將加權值配置得低一點。
  • lc,就是 Least-Connection,最少連線演算法。請求被轉發到活動連線較少的伺服器上。連線數是通過 IPVS Table 來動態跟蹤的。
  • wlc,加權最少連線。根據權重 + 連線數 分配請求。
  • sh,目標地址雜湊演算法,通過在靜態 Hash 表中查詢目的 IP 地址來確定請求要轉發的伺服器,這類演算法主要用於快取代理伺服器中。
  • dh,源地址雜湊演算法,通過在靜態 Hash 表中查詢源 IP 地址來確定請求要轉發的伺服器,這類演算法主要用於防火牆的 LVS Router 中。

五、總結

Keepalived 作為高可用、高效能元件,在叢集環境中用得還是挺多的,所以去理解 Keepalived 的底層原理,也可以學到很多高可用和負載均衡的通用原理。

本篇介紹了 Keepalived 的 IPVS 功能,啟動了一個虛擬伺服器,虛擬化了一個 VIP,用來接收客戶端的請求,然後通過負載排程演算法將流量轉發給真實伺服器。

Keepalived 一般用在都是一主一備或一主多備的場景,而對於主的選舉是通過配置 state、privority、nopreemt、weight 欄位來達到的。

下篇我們再來看下真實伺服器處理完請求後,如何將資料返回給客戶端,這個涉及到 LVS 的路由規則。以及監控和故障切換也是 Keepalived 的核心功能,這個很有必要深入探索下。

我是悟空,我們下期見~

- END -