Skip to content

Commit

Permalink
Docker Stats Receiver (#495)
Browse files Browse the repository at this point in the history
Adds a new docker_stats receiver that fetches container stats on a configured interval. Functionality is largely ported from the SignalFx Smart Agent docker container stats monitor: https://github.com/signalfx/signalfx-agent/blob/master/docs/monitors/docker-container-stats.md

Testing: Provides unit and integration tests.

Documentation: Readme section added.
  • Loading branch information
rmfitzpatrick committed Sep 3, 2020
1 parent 8c505df commit 34027d2
Show file tree
Hide file tree
Showing 22 changed files with 4,306 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ updates:
directory: "/receiver/collectdreceiver"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/receiver/dockerstatsreceiver"
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/receiver/k8sclusterreceiver"
schedule:
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ OTEL_VERSION=master
# Modules to run integration tests on.
# XXX: Find a way to automatically populate this. Too slow to run across all modules when there are just a few.
INTEGRATION_TEST_MODULES := \
receiver/dockerstatsreceiver \
receiver/redisreceiver \
internal/common

Expand Down
1 change: 1 addition & 0 deletions receiver/dockerstatsreceiver/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../Makefile.Common
53 changes: 53 additions & 0 deletions receiver/dockerstatsreceiver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Docker Stats Receiver

The Docker Stats receiver queries the local Docker daemon's container stats API for
all desired running containers on a configured interval. These stats are for container
resource usage of cpu, memory, network, and the
[blkio controller](https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt).

Requires Docker API version 1.22+. At this time only Linux is supported.

## Configuration

The following settings are required:

* `endpoint` (default = `unix:https:///var/run/docker.sock`): Address to reach the desired Docker daemon.

The following settings are optional:

* `collection_interval`: (default = `10s`) The interval at which to gather container stats.
* `timeout` (default = `5s`): The request timeout for any docker daemon query.
* `container_labels_to_metric_labels` (no default): A map of Docker container label names whose label values to use
as the specified metric label key.
* `env_vars_to_metric_labels` (no default): A map of Docker container environment variables whose values to use
as the specified metric label key.
* `excluded_images` (no default, all running containers monitored): A list of strings,
[regexes](https://golang.org/pkg/regexp/), or [globs](https://github.com/gobwas/glob) whose referent container image
names will not be among the queried containers. `!`-prefixed negations are possible for all item types to signify that
only unmatched container image names should be monitored.
* Regexes must be placed between `/` characters: `/my?egex/`. Negations are to be outside the forward slashes:
`!/my?egex/` will monitor all containers whose name doesn't match the compiled regex `my?egex`.
* Globs are non-regex items (e.g. `/items/`) containing any of the following: `*[]{}?`. Negations are supported:
`!my*container` will monitor all containers whose image name doesn't match the blob `my*container`.
* `provide_per_core_cpu_metrics` (default = `false`): Whether to report `cpu.usage.percpu` metrics.

Example:

```yaml
receivers:
docker_stats:
endpoint: http:https://example.com/
collection_interval: 2s
timeout: 20s
container_labels_to_metric_labels:
my.container.label: my-metric-label
my.other.container.label: my-other-metric-label
env_vars_to_metric_labels:
MY_ENVIRONMENT_VARIABLE: my-metric-label
MY_OTHER_ENVIRONMENT_VARIABLE: my-other-metric-label
excluded_images:
- undesired-container
- /.*undesired.*/
- another-*-container
provide_per_core_cpu_metrics: true
```
66 changes: 66 additions & 0 deletions receiver/dockerstatsreceiver/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2020, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dockerstatsreceiver

import (
"errors"
"time"

"go.opentelemetry.io/collector/config/configmodels"
)

var _ configmodels.Receiver = (*Config)(nil)

type Config struct {
configmodels.ReceiverSettings `mapstructure:",squash"`
// The URL of the docker server. Default is "unix:https:///var/run/docker.sock"
Endpoint string `mapstructure:"endpoint"`
// The time between each collection event. Default is 10s.
CollectionInterval time.Duration `mapstructure:"collection_interval"`

// The maximum amount of time to wait for docker API responses. Default is 5s
Timeout time.Duration `mapstructure:"timeout"`

// A mapping of container label names to MetricDescriptor label keys.
// The corresponding container label value will become the DataPoint label value
// for the mapped name. E.g. `io.kubernetes.container.name: container_spec_name`
// would result in a MetricDescriptor label called `container_spec_name` whose
// Metric DataPoints have the value of the `io.kubernetes.container.name` container label.
ContainerLabelsToMetricLabels map[string]string `mapstructure:"container_labels_to_metric_labels"`

// A mapping of container environment variable names to MetricDescriptor label
// keys. The corresponding env var values become the DataPoint label value.
// E.g. `APP_VERSION: version` would result MetricDescriptors having a label
// key called `version` whose DataPoint label values are the value of the
// `APP_VERSION` environment variable configured for that particular container, if
// present.
EnvVarsToMetricLabels map[string]string `mapstructure:"env_vars_to_metric_labels"`

// A list of filters whose matching images are to be excluded. Supports literals, globs, and regex.
ExcludedImages []string `mapstructure:"excluded_images"`

// Whether to report all CPU metrics. Default is false
ProvidePerCoreCPUMetrics bool `mapstructure:"provide_per_core_cpu_metrics"`
}

func (config Config) Validate() error {
if config.Endpoint == "" {
return errors.New("config.Endpoint must be specified")
}
if config.CollectionInterval == 0 {
return errors.New("config.CollectionInterval must be specified")
}
return nil
}
80 changes: 80 additions & 0 deletions receiver/dockerstatsreceiver/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2020, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dockerstatsreceiver

import (
"path"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config/configmodels"
"go.opentelemetry.io/collector/config/configtest"
)

func TestLoadConfig(t *testing.T) {
factories, err := componenttest.ExampleComponents()
assert.NoError(t, err)

factory := NewFactory()
factories.Receivers[configmodels.Type(typeStr)] = factory
config, err := configtest.LoadConfigFile(
t, path.Join(".", "testdata", "config.yaml"), factories,
)

require.NoError(t, err)
require.NotNil(t, config)
assert.Equal(t, 2, len(config.Receivers))

defaultConfig := config.Receivers["docker_stats"]
assert.Equal(t, factory.CreateDefaultConfig(), defaultConfig)

dcfg := defaultConfig.(*Config)
assert.Equal(t, "docker_stats", dcfg.Name())
assert.Equal(t, "unix:https:///var/run/docker.sock", dcfg.Endpoint)
assert.Equal(t, 10*time.Second, dcfg.CollectionInterval)
assert.Equal(t, 5*time.Second, dcfg.Timeout)

assert.Nil(t, dcfg.ExcludedImages)
assert.Nil(t, dcfg.ContainerLabelsToMetricLabels)
assert.Nil(t, dcfg.EnvVarsToMetricLabels)

assert.False(t, dcfg.ProvidePerCoreCPUMetrics)

ascfg := config.Receivers["docker_stats/allsettings"].(*Config)
assert.Equal(t, "docker_stats/allsettings", ascfg.Name())
assert.Equal(t, "http:https://example.com/", ascfg.Endpoint)
assert.Equal(t, 2*time.Second, ascfg.CollectionInterval)
assert.Equal(t, 20*time.Second, ascfg.Timeout)

assert.Equal(t, []string{
"undesired-container",
"another-*-container",
}, ascfg.ExcludedImages)

assert.Equal(t, map[string]string{
"my.container.label": "my-metric-label",
"my.other.container.label": "my-other-metric-label",
}, ascfg.ContainerLabelsToMetricLabels)

assert.Equal(t, map[string]string{
"my_environment_variable": "my-metric-label",
"my_other_environment_variable": "my-other-metric-label",
}, ascfg.EnvVarsToMetricLabels)

assert.True(t, ascfg.ProvidePerCoreCPUMetrics)
}
Loading

0 comments on commit 34027d2

Please sign in to comment.