'GitLab官網在線倉庫SSH連接故障排查和經驗總結實例'

Git 虛擬機 防火牆 Wireshark GitHub 蟲蟲安全 2019-09-03
"

GitLab官網在線倉庫SSH連接故障排查和經驗總結實例學習

GitLab在線倉庫服務是世界上最大的Git倉庫之一(github、gitlab和Bitbucket)。那麼GitLab的工程師是如何定位和解決故障的呢?日前GitLab博客中介紹了這樣的一個實例,今天蟲蟲帶領大家一起學習。

緣起

運維團隊接收到客戶報告,他們在訪問Gitlab時候會間歇性地報告Git錯誤,通常是來自CI工作段或類似的自動化系統。錯誤內容為:

ssh_exchange_identification: connection closed by remote host

fatal: Could not read from remote repository

比較要命的是錯誤信息是間歇性的隨機爆發,無法預測的,不能重現,也沒有明確指出截圖和日誌來說明問題發生的各種現象和環境,當然該錯誤信息也太籠統沒有啥太大的意義。SSH客戶端報告該信息可能的原因:碎片化的客戶端或虛擬機,網絡防火牆行為,ISP做的手腳,當然也有可能是GitLab應用程序問題。GitLab在線倉庫每天要處理2600萬的Git-over-SSH的連接,大約平均每秒300個的鏈接,要從中發現少數幾個失敗,顯然是一件很有挑戰性的問題。

第一個線索

經過初步分析後,運維團隊聯繫了有問題的客戶,他說每天都會有多次出現這個問題,所以以此來作為出發點顯然很合適。客戶還提供了問題是對應的公網IP地址,所以可以在的GitLab前端代理HAproxy節點抓包分析,定位到具體有問題的數據包信息。好消息是,客戶使用了alternate-ssh端口,這樣就可以只分析兩臺HAProxy服務器,將問題定位範圍進一步由16減少到2。

抓包後,對篩選的IP大概6.5小時內捕獲了大約500MB的數據包。在數據包中發現了一些短連接:建立了TCP連接,客戶端發送了一個版本字符串標識符,然後HAProxy立即用正確的TCP FIN序列刪除連接。這是第一個很好的線索,它顯示,是由於GitLab服務器端主動刪除鏈接的,所以問題的來源還是服務器端。

經驗一:Wireshark統計菜單有很多有用的工具,如果沒有真正實踐過都不會注意到。

比如"會話"會顯示捕獲中每個TCP連接的時間、數據包和字節大小,用他們進行篩選。應該在開始時使用它,而不是手動拖動捕獲。比如,該實例中需要尋找具有小數據包數量的連接,用會話視圖很容易顯示出來。然後,可以使用此功能查找其他實例,並驗證第一個實例不是個一個個例。

日誌分析

那麼是什麼導致HAProxy主動刪除客戶端的連接的原因是啥?二期似乎不是任意做的,應該有更深層次的原因。除了抓包日誌,HAProxy日誌是下一個要檢查的地方。GitLab在GCP BigQuery中存儲,並可以通過他們瀏覽分析日誌。首先,能夠根據時間和TCP端口識別數據包捕獲中的一個事件的日誌條目,這是一個重大突破。該條目中最有趣的細節是t_state(終止狀態)屬性,即SD。HAProxy文檔顯示:

S:服務器中止,或者服務器明確拒絕它

D:會話處於DATA階段。

D很清楚,已正確建立TCP連接,並且正在發送數據,這與數據包捕獲證據相匹配。S表示HAProxy從後端收到RST或ICMP失敗消息。至於是哪種情況發生或可能的原因沒有直接的頭緒。可能是從網絡問題(例如故障或堵塞)直到應用程序級問題的很多原因。使用BigQuery通過Git後端進行聚合,很明顯它並不是特定於任何VM。

事實證明,帶有SD的日誌並不是該問題所獨有的。在alternate-ssh端口上,會對HTTPS進行大量掃描,會導致在SSH服務器看到TLS ClientHello消息時遇到SSH問候語時會記錄SD。

在捕獲HAProxy和Git服務器之間的一些流量並再次通過使用Wireshark統計工具時,很明顯Git服務器上的SSHD在TCP三次握手後立即拆除了與TCP FIN-ACK的連接。 HAProxy仍然沒有發送第一個數據包,但是即將發送狀態,當它很快要發送時,Git服務器以TCP RST響應。因此有理由HAProxy使用SD記錄連接失敗。SSH正在關閉連接,顯然就已經關閉了。RST只是SSH服務器在FIN-ACK之後接收數據包,並不代表什麼。

一張統計圖

在觀察和分析BigQuery中的SD日誌時,很明顯在時間維度上有相當多的聚類,在每分鐘頂部後的前10秒內出現峰值,在開始約5-6秒後達到峰值:

"

GitLab官網在線倉庫SSH連接故障排查和經驗總結實例學習

GitLab在線倉庫服務是世界上最大的Git倉庫之一(github、gitlab和Bitbucket)。那麼GitLab的工程師是如何定位和解決故障的呢?日前GitLab博客中介紹了這樣的一個實例,今天蟲蟲帶領大家一起學習。

緣起

運維團隊接收到客戶報告,他們在訪問Gitlab時候會間歇性地報告Git錯誤,通常是來自CI工作段或類似的自動化系統。錯誤內容為:

ssh_exchange_identification: connection closed by remote host

fatal: Could not read from remote repository

比較要命的是錯誤信息是間歇性的隨機爆發,無法預測的,不能重現,也沒有明確指出截圖和日誌來說明問題發生的各種現象和環境,當然該錯誤信息也太籠統沒有啥太大的意義。SSH客戶端報告該信息可能的原因:碎片化的客戶端或虛擬機,網絡防火牆行為,ISP做的手腳,當然也有可能是GitLab應用程序問題。GitLab在線倉庫每天要處理2600萬的Git-over-SSH的連接,大約平均每秒300個的鏈接,要從中發現少數幾個失敗,顯然是一件很有挑戰性的問題。

第一個線索

經過初步分析後,運維團隊聯繫了有問題的客戶,他說每天都會有多次出現這個問題,所以以此來作為出發點顯然很合適。客戶還提供了問題是對應的公網IP地址,所以可以在的GitLab前端代理HAproxy節點抓包分析,定位到具體有問題的數據包信息。好消息是,客戶使用了alternate-ssh端口,這樣就可以只分析兩臺HAProxy服務器,將問題定位範圍進一步由16減少到2。

抓包後,對篩選的IP大概6.5小時內捕獲了大約500MB的數據包。在數據包中發現了一些短連接:建立了TCP連接,客戶端發送了一個版本字符串標識符,然後HAProxy立即用正確的TCP FIN序列刪除連接。這是第一個很好的線索,它顯示,是由於GitLab服務器端主動刪除鏈接的,所以問題的來源還是服務器端。

經驗一:Wireshark統計菜單有很多有用的工具,如果沒有真正實踐過都不會注意到。

比如"會話"會顯示捕獲中每個TCP連接的時間、數據包和字節大小,用他們進行篩選。應該在開始時使用它,而不是手動拖動捕獲。比如,該實例中需要尋找具有小數據包數量的連接,用會話視圖很容易顯示出來。然後,可以使用此功能查找其他實例,並驗證第一個實例不是個一個個例。

日誌分析

那麼是什麼導致HAProxy主動刪除客戶端的連接的原因是啥?二期似乎不是任意做的,應該有更深層次的原因。除了抓包日誌,HAProxy日誌是下一個要檢查的地方。GitLab在GCP BigQuery中存儲,並可以通過他們瀏覽分析日誌。首先,能夠根據時間和TCP端口識別數據包捕獲中的一個事件的日誌條目,這是一個重大突破。該條目中最有趣的細節是t_state(終止狀態)屬性,即SD。HAProxy文檔顯示:

S:服務器中止,或者服務器明確拒絕它

D:會話處於DATA階段。

D很清楚,已正確建立TCP連接,並且正在發送數據,這與數據包捕獲證據相匹配。S表示HAProxy從後端收到RST或ICMP失敗消息。至於是哪種情況發生或可能的原因沒有直接的頭緒。可能是從網絡問題(例如故障或堵塞)直到應用程序級問題的很多原因。使用BigQuery通過Git後端進行聚合,很明顯它並不是特定於任何VM。

事實證明,帶有SD的日誌並不是該問題所獨有的。在alternate-ssh端口上,會對HTTPS進行大量掃描,會導致在SSH服務器看到TLS ClientHello消息時遇到SSH問候語時會記錄SD。

在捕獲HAProxy和Git服務器之間的一些流量並再次通過使用Wireshark統計工具時,很明顯Git服務器上的SSHD在TCP三次握手後立即拆除了與TCP FIN-ACK的連接。 HAProxy仍然沒有發送第一個數據包,但是即將發送狀態,當它很快要發送時,Git服務器以TCP RST響應。因此有理由HAProxy使用SD記錄連接失敗。SSH正在關閉連接,顯然就已經關閉了。RST只是SSH服務器在FIN-ACK之後接收數據包,並不代表什麼。

