系統高可用之健康檢查和健康度量那些事

vivo網際網路技術發表於2021-04-26

一、前言

隨著人們的生活水平的不斷提高,人們對身體健康越來越重視,很多人都做過體檢,一般公司都會有一年一度的體檢福利,健康體檢是家喻戶曉了。

隨著網際網路的快速發展,同類同質產品之間的競爭越來越大,產品之間一個重要的差異就是使用者體驗。影響使用者體驗的,除了產品設計因素外,技術層面也是一個重要的影響因素,主要體現在服務的可用性和響應速度。提升服務可用性和響應速度如此重要,為了實現這樣的目標,必須要有相應的手段,其中健康檢查就是保障服務可用性和快速響應一個非常重要的前提。

健康檢查有哪些專案、指標和方法呢?此文帶你一一揭曉。

二、什麼是健康檢查

健康體檢是指通過醫學手段和方法對受檢者進行身體檢查,瞭解受檢者健康狀況、早期發現疾病線索和健康隱患的診療行為。而系統的健康檢查是利用技術手段檢測網路、主機、應用、服務等一系列物件是否健康或可用的過程。

三、為什麼需要做健康檢查

網際網路產品對使用者體驗提出了很高的要求,但常常由於技術側原因,發生服務響應慢或者服務不可用等一系列影響使用者體驗的問題,導致業務中斷,影響收入,公司品牌和口碑也會受到巨大的負面影響。

影響服務不可用和響應慢的因素很多,可能是服務硬體損壞、光纖被挖斷,可能是請求量過大導致資料庫CPU負載、磁碟IO過高,又可能是某同學埋了雷,新上線的功能第一次執行就發生了OOM……

要保證系統高可用,我們應該怎麼做呢?有人說,系統節點冗餘消除單節點故障不就行了嗎。說的沒錯,消除單節點是系統高可用的常用手段。消除單節點有一個很重要的前提是發現問題節點,把問題節點踢除或者把流量切換到其他正常節點。

如何“發現問題節點”,就是系統健康檢查需要做的事情。

四、如何做健康檢查

談論如何做健康檢查前,首先要弄明白的是要檢查的物件究竟是誰。物件可以網路連線,可以是一個小小的功能元件,可以是一個程式,可以是服務叢集,也可以是機房單元。所以,要做到“高可用”,首先要弄清楚要做哪層面的高可用,哪些物件可能存在單點問題,要把“物件”搞清楚。

那麼,健康檢查如何做呢?通常有兩種方式:主動和被動。

4.1 主動模式

由檢查方作為主動方,定時主動發起健康檢查請求,請求的報文內容或者格式通常是獨立設計的,被健康的物件作簡單自檢後返回響應。舉個例子:

check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "HEAD /check.do HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;

配置間隔2000豪秒定時向後臺web伺服器http://(ip:port)/check.do介面傳送檢查請求,如果連續失敗次數達到fall=5次,伺服器被認為當機,如果連續成功次數達到rise=2次,伺服器被認為是up健康狀態。當然了,響應狀態碼必須是2xx或者3xx才被認為是健康狀態。

4.2 被動模式

被動健康檢查不設計獨立的健康檢查請求,而是以正常連線情況或者業務請求的響應作為指標來衡量檢查物件的健康狀態。例如nginx官方開源版本的被動健康檢查配置:

server 127.0.0.1:8080       max_fails=3 fail_timeout=30s;

Nginx是基於連線探測,如果在30s內嘗試連線3次失敗,則認為後端web服務不可用。

4.3 消除單點

上面談到,要實現高可用就要消除單點故障,最簡單直接的方案加備服務節點,通過定時心跳健康檢查發現主服務節點當機後,備服務節點把主的工作接管過來,客戶端把請求流量切換到備服務節點。

主服務節點與備服務節點之間通過專用的心跳線進行健康檢查,由於網路分割槽等原因它們可能無法收到對方心跳,這時備節點會認為主節點已當機,主節點也認為備節點已當機,但其實主從兩節點狀態都是正常的,客戶端能正常訪問到主從兩節點,出現“雙寫”,這種現象在業界稱為“腦裂(split-brain)”。

