談談上線變更

  • 2019 年 10 月 13 日
  • 筆記

文章首發於公眾號松花皮蛋的黑板報

作者就職於京東,在穩定性保障、敏捷開發、高級JAVA、微服務架構有深入的理解

 

 

為什麼今天要討論這個話題呢?因為我最近上線時就犯了一個錯誤,想把這事和後來的復盤分享給大家,事故的過程如果沒看懂可以直接往下拉看復盤。

過程是這樣的:我的需求是在方法參數POJO類中新增一個可選參數,我將這個參數定義在POJO類的父類的最後一個。其中提供的是遠程調用RPC服務,所以需要打一個描述類的JAR包供調用方使用,這個JAR包包含服務接口和入參、出參實體。

聯調測試時使用的是1.1-SHAPSHOT版本,上線前我將版本號修改成1.0-SHAPSHOT版本,並且將私服中1.0-SHAPSHOT快照包更新了,業務代碼沒做任何改動。快照包的意思是同一個版本每次更新時都重新生成一個附有時間戳的包,當你構建下載包時都會下載指定版本最新更新的包,比如可以同時存在1.0-SHAPSHOT-2019101309和1.0-SHAPSHOT-2019080808版本。我當時上線後驗證是通過的,說明版本是向下兼容的,就是調用方使用的1.0-SHAPSHOT-201908080也能正常調用我的1.0-SHAPSHOT-2019101309版本的服務。過幾個小時後調用方使用了1.1-SHAPSHOT版本的包上線了,全量上線後,發現請求根本達到不了我這,因為在RPC框架中序列化異常了,此時調用方開始回滾,使用1.0最新的1.0-SHAPSHOT-2019101309開始構建上線,還是出錯了。於是我比較着急也開始回滾,不過呢,我是將歷史構建的包發佈上線,也就是說使用的是1.0舊的1.0-SHAPSHOT-201908080包,當然毫無疑問還是會調用出錯,此時才發現調用方回退時重新構建了,於是聯繫私服的同事將1.0最新的1.0-SHAPSHOT-2019101309刪除,隨後通知調用方重新構建上線,此時服務才恢復。

整個過程雙方都有很多操作缺陷,比如我上線的並不是嚴格測試的包,甚至沒有經過雙方回歸驗證。比如我處理線上問題的方法方式,我其實是不需要回滾的。比如我告知了下游使用的版本號,但是下游還是使用了測試的版本。再比如下游沒有進行灰度發佈驗證、異常後回退時重新構建了,等等。一個看似很簡單的上線卻失敗了,說明上線發版規範沒有完全把握好。

讀者可能覺得上線變更沒有值得深入探討的地方,上線無非就是將要發佈的包通過一定的技術手段替換現在線上運行的包,或者將配置信息覆蓋更新,然後重啟服務,並且現在都是通過鼠標點點按鈕就能完成的事。但是呢,這其中有很多細節,稍微不注意就會像我一樣犯錯。接下來我將說說上線前、上線中、上線後、上線失敗需要注意的地方。

先來說說上線前,這個上線包含新增實例分組發佈、新增機器發佈、在原有機器上發佈。新增實例分組意味着你需要和舊分組仔細對比配置,包括日記級別配置。新增機器發佈意味着你的機器網段可能是新的、你的調用外網服務權限可能是沒有的、你的依賴系統庫可能是沒有安裝的、你的IP可能不在白名單內,這些都是我在實際工作中碰到過的問題。

當上線條件和環境具備,包括前面說的機器配置,還包括上線時間,我們就可以提出上線申請了。原則上節假日(包括周末)前一天、重大促銷活動(比如產品發佈會)當天、流量高峰時間段都是不允許上線的。上線申請內容一般包括背景描述、操作對象、操作步驟、CHECKLIST、預期結果、回滾方案,還會包括自測情況。任何操作都應該有明確的文字說明,拒絕模糊或僅在大腦中認為可行的方案。同時你的操作變更還得周知產品、測試人員和其他同事,不能只有你、代碼評審者、領導知道本次變更操作。避免當其他服務受到牽連時,其他人只能通過查看上線記錄或者翻查代碼提交記錄才知道應該找誰。如果你修改的是公共代碼或者協議,那更要提前周知了。

上線前檢查還有一個需要注意的地方,就是確保你構建出來的上線包包括了你合併的代碼塊,現在大部分的平台構建出來的上線包,包名會附有最新提交到GIT代碼倉庫的COMMIT_ID,非常直觀明了。

當上線前檢查完成後,就可以發佈部署了,一般將操作的服務實例按分組、機房分別分批部署,我這裡強調的是分組、分機房,強調的是並不僅僅是以全部實例按多少比例部署,當然我們通常按30%的比例進行分批部署。不過,如果你的比例剛好命中某個分組或者某個機房全部實例時,那就意味着這個分組、機房的服務在全量上線,就很有可能出現無服務提供者的情況,如果上線失敗,情況就不是短暫性的了。

分批部署第一步一般是每個機房選擇一台進行發佈驗證,這樣有助於我們及時地發現問題,避免影響擴散,甚至有些功能需要數據的積累才能驗證,所以有時也會分時間段部署,每間隔一個小時部署一小比例的服務實例。當然分批部署只是我們規避線上風險的手段,不具備測試的目的,不能取代測試,也就是禁止將沒有進行測試的包部署到生產環境,那怕只是修改了RPC服務的JAR包版本號。

分批部署一般需要驗證原有功能是否有受到影響、業務監控是否有異常、服務實例是否正常啟動、流量是否正常到達、功能是否生效是否有缺陷、程序資源消耗是否正常、程序性能是否正常。我碰到過發佈平台顯示上線成功,但是服務實例OOM崩潰了,如果此時貿然將此實例掛載到服務下,等待我的就是異常告警了。我還碰到過同事將新擴的機器部署後,卻忘記掛載流量的情況,浪費了時間還浪費了機器資源,所幸原有機器成功抗住了流量。

說到這啊,我再補充一種情況,部署時發現有台機器連通性異常了,處在運維狀態,可能只是發包使用的端口受到影響而已,服務監聽的端口是沒有受到影響的,此時你需要將這台機器流量摘掉,避免狀態正常後流量打到了錯誤的服務版本了。為什麼要將這個事單獨拿出來說呢,其實是想強調我們要確認全部實例都更新為新功能版本了,包括避免漏發實例。

上線變更是事故的高發場景,當真的發生問題時,我們也不要慌張,先報備領導,然後第一時間止損和恢復服務。如果在發第一台驗證的時候就出現異常了,最快的方式是修改Nginx配置將流量打到其他正常機器上,如果你摘取流量或者停止實例,其實都是有非同步狀態的,因為用戶接入層的負載均衡心跳檢測可能是有延遲的。如果全量發佈後發現異常,按照應急預案也無法及時止損的話,只能選擇回滾服務,要避免造成二次影響。

實際上,當用戶碰到問題時極少會選擇反饋,沉默的是大多數,所以上線務必進行充分驗證和全方面的監控,不要乾等着用戶來反饋,當用戶來反饋時影響範圍可能很大了。那麼我們就需要規範化工作過程和輸出,提高穩定性和質量。

好,本次的分享就到這,如果有幫助到你,歡迎點個在看或者分享給你的朋友們。

文章來源:www.liangsonghua.me

作者介紹:京東資深工程師-梁松華,在穩定性保障、敏捷開發、JAVA高級、微服務架構方面有深入的理解