一張統計圖

在觀察和分析BigQuery中的SD日誌時,很明顯在時間維度上有相當多的聚類,在每分鐘頂部後的前10秒內出現峰值,在開始約5-6秒後達到峰值:

GitLab官網在線倉庫SSH連接故障排查和經驗總結實例

連接錯誤,按秒分組

上圖是根據幾個小時整理的數據創建的,因此實際的事實表明在幾分鐘和幾小時內是一致的,甚至可能在一天的特定時間更糟。更有趣的是,平均峰值是基本負載的3倍,所以這意味著我們有擴展問題,並且簡單地為虛擬機配置"更多資源"以滿足峰值負載可能會非常昂貴。也表明GitLab正在達到一些硬性限制,這是潛在系統性問題的第一個線索。

Cron或類似的調度系統通常只能細分到分鐘級別。所以很多任務都在分鐘或小時的開始或其整數運行。如果他們在從GitLab進行git獲取之前花費幾秒鐘做準備工作,這將解釋連接模式在數秒鐘到一分鐘內增加,從而導致這些時間點的錯誤累積。

經驗二:顯然很多人都正確地設置了時間同步(通過NTP或其他方式)

如果他們沒有,這個問題就不會那麼清楚了。那麼是什麼可能導致SSH丟棄連接的呢?

接近問題實質

通過查看SSHD的文檔,發現MaxStartups參數,它可以控制處於預認證狀態的最大連接數。在波峰的時刻,互聯網預定的任務的衝擊下,似乎超出了連接限制似乎也是合理的。MaxStartups實際上有三個數字:低watermark(開始丟棄連接的數量),低watermark以上任何連接的(隨機)丟棄連接的百分比,以及超過所有新連接的絕對最大值。默認值為10:30和100,GitLab的設置為100:30:200,顯然我們過去曾經增加過連接設置,也需要再加大配置。

問題是,Gitlab服務器上openssh版本是7.2,並且在該版本中查看MaxStartups設置的唯一方法需要打開調試級別日誌記錄。這是可能對導致數據洩漏,所以只能在一臺服務器上測試打開。打開調試日誌後,在幾分鐘內,明顯MaxStartups設置就被超出了,因此連接被提前放棄。

事實證明,OpenSSH 7.6(Ubuntu 18.04附帶的版本)有更好的MaxStartups日誌記錄,只需要Verbose日誌記錄就可以獲得該信息。雖然不很理想,但總比Debug級別更好。

經驗三:在默認日誌級別記錄有用信息很有意義,出於任何原因服務器主動丟棄連接絕對是運維的鍋。

問題的原因找到了,那麼如何解決呢?我們可以調大MaxStartups配置,但是這麼做的代價是多少呢?這會增加一小部分內存,但它會有其他影響?不能光靠推測,所以需要實際測試。首先是將值MaxStartups提高到50%,調整為150:30:300。該配置帶來很大積極作用,並且沒有明顯的負面影響(例如CPU負載,負載等):

"

GitLab官網在線倉庫SSH連接故障排查和經驗總結實例學習

GitLab在線倉庫服務是世界上最大的Git倉庫之一(github、gitlab和Bitbucket)。那麼GitLab的工程師是如何定位和解決故障的呢?日前GitLab博客中介紹了這樣的一個實例,今天蟲蟲帶領大家一起學習。

緣起

運維團隊接收到客戶報告,他們在訪問Gitlab時候會間歇性地報告Git錯誤,通常是來自CI工作段或類似的自動化系統。錯誤內容為:

ssh_exchange_identification: connection closed by remote host

fatal: Could not read from remote repository

比較要命的是錯誤信息是間歇性的隨機爆發,無法預測的,不能重現,也沒有明確指出截圖和日誌來說明問題發生的各種現象和環境,當然該錯誤信息也太籠統沒有啥太大的意義。SSH客戶端報告該信息可能的原因:碎片化的客戶端或虛擬機,網絡防火牆行為,ISP做的手腳,當然也有可能是GitLab應用程序問題。GitLab在線倉庫每天要處理2600萬的Git-over-SSH的連接,大約平均每秒300個的鏈接,要從中發現少數幾個失敗,顯然是一件很有挑戰性的問題。

第一個線索

經過初步分析後,運維團隊聯繫了有問題的客戶,他說每天都會有多次出現這個問題,所以以此來作為出發點顯然很合適。客戶還提供了問題是對應的公網IP地址,所以可以在的GitLab前端代理HAproxy節點抓包分析,定位到具體有問題的數據包信息。好消息是,客戶使用了alternate-ssh端口,這樣就可以只分析兩臺HAProxy服務器,將問題定位範圍進一步由16減少到2。

