使用 golang gopacket 實現(xiàn)進程級流量監(jiān)控
如何分析某服務(wù)器上哪個進程的流量多 ? 猜不少人都有類似的需求, 社區(qū)里已經(jīng)有 c 寫的 nethogs 工具, 該工具很方便的獲取流量較高的 TOP N 進程列表。
既然有 nethogs 工具,為什么還需要用 goalng 來實現(xiàn)一遍 ?主要是 nethogs 不夠靈活,沒有開放接口供其他程序調(diào)用。對的,我們現(xiàn)在就需要這類接口。當(dāng)然,不是每個公司都有這類需求,大家通常都是當(dāng)流量異常時,登上去分析下異常流量。
代碼
項目名為 go-netflow, 代碼已經(jīng)提交到 github。
地址為 https://github.com/rfyiamcool/go-netflow,有興趣的可以看看,記得點 star 哈。
從設(shè)計到流量測試完畢,用了差不多 兩天半 的時間。go-netflow 的接口方面還算完善,最少滿足了我們?nèi)萜髟茍F隊及 sre 的需求。但他的 cli 終端部分很是簡陋,有興趣的同學(xué)可以幫忙提交 pr。
實現(xiàn)
像 promethues, openfalcon 這類開源監(jiān)控軟件,通過 /proc/net/snmp 和 /proc/net/netstat 獲取連接和流量信息。
/proc/net/snmp文件只是提供了主機各層的IP、ICMP、ICMPMsg、TCP、UDP詳細(xì)數(shù)據(jù)/proc/net/netstat文件提供了主機的收發(fā)包數(shù)、收包字節(jié)數(shù)據(jù)。
這兩個文件只能看到主機級別的信息,沒法實現(xiàn)進程級別的流量采集。
但是我們可以從其他路子來實現(xiàn)細(xì)粒度的進程級別的流量監(jiān)控。??
通過 /proc/net/tcp ,/proc/net/udp 文件,我們可以拿到 tcp及udp 的四元組和 inode 信息。通過 /proc/{pid}/fd/ 可以拿到 pid 及 socket inode文件描述符的映射關(guān)系。
那么怎么流量從哪里獲取?抓包,類似 tcpdump 這類使用 pcap 來抓包。
這里詳細(xì)的說下,基于 pcap 的進程流量統(tǒng)計的實現(xiàn).
- 啟動階段掃描
/proc/net/tcp和/proc/{pid}/fd/{socket inode}。構(gòu)建三個緩存hashmap
- connInodesMap, 定義了 tcp 連接和 inode 號 的對應(yīng)關(guān)系。
- inodePidMap, 定義了 inode 和 進程 pid 號 的對應(yīng)關(guān)系。
- processMap,存放了進程的基本信息, 最近15秒的流量緩存及流量統(tǒng)計結(jié)果。
地址:源端口_目標(biāo)地址:目標(biāo)端口從 connInodeMap 里獲取 inode 索引號, 再通過 inode 到 inodePidMap 獲取 pid 進程號,再用 pid 到 processMap 里獲取進程對象,然后進行進出流量的累加計算。上面說的正常的流程,如果在 pcap 捕獲流量時期,有新的連接和新的進程創(chuàng)建了,那么就會拿不到對應(yīng)的 inode 或者 pid 信息。所以,需要定時重新掃描那幾個元數(shù)據(jù)文件,然后對這幾個緩存 map 進行增減處理。把暫未找到進程的 packet ,放到一個 ringbuffer 緩存里,每次定時掃描完 /proc 元數(shù)據(jù)后,再嘗試計算 ringbuffer 里的緩存。
注意的幾個點:
/proc 的那個幾個元數(shù)據(jù)不能使用 inotify 來監(jiān)聽,只能是定時掃描或者利用你通知來被動更新。udp也是同理,這里就不復(fù)述了。
如何判斷是進出流量?
如果 src.ip 是本地的ip地址,那么就是出流量,src.ip 不是本地ip地址,那么肯定就是進流量了。go-netflow 在啟動階段就需要獲取本地的ip地址列表。
如何監(jiān)聽多個網(wǎng)卡
google golang gopacket 庫默認(rèn)只能 openlive 一個網(wǎng)卡,沒找到可以 openlive 多個設(shè)備網(wǎng)卡的相關(guān)方法。平時我們用 tcpdump 抓包時,可以通過 -i any 匹配多個網(wǎng)卡。那么在 gopacket 如何實現(xiàn)多個設(shè)備網(wǎng)卡監(jiān)聽?實例化多個 openlive 實例不就行了,我居然還還提了 issue 詢問該問題,當(dāng)然沒人回復(fù)我了。
如何限定 cpu/mem 占用資源
tcpdump 在大流量網(wǎng)關(guān)下是相當(dāng)消耗 cpu 資源的,netflow 里做了一些簡單的限制,不是同步處理全量的包,而是扔到隊列中異步處理,如果隊列滿了,則直接丟棄。通過簡單的 atomic 計數(shù)器來進行限制處理的包數(shù)。
但難免還是有意外造成 cpu 資源大量占用,如何限定 cpu 資源?
這里直接使用 linux cgroups 來限定cpu及mem資源,new netflow 對象時可以傳入 cgroups 的參數(shù)。cpu為核數(shù),可以為 0.5 ,也就是僅占用一個 cpu core 的 50% 資源。mem 是限定的內(nèi)存量,單位是 MB,默認(rèn)為0,及為不限制。
WithLimitCgroup(cpu?float64,?mem?int)
寫入pcap文件
netflow支持pcap文件的寫入,后面可通過 tcpdump 或 wireshark 來讀取 netflow 寫入的pcap文件。
超時機制
netflow 限定了默認(rèn)超時時間為 5 分鐘,當(dāng)超過 5 分鐘后會關(guān)閉所有設(shè)備的監(jiān)聽。這么做主要為了避免長時間運行忘了關(guān)閉,尤其是通過 netflow 接口來進行抓包的。
效果
img優(yōu)化
go-netflow 還是有一些優(yōu)化空間的, 內(nèi)存方面可以針對一些對象做復(fù)用, 使用 sync.pool 減少新對象的創(chuàng)建。CPU的開銷主要在 google gopacket 調(diào)用上, cgo 的調(diào)用一點也不便宜, 暫時沒有好的方法來優(yōu)化。另外, 進程的流量監(jiān)控?zé)o需太細(xì)致,粗粒度采樣足夠了。
大家覺得文章對你有些作用!如果想賞錢,可以用微信掃描下面的二維碼,感謝! 另外再次標(biāo)注博客原地址 ?xiaorui.cc

