Add calculated cpu and memory percentages to docker input (via config option)

This commit is contained in:
Mike Tonks 2016-02-09 15:20:56 +00:00 committed by Gabriel Levine
parent 28664fedb2
commit 300f2ee650
3 changed files with 123 additions and 49 deletions

View File

@ -22,8 +22,16 @@ for the stat structure can be found
endpoint = "unix:///var/run/docker.sock" endpoint = "unix:///var/run/docker.sock"
# Only collect metrics for these containers, collect all if empty # Only collect metrics for these containers, collect all if empty
container_names = [] container_names = []
calculate_percentages = false
``` ```
### Calculate Percentages
Optionally percentages can be calculated for cpu and memory usage. This uses
the same calculation as the 'docker stats' command line tool. Set this option
to 'true' to enable this feature.
### Measurements & Fields: ### Measurements & Fields:
Every effort was made to preserve the names based on the JSON response from the Every effort was made to preserve the names based on the JSON response from the

View File

@ -14,8 +14,9 @@ import (
) )
type Docker struct { type Docker struct {
Endpoint string Endpoint string
ContainerNames []string ContainerNames []string
CalculatePercentages bool
client *docker.Client client *docker.Client
} }
@ -27,6 +28,8 @@ var sampleConfig = `
endpoint = "unix:///var/run/docker.sock" endpoint = "unix:///var/run/docker.sock"
### Only collect metrics for these containers, collect all if empty ### Only collect metrics for these containers, collect all if empty
container_names = [] container_names = []
# Add calculated percentages for mem and cpu, as per 'docker stats' command
calculate_percentages = false
` `
func (d *Docker) Description() string { func (d *Docker) Description() string {
@ -67,6 +70,7 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(containers)) wg.Add(len(containers))
for _, container := range containers { for _, container := range containers {
go func(c docker.APIContainers) { go func(c docker.APIContainers) {
defer wg.Done() defer wg.Done()
err := d.gatherContainer(c, acc) err := d.gatherContainer(c, acc)
@ -131,7 +135,7 @@ func (d *Docker) gatherContainer(
tags[k] = v tags[k] = v
} }
gatherContainerStats(stat, acc, tags) gatherContainerStats(stat, acc, tags, d.CalculatePercentages)
return nil return nil
} }
@ -140,6 +144,7 @@ func gatherContainerStats(
stat *docker.Stats, stat *docker.Stats,
acc telegraf.Accumulator, acc telegraf.Accumulator,
tags map[string]string, tags map[string]string,
calculate_percentages bool,
) { ) {
now := stat.Read now := stat.Read
@ -178,6 +183,9 @@ func gatherContainerStats(
"inactive_file": stat.MemoryStats.Stats.InactiveFile, "inactive_file": stat.MemoryStats.Stats.InactiveFile,
"total_pgpgin": stat.MemoryStats.Stats.TotalPgpgin, "total_pgpgin": stat.MemoryStats.Stats.TotalPgpgin,
} }
if calculate_percentages {
memfields["usage_percent"] = float64(stat.MemoryStats.Usage) / float64(stat.MemoryStats.Limit) * 100.0
}
acc.AddFields("docker_mem", memfields, tags, now) acc.AddFields("docker_mem", memfields, tags, now)
cpufields := map[string]interface{}{ cpufields := map[string]interface{}{
@ -189,6 +197,9 @@ func gatherContainerStats(
"throttling_throttled_periods": stat.CPUStats.ThrottlingData.ThrottledPeriods, "throttling_throttled_periods": stat.CPUStats.ThrottlingData.ThrottledPeriods,
"throttling_throttled_time": stat.CPUStats.ThrottlingData.ThrottledTime, "throttling_throttled_time": stat.CPUStats.ThrottlingData.ThrottledTime,
} }
if calculate_percentages {
cpufields["usage_percent"] = calculateCPUPercent(stat)
}
cputags := copyTags(tags) cputags := copyTags(tags)
cputags["cpu"] = "cpu-total" cputags["cpu"] = "cpu-total"
acc.AddFields("docker_cpu", cpufields, cputags, now) acc.AddFields("docker_cpu", cpufields, cputags, now)
@ -219,6 +230,21 @@ func gatherContainerStats(
gatherBlockIOMetrics(stat, acc, tags, now) gatherBlockIOMetrics(stat, acc, tags, now)
} }
func calculateCPUPercent(stat *docker.Stats) float64 {
var (
cpuPercent = 0.0
// calculate the change for the cpu usage of the container in between readings
cpuDelta = float64(stat.CPUStats.CPUUsage.TotalUsage) - float64(stat.PreCPUStats.CPUUsage.TotalUsage)
// calculate the change for the entire system between readings
systemDelta = float64(stat.CPUStats.SystemCPUUsage) - float64(stat.PreCPUStats.SystemCPUUsage)
)
if systemDelta > 0.0 && cpuDelta > 0.0 {
cpuPercent = (cpuDelta / systemDelta) * float64(len(stat.CPUStats.CPUUsage.PercpuUsage)) * 100.0
}
return cpuPercent
}
func gatherBlockIOMetrics( func gatherBlockIOMetrics(
stat *docker.Stats, stat *docker.Stats,
acc telegraf.Accumulator, acc telegraf.Accumulator,

View File

@ -18,7 +18,7 @@ func TestDockerGatherContainerStats(t *testing.T) {
"cont_name": "redis", "cont_name": "redis",
"cont_image": "redis/image", "cont_image": "redis/image",
} }
gatherContainerStats(stats, &acc, tags) gatherContainerStats(stats, &acc, tags, false)
// test docker_net measurement // test docker_net measurement
netfields := map[string]interface{}{ netfields := map[string]interface{}{
@ -45,55 +45,13 @@ func TestDockerGatherContainerStats(t *testing.T) {
acc.AssertContainsTaggedFields(t, "docker_blkio", blkiofields, blkiotags) acc.AssertContainsTaggedFields(t, "docker_blkio", blkiofields, blkiotags)
// test docker_mem measurement // test docker_mem measurement
memfields := map[string]interface{}{ memfields := sample_mem_fields()
"max_usage": uint64(1001),
"usage": uint64(1111),
"fail_count": uint64(1),
"limit": uint64(20),
"total_pgmafault": uint64(0),
"cache": uint64(0),
"mapped_file": uint64(0),
"total_inactive_file": uint64(0),
"pgpgout": uint64(0),
"rss": uint64(0),
"total_mapped_file": uint64(0),
"writeback": uint64(0),
"unevictable": uint64(0),
"pgpgin": uint64(0),
"total_unevictable": uint64(0),
"pgmajfault": uint64(0),
"total_rss": uint64(44),
"total_rss_huge": uint64(444),
"total_writeback": uint64(55),
"total_inactive_anon": uint64(0),
"rss_huge": uint64(0),
"hierarchical_memory_limit": uint64(0),
"total_pgfault": uint64(0),
"total_active_file": uint64(0),
"active_anon": uint64(0),
"total_active_anon": uint64(0),
"total_pgpgout": uint64(0),
"total_cache": uint64(0),
"inactive_anon": uint64(0),
"active_file": uint64(1),
"pgfault": uint64(2),
"inactive_file": uint64(3),
"total_pgpgin": uint64(4),
}
acc.AssertContainsTaggedFields(t, "docker_mem", memfields, tags) acc.AssertContainsTaggedFields(t, "docker_mem", memfields, tags)
// test docker_cpu measurement // test docker_cpu measurement
cputags := copyTags(tags) cputags := copyTags(tags)
cputags["cpu"] = "cpu-total" cputags["cpu"] = "cpu-total"
cpufields := map[string]interface{}{ cpufields := sample_cpu_fields()
"usage_total": uint64(500),
"usage_in_usermode": uint64(100),
"usage_in_kernelmode": uint64(200),
"usage_system": uint64(100),
"throttling_periods": uint64(1),
"throttling_throttled_periods": uint64(0),
"throttling_throttled_time": uint64(0),
}
acc.AssertContainsTaggedFields(t, "docker_cpu", cpufields, cputags) acc.AssertContainsTaggedFields(t, "docker_cpu", cpufields, cputags)
cputags["cpu"] = "cpu0" cputags["cpu"] = "cpu0"
@ -109,6 +67,30 @@ func TestDockerGatherContainerStats(t *testing.T) {
acc.AssertContainsTaggedFields(t, "docker_cpu", cpu1fields, cputags) acc.AssertContainsTaggedFields(t, "docker_cpu", cpu1fields, cputags)
} }
func TestDockerGatherContainerPercentages(t *testing.T) {
var acc testutil.Accumulator
stats := testStats()
tags := map[string]string{
"cont_id": "foobarbaz",
"cont_name": "redis",
"cont_image": "redis/image",
}
gatherContainerStats(stats, &acc, tags, true)
// test docker_mem measurement
memfields := sample_mem_fields()
memfields["usage_percent"] = 55.55
acc.AssertContainsTaggedFields(t, "docker_mem", memfields, tags)
// test docker_cpu measurement
cputags := copyTags(tags)
cputags["cpu"] = "cpu-total"
cpufields := sample_cpu_fields()
cpufields["usage_percent"] = 400.0
acc.AssertContainsTaggedFields(t, "docker_cpu", cpufields, cputags)
}
func testStats() *docker.Stats { func testStats() *docker.Stats {
stats := &docker.Stats{ stats := &docker.Stats{
Read: time.Now(), Read: time.Now(),
@ -122,6 +104,9 @@ func testStats() *docker.Stats {
stats.CPUStats.SystemCPUUsage = 100 stats.CPUStats.SystemCPUUsage = 100
stats.CPUStats.ThrottlingData.Periods = 1 stats.CPUStats.ThrottlingData.Periods = 1
stats.PreCPUStats.CPUUsage.TotalUsage = 400
stats.PreCPUStats.SystemCPUUsage = 50
stats.MemoryStats.Stats.TotalPgmafault = 0 stats.MemoryStats.Stats.TotalPgmafault = 0
stats.MemoryStats.Stats.Cache = 0 stats.MemoryStats.Stats.Cache = 0
stats.MemoryStats.Stats.MappedFile = 0 stats.MemoryStats.Stats.MappedFile = 0
@ -155,7 +140,7 @@ func testStats() *docker.Stats {
stats.MemoryStats.MaxUsage = 1001 stats.MemoryStats.MaxUsage = 1001
stats.MemoryStats.Usage = 1111 stats.MemoryStats.Usage = 1111
stats.MemoryStats.Failcnt = 1 stats.MemoryStats.Failcnt = 1
stats.MemoryStats.Limit = 20 stats.MemoryStats.Limit = 2000
stats.Networks["eth0"] = docker.NetworkStats{ stats.Networks["eth0"] = docker.NetworkStats{
RxDropped: 1, RxDropped: 1,
@ -188,3 +173,58 @@ func testStats() *docker.Stats {
return stats return stats
} }
func sample_mem_fields() map[string]interface{} {
memfields := map[string]interface{}{
"max_usage": uint64(1001),
"usage": uint64(1111),
"fail_count": uint64(1),
"limit": uint64(2000),
"total_pgmafault": uint64(0),
"cache": uint64(0),
"mapped_file": uint64(0),
"total_inactive_file": uint64(0),
"pgpgout": uint64(0),
"rss": uint64(0),
"total_mapped_file": uint64(0),
"writeback": uint64(0),
"unevictable": uint64(0),
"pgpgin": uint64(0),
"total_unevictable": uint64(0),
"pgmajfault": uint64(0),
"total_rss": uint64(44),
"total_rss_huge": uint64(444),
"total_writeback": uint64(55),
"total_inactive_anon": uint64(0),
"rss_huge": uint64(0),
"hierarchical_memory_limit": uint64(0),
"total_pgfault": uint64(0),
"total_active_file": uint64(0),
"active_anon": uint64(0),
"total_active_anon": uint64(0),
"total_pgpgout": uint64(0),
"total_cache": uint64(0),
"inactive_anon": uint64(0),
"active_file": uint64(1),
"pgfault": uint64(2),
"inactive_file": uint64(3),
"total_pgpgin": uint64(4),
}
return memfields
}
func sample_cpu_fields() map[string]interface{} {
cpufields := map[string]interface{}{
"usage_total": uint64(500),
"usage_in_usermode": uint64(100),
"usage_in_kernelmode": uint64(200),
"usage_system": uint64(100),
"throttling_periods": uint64(1),
"throttling_throttled_periods": uint64(0),
"throttling_throttled_time": uint64(0),
}
return cpufields
}