抓包後,對篩選的IP大概6.5小時內捕獲了大約500MB的數據包。在數據包中發現了一些短連接:建立了TCP連接,客戶端發送了一個版本字符串標識符,然後HAProxy立即用正確的TCP FIN序列刪除連接。這是第一個很好的線索,它顯示,是由於GitLab服務器端主動刪除鏈接的,所以問題的來源還是服務器端。

經驗一:Wireshark統計菜單有很多有用的工具,如果沒有真正實踐過都不會注意到。

比如"會話"會顯示捕獲中每個TCP連接的時間、數據包和字節大小,用他們進行篩選。應該在開始時使用它,而不是手動拖動捕獲。比如,該實例中需要尋找具有小數據包數量的連接,用會話視圖很容易顯示出來。然後,可以使用此功能查找其他實例,並驗證第一個實例不是個一個個例。

日誌分析

那麼是什麼導致HAProxy主動刪除客戶端的連接的原因是啥?二期似乎不是任意做的,應該有更深層次的原因。除了抓包日誌,HAProxy日誌是下一個要檢查的地方。GitLab在GCP BigQuery中存儲,並可以通過他們瀏覽分析日誌。首先,能夠根據時間和TCP端口識別數據包捕獲中的一個事件的日誌條目,這是一個重大突破。該條目中最有趣的細節是t_state(終止狀態)屬性,即SD。HAProxy文檔顯示:

S:服務器中止,或者服務器明確拒絕它

D:會話處於DATA階段。

D很清楚,已正確建立TCP連接,並且正在發送數據,這與數據包捕獲證據相匹配。S表示HAProxy從後端收到RST或ICMP失敗消息。至於是哪種情況發生或可能的原因沒有直接的頭緒。可能是從網絡問題(例如故障或堵塞)直到應用程序級問題的很多原因。使用BigQuery通過Git後端進行聚合,很明顯它並不是特定於任何VM。

事實證明,帶有SD的日誌並不是該問題所獨有的。在alternate-ssh端口上,會對HTTPS進行大量掃描,會導致在SSH服務器看到TLS ClientHello消息時遇到SSH問候語時會記錄SD。

在捕獲HAProxy和Git服務器之間的一些流量並再次通過使用Wireshark統計工具時,很明顯Git服務器上的SSHD在TCP三次握手後立即拆除了與TCP FIN-ACK的連接。 HAProxy仍然沒有發送第一個數據包,但是即將發送狀態,當它很快要發送時,Git服務器以TCP RST響應。因此有理由HAProxy使用SD記錄連接失敗。SSH正在關閉連接,顯然就已經關閉了。RST只是SSH服務器在FIN-ACK之後接收數據包,並不代表什麼。

一張統計圖

在觀察和分析BigQuery中的SD日誌時,很明顯在時間維度上有相當多的聚類,在每分鐘頂部後的前10秒內出現峰值,在開始約5-6秒後達到峰值:

GitLab官網在線倉庫SSH連接故障排查和經驗總結實例

連接錯誤,按秒分組

上圖是根據幾個小時整理的數據創建的,因此實際的事實表明在幾分鐘和幾小時內是一致的,甚至可能在一天的特定時間更糟。更有趣的是,平均峰值是基本負載的3倍,所以這意味著我們有擴展問題,並且簡單地為虛擬機配置"更多資源"以滿足峰值負載可能會非常昂貴。也表明GitLab正在達到一些硬性限制,這是潛在系統性問題的第一個線索。

Cron或類似的調度系統通常只能細分到分鐘級別。所以很多任務都在分鐘或小時的開始或其整數運行。如果他們在從GitLab進行git獲取之前花費幾秒鐘做準備工作,這將解釋連接模式在數秒鐘到一分鐘內增加,從而導致這些時間點的錯誤累積。

經驗二:顯然很多人都正確地設置了時間同步(通過NTP或其他方式)

如果他們沒有,這個問題就不會那麼清楚了。那麼是什麼可能導致SSH丟棄連接的呢?

接近問題實質

通過查看SSHD的文檔,發現MaxStartups參數,它可以控制處於預認證狀態的最大連接數。在波峰的時刻,互聯網預定的任務的衝擊下,似乎超出了連接限制似乎也是合理的。MaxStartups實際上有三個數字:低watermark(開始丟棄連接的數量),低watermark以上任何連接的(隨機)丟棄連接的百分比,以及超過所有新連接的絕對最大值。默認值為10:30和100,GitLab的設置為100:30:200,顯然我們過去曾經增加過連接設置,也需要再加大配置。

問題是,Gitlab服務器上openssh版本是7.2,並且在該版本中查看MaxStartups設置的唯一方法需要打開調試級別日誌記錄。這是可能對導致數據洩漏,所以只能在一臺服務器上測試打開。打開調試日誌後,在幾分鐘內,明顯MaxStartups設置就被超出了,因此連接被提前放棄。

事實證明,OpenSSH 7.6(Ubuntu 18.04附帶的版本)有更好的MaxStartups日誌記錄,只需要Verbose日誌記錄就可以獲得該信息。雖然不很理想,但總比Debug級別更好。

經驗三:在默認日誌級別記錄有用信息很有意義,出於任何原因服務器主動丟棄連接絕對是運維的鍋。

問題的原因找到了,那麼如何解決呢?我們可以調大MaxStartups配置,但是這麼做的代價是多少呢?這會增加一小部分內存,但它會有其他影響?不能光靠推測,所以需要實際測試。首先是將值MaxStartups提高到50%,調整為150:30:300。該配置帶來很大積極作用,並且沒有明顯的負面影響(例如CPU負載,負載等):

GitLab官網在線倉庫SSH連接故障排查和經驗總結實例

看上圖,注意到0​​1:15之後錯誤數大幅減少。明顯已經消除了很大一部分錯誤,儘管仍然存在非常微不足道的數量。圖形顯示聚集在一致的高峰的時間,每隔30分鐘,15分鐘和10分鐘。重要的時刻是最高峰,這也跟實際使用相吻合,很多人會安排他們的任務在每小時0分鐘的時間內每小時運行一次。這一發現更加證實事實,由於預定的工作導致連接峰值,連接錯誤是由於最大連接數的限制。

沒有明顯的負面影響,SSH服務器上的CPU使用率保持不變,沒有任何明顯的負載增加,通過簡單配置就釋放了之前被刪除的很多連接。

限速

但是並不能一味的簡單調高MaxStartups配置,雖然通過配置增加50%的數值,但繼續增加這一數字也不行,還有其他可以做優化的事情。

HAProxy為其前端偵聽器提供了一個很好的"速率限制會話"選項。配置後,它會限制前端將傳遞給後端的每秒新TCP連接數,並在TCP套接字上保留其他連接。如果傳入速率超過限制(每毫秒測量),則新連接將被延遲。TCP客戶端(在這種情況下為SSH)只是在建立TCP連接之前看到延遲,這樣不會有太大影響,只要總體從未超過限制太長時間。由於Gitlab有27個SSH後端和18個HAproxy前端(16個主要,兩個alt-ssh),並且前端之間沒有做過速率限制優化,調整比較麻煩,必須考慮新的SSH會話需要多長時間通過身份驗證:假設MaxStartups為150,如果auth階段需要兩秒鐘,每秒只能向每個後端發送75個新會話。計算速率限制需要四個數量:兩種服務器類型的數量,MaxStartups的值和T,這是SSH會話進行身份驗證所需的時間。T值很關鍵,但只能估計。最後配置為每個前端的速率限制大約為112.5,並向下舍入到110。

調整部署後,一切都OK了?錯誤連接數應該為零,但是,實際上,這個配置並沒有對錯誤率有明顯影響。陷入深深的沉思,一定錯過了了啥!

所以回到日誌(最終是HAProxy指標),確保能夠驗證速率限制是否生效了,並且歷史顯示這個數字已經更高,所以成功地約束了發送連接的速率。但顯然這個比率仍然太高,它還不足以接近正確的數字,以產生可衡量的影響。

查看後端(由HAproxy記錄)顯示了一個奇怪的現象:在一小時的峰值,後端連接並非均勻分佈在所有SSH服務器上。在所選的時間內,它在給定的秒內從30變化到121,這意味著負載平衡不是很平衡。查看配置使用的"balance source",因此給定的客戶端IP地址始終會連接到同一後端。如果需要會話粘性,這是很好的策略,但是對SSH,沒有這樣的需求。因此嘗試將其更改為"leastconn",使用最少數量的當前連接將新的傳入連接分發到後端。使用新策略後SSH(Git)CPU使用率的結果:

"

GitLab官網在線倉庫SSH連接故障排查和經驗總結實例學習

GitLab在線倉庫服務是世界上最大的Git倉庫之一(github、gitlab和Bitbucket)。那麼GitLab的工程師是如何定位和解決故障的呢?日前GitLab博客中介紹了這樣的一個實例,今天蟲蟲帶領大家一起學習。

