parent
a4e8f24b16
commit
a12bd878e0
|
@ -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.
|
- [#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.
|
- [#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.
|
- [#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
|
### Bugfixes
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import (
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/iptables"
|
_ "github.com/influxdata/telegraf/plugins/inputs/iptables"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/jolokia"
|
_ "github.com/influxdata/telegraf/plugins/inputs/jolokia"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/kafka_consumer"
|
_ "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/leofs"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/logparser"
|
_ "github.com/influxdata/telegraf/plugins/inputs/logparser"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/lustre2"
|
_ "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