對所有人都好!
我叫Nikita,是Cian工程團隊的組長。我在公司的職責之一是將與生產基礎設施相關的事故數量減少到零。
以下將要討論的內容給我們帶來了極大的痛苦,而本文的目的是防止其他人重複我們的錯誤或至少盡量減少其影響。
前言
很久以前,當 Cian 由單體組成時,還沒有微服務的跡象,我們透過檢查 3-5 個頁面來衡量資源的可用性。
他們回答 - 一切都很好,如果他們很長時間不回答 - 警報。他們需要下班多長時間才能被視為事件,這是由人們在會議上決定的。工程師團隊始終參與事件調查。調查完成後,他們寫了一份事後分析——一種透過電子郵件發送的報告,格式如下:發生了什麼、持續了多長時間、我們當時做了什麼、將來會做什麼。
網站的主要頁面或我們如何理解我們已經觸底
為了以某種方式了解錯誤的優先級,我們已經確定了業務功能最關鍵的網站頁面。使用它們,我們可以計算成功/不成功的請求和超時的數量。這就是我們衡量正常運作時間的方式。
假設我們發現該網站有許多非常重要的部分負責主要服務 - 搜尋和提交廣告。如果失敗的請求數量超過 1%,則屬於嚴重事件。如果在黃金時段15分鐘內錯誤率超過0,1%,那麼這也被視為嚴重事件。這些標準涵蓋了大多數事件;其餘事件超出了本文的範圍。
最佳最佳事件 青色
所以,我們肯定已經學會了確定事件發生的事實。
現在,每個事件都被詳細描述並反映在 Jira 史詩中。順便說一句:為此我們啟動了一個單獨的項目,稱之為「失敗」——只能在其中創建史詩。
如果你收集過去幾年的所有失敗,領導者是:
- mssql相關事件;
- 由外在因素引起的事件;
- 管理錯誤。
讓我們更詳細地看看管理員的錯誤,以及其他一些有趣的失敗。
第五名——“在 DNS 中把事情整理好”
那是一個風雨交加的星期二。我們決定恢復 DNS 叢集中的秩序。
我想將內部 DNS 伺服器從 bind 轉移到 powerdns,為此分配完全獨立的伺服器,除了 DNS 之外什麼都沒有。
我們在 DC 的每個位置放置了一台 DNS 伺服器,現在是將區域從 Bind 移動到 powerdns 並將基礎設施切換到新伺服器的時刻到來了。
在遷移過程中,在所有伺服器上的本機快取綁定中指定的所有伺服器中,只剩下一台位於聖彼得堡的資料中心。該 DC 最初被聲明為對我們來說不重要,但突然變成了單點故障。
正是在搬遷期間,莫斯科和聖彼得堡之間的運河垮塌了。實際上,我們有五分鐘沒有 DNS 服務,當託管服務商解決了問題後,我們又恢復正常了。
結論:
如果說以前我們在準備工作時忽略了外部因素,那麼現在它們也被納入了我們正在準備的清單中。現在我們努力確保所有元件都保留n-2,並且在工作過程中我們可以將這個等級降低到n-1。
- 在製定行動計劃時,標記出服務可能失敗的點,並提前考慮一切「每況愈下」的場景。
- 將內部 DNS 伺服器分佈在不同的地理位置/資料中心/機架/交換器/輸入上。
- 在每台伺服器上,安裝本機快取 DNS 伺服器,該伺服器將請求重新導向到主 DNS 伺服器,如果不可用,則會從快取中回應。
第四名——“在 Nginx 中把事情整理好”
有一天,我們的團隊決定“我們已經受夠了”,重構 nginx 配置的過程開始了。主要目標是將配置引入直覺的結構。以前一切都是“歷史既定”,沒有任何邏輯。現在,每個 server_name 已移至同名文件,並且所有配置已分發到資料夾中。順便說一句,該配置包含 253949 行或 7836520 個字符,佔用近 7 MB 的空間。頂層結構:
Nginx結構
├── access
│ ├── allow.list
...
│ └── whitelist.conf
├── geobase
│ ├── exclude.conf
...
│ └── geo_ip_to_region_id.conf
├── geodb
│ ├── GeoIP.dat
│ ├── GeoIP2-Country.mmdb
│ └── GeoLiteCity.dat
├── inc
│ ├── error.inc
...
│ └── proxy.inc
├── lists.d
│ ├── bot.conf
...
│ ├── dynamic
│ └── geo.conf
├── lua
│ ├── cookie.lua
│ ├── log
│ │ └── log.lua
│ ├── logics
│ │ ├── include.lua
│ │ ├── ...
│ │ └── utils.lua
│ └── prom
│ ├── stats.lua
│ └── stats_prometheus.lua
├── map.d
│ ├── access.conf
│ ├── ..
│ └── zones.conf
├── nginx.conf
├── robots.txt
├── server.d
│ ├── cian.ru
│ │ ├── cian.ru.conf
│ │ ├── ...
│ │ └── my.cian.ru.conf
├── service.d
│ ├── ...
│ └── status.conf
└── upstream.d
├── cian-mcs.conf
├── ...
└── wafserver.conf
它變得好多了,但在重命名和分發配置的過程中,其中一些配置的擴展名錯誤,並且沒有包含在 include *.conf 指令中。導致部分主機無法使用,返回301返回主頁。由於回應代碼不是 5xx/4xx,所以沒有立即註意到這一點,而是在早上才注意到。之後,我們開始編寫測試來檢查基礎設施元件。
結論:
- 正確建立您的配置(不僅僅是 nginx)並在專案的早期階段仔細考慮結構。透過這種方式,您將使團隊更容易理解它們,從而減少 TTM。
- 為一些基礎設施元件編寫測試。例如:檢查所有關鍵伺服器名稱是否給予正確的狀態+回應正文。手邊只要有幾個腳本來檢查組件的基本功能就足夠了,這樣就不用在凌晨 3 點瘋狂地記住還需要檢查什麼。
第三名——“卡桑德拉突然空間不足”
資料穩定成長,一切都很好,直到 Cassandra 叢集中大型案例空間的修復開始失敗的那一刻,因為壓縮無法對它們起作用。
在一個暴風雨天,該簇幾乎變成了一個南瓜,即:
- 集群中大約剩下總空間的 20%;
- 無法完全新增節點,因為新增節點後因分區空間不足而無法進行清理;
- 由於壓實不起作用,生產率逐漸下降;
- 集群處於緊急模式。
退出 - 我們在沒有清理的情況下又添加了 5 個節點,之後我們開始系統地從叢集中刪除它們並重新進入它們,就像空間不足的空節點一樣。花費的時間比我們想要的要多得多。存在集群部分或完全不可用的風險。
結論:
- 在所有 cassandra 伺服器上,每個分割區上的空間佔用量不得超過 60%。
- 它們的負載不應超過 50% cpu。
- 您不應忘記容量規劃,並且需要根據每個組件的具體情況對其進行仔細考慮。
- 叢集中的節點越多越好。包含少量資料的伺服器過載速度更快,這樣的叢集更容易復活。
第二名—“資料從consul鍵值儲存中消失”
對於服務發現,我們像許多人一樣使用 consul。但我們也將其鍵值用於整體的藍綠色佈局。它儲存有關活動和非活動上游的信息,這些資訊在部署期間會改變位置。為此,編寫了與 KV 互動的部署服務。某個時刻,KV 的數據消失了。從記憶中恢復,但有一些錯誤。結果,在上傳過程中,上游負載分配不均,並且由於後端CPU過載而導致我們收到許多502錯誤。因此,我們從 consul KV 遷移到 postgres,從那裡刪除它們不再那麼容易。
結論:
- 未經任何授權的服務不應包含對網站運作至關重要的資料。例如,如果您在 ES 中沒有授權,那麼最好在不需要的地方拒絕網路層級的訪問,只保留必要的,並設定 action.delta_requires_name: true。
- 提前練習您的備份和復原機制。例如,提前製作一個可以備份和還原的腳本(例如Python)。
第一名-《不明顯的船長》
在某些時候,我們注意到在後端有 10 多台伺服器的情況下,nginx 上游的負載分佈不均勻。由於輪詢方式是按順序從第一個上游發送請求到最後一個上游,並且每次nginx 重新加載都會重新開始,所以第一個上游總是比其他上游收到更多的請求,導致它們的工作速度變慢,整個站點受到影響。隨著流量的增加,這一點變得越來越明顯。簡單地更新 nginx 來啟用隨機是行不通的——我們需要重做一堆在 1 版本上沒有成功的 lua 程式碼(當時)。我們必須修補 nginx 1.15,引入隨機支援。這解決了問題。該錯誤贏得了“非顯而易見性隊長”類別。
結論:
探索這個錯誤是非常有趣和令人興奮的)。
- 組織您的監控,以便幫助您快速發現此類波動。例如,您可以使用ELK來監控每個upstream的每個後端的rps,從nginx的角度監控它們的回應時間。在本例中,這幫助我們識別了問題。
因此,透過更謹慎地對待所做的事情,大多數失敗都是可以避免的。我們必須永遠記住墨菲定律: 任何可能出錯的事情都會出錯 並基於它構建組件。
來源: www.habr.com