緣起

運維團隊接收到客戶報告,他們在訪問Gitlab時候會間歇性地報告Git錯誤,通常是來自CI工作段或類似的自動化系統。錯誤內容為:

ssh_exchange_identification: connection closed by remote host

fatal: Could not read from remote repository

比較要命的是錯誤信息是間歇性的隨機爆發,無法預測的,不能重現,也沒有明確指出截圖和日誌來說明問題發生的各種現象和環境,當然該錯誤信息也太籠統沒有啥太大的意義。SSH客戶端報告該信息可能的原因:碎片化的客戶端或虛擬機,網絡防火牆行為,ISP做的手腳,當然也有可能是GitLab應用程序問題。GitLab在線倉庫每天要處理2600萬的Git-over-SSH的連接,大約平均每秒300個的鏈接,要從中發現少數幾個失敗,顯然是一件很有挑戰性的問題。

第一個線索

經過初步分析後,運維團隊聯繫了有問題的客戶,他說每天都會有多次出現這個問題,所以以此來作為出發點顯然很合適。客戶還提供了問題是對應的公網IP地址,所以可以在的GitLab前端代理HAproxy節點抓包分析,定位到具體有問題的數據包信息。好消息是,客戶使用了alternate-ssh端口,這樣就可以只分析兩臺HAProxy服務器,將問題定位範圍進一步由16減少到2。

抓包後,對篩選的IP大概6.5小時內捕獲了大約500MB的數據包。在數據包中發現了一些短連接:建立了TCP連接,客戶端發送了一個版本字符串標識符,然後HAProxy立即用正確的TCP FIN序列刪除連接。這是第一個很好的線索,它顯示,是由於GitLab服務器端主動刪除鏈接的,所以問題的來源還是服務器端。

經驗一:Wireshark統計菜單有很多有用的工具,如果沒有真正實踐過都不會注意到。

比如"會話"會顯示捕獲中每個TCP連接的時間、數據包和字節大小,用他們進行篩選。應該在開始時使用它,而不是手動拖動捕獲。比如,該實例中需要尋找具有小數據包數量的連接,用會話視圖很容易顯示出來。然後,可以使用此功能查找其他實例,並驗證第一個實例不是個一個個例。

日誌分析

那麼是什麼導致HAProxy主動刪除客戶端的連接的原因是啥?二期似乎不是任意做的,應該有更深層次的原因。除了抓包日誌,HAProxy日誌是下一個要檢查的地方。GitLab在GCP BigQuery中存儲,並可以通過他們瀏覽分析日誌。首先,能夠根據時間和TCP端口識別數據包捕獲中的一個事件的日誌條目,這是一個重大突破。該條目中最有趣的細節是t_state(終止狀態)屬性,即SD。HAProxy文檔顯示:

S:服務器中止,或者服務器明確拒絕它

D:會話處於DATA階段。

D很清楚,已正確建立TCP連接,並且正在發送數據,這與數據包捕獲證據相匹配。S表示HAProxy從後端收到RST或ICMP失敗消息。至於是哪種情況發生或可能的原因沒有直接的頭緒。可能是從網絡問題(例如故障或堵塞)直到應用程序級問題的很多原因。使用BigQuery通過Git後端進行聚合,很明顯它並不是特定於任何VM。

事實證明,帶有SD的日誌並不是該問題所獨有的。在alternate-ssh端口上,會對HTTPS進行大量掃描,會導致在SSH服務器看到TLS ClientHello消息時遇到SSH問候語時會記錄SD。

在捕獲HAProxy和Git服務器之間的一些流量並再次通過使用Wireshark統計工具時,很明顯Git服務器上的SSHD在TCP三次握手後立即拆除了與TCP FIN-ACK的連接。 HAProxy仍然沒有發送第一個數據包,但是即將發送狀態,當它很快要發送時,Git服務器以TCP RST響應。因此有理由HAProxy使用SD記錄連接失敗。SSH正在關閉連接,顯然就已經關閉了。RST只是SSH服務器在FIN-ACK之後接收數據包,並不代表什麼。

一張統計圖

在觀察和分析BigQuery中的SD日誌時,很明顯在時間維度上有相當多的聚類,在每分鐘頂部後的前10秒內出現峰值,在開始約5-6秒後達到峰值:

GitLab官網在線倉庫SSH連接故障排查和經驗總結實例

連接錯誤,按秒分組

