無縫切換在線升級的終極探索

本文將介紹整年4個9(99.99%)的系統可用性方案nginx

在系統的高可靠性裏有個衡量其可靠性的標準——X個9,這個X是表明數字3~5。X個9表示在系統1年時間的使用過程當中,系統能夠正常使用時間與總時間(1年)之比git

系統可用性的計算公式:A=MTBF/(MTBF+MTTR)github

拿365天(1年)作計算吧,看看幾個9要停機多久時間作能才能達到!web

1年 = 365天 = 8760小時
99.9 = 8760 * 0.1% = 8760 * 0.001 = 8.76小時
99.99 = 8760 * 0.0001 = 0.876小時 = 0.876 * 60 = 52.6分鐘
99.999 = 8760 * 0.00001 = 0.0876小時 = 0.0876 * 60 = 5.26分鐘算法

本文設計技術點:spring

  • LVS
  • keepalived
  • nginx
  • 集羣高可用
  • spring cloud Nacos(其餘服務註冊中心同理)
  • spring cloud Gateway(其餘Api網關同理)
  • spring boot(非Java應用同理)

LVS && Keepalived(瞭解)

LVS 是什麼?

LVS 是 Linux Virtual Server(Linux 虛擬服務器) 的簡稱,目前 LVS 已是 Linux 內核標準的一部分,LVS 工做在網絡 4 層之上僅做分發之用,因此抗負載能力比較強。LVS 有完整的雙機熱備方案,幾乎支持對全部應用作負載均衡。LVS 在互聯網應用中的位置是在 Nginx 之上後端

LVS 該如何應用?

LVS 自己只是一個叫 IP_VS 的內核模塊,這個模塊能夠作負載均衡,可是隻用這個模塊來作應用負載是遠遠不夠的,好比 LVS 自己宕機後如何處理?實際生產環境中,通常配合 Keepalived 來使用 LVS,keepalived 支持 VRRP 心跳協議,能夠實現 LVS 主備冗餘,以解決 LVS 自己單點故障。另外,Keepalived 支持健康檢測,網絡 4 層和 7 層健康檢測,防止服務器宕機。 瀏覽器

Keepalived工做原理

Keepalived做爲一個高性能集羣軟件,它還能實現對集羣中服務器運行狀態的監控以及故障隔離,下面咱們介紹一下Keepalived對服務器運行狀態和故障隔離的工做原理。tomcat

Keepalived工做在TCP/IP 模型的 三層(網絡層)、四層(傳輸層)、七層(應用層),根據TCP、IP 模型各層所能實現的功能,Keepalived運行機制以下:服務器

  • 在網絡層(3):咱們知道運行中有4個重要的協議。互聯網絡IP協議、互聯網絡可控制報文協議ICMP、地址轉換協議ARP、反向地址轉換協議RARP,Keepalived在網絡層採用最多見的工做方式是經過ICMP協議向服務器集羣中的每個節點發送一個ICMP數據包(有點相似與Ping的功能),若是某個節點沒有返回響應數據包,那麼認爲該節點發生了故障,Keepalived將報告這個節點失效,並從服務器集羣中剔除故障節點。

  • 在傳輸層(4):提供了兩個主要的協議:傳輸控制協議TCP和用戶數據協議UDP,傳輸控制協議TCP能夠提供可靠的數據輸出服務、IP地址和端口,表明TCP的一個鏈接端,要得到TCP服務,須要在發送機的一個端口和接收機的一個端口上創建鏈接,而Keepalived在傳輸層裏利用了TCP協議的端口鏈接和掃描技術來判斷集羣節點的端口是否正常,好比對於常見的WEB服務器80端口。或者SSH服務22端口,Keepalived一旦在傳輸層探測到這些端口號沒有數據響應和數據返回,就認爲這些端口發生異常,而後強制將這些端口所對應的節點從服務器集羣中剔除掉。

  • 在應用層(7):能夠運行FTP,TELNET,SMTP,DNS等各類不一樣類型的高層協議,Keepalived的運行方式也更加全面化和複雜化,用戶能夠經過自定義Keepalived工做方式,例如:能夠經過編寫程序或者腳原本運行Keepalived,而Keepalived將根據用戶的設定參數檢測各類程序或者服務是否容許正常,若是Keepalived的檢測結果和用戶設定的不一致時,Keepalived將把對應的服務器從服務器集羣中剔除

什麼是VRRP?

VRRP能夠將兩臺或者多臺物理路由器設備虛擬成一個虛擬路由,這個虛擬路由器經過虛擬IP(一個或者多個)對外提供服務,而在虛擬路由器內部十多個物理路由器協同工做,同一時間只有一臺物理路由器對外提供服務,這臺物理路由設備被成爲:主路由器(Master角色),通常狀況下Master是由選舉算法產生,它擁有對外服務的虛擬IP,提供各類網絡功能,如:ARP請求,ICMP 數據轉發等,並且其它的物理路由器不擁有對外的虛擬IP,也不提供對外網絡功能,僅僅接收MASTER的VRRP狀態心跳信息,這些路由器被統稱爲BACKUP的角色」,當主路由器失敗時,處於BACKUP角色的備份路由器將從新進行選舉,產生一個新的主路由器進入MASTER角色,繼續提供對外服務,整個切換對用戶來講是徹底透明的。

