golang实现对docker容器心跳监控功能
自己写的go程序放到线上本来编译成二进制扔上去就行啦,但是怀着一颗docker的心,最终还是将它放到docker容器中运行起来了,运行起来也ok,一个最小容器64M,统一管理起来也方便,但是毕竟是个线上长驻内存的服务程序,万一跑挂了怎么办,如何才能监控它,直接上go代码,网上代码,略微做了下注释,但实测过,真实有效:
packagemain import( "encoding/json" "errors" "flag" "fmt" "io/ioutil" "log" "net" "os" "strings" "time" ) //镜像结构 typeImagestruct{ Createduint64 Idstring ParentIdstring RepoTags[]string Sizeuint64 VirtualSizeuint64 } //容器结构 typeContainerstruct{ Idstring`json:"Id"` Names[]string`json:"Names"` Imagestring`json:"Image"` ImageIDstring`json:"ImageID"` Commandstring`json:"Command"` Createduint64`json:"Created"` Statestring`json:"State"` Statusstring`json:"Status"` Ports[]Port`json:"Ports"` Labelsmap[string]string`json:"Labels"` HostConfigmap[string]string`json:"HostConfig"` NetworkSettingsmap[string]interface{}`json:"NetworkSettings"` Mounts[]Mount`json:"Mounts"` } //docker端口映射 typePortstruct{ IPstring`json:"IP"` PrivatePortint`json:"PrivatePort"` PublicPortint`json:"PublicPort"` Typestring`json:"Type"` } //docker挂载 typeMountstruct{ Typestring`json:"Type"` Sourcestring`json:"Source"` Destinationstring`json:"Destination"` Modestring`json:"Mode"` RWbool`json:"RW"` Propatationstring`json:"Propagation"` } //连接列表 varSockAddr="/var/run//docker.sock"//这可不是随便写的,是docker官网文档的套接字默认值,当然守护进程通讯方式还有tcp,fd等方式,各自都有适用场景。。。 varimagesSock="GET/images/jsonHTTP/1.0\r\n\r\n"//docker对外的镜像api操作 varcontainerSock="GET/containers/json?all=trueHTTP/1.0\r\n\r\n"//docker对外的容器查看api varstartContainerSock="POST/containers/%s/startHTTP/1.0\r\n\r\n"//docker对外的容器启动api //白名单 varwhiteList[]string funcmain(){ //读取命令行参数 //白名单列表 list:=flag.String("list","","dockerwhitelisttorestart,eg:token,explorer") //轮询的时间间隔,单位秒 times:=flag.Int64("time",10,"timeintervaltosetreaddockercontainers[second],defaultis10second") flag.Parse() //解析list=>whiteList whiteList=strings.Split(*list,",")//将我们命令行中list参数的容器列表解析到代码中 log.SetOutput(os.Stdout) log.Println("startdockerwatching...") log.Printf("YourwhiteList:%v\n",*list) log.Printf("Yoursheduletimes:%ds\n",*times) //接下来的这个for循环就是每隔一定时间监控docker容器是否正常运行,不正常就重新启动它 for{ //轮询docker err:=listenDocker() iferr!=nil{ log.Println(err.Error()) } time.Sleep(time.Duration(*times)*time.Second) } } funclistenDocker()error{ //获取容器列表,拿到所有的容器信息 containers,err:=readContainer() iferr!=nil{ returnerrors.New("readcontainererror:"+err.Error()) } //先遍历白名单快,次数少 for_,name:=rangewhiteList{ Name: for_,container:=rangecontainers{ for_,cname:=rangecontainer.Names{ //如果匹配到白名单 ifcname[1:]==name{ //关心一下容器状态 log.Printf("id=%s,name=%s,state=%s",container.Id[:12],container.Names,container.Status) ifstrings.Contains(container.Status,"Exited"){ //如果出现异常退出的容器,启动它 log.Printf("findcontainer:[%s]hasexited,readytostartit.",name) e:=startContainer(container.Id) ife!=nil{ log.Println("startcontainererror:",e.Error()) } breakName } } } } } returnnil } //获取unixsock连接 funcconnectDocker()(*net.UnixConn,error){ addr:=net.UnixAddr{SockAddr,"unix"}//SockAddr这个变量的值被设定为docker的/var/run/docker套接字路径值,也就是说此处就是拨通与docker的daemon通讯建立的关键处,其他处的代码就是些正常的逻辑处理了 returnnet.DialUnix("unix",nil,&addr) } //启动容器 funcstartContainer(idstring)error{ conn,err:=connectDocker() iferr!=nil{ returnerrors.New("connecterror:"+err.Error()) } start:=fmt.Sprintf(startContainerSock,id) fmt.Println(start) cmd:=[]byte(start) code,err:=conn.Write(cmd) iferr!=nil{ returnerr } log.Println("startcontainerresponsecode:",code) //启动容器等待20秒,防止数据重发 time.Sleep(20*time.Second) returnnil } //获取容器列表 funcreadContainer()([]Container,error){ conn,err:=connectDocker()//建立一个unix连接,这其实是一个关键点,需要你了解unix套接字建立连接 iferr!=nil{ returnnil,errors.New("connecterror:"+err.Error()) } _,err=conn.Write([]byte(containerSock)) iferr!=nil{ returnnil,err } result,err:=ioutil.ReadAll(conn) iferr!=nil{ returnnil,err } body:=getBody(result) varcontainers[]Container err=json.Unmarshal(body,&containers) iferr!=nil{ returnnil,err } log.Println("lenofcontainers:",len(containers)) iflen(containers)==0{ returnnil,errors.New("nocontainers") } returncontainers,nil } //获取镜像列表 funcreadImage(conn*net.UnixConn)([]Image,error){ _,err:=conn.Write([]byte(imagesSock)) iferr!=nil{ returnnil,err } result,err:=ioutil.ReadAll(conn) iferr!=nil{ returnnil,err } body:=getBody(result[:]) varimages[]Image err=json.Unmarshal(body,&images) iferr!=nil{ returnnil,err } returnimages,nil } //从返回的http响应中提取body funcgetBody(result[]byte)(body[]byte){ fori:=0;i<=len(result)-4;i++{ ifresult[i]==13&&result[i+1]==10&&result[i+2]==13&&result[i+3]==10{ body=result[i+4:] break } } return } /* errorlog: 1、writeunix@->/var/run/docker.sock:write:brokenpipe 建立的tcp连接不能复用,每次操作都建立连接 */
使用方法
1.编译
gobuild-omainmain.go
2.linux下直接当可执行文件执行便可
./main-list="容器名称1,容器名称2..."
思路分析:
原来docker这个软件对外是提供了一些列api用来管理容器的增删该查的 官方api文档,既然提供了api了那么任何语言都能实现对其的管理控制及动态部署了。
但其实这里面真要弄明白还是有很多话要讲了
docker这个服务已经已进程的形式运行在linux的系统中了,为什么我们输入docker有关的命令能够与之交互,这好像是一个习以为常的行为,貌似理应如此,但是要知道我们是在与一个正在运行的进程发生通讯,若仍不以为然,请接以下问:
1.进程间都是如何通讯的? 进程通讯间方式
在明白了进程之间的通讯方式之后,我明白了docker的这个daemon通讯原理,瞬间就打通了之前对k8管理docker的疑惑(老实讲只知道kubernetes很强大,却没想明白它是如何能动态增容我的容器配置,负载等等等),套接字(socket)/var/run/docker这个我们使用起来不会接触到,理解起来却必须打通的关键点请务必了解它。
总结
以上所述是小编给大家介绍的golang实现对docker容器心跳监控功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!