上圖是根據幾個小時整理的數據創建的,因此實際的事實表明在幾分鐘和幾小時內是一致的,甚至可能在一天的特定時間更糟。更有趣的是,平均峰值是基本負載的3倍,所以這意味著我們有擴展問題,並且簡單地為虛擬機配置"更多資源"以滿足峰值負載可能會非常昂貴。也表明GitLab正在達到一些硬性限制,這是潛在系統性問題的第一個線索。

Cron或類似的調度系統通常只能細分到分鐘級別。所以很多任務都在分鐘或小時的開始或其整數運行。如果他們在從GitLab進行git獲取之前花費幾秒鐘做準備工作,這將解釋連接模式在數秒鐘到一分鐘內增加,從而導致這些時間點的錯誤累積。

經驗二:顯然很多人都正確地設置了時間同步(通過NTP或其他方式)

如果他們沒有,這個問題就不會那麼清楚了。那麼是什麼可能導致SSH丟棄連接的呢?

接近問題實質

通過查看SSHD的文檔,發現MaxStartups參數,它可以控制處於預認證狀態的最大連接數。在波峰的時刻,互聯網預定的任務的衝擊下,似乎超出了連接限制似乎也是合理的。MaxStartups實際上有三個數字:低watermark(開始丟棄連接的數量),低watermark以上任何連接的(隨機)丟棄連接的百分比,以及超過所有新連接的絕對最大值。默認值為10:30和100,GitLab的設置為100:30:200,顯然我們過去曾經增加過連接設置,也需要再加大配置。

問題是,Gitlab服務器上openssh版本是7.2,並且在該版本中查看MaxStartups設置的唯一方法需要打開調試級別日誌記錄。這是可能對導致數據洩漏,所以只能在一臺服務器上測試打開。打開調試日誌後,在幾分鐘內,明顯MaxStartups設置就被超出了,因此連接被提前放棄。

事實證明,OpenSSH 7.6(Ubuntu 18.04附帶的版本)有更好的MaxStartups日誌記錄,只需要Verbose日誌記錄就可以獲得該信息。雖然不很理想,但總比Debug級別更好。

經驗三:在默認日誌級別記錄有用信息很有意義,出於任何原因服務器主動丟棄連接絕對是運維的鍋。

問題的原因找到了,那麼如何解決呢?我們可以調大MaxStartups配置,但是這麼做的代價是多少呢?這會增加一小部分內存,但它會有其他影響?不能光靠推測,所以需要實際測試。首先是將值MaxStartups提高到50%,調整為150:30:300。該配置帶來很大積極作用,並且沒有明顯的負面影響(例如CPU負載,負載等):

GitLab官網在線倉庫SSH連接故障排查和經驗總結實例

看上圖,注意到0​​1:15之後錯誤數大幅減少。明顯已經消除了很大一部分錯誤,儘管仍然存在非常微不足道的數量。圖形顯示聚集在一致的高峰的時間,每隔30分鐘,15分鐘和10分鐘。重要的時刻是最高峰,這也跟實際使用相吻合,很多人會安排他們的任務在每小時0分鐘的時間內每小時運行一次。這一發現更加證實事實,由於預定的工作導致連接峰值,連接錯誤是由於最大連接數的限制。

沒有明顯的負面影響,SSH服務器上的CPU使用率保持不變,沒有任何明顯的負載增加,通過簡單配置就釋放了之前被刪除的很多連接。

限速

但是並不能一味的簡單調高MaxStartups配置,雖然通過配置增加50%的數值,但繼續增加這一數字也不行,還有其他可以做優化的事情。

HAProxy為其前端偵聽器提供了一個很好的"速率限制會話"選項。配置後,它會限制前端將傳遞給後端的每秒新TCP連接數,並在TCP套接字上保留其他連接。如果傳入速率超過限制(每毫秒測量),則新連接將被延遲。TCP客戶端(在這種情況下為SSH)只是在建立TCP連接之前看到延遲,這樣不會有太大影響,只要總體從未超過限制太長時間。由於Gitlab有27個SSH後端和18個HAproxy前端(16個主要,兩個alt-ssh),並且前端之間沒有做過速率限制優化,調整比較麻煩,必須考慮新的SSH會話需要多長時間通過身份驗證:假設MaxStartups為150,如果auth階段需要兩秒鐘,每秒只能向每個後端發送75個新會話。計算速率限制需要四個數量:兩種服務器類型的數量,MaxStartups的值和T,這是SSH會話進行身份驗證所需的時間。T值很關鍵,但只能估計。最後配置為每個前端的速率限制大約為112.5,並向下舍入到110。

