Automatic dependency discovery and mapping
An idea from PUBG Hack
最近周末和朋友经常吃鸡(一款PC游戏),游戏过程中经常被外挂虐,但是大部分外挂都是读内存,听朋友说游戏过程中传输的UDP包是没有加密的,于是用golang写了个demo抓取游戏过程中的本机的UDP数据包,不过最终只拿到了自己的位置,了解了之后,也没有再继续研究下去。后来想了想sniffer这个东西能不能用在监控系统上呢
Automatic dependency discovery
- 当一台服务器出现宕机,你能立刻知道这台及其可能会波及哪些服务么
- 当你复杂的业务出现异常时,你并没有做任何调整,你如何快速确定在你调用的众多接口中,是哪一个出了问题
很多公司都在做服务间的依赖关系来解决这些问题,OpenTracing
做为一种主动上报关联的标准(作用不仅于此),在一定程度上主动解决了服务依赖的问题,但是主动去发现服务依赖存在一定缺陷,首先要依赖人准确毫无遗漏的在代码中提前植入相应log或则tag,涉及到部门之间的合作,语言的不同,将OpenTracing推到所有的业务中存在一定阻力,当然这里仍然不排除OpenTracing是个非常好的标准。
有没有一种非常好的方法无侵入的,单方面的把这个问题解决掉呢,嗯,可以尝试在系统层面sniffer抓包。我们基于底层系统进行四层协议抽样抓包,统计去重就可以找到所有与这台机器交互过后的目的IP和源IP。说到抓包就不得不提及 libpcap
这个库了,tcpdump
和 wireshark
等软件都是依赖于此库,当然我们也不例外,另外由于我们不是用c直接写的agent,所以还要调用google
的一个golang的lib, https://github.com/google/gopacket
下面是一个简单的demo:
package main
import (
"fmt"
"log"
"github.com/google/gopacket/pcap"
)
func main() {
// Find all devices
devices, err := pcap.FindAllDevs()
if err != nil {
log.Fatal(err)
}
// Print device information
fmt.Println("Devices found:")
for _, device := range devices {
fmt.Println("\nName: ", device.Name)
fmt.Println("Description: ", device.Description)
fmt.Println("Devices addresses: ", device.Description)
for _, address := range device.Addresses {
fmt.Println("- IP address: ", address.IP)
fmt.Println("- Subnet mask: ", address.Netmask)
}
}
}
Open Device for Live Capture
package main
import (
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/pcap"
"log"
"time"
)
var (
device string = "eth0"
snapshot_len int32 = 1024
promiscuous bool = false
err error
timeout time.Duration = 30 * time.Second
handle *pcap.Handle
)
func main() {
// Open device
handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
if err != nil {log.Fatal(err) }
defer handle.Close()
// Use the handle as a packet source to process all packets
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {
// Process packet here
fmt.Println(packet)
}
}
Data
考虑搭配抓包操作带来的性能消耗是非常大的,尤其会大量占用内存和io资源,所以寻找一个好的抽样策略非常重要,我们最终选择每5分钟随机上报30条不重复的数据,如果1min内并没有采集够(服务器可能没有什么请求),无论多少,直接上报。
基于时间序列的存储,最终上报的数据格式如下:
name: pcap.ipv4
time DstIP SrcIP host interface ip value
---- ----- ----- ---- --------- -- -----
1515134229000000000 10.10.1.220 10.10.2.142 game_test_webdb142v2_taiji eth0 10.10.2.142 1
1515134229000000000 10.10.40.131 10.10.2.142 game_test_webdb142v2_taiji eth0 10.10.2.142 2
1515134229000000000 10.10.40.250 10.10.2.142 game_test_webdb142v2_taiji eth0 10.10.2.142 3
1515134229000000000 10.10.2.131 10.10.2.142 game_test_webdb142v2_taiji eth0 10.10.2.142 1
1515134229000000000 10.10.2.142 10.10.1.220 game_test_webdb142v2_taiji eth0 10.10.2.142 1
1515134229000000000 10.10.2.142 10.10.40.131 game_test_webdb142v2_taiji eth0 10.10.2.142 1
1515134229000000000 10.10.2.142 10.10.40.250 game_test_webdb142v2_taiji eth0 10.10.2.142 2
1515134229000000000 10.10.2.142 10.10.2.131 game_test_webdb142v2_taiji eth0 10.10.2.142 2
1515134229000000000 10.10.2.142 10.10.2.145 game_test_webdb142v2_taiji eth0 10.10.2.142 1
1515134229000000000 10.10.2.142 10.10.1.244 game_test_webdb142v2_taiji eth0 10.10.2.142 1
mapping
经过两个多星期的不断测试和调优,在FE的努力下,最终上线了, 离圆心越近说明交互越多,权重越大。
改进和展望
其实通过sniffer可以做的东西非常多,后面可能直接把端口号抓住来,关联进程,进而可以确定服务。另外可以把本服务器所有的DNS请求记录下来,可以知道该服务器依赖了多少域名提供服务,方便后期排查问题。总之,可以做的还很多,你有什么想法没?