Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,12 @@ that every minute, and by default Prometheus will reload the
file the minute it is written). After reloading your Prometheus
master configuration, this program will begin informing via
the discovery file of new targets that Prometheus must scrape.

### Multiple Ports

If your container exposes metrics on more than one port for a single task you can use multiple labels as so:

* `PROMETHEUS_EXPORTER_PORT.1` `9180` - Set the first port exposing metrics
* `PROMETHEUS_EXPORTER_PORT.2` `8080` - Set the second port exposing metrics
* `PROMETHEUS_EXPORTER_PATH.2` `/api/v1/metrics` - The second port exposes metrics on a non-standard path of `/api/v1/metrics`
* `PROMETHEUS_EXPORTER_JOB_NAME.2` `logging` - The second port has a separate job name, `logging`, useful for differentiating otherwise identical metrics
147 changes: 93 additions & 54 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"fmt"
"io/ioutil"
"log"
"regexp"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -203,8 +204,8 @@ func (t *AugmentedTask) ExporterInformation() []*PrometheusTaskInfo {
// Nope, no match, this container cannot be exported. We continue.
continue
}
portPairs := make(map[string]PortPair)

var hostPort int64
if *prometheusDynamicPortDetection {
v, ok := d.DockerLabels[dynamicPortLabel]
if !ok || v != "1" {
Expand All @@ -220,13 +221,43 @@ func (t *AugmentedTask) ExporterInformation() []*PrometheusTaskInfo {
}

if port := i.NetworkBindings[0].HostPort; port != nil {
hostPort = *port
portPairs[""] = PortPair{
exporterPort: int(*port),
hostPort: int(*port),
}
}
} else {
v, ok := d.DockerLabels[*prometheusPortLabel]
if !ok {
// Nope, no Prometheus-exported port in this container def.
// This container is no good. We continue.
re := regexp.MustCompile(`(` + *prometheusPortLabel + `)(.*)`)

var exporterPort int
for key := range d.DockerLabels {
result := re.FindAllSubmatch([]byte(key), -1)
if len(result) == 0 {
continue
}

var portString string
var portName string
if len(result[0]) == 1 || len(result[0]) == 2 {
portString = d.DockerLabels[string(result[0][0])]
portName = ""
} else if len(result[0]) > 2 {
portString = d.DockerLabels[string(result[0][0])]
portName = string(result[0][2])
} else {
continue
}

if exporterPort, err := strconv.Atoi(portString); err != nil || exporterPort < 0 {
// This container has an invalid port definition.
// This container is no good. We continue.
continue
} else {
portPairs[portName] = PortPair{exporterPort: exporterPort}
}
}

if len(portPairs) == 0 {
continue
}

Expand All @@ -242,62 +273,65 @@ func (t *AugmentedTask) ExporterInformation() []*PrometheusTaskInfo {
}
}

var exporterPort int
var err error
if exporterPort, err = strconv.Atoi(v); err != nil || exporterPort < 0 {
// This container has an invalid port definition.
// This container is no good. We continue.
continue
}

if len(i.NetworkBindings) > 0 {
for _, nb := range i.NetworkBindings {
if int(*nb.ContainerPort) == exporterPort {
hostPort = *nb.HostPort
for key, portPathPair := range portPairs {
if len(i.NetworkBindings) > 0 {
for _, nb := range i.NetworkBindings {
if int(*nb.ContainerPort) == portPathPair.exporterPort {
portPathPair.hostPort = int(*nb.HostPort)
portPairs[key] = portPathPair
}
}
}
} else {
for _, ni := range i.NetworkInterfaces {
if *ni.PrivateIpv4Address != "" {
ip = *ni.PrivateIpv4Address
} else {
for _, ni := range i.NetworkInterfaces {
if *ni.PrivateIpv4Address != "" {
ip = *ni.PrivateIpv4Address
}
}
portPathPair.hostPort = exporterPort
portPairs[key] = portPathPair
}
hostPort = int64(exporterPort)
}
}
var exporterServerName string
var exporterPath string
var exporterJobName string
var ok bool
exporterServerName, ok = d.DockerLabels[*prometheusServerNameLabel]
if ok {
host = strings.TrimRight(exporterServerName, "/")
} else {
// No server name, so fall back to ip address
host = ip
}

var exporterServerName string
var exporterPath string
var ok bool
exporterServerName, ok = d.DockerLabels[*prometheusServerNameLabel]
if ok {
host = strings.TrimRight(exporterServerName, "/")
} else {
// No server name, so fall back to ip address
host = ip
}
for key, portPathPair := range portPairs {
labels := labels{
TaskArn: *t.TaskArn,
TaskName: *t.TaskDefinition.Family,
JobName: d.DockerLabels[*prometheusJobNameLabel],
TaskRevision: fmt.Sprintf("%d", *t.TaskDefinition.Revision),
TaskGroup: *t.Group,
ClusterArn: *t.ClusterArn,
ContainerName: *i.Name,
ContainerArn: *i.ContainerArn,
DockerImage: *d.Image,
}

labels := labels{
TaskArn: *t.TaskArn,
TaskName: *t.TaskDefinition.Family,
JobName: d.DockerLabels[*prometheusJobNameLabel],
TaskRevision: fmt.Sprintf("%d", *t.TaskDefinition.Revision),
TaskGroup: *t.Group,
ClusterArn: *t.ClusterArn,
ContainerName: *i.Name,
ContainerArn: *i.ContainerArn,
DockerImage: *d.Image,
}
exporterPath, ok = d.DockerLabels[*prometheusPathLabel+key]
if ok {
labels.MetricsPath = exporterPath
}

exporterPath, ok = d.DockerLabels[*prometheusPathLabel]
if ok {
labels.MetricsPath = exporterPath
}
exporterJobName, ok = d.DockerLabels[*prometheusJobNameLabel+key]
if ok {
labels.JobName = exporterJobName
}

ret = append(ret, &PrometheusTaskInfo{
Targets: []string{fmt.Sprintf("%s:%d", host, hostPort)},
Labels: labels,
})
ret = append(ret, &PrometheusTaskInfo{
Targets: []string{fmt.Sprintf("%s:%d", host, portPathPair.hostPort)},
Labels: labels,
})
}
}
}
return ret
}
Expand Down Expand Up @@ -688,3 +722,8 @@ func main() {
}
}
}

type PortPair struct {
exporterPort int
hostPort int
}