調整部署後,一切都OK了?錯誤連接數應該為零,但是,實際上,這個配置並沒有對錯誤率有明顯影響。陷入深深的沉思,一定錯過了了啥!

所以回到日誌(最終是HAProxy指標),確保能夠驗證速率限制是否生效了,並且歷史顯示這個數字已經更高,所以成功地約束了發送連接的速率。但顯然這個比率仍然太高,它還不足以接近正確的數字,以產生可衡量的影響。

查看後端(由HAproxy記錄)顯示了一個奇怪的現象:在一小時的峰值,後端連接並非均勻分佈在所有SSH服務器上。在所選的時間內,它在給定的秒內從30變化到121,這意味著負載平衡不是很平衡。查看配置使用的"balance source",因此給定的客戶端IP地址始終會連接到同一後端。如果需要會話粘性,這是很好的策略,但是對SSH,沒有這樣的需求。因此嘗試將其更改為"leastconn",使用最少數量的當前連接將新的傳入連接分發到後端。使用新策略後SSH(Git)CPU使用率的結果:

GitLab官網在線倉庫SSH連接故障排查和經驗總結實例

顯然,該策略很有效果。兩條低使用率的線條,顯示的是Canary服務器,可以忽略不計,但在策略更改之前由於源IP分佈不均勻服務器之間負載差異是2:1(30%到60%),很明顯我們的一些後端比其他後端更加繁忙。。

經驗四:當選擇特定的非默認設置時,需要註釋或鏈接到文檔/問題,為什麼,後來者會感謝你。

啟用leastconn策略後錯誤率也明顯降低了。繼續,試驗限速配置,將速率限制降低到100,這進一步降低了錯誤率,表明可能對T的初始估計是錯誤的。但如果是這樣限制會導致速率限制太高,甚至100/s感覺非常低,我們不想進一步放棄它。隨著速率限制儘可能低,而且最小化不足,嘗試增加MaxStartups:首先是為200,有效,然後到250,錯誤幾乎消失了,而沒有任何負面結果。

經驗四:儘管很違常理,但是MaxStartups似乎對性能影響很小,即使它的提升遠遠高於默認值時。

如果有必要,這可能是未來可以提供的一個更大水平。如果它提高到成千上萬或數萬,可能會注意到其影響問題。

這說明對T的估計,建立和驗證SSH會話的時間是什麼?通過測試知道200對MaxStartups來說還不夠,250就足夠了,可以計算出T可能在2.7到3.4秒之間。。

善後

事後通過查看日誌,經過一番思考後,發現可以識別出一個特定的故障模式:t_state為SD,b_read(客戶端讀取的字節數)為0。如上所述,GitLab每天處理大約26-28萬個SSH連接。令人不快的是,在最嚴重情況下,大約有1.5%的連接被嚴重丟棄。顯然,問題比開始時意識到的要大,之前無法確定這一點(當發現t_state ="SD"表示問題時,沒有想到這一點)。

經驗六:儘早測量錯誤佔比

如果能意識到問題的嚴重程度,可能會更早地優先考慮該問題,儘管它仍然依賴於瞭解識別特徵。從好的方面來看,在遇到MaxStartups和速率限制後,錯誤率降至0.001%,即每天幾千。這比較好,但仍然高於期望。在解決其他一些操作問題之後,希望能夠正式部署最小的變更,並且可以完全消除了錯誤。

進一步的工作

顯然SSH身份驗證階段仍需要很長時間,可能長達3.4秒。GitLab可以使用AuthorizedKeysCommand直接在數據庫中查找SSH密鑰。當擁有大量用戶時,這對於快速操作至關重要,否則SSHD必須按順序讀取非常大的authorized_keys文件以查找用戶的公鑰,並且這不能很好實現橫向擴展。目前使用一些調用內部HTTP API的ruby實現查找。工程研究員Stan Hu和GitLab知識的常駐來源,確定Git/ SH服務器上的實例正在經歷大量排隊。這可能是~3秒預認證階段的重要貢獻者,因此需要進一步研究解決。

當看到表明問題正在發生的HAProxy日誌時,還需要發出警報。後續可能還需要進一步增加MaxStartup配置,或者如果資源限制了,則添加更多Git/SSH節點。

總結

本文通過GitLab線上服務的一個實例分析,顯示了線上問題排查的全過程,通過抓包、日誌定位,參數配置調整測試和問題總結等過程全面展示了從問題發現、定位到解決的全過程,並且在該過程中進行經驗總結。總之該問題不是特例,該問題解決方法、步驟和思路更是可以學習和推廣的好案例。

"