您的位置:首頁>正文

Cloudflare如何分析每秒上百萬的DNS查詢

上週五, 我們宣佈了所有Cloudflare DNS分析工具。 由於我們的規模很大(當你讀完這篇文章的時候, Cloudflare DNS將處理數以百萬計的DNS查詢) 我們必須非常有創意的解決該問題。 在本文中, 我們將介紹DNS分析工具的元件, 這些元件説明我們每月處理數以萬億計的日誌。

Cloudflare已經有一個用於HTTP日誌的資料管道。 我們希望DNS分析工具可以利用該功能。 每當邊緣服務處理一個HTTP請求時, 它就會以Cap'n Proto格式生成一個結構化的日誌消息, 並將其發送到本地多工服務。 考慮到資料量, 我們只記錄部分DNS消息資料, 只包含我們感興趣的資料, 例如響應碼,

大小或query name, 這使得我們每個消息平均只保留約150位元組資料。 然後將其與中繼資料處理(例如在查詢處理期間觸發的定時資訊和異常)融合。 在邊緣融合資料和中繼資料的好處是, 我們可以將計算成本分散到成千上萬的邊緣伺服器, 並且只記錄我們需要的資訊。

多工服務(稱為“日誌轉發器”)正在每個邊緣節點上運行, 從多個服務組裝日誌消息並將其傳輸到我們的倉庫, 以便通過TLS秘密頻道進行處理。 運行在倉庫中的對應服務將日誌接收並解分解到幾個Apache Kafka集群中。 Apache Kafka用於生產者和下游消費者之間做緩衝, 防止消費者故障或需要維護時的資料丟失。 自0.10版本以來, Kafka允許通過機架感知分配副本, 從而提高對機架或網站故障的恢復能力,

為我們提供容錯的未處理消息存儲。

擁有結構化日誌佇列使我們能夠追溯性地查問題, 而不需要訪問生產節點。 在項目的早期階段, 我們會跳過佇列並找到我們所需的粗略時間段的偏移量, 然後將資料以Parquet格式提取到HDFS中, 以供離線分析。

關於聚合

HTTP分析服務是圍繞生成聚合的流處理器構建的, 因此我們計畫利用Apache Spark將日誌自動傳輸到HDFS。 由於Parquet本身不支援索引以避免全資料表掃描, 因此線上分析或通過API提供報告是不切實際的。 雖然有像parquet-index這樣的擴展可以在資料上創建索引, 但也不能即時運行。 鑒於此, 最初的設計是僅向客戶顯示匯總報告, 並保留原始資料用以內部故障排除。

匯總摘要的問題在於, 它們只能處理基數較低(大量唯一值)的列。 通過聚合, 給定時間範圍內的每個列都會擴展到很大的行數(與唯一條目個數相等), 因此可以將響應碼(例如只有12個可能的值, 但不包含查詢名稱)進行聚合。 如果功能變數名稱受歡迎, 假設每分鐘被查詢1000次, 那麼可以預期每分鐘做聚合可以減少1000倍的資料,

然而實際上並不是這樣。

由於DNS緩存的存在, 解析器在TTL期間不會進行DNS查詢。 TTL往往超過一分鐘。 因此, 伺服器多次看到相同的請求, 而我們的資料則偏向於不可緩存的查詢, 如拼寫錯誤或隨機首碼子功能變數名稱攻擊。 在實踐中, 當用功能變數名稱進行聚合時, 我們可以看到最多可以減少為原來的1/60的行數, 而多個解析度存儲聚合幾乎可以抵消行減少。 使用多個解析度和鍵組合也可以完成聚合, 因此聚合在高基數列上甚至可以產生比原始資料更多的行。

由於這些原因, 我們首先在zone層次上匯總日誌, 這對於趨勢分析來說已經足夠, 但是對於具體原因分析來說則太過粗糙。

