需求
想要监控docker容器的状态,CPU,内存等指标,并集成到zabbix监控中
由于服务器docker版本较低(系统为centos6.9,docker 版本为:1.7.1),所以采用API方式获取相关信息。
为了不重启docker,采用socket方式连接docker服务
func dialDocker(proto, addr string) (conn net.Conn, err error) { return net.Dial("unix", "/var/run/docker.sock")}
大概思路,使用zabbix自动发现功能,自动发现容器列表并添加监控项。
注意:容器ID每次重建(服务更新)是会变的,但可以保证容器的name不变,所以zabbix监控项的参数要传Container name,而不能是ContainerID,不然,每次更新之前所有监控项都会失效,又添加一批新的监控项。
获取容器列表
生成自动发现JSON数据,用于zabbix自动把所有容器添加到监控项,代码如下:
package mainimport ("encoding/json""flag""fmt""io/ioutil""net""net/http""strings""github.com/tidwall/gjson")var url stringfunc init() {flag.StringVar(&url, "u", `http://v1.24/containers/json?all=true`, "url")flag.Parse()}func dialDocker(proto, addr string) (conn net.Conn, err error) {return net.Dial("unix", "/var/run/docker.sock")}type container struct {ContainerID string `json:"{#CONTAINERID},omitempty"` // 这项可以去掉,这里我们只用获取到所有容器的Container Name即可。ContainerName string `json:"{#CONTAINERNAME},omitempty"`}type containers struct {Data []container `json:"data,omitempty"`}func main() {tr := &http.Transport{Dial: dialDocker,}client := &http.Client{Transport: tr,}resp, err := client.Get(url)if err != nil {fmt.Println(err)}data, err := ioutil.ReadAll(resp.Body)if err != nil {fmt.Println(err)}result := gjson.ParseBytes(data)var d containersfor _, line := range result.Array() {containerID := line.Get("Id").String()containerName := strings.TrimLeft(line.Get("Names").Array()[0].String(), "/")d.Data = append(d.Data, container{ContainerID: containerID, ContainerName: containerName})}b, err := json.Marshal(d)if err != nil {panic(err)}fmt.Println(string(b))}
执行输出:
{"data": [ { "{#CONTAINERID}": "6913da77edaa1d67ddbcb762da139044a063b251b409e7a532949fdd01978c04", "{#CONTAINERNAME}": "skywalking-ui" }, { "{#CONTAINERID}": "d2d3577447326fdf01f551c6636fee5f6c1ff1ef63dc2cf526c74c5eb4e823b8", "{#CONTAINERNAME}": "skywalking-oap-server" }, { "{#CONTAINERID}": "3635eacdaf7a63dd75e5dcaa33d741c257d74e2faeaa810a5c00db2a0d025f56", "{#CONTAINERNAME}": "soffice" }, { "{#CONTAINERID}": "686a815d229fd5b9f6a6cefb41e3efa72532a80df756c4c17742fae5e65f9ece", "{#CONTAINERNAME}": "rabbitmq3.7.7" } ]}
获取容器CPU和内存使用率
然后根据容器名字获取容器CPU和内存使用率,需要传入两个参数:
- -c containerName容器名称
- -r resource 获取资源类型,cpu/mem/disk/net,
暂时仅实现了获取CPU和内存使用率
结构定义和计算参考文档:
https://github.com/moby/moby/blob/eb131c5383db8cac633919f82abad86c99bffbe5/cli/command/container/stats_helpers.go
https://github.com/moby/moby/blob/c1d090fcc88fa3bc5b804aead91ec60e30207538/api/types/stats.go
https://docs.docker.com/engine/api/v1.24/
代码如下:
package mainimport ("encoding/json""flag""fmt""log""net""net/http")type ThrottlingData struct {Periods uint64 `json:"periods,omitempty"`ThrottledPeriods uint64 `json:"throttled_periods,omitempty"`ThrottledTime uint64 `json:"throttled_time,omitempty"`}type CPUUsage struct {TotalUsage int64 `json:"total_usage,omitempty"`PercpuUsage []int64 `json:"percpu_usage,omitempty"`UsageInKernelmode int64 `json:"usage_in_kernelmode,omitempty"`UsageInUsermode int64 `json:"usage_in_usermode,omitempty"`}type CPUStats struct {CPUUsage CPUUsage `json:"cpu_usage,omitempty"`SystemUsage uint64 `json:"system_cpu_usage,omitempty"`OnlineCPUs uint32 `json:"online_cpus,omitempty"`ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`}type MemoryStats struct {Usage uint64 `json:"usage,omitempty"`MaxUsage uint64 `json:"max_usage,omitempty"`Stats map[string]uint64 `json:"stats,omitempty"`Failcnt uint64 `json:"failcnt,omitempty"`Limit uint64 `json:"limit,omitempty"`}type Status struct {CPUStats CPUStats `json:"cpu_stats,omitempty"`PreCPUStats CPUStats `json:"precpu_stats,omitempty"`MemoryStats MemoryStats `json:"memory_stats,omitempty"`}func (s *Status) CPUPercent() float64 {var cpuPercent = 0.0previousCPU := s.PreCPUStats.CPUUsage.TotalUsagepreviousSystem := s.PreCPUStats.SystemUsagecpuDelta := float64(s.CPUStats.CPUUsage.TotalUsage) - float64(previousCPU)systemDelta := float64(s.CPUStats.SystemUsage) - float64(previousSystem)if systemDelta > 0.0 && cpuDelta > 0.0 {cpuPercent = (cpuDelta / systemDelta) * float64(len(s.CPUStats.CPUUsage.PercpuUsage)) * 100.0}return cpuPercent}func (s *Status) MemPercent() float64 {var memPercent float64 = 0.0if s.MemoryStats.Limit != 0 {memPercent = float64(s.MemoryStats.Usage) / float64(s.MemoryStats.Limit) * 100.0}return memPercent}var (containerName stringresource string)func init() {flag.StringVar(&containerName, "c", "", "containerName")flag.StringVar(&resource, "r", "", "mem/cpu/disk/net")flag.Parse()}func dialDocker(proto, addr string) (conn net.Conn, err error) {return net.Dial("unix", "/var/run/docker.sock")}func getResponse(client *http.Client, url string) (*Status, error) {resp, err := client.Get(url)if err != nil {return nil, err}defer resp.Body.Close()dec := json.NewDecoder(resp.Body)var s *Statuserr = dec.Decode(&s)return s, err}func main() {tr := &http.Transport{Dial: dialDocker,}client := &http.Client{Transport: tr,}// get status based on container namestatsUrl := fmt.Sprintf("http://v1.30/containers/%s/stats?stream=false", containerName)info, err := getResponse(client, statsUrl)if err != nil {log.Println(err)}switch resource {case "mem":fmt.Printf("%.3f", info.MemPercent())case "cpu":fmt.Printf("%.3f", info.CPUPercent())}}
由于是作为zabbix的监控脚本,所以没有对入参进行校验
输出如下: