golang实现对docker容器心跳监控功能

自己写的go程序放到线上本来编译成二进制扔上去就行啦,但是怀着一颗docker的心,最终还是将它放到docker容器中运行起来了,运行起来也ok,一个最小容器64M,统一管理起来也方便,但是毕竟是个线上长驻内存的服务程序,万一跑挂了怎么办,如何才能监控它,直接上go代码,网上代码,略微做了下注释,但实测过,真实有效:

package main
import (
 "encoding/json"
 "errors"
 "flag"
 "fmt"
 "io/ioutil"
 "log"
 "net"
 "os"
 "strings"
 "time"
)
// 镜像结构
type Image struct {
 Created uint64
 Id string
 ParentId string
 RepoTags []string
 Size uint64
 VirtualSize uint64
}
// 容器结构
type Container struct {
 Id string `json:"Id"`
 Names []string `json:"Names"`
 Image string `json:"Image"`
 ImageID string `json:"ImageID"`
 Command string `json:"Command"`
 Created uint64 `json:"Created"`
 State string `json:"State"`
 Status string `json:"Status"`
 Ports []Port `json:"Ports"`
 Labels map[string]string `json:"Labels"`
 HostConfig map[string]string `json:"HostConfig"`
 NetworkSettings map[string]interface{} `json:"NetworkSettings"`
 Mounts []Mount `json:"Mounts"`
}
// docker 端口映射
type Port struct {
 IP string `json:"IP"`
 PrivatePort int `json:"PrivatePort"`
 PublicPort int `json:"PublicPort"`
 Type string `json:"Type"`
}
// docker 挂载
type Mount struct {
 Type string `json:"Type"`
 Source string `json:"Source"`
 Destination string `json:"Destination"`
 Mode string `json:"Mode"`
 RW bool `json:"RW"`
 Propatation string `json:"Propagation"`
}
// 连接列表
var SockAddr = "/var/run//docker.sock" //这可不是随便写的,是docker官网文档的套接字默认值,当然守护进程通讯方式还有tcp,fd等方式,各自都有适用场景。。。
var imagesSock = "GET /images/json HTTP/1.0\r\n\r\n" //docker对外的镜像api操作
var containerSock = "GET /containers/json?all=true HTTP/1.0\r\n\r\n"  //docker对外的容器查看api
var startContainerSock = "POST /containers/%s/start HTTP/1.0\r\n\r\n" //docker对外的容器启动api
// 白名单
var whiteList []string
func main() {
 // 读取命令行参数
 // 白名单列表
 list := flag.String("list", "", "docker white list to restart, eg: token,explorer")
 // 轮询的时间间隔,单位秒
 times := flag.Int64("time", 10, "time interval to set read docker containers [second], default is 10 second")
 flag.Parse()
 // 解析list => whiteList
 whiteList = strings.Split(*list, ",") //将我们命令行中list参数的容器列表解析到代码中
 log.SetOutput(os.Stdout)
 log.Println("start docker watching...")
 log.Printf("Your whiteList: %v\n", *list)
 log.Printf("Your shedule times: %ds\n", *times)
  //接下来的这个for循环就是每隔一定时间监控docker容器是否正常运行,不正常就重新启动它
 for {
  // 轮询docker
  err := listenDocker()
  if err != nil {
   log.Println(err.Error())
  }
  time.Sleep(time.Duration(*times)*time.Second)
 }
}
func listenDocker() error {
 // 获取容器列表,拿到所有的容器信息
 containers, err := readContainer()
 if err != nil {
  return errors.New("read container error: " + err.Error())
 }
 // 先遍历白名单快,次数少
 for _, name := range whiteList {
 Name:
  for _, container := range containers {
   for _, cname := range container.Names {
    // 如果匹配到白名单
    if cname[1:] == name {
     // 关心一下容器状态
     log.Printf("id=%s, name=%s, state=%s", container.Id[:12], container.Names, container.Status)
     if strings.Contains(container.Status, "Exited") {
      // 如果出现异常退出的容器,启动它
      log.Printf("find container: [%s] has exited, ready to start it. ", name)
      e := startContainer(container.Id)
      if e != nil {
       log.Println("start container error: ", e.Error())
      }
      break Name
     }
    }
   }
  }
 }
 return nil
}
// 获取 unix sock 连接
func connectDocker() (*net.UnixConn, error) {
 addr := net.UnixAddr{SockAddr, "unix"}  // SockAddr 这个变量的值被设定为docker的/var/run/docker 套接字路径值,也就是说此处就是拨通与docker的daemon通讯建立的关键处,其他处的代码就是些正常的逻辑处理了
 return net.DialUnix("unix", nil, &addr)
}
// 启动容器
func startContainer(id string) error {
 conn, err := connectDocker()
 if err != nil {
  return errors.New("connect error: " + err.Error())
 }
 start := fmt.Sprintf(startContainerSock, id)
 fmt.Println(start)
 cmd := []byte(start)
 code, err := conn.Write(cmd)
 if err != nil {
  return err
 }
 log.Println("start container response code: ", code)
 // 启动容器等待20秒,防止数据重发
 time.Sleep(20*time.Second)
 return nil
}
// 获取容器列表
func readContainer() ([]Container, error) {
 conn, err := connectDocker() //建立一个unix连接,这其实是一个关键点,需要你了解unix 套接字 建立连接
 if err != nil {
  return nil, errors.New("connect error: " + err.Error())
 }
 _, err = conn.Write([]byte(containerSock))
 if err != nil {
  return nil, err
 }
 result, err := ioutil.ReadAll(conn)
 if err != nil {
  return nil, err
 }
 body := getBody(result)
 var containers []Container
 err = json.Unmarshal(body, &containers)
 if err != nil {
  return nil, err
 }
 log.Println("len of containers: ", len(containers))
 if len(containers) == 0 {
  return nil, errors.New("no containers")
 }
 return containers, nil
}
// 获取镜像列表
func readImage(conn *net.UnixConn) ([]Image, error) {
 _, err := conn.Write([]byte(imagesSock))
 if err != nil {
  return nil, err
 }
 result, err := ioutil.ReadAll(conn)
 if err != nil {
  return nil, err
 }
 body := getBody(result[:])
 var images []Image
 err = json.Unmarshal(body, &images)
 if err != nil {
  return nil, err
 }
 return images, nil
}
// 从返回的 http 响应中提取 body
func getBody(result []byte) (body []byte) {
 for i:=0; i<=len(result)-4; i++ {
  if result[i] == 13 && result[i+1] == 10 && result[i+2] == 13 && result[i+3] == 10 {
   body = result[i+4:]
   break
  }
 }
 return
}
/*
error log :
 1、write unix @->/var/run/docker.sock: write: broken pipe
  建立的tcp连接不能复用,每次操作都建立连接
 */