例如, 我們正在調查其中一個資料中心的流量短暫爆發。 具有未聚合的資料使我們能夠將問題縮小到特定DNS查詢, 然後將查詢與錯誤配置的防火牆規則相關聯。 像這樣的情況下, 只有匯總日誌就有問題, 因為它只聚合一小部分請求。

所以我們開始研究幾個OLAP系統。 我們研究的第一個系統是Druid。 我們對前端(Pivot和以前的Caravel)是如何切分資料的能力印象很深刻, 他使我們能夠生成具有任意維度的報告。 Druid已經被部署在類似的環境中, 每天超過1000億事件, 所以我們對它可以工作很有信心, 但是在對抽樣資料進行測試之後, 我們無法證明數百個節點的硬體成本。 幾乎在同一時間, Yandex開源了他們的OLAP系統ClickHouse。

ClickHouse

ClickHouse的系統設計更加簡單, 集群中的所有節點具有相同的功能, 使用ZooKeeper進行協調。我們建立了一個由幾個節點組成的集群,發現性能相當可觀,所以我們繼續構建了一個概念驗證。我們遇到的第一個障礙是缺少工具和社區規模的規模太小,所以我們鑽研了ClickHouse設計,以瞭解它是如何工作的。

ClickHouse不直接支援Kafka,因為它只是一個資料庫,所以我們使用Go寫了一個適配器服務。它讀取來自Kafka的使用Cap'n Proto編碼的消息,將它們轉換為TSV,並通過HTTP介面分批插入ClickHouse。後來,我們講ClickHouse的HTTP介面替換為GO SQL驅動,以提高性能。從那以後,我們就開始為該專案提供了性能改進。我們在性能評估過程中學到的一件事是,ClickHouse寫入性能很大程度上取決於批量的大小,即一次插入的行數。為了理解為什麼,我們需要進一步瞭解了ClickHouse如何存儲資料。

ClickHouse用於存儲的最常見的表引擎是MergeTree系列。它在概念上類似於Google的BigTable或Apache Cassandra中使用的LSM演算法,但它避免了中間記憶體表,並直接寫入磁片。這使得寫入輸送量非常出色,因為每個插入的批次只能通過“主鍵”進行排序,壓縮並寫入磁片以形成一個段。沒有記憶體表也意味著他僅僅追加資料,並且不支援資料修改或刪除。當前刪除資料的唯一方法是按日曆月份刪除資料,因為段不會與月份邊界重疊。 ClickHouse團隊正在積極致力於使這個功能可配置。另一方面,這使得寫入和段合併無衝突,因此輸送量與並行插入的數量成線性比例關係,直到I/O跑滿。但是,這也意味著它不適合小批量生產,這就是為什麼我們依靠Kafka和插入器服務進行緩衝的原因。 ClickHouse在後臺不斷合併,所以很多段將被合併和寫多次(從而增加寫放大),太多未合併的段將觸發寫入限流,直到合併完成。我們發現,每秒鐘每張表的插入一次效果最好。

表讀性能的關鍵是索引和資料在磁片上的排列。無論處理速度有多快,當引擎需要從磁片掃描太多資料時,這都需要大量時間。ClickHouse是一個列式存儲,因此每個段都包含每個列的檔,每行都有排序值。通過這種方式,可以跳過查詢中不存在的列,然後可以通過向量化執行並行處理多個單元。為了避免完整的掃描,每個段也有一個稀疏的索引檔。鑒於所有列都按“主鍵”排序,索引檔僅包含每第N行的標記(捕獲行),以便即使對於非常大的表也可以將其保留在記憶體中。例如,默認設置是每隔8192行做一個標記。這種方式只需要122,070個標記來具有1萬億行的表格進行索引。在這裡可以查看ClickHouse中的主鍵,深入瞭解它的工作原理。

