Add calculated cpu and memory percentages to docker input (via config option)
This commit is contained in:
parent
28664fedb2
commit
300f2ee650
|
@ -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
|
||||||
|
|
|
@ -16,6 +16,7 @@ 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,
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue