├── cfg.example.json ├── g ├── g.go ├── rpc.go └── cfg.go ├── .gitignore ├── main.go ├── README.md └── cron └── cron.go /cfg.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug": true, 3 | "localIp": "", 4 | "servers": [ 5 | "10.1.2.3:1970", 6 | "10.1.2.4:1970" 7 | ], 8 | "interval": 5, 9 | "timeout": 1000, 10 | "docker": "http://127.0.0.1:2375" 11 | } 12 | -------------------------------------------------------------------------------- /g/g.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "log" 5 | "runtime" 6 | ) 7 | 8 | const ( 9 | VERSION = "1.0.0" 10 | ) 11 | 12 | func init() { 13 | runtime.GOMAXPROCS(runtime.NumCPU()) 14 | log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) 15 | } 16 | -------------------------------------------------------------------------------- /g/rpc.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "github.com/dinp/common/rpc" 5 | "time" 6 | ) 7 | 8 | var ( 9 | RpcClient *rpc.SingleConnRpcClient 10 | ) 11 | 12 | func InitRpcClient() { 13 | RpcClient = &rpc.SingleConnRpcClient{ 14 | RpcServers: Config().Servers, 15 | Timeout: time.Duration(Config().Timeout) * time.Millisecond, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | *.swp 27 | *.swo 28 | .DS_Store 29 | /agent 30 | /paas-agent 31 | /dinp-agent 32 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/dinp/agent/cron" 7 | "github.com/dinp/agent/g" 8 | "os" 9 | ) 10 | 11 | func main() { 12 | cfg := flag.String("c", "cfg.json", "configuration file") 13 | version := flag.Bool("v", false, "show version") 14 | flag.Parse() 15 | 16 | if *version { 17 | fmt.Println(g.VERSION) 18 | os.Exit(0) 19 | } 20 | 21 | g.ParseConfig(*cfg) 22 | g.InitRpcClient() 23 | 24 | cron.Heartbeat() 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Agent 2 | ========== 3 | 4 | 这个模块部署在资源池的所有节点机器上,主要用于收集数据做汇报 5 | 6 | 收集的数据分成两部分 7 | 8 | - 机器剩余内存,调度模块拿到这个信息之后才能做调度 9 | - container情况,这是读取的Docker Daemon的接口,list所有container 10 | 11 | 对于container这块,目前只是拿到了本机的container列表,得知这些container的PublicPort,汇报给server,server把路由信息写入redis中的路由表 12 | 13 | #### Q1:如何得知某个container是哪个app的呢? 14 | 15 | 刚开始的做法是把app的名称写入image的url中,container本身是可以知道使用了哪个image创建的,这样就知道了app与container的对 16 | 应关系。但是这个规范比较强,太具侵入性 17 | 18 | 现在的做法是把app的名称写入ENV,在创建container的时候写入,之后再通过inspect拿到ENV["APP\_NAME"] 19 | 20 | ## 注意: 21 | 22 | - agent会把本机的ip汇报给server,server用此构建路由表,那本机的ip是如何获取的呢?多个ip的情况怎么办?现在的做法是只拿内网ip,然后过滤掉回环ip,只要网卡名称使用eth打头的,过滤掉Docker创建的虚拟网卡,如果用户配置了localIp,就直接使用用户配置的localIp 23 | - agent是个前台进程,真正线上部署的时候可以使用god或者supervisor之类的管理 24 | 25 | ## 配置项说明 26 | 27 | - **debug**: true/false 只影响打印的log 28 | - **localIp**: 本机Ip地址,server会用这个ip地址和docker daemon通信 29 | - **servers**: server的地址 30 | - **interval**: 心跳周期,单位是秒 31 | - **timeout**: 连接server的超时时间,单位是毫秒 32 | - **docker**: Docker Daemon的接口地址,推荐Docker Daemon监听一个127.0.0.1的tcp接口,unix socket也可以,不过要注意文件权限了 33 | 34 | ## install 35 | 36 | ``` 37 | mkdir -p $GOPATH/src/github.com/dinp 38 | cd $GOPATH/src/github.com/dinp; git clone https://github.com/dinp/agent.git 39 | cd agent 40 | go get ./... 41 | 42 | # check cfg.json, depend docker daemon and server 43 | ./control start 44 | ``` 45 | 46 | -------------------------------------------------------------------------------- /g/cfg.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/toolkits/file" 6 | "github.com/toolkits/net" 7 | "log" 8 | "sync" 9 | ) 10 | 11 | type GlobalConfig struct { 12 | Debug bool `json:"debug"` 13 | LocalIp string `json:"localIp"` 14 | Servers []string `json:"servers"` 15 | Interval int `json:"interval"` 16 | Timeout int `json:"timeout"` 17 | Docker string `json:"docker"` 18 | } 19 | 20 | var ( 21 | ConfigFile string 22 | config *GlobalConfig 23 | configLock = new(sync.RWMutex) 24 | ) 25 | 26 | func Config() *GlobalConfig { 27 | configLock.RLock() 28 | defer configLock.RUnlock() 29 | return config 30 | } 31 | 32 | func ParseConfig(cfg string) { 33 | if cfg == "" { 34 | log.Fatalln("use -c to specify configuration file") 35 | } 36 | 37 | if !file.IsExist(cfg) { 38 | log.Fatalln("config file:", cfg, "is not existent") 39 | } 40 | 41 | ConfigFile = cfg 42 | 43 | configContent, err := file.ToTrimString(cfg) 44 | if err != nil { 45 | log.Fatalln("read config file:", cfg, "fail:", err) 46 | } 47 | 48 | var c GlobalConfig 49 | err = json.Unmarshal([]byte(configContent), &c) 50 | if err != nil { 51 | log.Fatalln("parse config file:", cfg, "fail:", err) 52 | } 53 | 54 | if c.LocalIp == "" { 55 | // detect local ip 56 | localIps, err := net.IntranetIP() 57 | if err != nil { 58 | log.Fatalln("get intranet ip fail:", err) 59 | } 60 | 61 | if len(localIps) == 0 { 62 | log.Fatalln("no intranet ip found") 63 | } 64 | 65 | c.LocalIp = localIps[0] 66 | } 67 | 68 | configLock.Lock() 69 | defer configLock.Unlock() 70 | 71 | config = &c 72 | 73 | if config.Debug { 74 | log.Println("read config file:", cfg, "successfully") 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /cron/cron.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "github.com/dinp/agent/g" 5 | "github.com/dinp/common/dock" 6 | "github.com/dinp/common/model" 7 | "github.com/toolkits/nux" 8 | "log" 9 | "time" 10 | ) 11 | 12 | func Heartbeat() { 13 | duration := time.Duration(g.Config().Interval) * time.Second 14 | for { 15 | heartbeat(duration) 16 | time.Sleep(duration) 17 | } 18 | } 19 | 20 | func heartbeat(duration time.Duration) { 21 | mem, err := nux.MemInfo() 22 | if err != nil { 23 | log.Println("[ERROR] get meminfo:", err) 24 | return 25 | } 26 | 27 | // use MB 28 | memFree := (mem.MemFree + mem.Buffers + mem.Cached) / 1024 / 1024 29 | 30 | localIp := g.Config().LocalIp 31 | 32 | containers, err := dock.Containers(g.Config().Docker) 33 | if err != nil { 34 | log.Println("[ERROR] list containers fail:", err) 35 | 36 | // this node dead 37 | var resp model.NodeResponse 38 | err = g.RpcClient.Call("NodeState.NodeDown", localIp, &resp) 39 | if err != nil || resp.Code != 0 { 40 | log.Println("[ERROR] call rpc: NodeState.NodeDown fail:", err, "resp:", resp) 41 | } else if g.Config().Debug { 42 | log.Println("[INFO] call rpc: NodeState.NodeDown successfully. I am dead...") 43 | } 44 | 45 | for { 46 | time.Sleep(duration) 47 | containers, err = dock.Containers(g.Config().Docker) 48 | if err == nil { 49 | break 50 | } else { 51 | log.Println("[ERROR] list containers fail:", err) 52 | } 53 | } 54 | } 55 | 56 | req := model.NodeRequest{ 57 | Node: model.Node{ 58 | Ip: localIp, 59 | MemFree: memFree, 60 | }, 61 | Containers: containers, 62 | } 63 | var resp model.NodeResponse 64 | err = g.RpcClient.Call("NodeState.Push", req, &resp) 65 | if err != nil || resp.Code != 0 { 66 | log.Println("[ERROR] call rpc: NodeState.Push fail:", err, "resp:", resp) 67 | } else if g.Config().Debug { 68 | log.Println("[DEBUG] =>", req) 69 | } 70 | } 71 | --------------------------------------------------------------------------------