使用主鍵列查詢時,索引返回考慮行的大致範圍。理想情況下,範圍應該是寬而連續的。例如,當典型用法是為單個區域生成報告時,將區域放在主鍵的第一個位置將導致按每個區域進行排序,使得磁片讀取單個區域連續,而如果按主要時間戳記排序則在生成報告是無法保證連續。行只能以一種方式排序,因此必須仔細選擇主鍵,並考慮典型的查詢負載。在我們的例子中,我們優化了單個區域的讀取查詢,並為探索性查詢提供了一個帶有採樣資料的獨立表格。從中吸取的教訓是,我們不是試圖為了各種目的而優化索引,而是分解差異並多加一些表。

這樣專有化設計的結果就是在區域上聚合的表格。由於沒有辦法過濾資料,因此掃描所有行的查詢都要昂貴得多。這使得分析師在長時間計算基本聚合時不那麼實際,所以我們決定使用物化視圖來增量計算預定義聚合,例如計數器,唯一鍵和分位數。物化視圖利用批量插入的排序階段來執行生產性工作 - 計算聚合。因此,在新插入的段被排序之後,它也會生成一個表格,其中的行代表維度,而列代表彙總函式狀態。聚合狀態和最終結果之間的區別在於,我們可以使用任意時間解析度生成報告,而無需實際存儲多個解析度的預計算數據。在某些情況下,狀態和結果可能是相同的 - 例如基本計數器,每小時計數可以通過累計每分鐘計數來產生,但是對獨特的訪問者或延遲分位數求和是沒有意義的。這是聚合狀態更有用的時候,因為它允許有意義地合併更複雜的狀態,如HyperLogLog(HLL)點陣圖,以便每小時聚合生成每小時獨立訪問者估計值。缺點是存儲狀態可能比存儲最終值要昂貴的多 - 上述HLL狀態在壓縮時大概有20-100位元組/行,而計數器只有8位元組(平均壓縮1個位元組)。使用這些表可以快速地將整個區域或網站的總體趨勢形象化, 並且我們的 API 服務也使用它們做簡單查詢。在同一位置同時使用增量聚合和沒有聚合的資料, 我們可以通過流處理完全簡化體系結構。

基礎設施和資料整合

我們使用12個6TB磁片做RAID-10,但在一次磁片故障之後重新進行了評估。在第二次反覆運算中,我們遷移到了RAID-0,原因有兩個。首先,不能熱插拔有故障的磁片,其次陣列重建花費了數十個小時,這降低了I/O性能。更換故障節點並使用內部複製通過網路(2x10GbE)填充資料比等待陣列完成重建要快得多。為了彌補節點故障的可能性較高,我們切換到3路複製,並將每個分片的副本分配到不同的機架,並開始規劃複製到單獨的資料倉庫。

另一個磁片故障突出了我們使用的檔案系統的問題。最初我們使用XFS,但它在複製過程中開始在同一時間從2個對等點進行鎖定, 從而在完成之前中斷了段複製。這個問題表現為大量I/O活動,由於損壞的部分被刪除,磁片使用量增加很少,所以我們逐漸遷移到了ext4,該檔案系統就沒有這個問題。

資料視覺化

當時我們只依靠Pandas和ClickHouse的HTTP介面進行臨時分析,但是我們希望使分析和監控更容易。 因為我們知道Caravel(現在更名為Superset ),我們就把它和ClickHouse進行了整合。

Superset是一個直觀的資料視覺化平臺,它允許分析人員在不寫一行SQL的情況下交互地切片和切分資料。 它最初是由AirBnB為Druid構建和開源的,但是隨著時間的推移,它已經通過使用SQLAlchemy(一種抽象和ORM)為數十種不同的資料庫方言提供了基於SQL的後端的支援。 所以我們編寫並開源了一個ClickHouse方言,並集成到Superset。

Superset為我們提供了特別的視覺化服務,但是對於我們的監控用例來說,它仍然不夠完善。 在Cloudflare,我們大量使用Grafana來視覺化所有指標,所以我們將它與Grafana集成並進行開源。

它使我們能夠用新的分析資料無縫地擴展我們現有的監測儀錶板。 我們非常喜歡這個功能,因此我們希望能夠為用戶提供同樣的能力來查看分析資料。 因此,我們構建了一個Grafana應用程式,以便視覺化來自Cloudflare DNS Analytics的資料。 最後,我們在您的Cloudflare儀錶板分析中提供了它。 隨著時間的推移,我們將添加新的資料來源,維度和其他有用的方法來顯示Cloudflare中的資料。

使用ZooKeeper進行協調。我們建立了一個由幾個節點組成的集群,發現性能相當可觀,所以我們繼續構建了一個概念驗證。我們遇到的第一個障礙是缺少工具和社區規模的規模太小,所以我們鑽研了ClickHouse設計,以瞭解它是如何工作的。

ClickHouse不直接支援Kafka,因為它只是一個資料庫,所以我們使用Go寫了一個適配器服務。它讀取來自Kafka的使用Cap'n Proto編碼的消息,將它們轉換為TSV,並通過HTTP介面分批插入ClickHouse。後來,我們講ClickHouse的HTTP介面替換為GO SQL驅動,以提高性能。從那以後,我們就開始為該專案提供了性能改進。我們在性能評估過程中學到的一件事是,ClickHouse寫入性能很大程度上取決於批量的大小,即一次插入的行數。為了理解為什麼,我們需要進一步瞭解了ClickHouse如何存儲資料。

ClickHouse用於存儲的最常見的表引擎是MergeTree系列。它在概念上類似於Google的BigTable或Apache Cassandra中使用的LSM演算法,但它避免了中間記憶體表,並直接寫入磁片。這使得寫入輸送量非常出色,因為每個插入的批次只能通過“主鍵”進行排序,壓縮並寫入磁片以形成一個段。沒有記憶體表也意味著他僅僅追加資料,並且不支援資料修改或刪除。當前刪除資料的唯一方法是按日曆月份刪除資料,因為段不會與月份邊界重疊。 ClickHouse團隊正在積極致力於使這個功能可配置。另一方面,這使得寫入和段合併無衝突,因此輸送量與並行插入的數量成線性比例關係,直到I/O跑滿。但是,這也意味著它不適合小批量生產,這就是為什麼我們依靠Kafka和插入器服務進行緩衝的原因。 ClickHouse在後臺不斷合併,所以很多段將被合併和寫多次(從而增加寫放大),太多未合併的段將觸發寫入限流,直到合併完成。我們發現,每秒鐘每張表的插入一次效果最好。

表讀性能的關鍵是索引和資料在磁片上的排列。無論處理速度有多快,當引擎需要從磁片掃描太多資料時,這都需要大量時間。ClickHouse是一個列式存儲,因此每個段都包含每個列的檔,每行都有排序值。通過這種方式,可以跳過查詢中不存在的列,然後可以通過向量化執行並行處理多個單元。為了避免完整的掃描,每個段也有一個稀疏的索引檔。鑒於所有列都按“主鍵”排序,索引檔僅包含每第N行的標記(捕獲行),以便即使對於非常大的表也可以將其保留在記憶體中。例如,默認設置是每隔8192行做一個標記。這種方式只需要122,070個標記來具有1萬億行的表格進行索引。在這裡可以查看ClickHouse中的主鍵,深入瞭解它的工作原理。

使用主鍵列查詢時,索引返回考慮行的大致範圍。理想情況下,範圍應該是寬而連續的。例如,當典型用法是為單個區域生成報告時,將區域放在主鍵的第一個位置將導致按每個區域進行排序,使得磁片讀取單個區域連續,而如果按主要時間戳記排序則在生成報告是無法保證連續。行只能以一種方式排序,因此必須仔細選擇主鍵,並考慮典型的查詢負載。在我們的例子中,我們優化了單個區域的讀取查詢,並為探索性查詢提供了一個帶有採樣資料的獨立表格。從中吸取的教訓是,我們不是試圖為了各種目的而優化索引,而是分解差異並多加一些表。

