Skip to content

Commit

Permalink
Merge pull request #3 from jedipunkz/feature/change-struct-name
Browse files Browse the repository at this point in the history
change struct name
  • Loading branch information
jedipunkz committed Jan 8, 2023
2 parents 7445d3f + 63b8cc0 commit c7a8671
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 12 deletions.
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### Builder for Golang
FROM golang:1.19 as go-builder
MAINTAINER @jedipunkz

WORKDIR /go/src/
ADD . /go/src/

RUN go mod download
RUN CGO_ENABLED=0 go build -o /go/bin/esp

## Deployable image
FROM alpine:latest as esp-base
MAINTAINER @jedipunkz

COPY --from=go-builder /go/bin/esp /go/bin/esp

ENTRYPOINT ["/bin/sh", "-c" , "/go/bin/esp"]
111 changes: 110 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,116 @@

## Description

ESP Retrives ECS Container Stats from Metadata Endpoint and Plots Stats to Cloudwatch Detailed Metrics.
ESP Retrives AWS ECS Container Stats from Metadata Endpoint and Plots Stats to Cloudwatch Detailed Metrics.
ESP enables faster autoscaling of AWS ECS Tasks by indexing AWS Cloudwatch high-resolution Custom Metrics.

## Requirement

### Add cloudwatch:PutMetricData Permission to Task Role.

add `cloudwatch:PutMetricData` to task role.

```hcl
statement {
effect = "Allow"
actions = [
"cloudwatch:PutMetricData"
]
resources = ["*"]
}
```

## Usage

### Build and Push to Repository

Build docker image and push to ECR repository.

```shell
docker build --platform linux/amd64 -t esp .
docker push *******.dkr.ecr.us-east-1.amazonaws.com/esp:latest
```

### Add ESP as a sidecar container

Add ESP as a ECS sidecar container in the container definition below.

```json
[
{
"name": "web",
...<snip>...
{
"name": "esp",
"image": "********.dkr.ecr.us-east-1.amazonaws.com/esp:latest",
"essential": true,
"environment": [
{
"name": "CONTAINER_NAME",
"value": "web"
},
{
"name": "REGION",
"value": "us-east-1"
},
{
"name": "NAMESPACE",
"value": "FOO"
}
],
"dependsOn": [
{
"containerName": "web",
"condition": "START"
}
]
}
]
```

| Environment Name | Description |
|---|---|
| CONTAINER_NAME | Container name for which you want to measure impossibility |
| REGION | AWS Region Name |
| NAMESPACE | Namespace name for Cloudwatch high resolution custom metrics |

### Set Autoscalling

Configure Cloudwatch Metric Alarm for Autoscalling. Below is an example of Terraform Code.

```hcl
resource "aws_cloudwatch_metric_alarm" "foo" {
alarm_name = "foo-CPU-Utilization-High-30"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "1"
metric_name = "CPUUtilization"
namespace = "FOO"
period = "60"
statistic = "Average"
threshold = "15"
dimensions = {
ClusterName = aws_ecs_cluster.foo.name
ServiceName = aws_ecs_service.foo.name
}
alarm_actions = [aws_appautoscaling_policy.scale_out.arn]
}

resource "aws_cloudwatch_metric_alarm" "foo" {
alarm_name = "foo-CPU-Utilization-Low-5"
comparison_operator = "LessThanThreshold"
evaluation_periods = "1"
metric_name = "CPUUtilization"
namespace = "FOO"
period = "60"
statistic = "Average"
threshold = "5"
dimensions = {
ClusterName = aws_ecs_cluster.foo.name
ServiceName = aws_ecs_service.foo.name
}
alarm_actions = [aws_appautoscaling_policy.scale_in.arn]
}
```

## Author

Expand Down
8 changes: 4 additions & 4 deletions cloudwatch/cloudwatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func NewCloudwatch(params *cloudwatch.PutMetricDataInput) *Cloudwatch {
return &Cloudwatch{sess, svc, params}
}

func PutMetricData(value float64) error {
func PutMetricData(value float64) (resp string, err error) {
namespace := os.Getenv("NAMESPACE")
if namespace == "" {
log.Printf("error retriving namespace from environment variable")
Expand All @@ -53,12 +53,12 @@ func PutMetricData(value float64) error {

client := NewCloudwatch(params)

_, err := client.svc.PutMetricData(params)
_, err = client.svc.PutMetricData(params)
if err != nil {
log.Println(err.Error())
return err
return "", err
}
// log.Println(resp)

return nil
return resp, nil
}
6 changes: 3 additions & 3 deletions ecs/ecsmetadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type TaskMetadata struct {
} `json:"Containers"`
}

type StatsMetadata struct {
type ContainerMetadata struct {
CPUStats struct {
CPUUsage struct {
TotalUsage int `json:"total_usage"`
Expand Down Expand Up @@ -123,8 +123,8 @@ func (c *Client) RetriveTaskMetadata(ctx context.Context) (TaskMetadata, error)
return output, err
}

func (c *Client) RetriveStatsMetadata(ctx context.Context) (map[string]StatsMetadata, error) {
output := make(map[string]StatsMetadata)
func (c *Client) RetriveContainersMetadata(ctx context.Context) (map[string]ContainerMetadata, error) {
output := make(map[string]ContainerMetadata)
err := c.request(ctx, c.endpoint+"/task/stats", &output)
return output, err
}
12 changes: 8 additions & 4 deletions esp.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ func main() {
log.Printf("error retrieving task metadata: %s", err)
}

statsMetadata, err := client.RetriveStatsMetadata(ctx)
containersMetadata, err := client.RetriveContainersMetadata(ctx)
if err != nil {
log.Printf("error retrieving task stats metadata: %s", err)
}

containerName := os.Getenv("CONTAINER_NAME")
if containerName == "" {
log.Printf("error retrieving container name from environment variable")
log.Fatal("error retrieving container name from environment variable")
}

for {
for _, container := range taskMetadata.Containers {
if container.Name == containerName {
s := statsMetadata[container.DockerID]
s := containersMetadata[container.DockerID]
if &s == nil {
log.Printf("Could not find stats for container %s", container.DockerID)
continue
Expand All @@ -45,7 +45,11 @@ func main() {
(float64(s.CPUStats.SystemCPUUsage) - float64(s.PreCPUStats.SystemCPUUsage)) *
float64(s.CPUStats.OnlineCPUs) * 100

cloudwatch.PutMetricData(cpuUsage)
_, err = cloudwatch.PutMetricData(cpuUsage)
if err != nil {
log.Printf("Error putting metric data: %s", err)
}

log.Printf("Container Name: %s, CPU Usage: %f", container.Name, cpuUsage)
}
}
Expand Down

0 comments on commit c7a8671

Please sign in to comment.