出現腦裂會導致資料混亂的災難事件發生,影響業務的正確性,這時引入第三方機構進行仲裁可以有效避免腦裂的發生。出現腦裂會導致資料混亂的災難事件發生,影響業務的正確性,這時引入第三方機構進行仲裁可以有效避免腦裂的發生。

4.4 第三方仲裁

既然主從雙方無法確認對方的存活,出現爭議時可以由第三方仲裁節點做出決定,到底誰是主由它說了算,第三方仲裁節點一般是由Zookeeper這種高可用方案來實現。

五、健康檢查例子

5.1 網路裝置

Keepalived是一款保證叢集高可用的服務軟體,其功能類似於heartbeat,用於防止單點故障。但是它一般不會單獨出現,而是與其它負載均衡技術(如LVS、HAProxy、Nginx)一起工作來達到叢集的高可用。

它的健康檢查也包含兩個方面,一個是Keepalived元件之間的健康檢查(通過VRRP心跳報文),如下圖所示

另一個是Keepalived元件與本地負載均衡元件的健康檢查,配置如下:

vrrp_script check_nginx_running {
    script "/usr/local/bin/check_running"(定義指令碼)
    interval 10(指令碼執行的間隔)
    weight -10(指令碼執行的優先順序)
}

其中,應用的健康檢查方式通過自定義指令碼實現。

Keepalived元件之間通過VRRP協議進行健康檢查,如果主伺服器當機,備伺服器通過VRRP協議選舉成為新的主伺服器,把虛擬IP從舊的主伺服器上爭搶過來,實現高可用。

VRRP報文是封裝在IP報文上的,支援各種上層協議,網路裝置通常也是使用VRRP協議實現主備高可用切換,如交換機、路由器、防火牆等。

當網路裝置發生故障時,VRRP機制能夠選舉出新的網路裝置承擔資料流量,從而保障網路的可靠通訊。

5.2 網路連線

移動裝置連線網際網路通過NAT方式,移動App的PUSH推送需要與伺服器保持長連線,但大部分行動網路運營商都在連線一段時間沒有資料互動時,會淘汰 NAT列表中的對應連線,造成連線中斷。為了保持網路連線的“健康”可用,我們可以在連線建立後,App與伺服器互相定期傳送Ping Pong心跳資訊來保持連線的持續有效。

以上是應用層的連線健康檢查方案,作業系統也支援底層網路的連線健康檢查即Keepalive。TCP Keepalive可以在連線無活動一段時間後,傳送一個空的探測報文,使TCP連線不會被客戶端或者防火牆等中間網路裝置關閉。Linux可以通過以下三個引數對Keepalive的間隔、頻率和閾值和進行配置:

net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9

5.3 主機與程式

主機之間的可達性可以通過Ping命令進行識別,Ping命令使用的是ICMP協議,它能識別從客戶端到目標主機整個路徑的網路連通性。Ping通常用於手工測試某臺主機是否啟動和網路是否聯通。

ICMP是網路層協議,與具體程式是沒關係的,無法通過Ping識別程式是否存在。但程式有埠,有程式資訊,可以通過telnet埠或ps命令檢測程式是否存在。程式可能會由於記憶體不足被kill或者其他原因異常關閉,可以通過cron定時指令碼檢測識別後自動拉起,這種方案對老破舊專案中只能單例項部署的應用的可用性提升非常有效。

5.4 中介軟體-RocketMQ

NameServer是RocketMQ的路由中心,NameServer中維護著Producer叢集、Broker叢集、 Consumer叢集的服務狀態和路由資訊。當有新的Consumer加入叢集時,除了上報自身資訊外,還獲取各個Broker的地址、Topic、佇列等資訊,這樣就能知道它消費的Topic訊息儲存到哪個Broker和佇列上。

NameServer可以部署多個,NameServer之間相互獨立不互通。Producer、Broker、Consumer服務啟動時需要指定多個NameServer,服務的資訊會同時註冊到多個指定的 NameServer上,達到高可用。

每個Broker節點與所有NameServer會保持TCP長連線,每隔30s給NameServer傳送心跳報文,告訴NameServer自己還活著。而每個NameServer每隔10s檢查一下各個Broker的最近一次心跳時間,如果發現某個Broker超過120s都沒傳送心跳報文,就認為這個Broker已經當機了,會關閉對應的網路連線channel,並將其從路由資訊裡移除。

5.5 應用層 - Spring Boot Actuator

一個服務例項或者程式會通過定期的心跳包向其他服務來報告它的存活,但有這個心跳包還是不夠的,不足以反映它的健康狀況。比如磁碟空間不足了,服務已經無法再寫資料了,但它還能響應心跳包;服務依賴Redis,但Redis服務出了問題連線不上,但它還能響應心跳包;服務的某些功能依賴分散式儲存服務,但分散式儲存服務不可用了,但它依然能響應心跳包。我們可以看到,要確定一個服務例項是否存活並且“健康”,還是有很多方面需要考慮的。Spring Boot Actuator能比較好的解決這個問題,它能反映整個服務的健康狀況,包括它所依賴的子系統的健康狀況。

Spring Boot Actuator是Spring Boot的一個子專案,Actuator提供Endpoint(端點)給外部應用程式進行訪問和互動。Actuator包括許多功能,比如健康檢查、審計、指標收集等等,可幫助我們監控和管理Spring Boot應用程式。Health就是其中一個Endpoint,它提供了關於Spring Boot應用的基本健康情況資訊,允許其他雲服務或者k8s等定時檢測到應用的健康狀況,對異常情況及時作出響應。

假如某微服務應用使用到了MySQL、Amazon S3、Elastic Search、DynamocDB這些資源系統,它的健康檢查結果就應該包含所有這些子系統的健康狀況:

Actuator的健康檢查由HealthIndicator介面實現,HealthIndicator介面只有一個health()方法,返回值是Health健康物件。

@FuncationalInterface
public class HealthIndicator {
 
    /**
     * Return an indication of health.
     * @result the health for
     */
    public Health health();
 
}

Health物件有狀態status和details兩個欄位,status預設有UNKNOWN、UP、DOWN和OUT_OF_SERVICE四個值,使用者可以自定義和擴充套件,details是一個KV結構,使用者可以隨意自定義要返回的資料值。

@JsonInclude(Include.NON_EMPTY)
public final class Health extends HealthComponent {
 
    private final Status status;
 
    private final Map<String, Object> details;
   
    ...
 
}

Actuator內建了很多常用的HealthIndicator:

使用者可以根據實際情況自定義,比如:

@Override
public Health health() {
    int errorCode = check(); // perform some specific health check
    if (errorCode != 0) {
        return Health.down().withDetail("Error Code", errorCode).build();
    }
    return Health.up().build();
}

預設情況下health的狀態是啟用且對外開放的,通過http://locahost:8080/actuator/health就可以查詢到應用的健康狀態:{“status”: “UP”},這是一個彙總的狀態,詳細的健康資訊可以通過配置項management.endpoint.health.show-details=always開啟,一個完整的包含details的健康檢查資訊如下:

彙總的健康狀態由 HealthAggregator 彙總而成的,彙總的演算法是:所有子系統的健康狀態按DOWN、OUT_OF_SERVICE、UP、UNKNOWN這個順序進行排序取最前面一個狀態值。

比如ehCache是UP,MySQL是UNKNOWN,diskSpace是OUT_OF_SERVICE;那麼排序下來就是:OUT_OF_SERVICE、UP、UNKNOWN,取第一個就是OUT_OF_SERVICE,即服務不可用。

六、總結

高可用是一個很複雜的工程問題,它是由一系列的子問題構成,健康檢查和健康度量只是其中一個。業務要保持連續不中斷,系統要保證持續執行,就要保證全鏈路所有參與的節點都是高可用的,避免出現單點故障。

如何及時發現不健康或故障的節點並告警,如何在節點出現不健康或故障時及時failfast/failover避免發生雪崩效應,健康檢查在其中扮演著非常重要的作用。

作者:vivo 網際網路伺服器團隊-Chen Jianbo

相關文章