使用方法

1.编译

go build -o main main.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容器心跳监控功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • Docker与Golang的巧妙结合

    Docker与Golang的巧妙结合 [编者的话]这是一个展示在使用Go语言时如何让Docker更有用的提示与技巧的简辑.例如,如何使用不同版本的Go工具链来编译Go代码,如何交叉编译到不同的平台(并且测试结果!),或者如何制作真正小的容器镜像. 下面的文章假定你已经安装了Docker.不必是最新版本(这篇文章不会使用Docker任何花哨的功能). 没有go的Go ...意思是:"不用安装go就能使用Go" 如果你写Go代码,或者你对Go语言有一点点兴趣,你肯定要安装了Go编译器和Go

  • docker中部署golang项目的步骤详解

    理解Docker Docker可以帮您为自己的应用程序创建一个单一的可部署"单位".这样的单位也叫做容器,其中包含了应用程序需要的一切.例如代码(或二进制文件).运行时.系统工具,以及系统库文件.将所有这些需要的内容打包为一个单一的单位,可确保无论将应用程序部署在何处,都能提供完全相同的环境.这种技术还可以帮您维持完全一致的开发和生产环境,通常这些环境是很难被追踪的. 一旦搭建完成,容器的创建和部署将可自动进行.这本身就可以避免一系列问题.这些问题中大部分都是因为文件不同步,或开发和生

  • 使用docker构建golang线上部署环境的步骤详解

    Docker用于开发 Docker不仅用于部署,它还可以用于开发. 1.为什么要在开发中使用Docker 主要有以下几个原因. 1)一致的开发环境 使用Docker,可以保证整个研发团队使用一致的开发环境. 2)开发环境与最终的生产环境保持一致 这减少了部署出错的可能性. 3)简化了编译和构建的复杂性 对于一些动辄数小时的编译和构建工作,可以用Docker来简化. 4)在开发时只需Docker 无需在自己的开发主机上搭建各种编程语言环境. 5)可以使用同一编程语言的多个版本 可以使用同一编程语言

  • golang实现对docker容器心跳监控功能

    自己写的go程序放到线上本来编译成二进制扔上去就行啦,但是怀着一颗docker的心,最终还是将它放到docker容器中运行起来了,运行起来也ok,一个最小容器64M,统一管理起来也方便,但是毕竟是个线上长驻内存的服务程序,万一跑挂了怎么办,如何才能监控它,直接上go代码,网上代码,略微做了下注释,但实测过,真实有效: package main import ( "encoding/json" "errors" "flag" "fmt&q

  • 基于spring-boot和docker-java实现对docker容器的动态管理和监控功能[附完整源码下载]

    docker简介 Docker 是一个开源的应用容器引擎,和传统的虚拟机技术相比,Docker 容器性能开销极低,因此也广受开发者喜爱.随着基于docker的开发者越来越多,docker的镜像也原来越丰富,未来各种企业级的完整解决方案都可以直接通过下载镜像拿来即用.因此docker变得越来越重要. 本文目的 本文通过一个项目实例来介绍如果通过docker对外接口来实现对docker容器的管理和监控. 应用场景: 对服务器资源池通过docker进行统一管理,按需分配资源和创建容器,达到资源最大化利

  • Docker 容器内存监控原理及应用

    Docker 容器内存监控 linux内存监控 要明白docker容器内存是如何计算的,首先要明白linux中内存的相关概念. 使用free命令可以查看当前内存使用情况. [root@localhost ~]$ free total used free shared buffers cached Mem: 264420684 213853512 50567172 71822688 2095364 175733516 -/+ buffers/cache: 36024632 228396052 Sw

  • 使用Grafana 展示Docker容器的监控图表并设置邮件报警规则(图解)

    一.Docker 容器监控报警方式 接着上篇文章的记录,看到grafana的版本已经更新到4.2了,并且在4.0以后的版本中,加入了Alert Notifications 功能,这样在对容器 监控完,可以加入报警规则.根据官网介绍,报警方式也有很多种,常见的Email.Slack即时通讯.webhook等. 本篇记录的是邮件的报警设置.环境和上篇基本一致,都是在Docker 平台测试环境下,另外本篇使用的grafana容器的版本是用的 dockerhub上最新版本,该版本为grafana/gra

  • golang 实现对Map进行键值自定义排序

    看代码吧~ //自定义结构体,用来自定义排序 type RecentlyUpdateListMapSorter []map[string]interface{} //根据MapSorter.Keys进行倒序排序 func (ms *RecentlyUpdateListMapSorter) SortReverse() { sort.Sort(sort.Reverse(ms)) } //自定义排序方法(Len(),Less(),Swap()) func (ms *RecentlyUpdateList

  • Golang实现对map的并发读写的方法示例

    在Golang多协程的情况下使用全局map时,如果不做线程同步,会出现panic的情况. 为了解决这个问题,通常有两种方式: 第一种是最常见的使用互斥锁或者读写锁的方法: 第二种是比较符合Golang特色的方法,启动单个协程对map进行读写,当其他协程需要读写map时,通过channel向这个协程发送信号即可. 写了一个模拟程序对map中的一项进行读或者写,后台一直运行的协程阻塞的接受读写信号,并对map进行操作,但是读操作的时候没想好怎么返回这个值. 后来想到用传引用的方式,定义结构体,第一个

  • Docker 容器监控原理及 cAdvisor的安装与使用说明

    生产环境中监控容器的运行状况十分重要,通过监控我们可以随时掌握容器的运行状态,做到线上隐患和问题早发现,早解决. 所以今天我就和你分享关于容器监控的知识(原理及工具 cAdvisor). 虽然传统的物理机和虚拟机监控已经有了比较成熟的监控方案,但是容器的监控面临着更大的挑战,因为容器的行为和本质与传统的虚拟机是不一样的,总的来说,容器具有以下特性: 容器是短期存活的,并且可以动态调度 容器的本质是进程,而不是一个完整操作系统 由于容器非常轻量,容器的创建和销毁也会比传统虚拟机更加频繁 Docke

  • 详解Docker容器可视化监控中心搭建

    概述 一个宿主机上可以运行多个容器化应用,容器化应用运行于宿主机上,我们需要知道该容器的运行情况,包括 CPU使用率.内存占用.网络状况以及磁盘空间等等一系列信息,而且这些信息随时间变化,我们称其为时序数据,本文将实操 如何搭建一个可视化的监控中心 来收集这些承载着具体应用的容器的时序信息并可视化分析与展示! 动手了,动手了... 准备镜像 adviser:负责收集容器的随时间变化的数据 influxdb:负责存储时序数据 grafana:负责分析和展示时序数据 部署Influxdb服务 可以将

  • 通过Python实现对SQL Server 数据文件大小的监控告警功能

    1.需求背景 系统程序突然报错,报错信息如下: The transaction log for database '@dbname' is full. To find out why space in the log cannot be reused, see the log_reuse_wait_desc column in sys.databases 此时查看log文件,已达2T. 当时的紧急处理方案是,移除掉镜像,修改数据库恢复模式(由full修改为simple),收缩日志. 为了防止类似

  • python脚本监控docker容器

    本文实例为大家分享了python脚本监控docker容器的方法,供大家参考,具体内容如下 脚本功能: 1.监控CPU使用率 2.监控内存使用状况 3.监控网络流量 具体代码: #!/usr/bin/env python # --*-- coding:UTF-8 --*-- import sys import tab import re import os import time from docker import Client import commands keys_container_st

随机推荐