Skip to content

Commit

Permalink
Merge pull request #131 from instana/fargate_support
Browse files Browse the repository at this point in the history
Send metrics and traces for services running in AWS Fargate
  • Loading branch information
Andrew Slotin authored Jul 3, 2020
2 parents a22f3e7 + 454bb41 commit 50ca247
Show file tree
Hide file tree
Showing 18 changed files with 1,424 additions and 75 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ matrix:

script:
- make test
- make integration

notifications:
slack:
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

MODULES = $(filter-out $(EXCLUDE_DIRS), $(shell find . -name go.mod -exec dirname {} \;))
LINTER ?= $(shell go env GOPATH)/bin/golangci-lint
INTEGRATION_TESTS = fargate_integration

ifdef RUN_LINTER
test: $(LINTER)
Expand All @@ -14,8 +15,13 @@ ifdef RUN_LINTER
cd $@ && $(LINTER) run
endif

integration: $(INTEGRATION_TESTS)

$(INTEGRATION_TESTS):
go test $(GOFLAGS) -tags $@ $(shell grep -lR "// +build $@" .)

$(LINTER):
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/a2bc9b7a99e3280805309d71036e8c2106853250/install.sh \
| sh -s -- -b $(basename $(GOPATH))/bin v1.23.8

.PHONY: test $(MODULES)
.PHONY: test $(MODULES) $(INTEGRATION_TESTS)
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,36 @@ The Instana Go sensor consists of two parts:
[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/instana/go-sensor)
[![OpenTracing Badge](https://img.shields.io/badge/OpenTracing-enabled-blue.svg)](http://opentracing.io)

## Installation

To add Instana Go sensor to your service run:

```bash
$ go get github.com/instana/go-sensor
```

To activate background metrics collection, add following line at the beginning of your service initialization (typically this would be the beginning of your `main()` function):

```go
func main() {
instana.InitSensor(instana.DefaultOptions())

// ...
}
```

Once initialized, the sensor performs a host agent lookup using following list of addresses (in order of priority):

1. The value of `INSTANA_AGENT_HOST` env variable
2. `localhost`
3. Default gateway

Once a host agent found listening on port `42699` (or the port specified in `INSTANA_AGENT_PORT` env variable) the sensor begins collecting in-app metrics and sending them to the host agent.

### Running on AWS Fargate

To use Instana Go sensor for monitoring a service running on AWS Fargate make sure that you have `INSTANA_ENDPOINT_URL` and `INSTANA_AGENT_KEY` env variables set in your task definition. Please refer to [Instana documentation](https://www.instana.com/docs/ecosystem/aws-fargate/#configure-your-task-definition) for detailed explanation on how to do this.

## Common Operations

The Instana Go sensor offers a set of quick features to support tracing of the most common operations like handling HTTP requests and executing HTTP requests.
Expand Down
191 changes: 191 additions & 0 deletions acceptor/plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// Package acceptor provides marshaling structs for Instana serverless acceptor API
package acceptor

import (
"strconv"
"time"

"github.com/instana/go-sensor/aws"
)

// PluginPayload represents the Instana acceptor message envelope containing plugin
// name and entity ID
type PluginPayload struct {
Name string `json:"name"`
EntityID string `json:"entityId"`
Data interface{} `json:"data"`
}

// AWSContainerLimits is used to send container limits (CPU, memory) to the acceptor plugin
type AWSContainerLimits struct {
CPU int `json:"cpu"`
Memory int `json:"memory"`
}

// ECSTaskData is a representation of an ECS task for com.instana.plugin.aws.ecs.task plugin
type ECSTaskData struct {
TaskARN string `json:"taskArn"`
ClusterARN string `json:"clusterArn"`
AvailabilityZone string `json:"availabilityZone,omitempty"`
TaskDefinition string `json:"taskDefinition"`
TaskDefinitionVersion string `json:"taskDefinitionVersion"`
DesiredStatus string `json:"desiredStatus"`
KnownStatus string `json:"knownStatus"`
Limits AWSContainerLimits `json:"limits"`
PullStartedAt time.Time `json:"pullStartedAt"`
PullStoppedAt time.Time `json:"pullStoppedAt"`
}

// NewECSTaskPluginPayload returns payload for the ECS task plugin of Instana acceptor
func NewECSTaskPluginPayload(entityID string, data ECSTaskData) PluginPayload {
const pluginName = "com.instana.plugin.aws.ecs.task"

return PluginPayload{
Name: pluginName,
EntityID: entityID,
Data: data,
}
}

// ECSContainerData is a representation of an ECS container for com.instana.plugin.aws.ecs.container plugin
type ECSContainerData struct {
Runtime string `json:"runtime"`
Instrumented bool `json:"instrumented,omitempty"`
DockerID string `json:"dockerId"`
DockerName string `json:"dockerName"`
ContainerName string `json:"containerName"`
Image string `json:"image"`
ImageID string `json:"imageId"`
TaskARN string `json:"taskArn"`
TaskDefinition string `json:"taskDefinition"`
TaskDefinitionVersion string `json:"taskDefinitionVersion"`
ClusterARN string `json:"clusterArn"`
DesiredStatus string `json:"desiredStatus"`
KnownStatus string `json:"knownStatus"`
Limits AWSContainerLimits `json:"limits"`
CreatedAt time.Time `json:"createdAt"`
StartedAt time.Time `json:"startedAt"`
Type string `json:"type"`
}

// NewECSContainerPluginPayload returns payload for the ECS container plugin of Instana acceptor
func NewECSContainerPluginPayload(entityID string, data ECSContainerData) PluginPayload {
const pluginName = "com.instana.plugin.aws.ecs.container"

return PluginPayload{
Name: pluginName,
EntityID: entityID,
Data: data,
}
}

// DockerData is a representation of a Docker container for com.instana.plugin.docker plugin
type DockerData struct {
ID string `json:"Id"`
Command string `json:"Command"`
CreatedAt time.Time `json:"Created"`
StartedAt time.Time `json:"Started"`
Image string `json:"Image"`
Labels aws.ContainerLabels `json:"Labels,omitempty"`
Ports string `json:"Ports,omitempty"`
PortBindings string `json:"PortBindings,omitempty"`
Names []string `json:"Names,omitempty"`
NetworkMode string `json:"NetworkMode,omitempty"`
StorageDriver string `json:"StorageDriver,omitempty"`
DockerVersion string `json:"docker_version,omitempty"`
DockerAPIVersion string `json:"docker_api_version,omitempty"`
Memory int `json:"Memory"`
}

// NewDockerPluginPayload returns payload for the Docker plugin of Instana acceptor
func NewDockerPluginPayload(entityID string, data DockerData) PluginPayload {
const pluginName = "com.instana.plugin.docker"

return PluginPayload{
Name: pluginName,
EntityID: entityID,
Data: data,
}
}

// ProcessData is a representation of a running process for com.instana.plugin.process plugin
type ProcessData struct {
PID int `json:"pid"`
Exec string `json:"exec"`
Args []string `json:"args,omitempty"`
Env map[string]string `json:"env,omitempty"`
User string `json:"user,omitempty"`
Group string `json:"group,omitempty"`
ContainerID string `json:"container,omitempty"`
ContainerPid int `json:"containerPid,string,omitempty"`
ContainerType string `json:"containerType,omitempty"`
Start int64 `json:"start"`
HostName string `json:"com.instana.plugin.host.name"`
HostPID int `json:"com.instana.plugin.host.pid,string"`
}

// NewProcessPluginPayload returns payload for the process plugin of Instana acceptor
func NewProcessPluginPayload(entityID string, data ProcessData) PluginPayload {
const pluginName = "com.instana.plugin.process"

return PluginPayload{
Name: pluginName,
EntityID: entityID,
Data: data,
}
}

// RuntimeInfo represents Go runtime info to be sent to com.insana.plugin.golang
type RuntimeInfo struct {
Name string `json:"name"`
Version string `json:"version"`
Root string `json:"goroot"`
MaxProcs int `json:"maxprocs"`
Compiler string `json:"compiler"`
NumCPU int `json:"cpu"`
}

// MemoryStats represents Go runtime memory stats to be sent to com.insana.plugin.golang
type MemoryStats struct {
Alloc uint64 `json:"alloc"`
TotalAlloc uint64 `json:"total_alloc"`
Sys uint64 `json:"sys"`
Lookups uint64 `json:"lookups"`
Mallocs uint64 `json:"mallocs"`
Frees uint64 `json:"frees"`
HeapAlloc uint64 `json:"heap_alloc"`
HeapSys uint64 `json:"heap_sys"`
HeapIdle uint64 `json:"heap_idle"`
HeapInuse uint64 `json:"heap_in_use"`
HeapReleased uint64 `json:"heap_released"`
HeapObjects uint64 `json:"heap_objects"`
PauseTotalNs uint64 `json:"pause_total_ns"`
PauseNs uint64 `json:"pause_ns"`
NumGC uint32 `json:"num_gc"`
GCCPUFraction float64 `json:"gc_cpu_fraction"`
}

// Metrics represents Go process metrics to be sent to com.insana.plugin.golang
type Metrics struct {
CgoCall int64 `json:"cgo_call"`
Goroutine int `json:"goroutine"`
MemoryStats `json:"memory"`
}

// GoProcessData is a representation of a Go process for com.instana.plugin.golang plugin
type GoProcessData struct {
PID int `json:"pid"`
Snapshot *RuntimeInfo `json:"snapshot,omitempty"`
Metrics Metrics `json:"metrics"`
}

// NewGoProcessPluginPayload returns payload for the Go process plugin of Instana acceptor
func NewGoProcessPluginPayload(data GoProcessData) PluginPayload {
const pluginName = "com.instana.plugin.golang"

return PluginPayload{
Name: pluginName,
EntityID: strconv.Itoa(data.PID),
Data: data,
}
}
68 changes: 68 additions & 0 deletions acceptor/plugins_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package acceptor_test

import (
"testing"

"github.com/instana/go-sensor/acceptor"
"github.com/stretchr/testify/assert"
)

func TestNewECSTaskPluginPayload(t *testing.T) {
data := acceptor.ECSTaskData{
TaskARN: "arn::task",
}

assert.Equal(t, acceptor.PluginPayload{
Name: "com.instana.plugin.aws.ecs.task",
EntityID: "id1",
Data: data,
}, acceptor.NewECSTaskPluginPayload("id1", data))
}

func TestNewECSContainerPluginPayload(t *testing.T) {
data := acceptor.ECSContainerData{
DockerID: "docker1",
}

assert.Equal(t, acceptor.PluginPayload{
Name: "com.instana.plugin.aws.ecs.container",
EntityID: "id1",
Data: data,
}, acceptor.NewECSContainerPluginPayload("id1", data))
}

func TestNewDockerPluginPayload(t *testing.T) {
data := acceptor.DockerData{
ID: "docker1",
}

assert.Equal(t, acceptor.PluginPayload{
Name: "com.instana.plugin.docker",
EntityID: "id1",
Data: data,
}, acceptor.NewDockerPluginPayload("id1", data))
}

func TestNewProcessPluginPayload(t *testing.T) {
data := acceptor.ProcessData{
PID: 42,
}

assert.Equal(t, acceptor.PluginPayload{
Name: "com.instana.plugin.process",
EntityID: "id1",
Data: data,
}, acceptor.NewProcessPluginPayload("id1", data))
}

func TestNewGoProcessPluginPayload(t *testing.T) {
data := acceptor.GoProcessData{
PID: 42,
}

assert.Equal(t, acceptor.PluginPayload{
Name: "com.instana.plugin.golang",
EntityID: "42",
Data: data,
}, acceptor.NewGoProcessPluginPayload(data))
}
Loading

0 comments on commit 50ca247

Please sign in to comment.