這樣專有化設計的結果就是在區域上聚合的表格。由於沒有辦法過濾資料,因此掃描所有行的查詢都要昂貴得多。這使得分析師在長時間計算基本聚合時不那麼實際,所以我們決定使用物化視圖來增量計算預定義聚合,例如計數器,唯一鍵和分位數。物化視圖利用批量插入的排序階段來執行生產性工作 - 計算聚合。因此,在新插入的段被排序之後,它也會生成一個表格,其中的行代表維度,而列代表彙總函式狀態。聚合狀態和最終結果之間的區別在於,我們可以使用任意時間解析度生成報告,而無需實際存儲多個解析度的預計算數據。在某些情況下,狀態和結果可能是相同的 - 例如基本計數器,每小時計數可以通過累計每分鐘計數來產生,但是對獨特的訪問者或延遲分位數求和是沒有意義的。這是聚合狀態更有用的時候,因為它允許有意義地合併更複雜的狀態,如HyperLogLog(HLL)點陣圖,以便每小時聚合生成每小時獨立訪問者估計值。缺點是存儲狀態可能比存儲最終值要昂貴的多 - 上述HLL狀態在壓縮時大概有20-100位元組/行,而計數器只有8位元組(平均壓縮1個位元組)。使用這些表可以快速地將整個區域或網站的總體趨勢形象化, 並且我們的 API 服務也使用它們做簡單查詢。在同一位置同時使用增量聚合和沒有聚合的資料, 我們可以通過流處理完全簡化體系結構。

基礎設施和資料整合

我們使用12個6TB磁片做RAID-10,但在一次磁片故障之後重新進行了評估。在第二次反覆運算中,我們遷移到了RAID-0,原因有兩個。首先,不能熱插拔有故障的磁片,其次陣列重建花費了數十個小時,這降低了I/O性能。更換故障節點並使用內部複製通過網路(2x10GbE)填充資料比等待陣列完成重建要快得多。為了彌補節點故障的可能性較高,我們切換到3路複製,並將每個分片的副本分配到不同的機架,並開始規劃複製到單獨的資料倉庫。

另一個磁片故障突出了我們使用的檔案系統的問題。最初我們使用XFS,但它在複製過程中開始在同一時間從2個對等點進行鎖定, 從而在完成之前中斷了段複製。這個問題表現為大量I/O活動,由於損壞的部分被刪除,磁片使用量增加很少,所以我們逐漸遷移到了ext4,該檔案系統就沒有這個問題。

資料視覺化

當時我們只依靠Pandas和ClickHouse的HTTP介面進行臨時分析,但是我們希望使分析和監控更容易。 因為我們知道Caravel(現在更名為Superset ),我們就把它和ClickHouse進行了整合。

Superset是一個直觀的資料視覺化平臺,它允許分析人員在不寫一行SQL的情況下交互地切片和切分資料。 它最初是由AirBnB為Druid構建和開源的,但是隨著時間的推移,它已經通過使用SQLAlchemy(一種抽象和ORM)為數十種不同的資料庫方言提供了基於SQL的後端的支援。 所以我們編寫並開源了一個ClickHouse方言,並集成到Superset。

Superset為我們提供了特別的視覺化服務,但是對於我們的監控用例來說,它仍然不夠完善。 在Cloudflare,我們大量使用Grafana來視覺化所有指標,所以我們將它與Grafana集成並進行開源。

它使我們能夠用新的分析資料無縫地擴展我們現有的監測儀錶板。 我們非常喜歡這個功能,因此我們希望能夠為用戶提供同樣的能力來查看分析資料。 因此,我們構建了一個Grafana應用程式,以便視覺化來自Cloudflare DNS Analytics的資料。 最後,我們在您的Cloudflare儀錶板分析中提供了它。 隨著時間的推移,我們將添加新的資料來源,維度和其他有用的方法來顯示Cloudflare中的資料。

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示