Docker 簡單監控

Docker 簡單監控

標籤(空格分隔): Docker 監控 Monitoringpython


做者是 LE JOURNAL DE CHRISTOPHE,原文地址是 Simple Monitoring for Docker (Part I)docker

從 VM 遷移到 Docker 容器是很是容易的,除了監控部分。一個簡單的方法,運行一個數據收集客戶端(好比 Zabbix 客戶端),絕對不是一個好的解決方案,由於它與 Docker 的每一個容器中只有一個清楚明確任務的哲學相悖,而且也由於它要求使用定製的鏡像。從收集 LXC 和 Docker 容器的指標開始,我用一個基於系統的簡單腳原本收集來自於 Docker 容器的指標。shell

我使用 Zabbix 來彙總性能指標,所以該腳本將被設計成用在 Zabbix 客戶端的 user parameter。一個 user parameter 是一個被 Zabbix 運行而且返回某些信息的基本腳本。User parameters 不得不被定義在客戶端的配置文件中,但可能接收參數是爲了單個腳本的多個信息。bash

主機指標

這些指標在 Docker 主機級別生成,而不是容器級別。這是一個熱身,一個概念驗證或者是一個冒煙測試用於斷言在個人監控系統,一切都是正確安裝的。目前是收集一些與容器相關的指標。網絡

  1. 正在運行的容器數量
  2. 已經定義的容器總數量
  3. 崩潰的容器數量 即多少個已經中止的容器以非 0 返回碼退出。

如下是一個簡單的 shell 實現:性能

#!/bin/bash

function countContainers() {
    docker ps -q $1 | wc -l
}

function countCrashedContainers() {
    docker ps -a | grep -v -F 'Exited (0)' | grep -c -F 'Exited ('
}

TYPE=${1-all}

case $TYPE in
    running) COUNT_FUNCTION="countContainers"; shift;;
    crashed) COUNT_FUNCTION="countCrashedContainers"; shift;;
    all) COUNT_FUNCTION="countContainers -a"; shift;;
esac

$COUNT_FUNCTION

而後對 Zabbix 作一些配置後,會生成看起來像這樣的圖:測試

此處輸入圖片的描述

由於我有 9 個容器長期運行,3 個數據容器和一個容器以定時每小時啓動一次,這是符合我指望的。ui

一個相似的腳本能夠被寫的用於收集鏡像的指標,好比鏡像的總數量以及多少是 dangling 的。spa

容器指標

首先我想收集每一個容器的如下指標:設計

  1. 容器的 IP 地址
  2. 容器的狀態(running, paused, stopped, crashed)
  3. user 和 system CPU time
  4. 容器進程的內存使用
  5. 網絡活動(in 和 out)

IP 地址和容器狀態

這些能夠在 docker inspect <container-id> 發現。IP 地址被髮如今 NetworkSettings.IPAddress 以及我從如下獲取到的 State 計算狀態:

  • 0 -> Running
  • 1 -> Paused
  • 2 -> Stopped
  • 3 -> Crashed (即以非零返回碼退出)

CPU 和 Memory

cpu 和 內存能夠在 /sys/fs/cgroup/docker 目錄下 cpuacct.statmemory.stat 文件檢索到。

Network activity

根據博客文章,目前檢索 network activity 比檢索 CPU 或 Memory 更復雜而且我不喜歡在文章中提到的方法。儘管如此,這些數據能夠十分容易的從容器中檢索到的,經過在容器實例運行一個簡單的 ifconfig eth0 命令或是從 /sys 層次結構中查看。感謝 Docker 1.3 中引進的 exec 命令,運行這個命令進入一個運行着的容器是十分容易的,不須要任何定製鏡像或者在啓動容器時指定任何命令。

腳本

#!/usr/bin/env python

__author__ = 'Christophe Labouisse'

import argparse
import re
import os

from docker import Client
from docker.utils import kwargs_from_env


def display_cpu(args):
    detail = c.inspect_container(args.container)
    if bool(detail["State"]["Running"]):
        container_id = detail['Id']
        cpu_usage = {}
        with open('/sys/fs/cgroup/cpuacct/docker/' + container_id + '/cpuacct.stat', 'r') as f:
            for line in f:
                m = re.search(r"(system|user)\s+(\d+)", line)
                if m:
                    cpu_usage[m.group(1)] = int(m.group(2))
        if args.type == "all":
            cpu = cpu_usage["system"] + cpu_usage["user"]
        else:
            cpu = cpu_usage[args.type]
        user_ticks = os.sysconf(os.sysconf_names['SC_CLK_TCK'])
        print(float(cpu) / user_ticks)
    else:
        print(0)


def display_ip(args):
    detail = c.inspect_container(args.container)
    print(detail['NetworkSettings']['IPAddress'])


def display_memory(args):
    detail = c.inspect_container(args.container)
    if bool(detail["State"]["Running"]):
        container_id = detail['Id']
        with open('/sys/fs/cgroup/memory/docker/' + container_id + '/memory.stat', 'r') as f:
            for line in f:
                m = re.search(r"total_rss\s+(\d+)", line)
                if m:
                    print(m.group(1))
                    return

    print(0)


def display_network(args):
    detail = c.inspect_container(args.container)
    if bool(detail["State"]["Running"]):
        ifconfig = c.execute(args.container, "ifconfig eth0")
        m = re.search(("RX" if args.direction == "in" else "TX") + r" bytes:(\d+)", str(ifconfig))
        if m:
            print(m.group(1))
        else:
            b = c.execute(args.container, "cat /sys/devices/virtual/net/eth0/statistics/"+("rx" if args.direction == "in" else "tx")+"_bytes")
            if re.match(r"\s*\d+\s*", b):
                print(b)
            else:
                print(0)
    else:
        print(0)


def display_status(args):
    detail = c.inspect_container(args.container)
    state = detail["State"]
    if bool(state["Paused"]):
        print(1) # Paused
    elif bool(state["Running"]):
        print(0) # Running
    elif int(state["ExitCode"]) == 0:
        print(2) # Stopped
    else:
        print(3) # Crashed


parser = argparse.ArgumentParser()

parser.add_argument("container", help="Container name")

subparsers = parser.add_subparsers(title="Counters", description="Available counters", dest="dataType")

cpu_parser = subparsers.add_parser("cpu", help="Display CPU usage")
cpu_parser.add_argument("type", choices=["system", "user", "all"])
cpu_parser.set_defaults(func=display_cpu)

ip_parser = subparsers.add_parser("ip", help="Display IP Address")
ip_parser.set_defaults(func=display_ip)

memory_parser = subparsers.add_parser("memory", help="Display memory usage")
memory_parser.set_defaults(func=display_memory)

network_parser = subparsers.add_parser("network", help="Display network usage")
network_parser.add_argument("direction", choices=["in", "out"])
network_parser.set_defaults(func=display_network)

status_parser = subparsers.add_parser("status", help="Display the container status")
status_parser.set_defaults(func=display_status)

c = Client(**(kwargs_from_env()))

args = parser.parse_args()
args.func(args)

延伸

相關文章
相關標籤/搜索