Merge pull request #1847 from jchauncey/kubernetes-plugin
feat(kubernetes): Add kubernetes input plugin
This commit is contained in:
commit
91f48e7ad5
|
@ -39,6 +39,7 @@ continue sending logs to /var/log/telegraf/telegraf.log.
|
|||
- [#1686](https://github.com/influxdata/telegraf/pull/1686): Mesos input plugin: very high-cardinality mesos-task metrics removed.
|
||||
- [#1838](https://github.com/influxdata/telegraf/pull/1838): Logging overhaul to centralize the logger & log levels, & provide a logfile config option.
|
||||
- [#1700](https://github.com/influxdata/telegraf/pull/1700): HAProxy plugin socket glob matching.
|
||||
- [#1847](https://github.com/influxdata/telegraf/pull/1847): Add Kubernetes plugin for retrieving pod metrics.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
_ "github.com/influxdata/telegraf/plugins/inputs/iptables"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/jolokia"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/kafka_consumer"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/kubernetes"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/leofs"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/logparser"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/lustre2"
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
# Kubernetes Input Plugin
|
||||
|
||||
**This plugin is experimental and may cause high cardinality issues with moderate to large Kubernetes deployments**
|
||||
|
||||
This input plugin talks to the kubelet api using the `/stats/summary` endpoint to gather metrics about the running pods and containers for a single host. It is assumed that this plugin is running as part of a `daemonset` within a kubernetes installation. This means that telegraf is running on every node within the cluster. Therefore, you should configure this plugin to talk to its locally running kubelet.
|
||||
|
||||
To find the ip address of the host you are running on you can issue a command like the following:
|
||||
```
|
||||
$ curl -s $API_URL/api/v1/namespaces/$POD_NAMESPACE/pods/$HOSTNAME --header "Authorization: Bearer $TOKEN" --insecure | jq -r '.status.hostIP'
|
||||
```
|
||||
In this case we used the downward API to pass in the `$POD_NAMESPACE` and `$HOSTNAME` is the hostname of the pod which is set by the kubernetes API.
|
||||
|
||||
## Summary Data
|
||||
|
||||
```json
|
||||
{
|
||||
"node": {
|
||||
"nodeName": "node1",
|
||||
"systemContainers": [
|
||||
{
|
||||
"name": "kubelet",
|
||||
"startTime": "2016-08-25T18:46:52Z",
|
||||
"cpu": {
|
||||
"time": "2016-09-27T16:57:31Z",
|
||||
"usageNanoCores": 56652446,
|
||||
"usageCoreNanoSeconds": 101437561712262
|
||||
},
|
||||
"memory": {
|
||||
"time": "2016-09-27T16:57:31Z",
|
||||
"usageBytes": 62529536,
|
||||
"workingSetBytes": 62349312,
|
||||
"rssBytes": 47509504,
|
||||
"pageFaults": 4769397409,
|
||||
"majorPageFaults": 13
|
||||
},
|
||||
"rootfs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800
|
||||
},
|
||||
"logs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800
|
||||
},
|
||||
"userDefinedMetrics": null
|
||||
},
|
||||
{
|
||||
"name": "bar",
|
||||
"startTime": "2016-08-25T18:46:52Z",
|
||||
"cpu": {
|
||||
"time": "2016-09-27T16:57:31Z",
|
||||
"usageNanoCores": 56652446,
|
||||
"usageCoreNanoSeconds": 101437561712262
|
||||
},
|
||||
"memory": {
|
||||
"time": "2016-09-27T16:57:31Z",
|
||||
"usageBytes": 62529536,
|
||||
"workingSetBytes": 62349312,
|
||||
"rssBytes": 47509504,
|
||||
"pageFaults": 4769397409,
|
||||
"majorPageFaults": 13
|
||||
},
|
||||
"rootfs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800
|
||||
},
|
||||
"logs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800
|
||||
},
|
||||
"userDefinedMetrics": null
|
||||
}
|
||||
],
|
||||
"startTime": "2016-08-25T18:46:52Z",
|
||||
"cpu": {
|
||||
"time": "2016-09-27T16:57:41Z",
|
||||
"usageNanoCores": 576996212,
|
||||
"usageCoreNanoSeconds": 774129887054161
|
||||
},
|
||||
"memory": {
|
||||
"time": "2016-09-27T16:57:41Z",
|
||||
"availableBytes": 10726387712,
|
||||
"usageBytes": 12313182208,
|
||||
"workingSetBytes": 5081538560,
|
||||
"rssBytes": 35586048,
|
||||
"pageFaults": 351742,
|
||||
"majorPageFaults": 1236
|
||||
},
|
||||
"network": {
|
||||
"time": "2016-09-27T16:57:41Z",
|
||||
"rxBytes": 213281337459,
|
||||
"rxErrors": 0,
|
||||
"txBytes": 292869995684,
|
||||
"txErrors": 0
|
||||
},
|
||||
"fs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800,
|
||||
"usedBytes": 16754286592
|
||||
},
|
||||
"runtime": {
|
||||
"imageFs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800,
|
||||
"usedBytes": 5809371475
|
||||
}
|
||||
}
|
||||
},
|
||||
"pods": [
|
||||
{
|
||||
"podRef": {
|
||||
"name": "foopod",
|
||||
"namespace": "foons",
|
||||
"uid": "6d305b06-8419-11e6-825c-42010af000ae"
|
||||
},
|
||||
"startTime": "2016-09-26T18:45:42Z",
|
||||
"containers": [
|
||||
{
|
||||
"name": "foocontainer",
|
||||
"startTime": "2016-09-26T18:46:43Z",
|
||||
"cpu": {
|
||||
"time": "2016-09-27T16:57:32Z",
|
||||
"usageNanoCores": 846503,
|
||||
"usageCoreNanoSeconds": 56507553554
|
||||
},
|
||||
"memory": {
|
||||
"time": "2016-09-27T16:57:32Z",
|
||||
"usageBytes": 30789632,
|
||||
"workingSetBytes": 30789632,
|
||||
"rssBytes": 30695424,
|
||||
"pageFaults": 10761,
|
||||
"majorPageFaults": 0
|
||||
},
|
||||
"rootfs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800,
|
||||
"usedBytes": 57344
|
||||
},
|
||||
"logs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800,
|
||||
"usedBytes": 24576
|
||||
},
|
||||
"userDefinedMetrics": null
|
||||
}
|
||||
],
|
||||
"network": {
|
||||
"time": "2016-09-27T16:57:34Z",
|
||||
"rxBytes": 70749124,
|
||||
"rxErrors": 0,
|
||||
"txBytes": 47813506,
|
||||
"txErrors": 0
|
||||
},
|
||||
"volume": [
|
||||
{
|
||||
"availableBytes": 7903948800,
|
||||
"capacityBytes": 7903961088,
|
||||
"usedBytes": 12288,
|
||||
"name": "volume1"
|
||||
},
|
||||
{
|
||||
"availableBytes": 7903956992,
|
||||
"capacityBytes": 7903961088,
|
||||
"usedBytes": 4096,
|
||||
"name": "volume2"
|
||||
},
|
||||
{
|
||||
"availableBytes": 7903948800,
|
||||
"capacityBytes": 7903961088,
|
||||
"usedBytes": 12288,
|
||||
"name": "volume3"
|
||||
},
|
||||
{
|
||||
"availableBytes": 7903952896,
|
||||
"capacityBytes": 7903961088,
|
||||
"usedBytes": 8192,
|
||||
"name": "volume4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Daemonset YAML
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: telegraf
|
||||
namespace: telegraf
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: telegraf
|
||||
spec:
|
||||
serviceAccount: telegraf
|
||||
containers:
|
||||
- name: telegraf
|
||||
image: quay.io/org/image:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: "HOST_PROC"
|
||||
value: "/rootfs/proc"
|
||||
- name: "HOST_SYS"
|
||||
value: "/rootfs/sys"
|
||||
volumeMounts:
|
||||
- name: sysro
|
||||
mountPath: /rootfs/sys
|
||||
readOnly: true
|
||||
- name: procro
|
||||
mountPath: /rootfs/proc
|
||||
readOnly: true
|
||||
- name: varrunutmpro
|
||||
mountPath: /var/run/utmp
|
||||
readOnly: true
|
||||
- name: logger-redis-creds
|
||||
mountPath: /var/run/secrets/deis/redis/creds
|
||||
volumes:
|
||||
- name: sysro
|
||||
hostPath:
|
||||
path: /sys
|
||||
- name: procro
|
||||
hostPath:
|
||||
path: /proc
|
||||
- name: varrunutmpro
|
||||
hostPath:
|
||||
path: /var/run/utmp
|
||||
```
|
||||
|
||||
### Line Protocol
|
||||
|
||||
#### kubernetes_pod_container
|
||||
```
|
||||
kubernetes_pod_container,host=ip-10-0-0-0.ec2.internal,
|
||||
container_name=deis-controller,namespace=deis,
|
||||
node_name=ip-10-0-0-0.ec2.internal, pod_name=deis-controller-3058870187-xazsr, cpu_usage_core_nanoseconds=2432835i,cpu_usage_nanocores=0i,
|
||||
logsfs_avaialble_bytes=121128271872i,logsfs_capacity_bytes=153567944704i,
|
||||
logsfs_used_bytes=20787200i,memory_major_page_faults=0i,
|
||||
memory_page_faults=175i,memory_rss_bytes=0i,
|
||||
memory_usage_bytes=0i,memory_working_set_bytes=0i,
|
||||
rootfs_available_bytes=121128271872i,rootfs_capacity_bytes=153567944704i,
|
||||
rootfs_used_bytes=1110016i 1476477530000000000
|
||||
```
|
||||
|
||||
#### kubernetes_pod_volume
|
||||
```
|
||||
kubernetes_pod_volume,host=ip-10-0-0-0.ec2.internal,name=default-token-f7wts,
|
||||
namespace=kube-system,node_name=ip-10-0-0-0.ec2.internal,
|
||||
pod_name=kubernetes-dashboard-v1.1.1-t4x4t, available_bytes=8415240192i,
|
||||
capacity_bytes=8415252480i,used_bytes=12288i 1476477530000000000
|
||||
```
|
||||
|
||||
#### kubernetes_pod_network
|
||||
```
|
||||
kubernetes_pod_network,host=ip-10-0-0-0.ec2.internal,namespace=deis,
|
||||
node_name=ip-10-0-0-0.ec2.internal,pod_name=deis-controller-3058870187-xazsr,
|
||||
rx_bytes=120671099i,rx_errors=0i,
|
||||
tx_bytes=102451983i,tx_errors=0i 1476477530000000000
|
||||
```
|
|
@ -0,0 +1,244 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
"github.com/influxdata/telegraf/internal/errchan"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
// Kubernetes represents the config object for the plugin
|
||||
type Kubernetes struct {
|
||||
URL string
|
||||
|
||||
// Bearer Token authorization file path
|
||||
BearerToken string `toml:"bearer_token"`
|
||||
|
||||
// Path to CA file
|
||||
SSLCA string `toml:"ssl_ca"`
|
||||
// Path to host cert file
|
||||
SSLCert string `toml:"ssl_cert"`
|
||||
// Path to cert key file
|
||||
SSLKey string `toml:"ssl_key"`
|
||||
// Use SSL but skip chain & host verification
|
||||
InsecureSkipVerify bool
|
||||
}
|
||||
|
||||
var sampleConfig = `
|
||||
## URL for the kubelet
|
||||
url = "http://1.1.1.1:10255"
|
||||
|
||||
## Use bearer token for authorization
|
||||
# bearer_token = /path/to/bearer/token
|
||||
|
||||
## Optional SSL Config
|
||||
# ssl_ca = /path/to/cafile
|
||||
# ssl_cert = /path/to/certfile
|
||||
# ssl_key = /path/to/keyfile
|
||||
## Use SSL but skip chain & host verification
|
||||
# insecure_skip_verify = false
|
||||
`
|
||||
|
||||
const (
|
||||
summaryEndpoint = `%s/stats/summary`
|
||||
)
|
||||
|
||||
func init() {
|
||||
inputs.Add("kubernetes", func() telegraf.Input {
|
||||
return &Kubernetes{}
|
||||
})
|
||||
}
|
||||
|
||||
//SampleConfig returns a sample config
|
||||
func (k *Kubernetes) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
//Description returns the description of this plugin
|
||||
func (k *Kubernetes) Description() string {
|
||||
return "Read metrics from the kubernetes kubelet api"
|
||||
}
|
||||
|
||||
//Gather collects kubernetes metrics from a given URL
|
||||
func (k *Kubernetes) Gather(acc telegraf.Accumulator) error {
|
||||
var wg sync.WaitGroup
|
||||
errChan := errchan.New(1)
|
||||
wg.Add(1)
|
||||
go func(k *Kubernetes) {
|
||||
defer wg.Done()
|
||||
errChan.C <- k.gatherSummary(k.URL, acc)
|
||||
}(k)
|
||||
wg.Wait()
|
||||
return errChan.Error()
|
||||
}
|
||||
|
||||
func buildURL(endpoint string, base string) (*url.URL, error) {
|
||||
u := fmt.Sprintf(endpoint, base)
|
||||
addr, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to parse address '%s': %s", u, err)
|
||||
}
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func (k *Kubernetes) gatherSummary(baseURL string, acc telegraf.Accumulator) error {
|
||||
url := fmt.Sprintf("%s/stats/summary", baseURL)
|
||||
var req, err = http.NewRequest("GET", url, nil)
|
||||
var token []byte
|
||||
var resp *http.Response
|
||||
|
||||
tlsCfg, err := internal.GetTLSConfig(k.SSLCert, k.SSLKey, k.SSLCA, k.InsecureSkipVerify)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rt http.RoundTripper = &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
TLSClientConfig: tlsCfg,
|
||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
||||
DisableKeepAlives: false,
|
||||
}
|
||||
|
||||
if k.BearerToken != "" {
|
||||
token, err = ioutil.ReadFile(k.BearerToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+string(token))
|
||||
}
|
||||
|
||||
resp, err = rt.RoundTrip(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making HTTP request to %s: %s", url, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("%s returned HTTP status %s", url, resp.Status)
|
||||
}
|
||||
|
||||
summaryMetrics := &SummaryMetrics{}
|
||||
err = json.NewDecoder(resp.Body).Decode(summaryMetrics)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`Error parsing response: %s`, err)
|
||||
}
|
||||
buildSystemContainerMetrics(summaryMetrics, acc)
|
||||
buildNodeMetrics(summaryMetrics, acc)
|
||||
buildPodMetrics(summaryMetrics, acc)
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildSystemContainerMetrics(summaryMetrics *SummaryMetrics, acc telegraf.Accumulator) {
|
||||
for _, container := range summaryMetrics.Node.SystemContainers {
|
||||
tags := map[string]string{
|
||||
"node_name": summaryMetrics.Node.NodeName,
|
||||
"container_name": container.Name,
|
||||
}
|
||||
fields := make(map[string]interface{})
|
||||
fields["cpu_usage_nanocores"] = container.CPU.UsageNanoCores
|
||||
fields["cpu_usage_core_nanoseconds"] = container.CPU.UsageCoreNanoSeconds
|
||||
fields["memory_usage_bytes"] = container.Memory.UsageBytes
|
||||
fields["memory_working_set_bytes"] = container.Memory.WorkingSetBytes
|
||||
fields["memory_rss_bytes"] = container.Memory.RSSBytes
|
||||
fields["memory_page_faults"] = container.Memory.PageFaults
|
||||
fields["memory_major_page_faults"] = container.Memory.MajorPageFaults
|
||||
fields["rootfs_available_bytes"] = container.RootFS.AvailableBytes
|
||||
fields["rootfs_capacity_bytes"] = container.RootFS.CapacityBytes
|
||||
fields["logsfs_avaialble_bytes"] = container.LogsFS.AvailableBytes
|
||||
fields["logsfs_capacity_bytes"] = container.LogsFS.CapacityBytes
|
||||
acc.AddFields("kubernetes_system_container", fields, tags)
|
||||
}
|
||||
}
|
||||
|
||||
func buildNodeMetrics(summaryMetrics *SummaryMetrics, acc telegraf.Accumulator) {
|
||||
tags := map[string]string{
|
||||
"node_name": summaryMetrics.Node.NodeName,
|
||||
}
|
||||
fields := make(map[string]interface{})
|
||||
fields["cpu_usage_nanocores"] = summaryMetrics.Node.CPU.UsageNanoCores
|
||||
fields["cpu_usage_core_nanoseconds"] = summaryMetrics.Node.CPU.UsageCoreNanoSeconds
|
||||
fields["memory_available_bytes"] = summaryMetrics.Node.Memory.AvailableBytes
|
||||
fields["memory_usage_bytes"] = summaryMetrics.Node.Memory.UsageBytes
|
||||
fields["memory_working_set_bytes"] = summaryMetrics.Node.Memory.WorkingSetBytes
|
||||
fields["memory_rss_bytes"] = summaryMetrics.Node.Memory.RSSBytes
|
||||
fields["memory_page_faults"] = summaryMetrics.Node.Memory.PageFaults
|
||||
fields["memory_major_page_faults"] = summaryMetrics.Node.Memory.MajorPageFaults
|
||||
fields["network_rx_bytes"] = summaryMetrics.Node.Network.RXBytes
|
||||
fields["network_rx_errors"] = summaryMetrics.Node.Network.RXErrors
|
||||
fields["network_tx_bytes"] = summaryMetrics.Node.Network.TXBytes
|
||||
fields["network_tx_errors"] = summaryMetrics.Node.Network.TXErrors
|
||||
fields["fs_available_bytes"] = summaryMetrics.Node.FileSystem.AvailableBytes
|
||||
fields["fs_capacity_bytes"] = summaryMetrics.Node.FileSystem.CapacityBytes
|
||||
fields["fs_used_bytes"] = summaryMetrics.Node.FileSystem.UsedBytes
|
||||
fields["runtime_image_fs_available_bytes"] = summaryMetrics.Node.Runtime.ImageFileSystem.AvailableBytes
|
||||
fields["runtime_image_fs_capacity_bytes"] = summaryMetrics.Node.Runtime.ImageFileSystem.CapacityBytes
|
||||
fields["runtime_image_fs_used_bytes"] = summaryMetrics.Node.Runtime.ImageFileSystem.UsedBytes
|
||||
acc.AddFields("kubernetes_node", fields, tags)
|
||||
}
|
||||
|
||||
func buildPodMetrics(summaryMetrics *SummaryMetrics, acc telegraf.Accumulator) {
|
||||
for _, pod := range summaryMetrics.Pods {
|
||||
for _, container := range pod.Containers {
|
||||
tags := map[string]string{
|
||||
"node_name": summaryMetrics.Node.NodeName,
|
||||
"namespace": pod.PodRef.Namespace,
|
||||
"container_name": container.Name,
|
||||
"pod_name": pod.PodRef.Name,
|
||||
}
|
||||
fields := make(map[string]interface{})
|
||||
fields["cpu_usage_nanocores"] = container.CPU.UsageNanoCores
|
||||
fields["cpu_usage_core_nanoseconds"] = container.CPU.UsageCoreNanoSeconds
|
||||
fields["memory_usage_bytes"] = container.Memory.UsageBytes
|
||||
fields["memory_working_set_bytes"] = container.Memory.WorkingSetBytes
|
||||
fields["memory_rss_bytes"] = container.Memory.RSSBytes
|
||||
fields["memory_page_faults"] = container.Memory.PageFaults
|
||||
fields["memory_major_page_faults"] = container.Memory.MajorPageFaults
|
||||
fields["rootfs_available_bytes"] = container.RootFS.AvailableBytes
|
||||
fields["rootfs_capacity_bytes"] = container.RootFS.CapacityBytes
|
||||
fields["rootfs_used_bytes"] = container.RootFS.UsedBytes
|
||||
fields["logsfs_avaialble_bytes"] = container.LogsFS.AvailableBytes
|
||||
fields["logsfs_capacity_bytes"] = container.LogsFS.CapacityBytes
|
||||
fields["logsfs_used_bytes"] = container.LogsFS.UsedBytes
|
||||
acc.AddFields("kubernetes_pod_container", fields, tags)
|
||||
}
|
||||
|
||||
for _, volume := range pod.Volumes {
|
||||
tags := map[string]string{
|
||||
"node_name": summaryMetrics.Node.NodeName,
|
||||
"pod_name": pod.PodRef.Name,
|
||||
"namespace": pod.PodRef.Namespace,
|
||||
"volume_name": volume.Name,
|
||||
}
|
||||
fields := make(map[string]interface{})
|
||||
fields["available_bytes"] = volume.AvailableBytes
|
||||
fields["capacity_bytes"] = volume.CapacityBytes
|
||||
fields["used_bytes"] = volume.UsedBytes
|
||||
acc.AddFields("kubernetes_pod_volume", fields, tags)
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"node_name": summaryMetrics.Node.NodeName,
|
||||
"pod_name": pod.PodRef.Name,
|
||||
"namespace": pod.PodRef.Namespace,
|
||||
}
|
||||
fields := make(map[string]interface{})
|
||||
fields["rx_bytes"] = pod.Network.RXBytes
|
||||
fields["rx_errors"] = pod.Network.RXErrors
|
||||
fields["tx_bytes"] = pod.Network.TXBytes
|
||||
fields["tx_errors"] = pod.Network.TXErrors
|
||||
acc.AddFields("kubernetes_pod_network", fields, tags)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package kubernetes
|
||||
|
||||
import "time"
|
||||
|
||||
// SummaryMetrics represents all the summary data about a paritcular node retrieved from a kubelet
|
||||
type SummaryMetrics struct {
|
||||
Node NodeMetrics `json:"node"`
|
||||
Pods []PodMetrics `json:"pods"`
|
||||
}
|
||||
|
||||
// NodeMetrics represents detailed information about a node
|
||||
type NodeMetrics struct {
|
||||
NodeName string `json:"nodeName"`
|
||||
SystemContainers []ContainerMetrics `json:"systemContainers"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
CPU CPUMetrics `json:"cpu"`
|
||||
Memory MemoryMetrics `json:"memory"`
|
||||
Network NetworkMetrics `json:"network"`
|
||||
FileSystem FileSystemMetrics `json:"fs"`
|
||||
Runtime RuntimeMetrics `json:"runtime"`
|
||||
}
|
||||
|
||||
// ContainerMetrics represents the metric data collect about a container from the kubelet
|
||||
type ContainerMetrics struct {
|
||||
Name string `json:"name"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
CPU CPUMetrics `json:"cpu"`
|
||||
Memory MemoryMetrics `json:"memory"`
|
||||
RootFS FileSystemMetrics `json:"rootfs"`
|
||||
LogsFS FileSystemMetrics `json:"logs"`
|
||||
}
|
||||
|
||||
// RuntimeMetrics contains metric data on the runtime of the system
|
||||
type RuntimeMetrics struct {
|
||||
ImageFileSystem FileSystemMetrics `json:"imageFs"`
|
||||
}
|
||||
|
||||
// CPUMetrics represents the cpu usage data of a pod or node
|
||||
type CPUMetrics struct {
|
||||
Time time.Time `json:"time"`
|
||||
UsageNanoCores int64 `json:"usageNanoCores"`
|
||||
UsageCoreNanoSeconds int64 `json:"usageCoreNanoSeconds"`
|
||||
}
|
||||
|
||||
// PodMetrics contains metric data on a given pod
|
||||
type PodMetrics struct {
|
||||
PodRef PodReference `json:"podRef"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
Containers []ContainerMetrics `json:"containers"`
|
||||
Network NetworkMetrics `json:"network"`
|
||||
Volumes []VolumeMetrics `json:"volume"`
|
||||
}
|
||||
|
||||
// PodReference is how a pod is identified
|
||||
type PodReference struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
// MemoryMetrics represents the memory metrics for a pod or node
|
||||
type MemoryMetrics struct {
|
||||
Time time.Time `json:"time"`
|
||||
AvailableBytes int64 `json:"availableBytes"`
|
||||
UsageBytes int64 `json:"usageBytes"`
|
||||
WorkingSetBytes int64 `json:"workingSetBytes"`
|
||||
RSSBytes int64 `json:"rssBytes"`
|
||||
PageFaults int64 `json:"pageFaults"`
|
||||
MajorPageFaults int64 `json:"majorPageFaults"`
|
||||
}
|
||||
|
||||
// FileSystemMetrics represents disk usage metrics for a pod or node
|
||||
type FileSystemMetrics struct {
|
||||
AvailableBytes int64 `json:"availableBytes"`
|
||||
CapacityBytes int64 `json:"capacityBytes"`
|
||||
UsedBytes int64 `json:"usedBytes"`
|
||||
}
|
||||
|
||||
// NetworkMetrics represents network usage data for a pod or node
|
||||
type NetworkMetrics struct {
|
||||
Time time.Time `json:"time"`
|
||||
RXBytes int64 `json:"rxBytes"`
|
||||
RXErrors int64 `json:"rxErrors"`
|
||||
TXBytes int64 `json:"txBytes"`
|
||||
TXErrors int64 `json:"txErrors"`
|
||||
}
|
||||
|
||||
// VolumeMetrics represents the disk usage data for a given volume
|
||||
type VolumeMetrics struct {
|
||||
Name string `json:"name"`
|
||||
AvailableBytes int64 `json:"availableBytes"`
|
||||
CapacityBytes int64 `json:"capacityBytes"`
|
||||
UsedBytes int64 `json:"usedBytes"`
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestKubernetesStats(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintln(w, response)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
k := &Kubernetes{
|
||||
URL: ts.URL,
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := k.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"cpu_usage_nanocores": int64(56652446),
|
||||
"cpu_usage_core_nanoseconds": int64(101437561712262),
|
||||
"memory_usage_bytes": int64(62529536),
|
||||
"memory_working_set_bytes": int64(62349312),
|
||||
"memory_rss_bytes": int64(47509504),
|
||||
"memory_page_faults": int64(4769397409),
|
||||
"memory_major_page_faults": int64(13),
|
||||
"rootfs_available_bytes": int64(84379979776),
|
||||
"rootfs_capacity_bytes": int64(105553100800),
|
||||
"logsfs_avaialble_bytes": int64(84379979776),
|
||||
"logsfs_capacity_bytes": int64(105553100800),
|
||||
}
|
||||
tags := map[string]string{
|
||||
"node_name": "node1",
|
||||
"container_name": "kubelet",
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "kubernetes_system_container", fields, tags)
|
||||
|
||||
fields = map[string]interface{}{
|
||||
"cpu_usage_nanocores": int64(576996212),
|
||||
"cpu_usage_core_nanoseconds": int64(774129887054161),
|
||||
"memory_usage_bytes": int64(12313182208),
|
||||
"memory_working_set_bytes": int64(5081538560),
|
||||
"memory_rss_bytes": int64(35586048),
|
||||
"memory_page_faults": int64(351742),
|
||||
"memory_major_page_faults": int64(1236),
|
||||
"memory_available_bytes": int64(10726387712),
|
||||
"network_rx_bytes": int64(213281337459),
|
||||
"network_rx_errors": int64(0),
|
||||
"network_tx_bytes": int64(292869995684),
|
||||
"network_tx_errors": int64(0),
|
||||
"fs_available_bytes": int64(84379979776),
|
||||
"fs_capacity_bytes": int64(105553100800),
|
||||
"fs_used_bytes": int64(16754286592),
|
||||
"runtime_image_fs_available_bytes": int64(84379979776),
|
||||
"runtime_image_fs_capacity_bytes": int64(105553100800),
|
||||
"runtime_image_fs_used_bytes": int64(5809371475),
|
||||
}
|
||||
tags = map[string]string{
|
||||
"node_name": "node1",
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "kubernetes_node", fields, tags)
|
||||
|
||||
fields = map[string]interface{}{
|
||||
"cpu_usage_nanocores": int64(846503),
|
||||
"cpu_usage_core_nanoseconds": int64(56507553554),
|
||||
"memory_usage_bytes": int64(30789632),
|
||||
"memory_working_set_bytes": int64(30789632),
|
||||
"memory_rss_bytes": int64(30695424),
|
||||
"memory_page_faults": int64(10761),
|
||||
"memory_major_page_faults": int64(0),
|
||||
"rootfs_available_bytes": int64(84379979776),
|
||||
"rootfs_capacity_bytes": int64(105553100800),
|
||||
"rootfs_used_bytes": int64(57344),
|
||||
"logsfs_avaialble_bytes": int64(84379979776),
|
||||
"logsfs_capacity_bytes": int64(105553100800),
|
||||
"logsfs_used_bytes": int64(24576),
|
||||
}
|
||||
tags = map[string]string{
|
||||
"node_name": "node1",
|
||||
"container_name": "foocontainer",
|
||||
"namespace": "foons",
|
||||
"pod_name": "foopod",
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "kubernetes_pod_container", fields, tags)
|
||||
|
||||
fields = map[string]interface{}{
|
||||
"available_bytes": int64(7903948800),
|
||||
"capacity_bytes": int64(7903961088),
|
||||
"used_bytes": int64(12288),
|
||||
}
|
||||
tags = map[string]string{
|
||||
"node_name": "node1",
|
||||
"volume_name": "volume1",
|
||||
"namespace": "foons",
|
||||
"pod_name": "foopod",
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "kubernetes_pod_volume", fields, tags)
|
||||
|
||||
fields = map[string]interface{}{
|
||||
"rx_bytes": int64(70749124),
|
||||
"rx_errors": int64(0),
|
||||
"tx_bytes": int64(47813506),
|
||||
"tx_errors": int64(0),
|
||||
}
|
||||
tags = map[string]string{
|
||||
"node_name": "node1",
|
||||
"namespace": "foons",
|
||||
"pod_name": "foopod",
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "kubernetes_pod_network", fields, tags)
|
||||
|
||||
}
|
||||
|
||||
var response = `
|
||||
{
|
||||
"node": {
|
||||
"nodeName": "node1",
|
||||
"systemContainers": [
|
||||
{
|
||||
"name": "kubelet",
|
||||
"startTime": "2016-08-25T18:46:52Z",
|
||||
"cpu": {
|
||||
"time": "2016-09-27T16:57:31Z",
|
||||
"usageNanoCores": 56652446,
|
||||
"usageCoreNanoSeconds": 101437561712262
|
||||
},
|
||||
"memory": {
|
||||
"time": "2016-09-27T16:57:31Z",
|
||||
"usageBytes": 62529536,
|
||||
"workingSetBytes": 62349312,
|
||||
"rssBytes": 47509504,
|
||||
"pageFaults": 4769397409,
|
||||
"majorPageFaults": 13
|
||||
},
|
||||
"rootfs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800
|
||||
},
|
||||
"logs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800
|
||||
},
|
||||
"userDefinedMetrics": null
|
||||
},
|
||||
{
|
||||
"name": "bar",
|
||||
"startTime": "2016-08-25T18:46:52Z",
|
||||
"cpu": {
|
||||
"time": "2016-09-27T16:57:31Z",
|
||||
"usageNanoCores": 56652446,
|
||||
"usageCoreNanoSeconds": 101437561712262
|
||||
},
|
||||
"memory": {
|
||||
"time": "2016-09-27T16:57:31Z",
|
||||
"usageBytes": 62529536,
|
||||
"workingSetBytes": 62349312,
|
||||
"rssBytes": 47509504,
|
||||
"pageFaults": 4769397409,
|
||||
"majorPageFaults": 13
|
||||
},
|
||||
"rootfs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800
|
||||
},
|
||||
"logs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800
|
||||
},
|
||||
"userDefinedMetrics": null
|
||||
}
|
||||
],
|
||||
"startTime": "2016-08-25T18:46:52Z",
|
||||
"cpu": {
|
||||
"time": "2016-09-27T16:57:41Z",
|
||||
"usageNanoCores": 576996212,
|
||||
"usageCoreNanoSeconds": 774129887054161
|
||||
},
|
||||
"memory": {
|
||||
"time": "2016-09-27T16:57:41Z",
|
||||
"availableBytes": 10726387712,
|
||||
"usageBytes": 12313182208,
|
||||
"workingSetBytes": 5081538560,
|
||||
"rssBytes": 35586048,
|
||||
"pageFaults": 351742,
|
||||
"majorPageFaults": 1236
|
||||
},
|
||||
"network": {
|
||||
"time": "2016-09-27T16:57:41Z",
|
||||
"rxBytes": 213281337459,
|
||||
"rxErrors": 0,
|
||||
"txBytes": 292869995684,
|
||||
"txErrors": 0
|
||||
},
|
||||
"fs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800,
|
||||
"usedBytes": 16754286592
|
||||
},
|
||||
"runtime": {
|
||||
"imageFs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800,
|
||||
"usedBytes": 5809371475
|
||||
}
|
||||
}
|
||||
},
|
||||
"pods": [
|
||||
{
|
||||
"podRef": {
|
||||
"name": "foopod",
|
||||
"namespace": "foons",
|
||||
"uid": "6d305b06-8419-11e6-825c-42010af000ae"
|
||||
},
|
||||
"startTime": "2016-09-26T18:45:42Z",
|
||||
"containers": [
|
||||
{
|
||||
"name": "foocontainer",
|
||||
"startTime": "2016-09-26T18:46:43Z",
|
||||
"cpu": {
|
||||
"time": "2016-09-27T16:57:32Z",
|
||||
"usageNanoCores": 846503,
|
||||
"usageCoreNanoSeconds": 56507553554
|
||||
},
|
||||
"memory": {
|
||||
"time": "2016-09-27T16:57:32Z",
|
||||
"usageBytes": 30789632,
|
||||
"workingSetBytes": 30789632,
|
||||
"rssBytes": 30695424,
|
||||
"pageFaults": 10761,
|
||||
"majorPageFaults": 0
|
||||
},
|
||||
"rootfs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800,
|
||||
"usedBytes": 57344
|
||||
},
|
||||
"logs": {
|
||||
"availableBytes": 84379979776,
|
||||
"capacityBytes": 105553100800,
|
||||
"usedBytes": 24576
|
||||
},
|
||||
"userDefinedMetrics": null
|
||||
}
|
||||
],
|
||||
"network": {
|
||||
"time": "2016-09-27T16:57:34Z",
|
||||
"rxBytes": 70749124,
|
||||
"rxErrors": 0,
|
||||
"txBytes": 47813506,
|
||||
"txErrors": 0
|
||||
},
|
||||
"volume": [
|
||||
{
|
||||
"availableBytes": 7903948800,
|
||||
"capacityBytes": 7903961088,
|
||||
"usedBytes": 12288,
|
||||
"name": "volume1"
|
||||
},
|
||||
{
|
||||
"availableBytes": 7903956992,
|
||||
"capacityBytes": 7903961088,
|
||||
"usedBytes": 4096,
|
||||
"name": "volume2"
|
||||
},
|
||||
{
|
||||
"availableBytes": 7903948800,
|
||||
"capacityBytes": 7903961088,
|
||||
"usedBytes": 12288,
|
||||
"name": "volume3"
|
||||
},
|
||||
{
|
||||
"availableBytes": 7903952896,
|
||||
"capacityBytes": 7903961088,
|
||||
"usedBytes": 8192,
|
||||
"name": "volume4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
Loading…
Reference in New Issue