Lvs+Keepalived+nginx 接入層高可用(重點)

流量接入層技術演進架構歷史概覽

單機架構

  • 瀏覽器經過DNS-server,域名解析到ip
  • 瀏覽器經過ip訪問web-server

缺點:

  1. 非高可用,web-server掛了整個系統就掛了
  2. 擴展性差,當吞吐量達到web-server上限時,沒法擴容

注:單機不涉及負載均衡的問題

簡易擴容方案&DNS輪詢

假設tomcat的吞吐量是1w次每秒,當系統總吞吐量達到3w時,如何擴容是首先要解決的問題,DNS輪詢是一個很容易想到的方案:

  • 多部署幾份web-server,1個tomcat抗1000,部署3個tomcat就能抗3000
  • 在DNS-server層面,域名每次解析到不一樣的ip

優勢:

  1. 零成本:在DNS-server上多配幾個ip便可,功能也不收費
  2. 部署簡單:多部署幾個web-server便可,原系統架構不須要作任何改造
  3. 負載均衡:變成了多機,但負載基本是均衡的

缺點:

  1. 非高可用:DNS-server只負責域名解析ip,這個ip對應的服務是否可用,DNS-server是不保證的,假設有一個web-server掛了,部分服務會受到影響
  2. 擴容非實時:DNS解析有一個生效週期
  3. 暴露了太多的外網ip

簡易擴容方案&nginx

tomcat的性能較差,但nginx做爲反向代理的性能就強多了,假設線上跑到1w,就比tomcat高了10倍,能夠利用這個特性來作擴容:

  • 站點層與瀏覽器層之間加入了一個反向代理層,利用高性能的nginx來作反向代理
  • nginx將http請求分發給後端多個web-server

優勢:

  1. DNS-server不須要動
  2. 負載均衡:經過nginx來保證
  3. 只暴露一個外網ip,nginx->tomcat之間使用內網訪問
  4. 擴容實時:nginx內部可控,隨時增長web-server隨時實時擴容
  5. 可以保證站點層的可用性:任何一臺tomcat掛了,nginx能夠將流量遷移到其餘tomcat

缺點:

  1. 時延增長+架構更復雜了:中間多加了一個反向代理層
  2. 反向代理層成了單點,非高可用:tomcat掛了不影響服務,nginx掛了怎麼辦?

高可用方案&keepalived

爲了解決高可用的問題,keepalived出場了

  • 作兩臺nginx組成一個集羣,分別部署上keepalived,設置成相同的虛IP,保證nginx的高可用
  • 當一臺nginx掛了,keepalived可以探測到,並將流量自動遷移到另外一臺nginx上,整個過程對調用方透明

優勢:

  1. 解決了高可用的問題

缺點:

  1. 資源利用率只有50%
  2. nginx仍然是接入單點,若是接入吞吐量超過的nginx的性能上限怎麼辦,例如qps達到了50000咧?

縱向(scale up)擴容方案&lvs

nginx畢竟是軟件,性能比tomcat好,但總有個上限,超出了上限,仍是扛不住。lvs就不同了,它實施在操做系統層面, 性能比nginx好不少,例如每秒能夠抗10w,這樣能夠利用他們來擴容,常見的架構圖以下:

  • 若是經過nginx能夠擴展多個tomcat同樣,能夠經過lvs來擴展多個nginx
  • 經過keepalived+VIP的方案能夠保證可用性
  • 99.9999%的公司到這一步基本就能解決接入層高可用、擴展性、負載均衡的問題。

缺點 不論是使用lvs仍是f5,這些都是scale up的方案,根本上,lvs/f5仍是會有性能上限,假設每秒能處理10w的請求,一天也只能處理80億的請求(10w秒吞吐量*8w秒),那萬一系統的日PV超過80億怎麼辦呢?(好吧,沒幾個公司要考慮這個問題)

橫向(scale out)擴容方案&DNS輪詢

水平擴展,是解決性能問題的根本方案,經過加機器擴充性能的方案才具有最好的擴展性。 facebook,google,baidu的PV是否是超過80億呢,它們的域名只對應一個ip麼,終點又是起點,仍是得經過DNS輪詢來進行擴容: 此時:

  • 經過DNS輪詢來線性擴展入口lvs層的性能
  • 經過keepalived來保證高可用
  • 經過lvs來擴展多個nginx
  • 經過nginx來作負載均衡,業務七層路由

總結

  • 接入層架構要考慮的問題域爲:高可用、擴展性、反向代理+擴展均衡
  • nginx、keepalived、lvs、f5能夠很好的解決高可用、擴展性、反向代理+擴展均衡的問題
  • 水平擴展scale out是解決擴展性問題的根本方案,DNS輪詢是不能徹底被nginx/lvs/f5所替代的
  • 企業可用購買雲平臺的服務實現相似功能,節省人力時間成本
  • 非雲上企業也能夠自行部署,上述所介紹的內容算是運維工程師的必備技能,並不會特別麻煩。

服務註冊中心實時刷新與高可用

服務集羣化

這一部分跳過了,微服務集羣化部署的相關的文章不少,本文篇幅有限,想了解的朋友請自行查找閱讀。

優雅停機

微服務架構中的應用優雅停機主要是指應用實例有計劃而平滑(即不產生須要處理的事故)的退出。應用服務器的停機主要分爲兩類:主動停機和被動停機,而其中主動停機和大部分的被動停機都是能夠實現優雅停機。若是應用不作優雅停機,則會帶來如下狀況:

  • 數據丟失:內存的中數據還沒有持久化至磁盤
  • 文件損壞:正在操做寫的文件因沒有更新完成,致使文件損壞
  • 請求丟失:排隊中等待處理的請求丟失
  • 響應丟失:成功的交易還沒來得及作出響應
  • 交易中斷:正在處理至中間狀態的交易被強制中斷
  • 服務未下線:上游服務依然還會繼續往下游服務發送消費請求

微服務的優雅升級的目標就是避免以上幾種狀況,從而避免人工干預的工做量和提高微服務架構的服務高可靠。

使用場景

優雅停機能夠解決如下場景:

  • KILL PID
  • 應用意外自動退出(System.exit(n))
  • 使用腳本命令的方式中止應用

優雅停機解決不了如下場景:

  • 忽然斷電
  • 機器物理破壞
  • ILL-9 PID 或 taskkill /f /pid

Java的優雅停機 ShutdownHook

Java的優雅停機一般經過註冊JDK的ShutdownHook(鉤子)來實現,當系統接收到退出指令後,首先標記系統處於退出狀態,再也不接收新的消息,而後將積壓的消息處理完,最後調用資源回收接口將資源銷燬,最後各線程退出執行。簡單的使用demo案例以下(簡單版):

/**
 * 優雅停機處理方式
 * 
 * @author lry
 **/
public class Main{

    /**
     * 啓動應用
     **/
    public void start(){
        // 第一步:啓動應用服務……

        // 第二步:註冊JDK鉤子
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("The hook running...");
                //第三步:調用停機處理
                stop();
            }
        }));
    }

    /**
     * 中止應用
     **/
    public void stop(){
        // 中止應用前停機處理(如:註銷服務、標記不接受請求等)
    }

}
複製代碼

注:一般優雅退出須要有超時控制機制,若是到達超時時間仍然沒有完成退出前的資源回收等操做,則由停機腳本直接調用KILL -9 PID的方式進行強制退出,否則可能會等待很長時間。

微服務優雅停機

能夠遵照如下建議規則來設計微服務的優雅停機機制

  1. 全部微服務應用都應該支持優雅停機
  2. 優先註銷註冊中心註冊的服務實例
  3. 待停機的服務應用的接入點標記拒絕服務(ShutdownHook)
  4. 上游服務支持故障轉移因優雅停機而拒絕的服務
  5. 根據具體業務也提供適當的停機接口

微服務應用的優雅停機根據其使用者角色的不一樣,而主要分爲兩種類型

  • 業務應用優雅停機
  • 網關應用優雅停機

業務應用優雅停機設計

上圖1-6步操做在大多數微服務框架中都已經集成了,無需開發人員自主開發,若是某些採用自研微服務框架的公司沒有這方面功能,開發人員能夠先行在本身負責的業務系統中編寫ShutdownHook來完成相同操做。

網關應用優雅停機設計

若是不支持Nginx動態發現網關,則停機升級切換的過程須要人工接入,稍微費力點,但一樣對用用戶來講是沒法感知到的。

總結

結合接入層的負載均衡高可用與微服務架構的高可用涉及,能夠作到任意時間升級而不影響用戶體驗,不形成生產事故。但仍是沒實現全自動的流程,由於Nginx不支持動態發現網關並修改配置生效。

Nginx動態上下線站點

社區經常使用的 upstream 動態更新方案有 4 個

  • ngx_http_dyups_module提供了粗粒度的upstream管理方法,能夠對整個upstream進行新增,刪除。
  • lua-upstream-nginx-module ,則提供了細粒度的管理方式,能夠對某一個服務IP進行管理,其中提供的set_peer_down方法,能夠對upstream中的某個ip進行上下線。
  • ngx_dynamic_upstream

這些插件有一個共同點,那就是在不須要重啓nginx的基礎上, 動態修改nginx的配置。

基於以上插件呢,能夠略作修改,使其支持nacos/zookeeper/consol/erueka等註冊中心的服務發現,定製須要的nginx reload upstream 動態更新模塊便可。

相關文章
相關標籤/搜索