# AWS

> ECS, Fargate, Lambda and EC2

Three separate things, and most accounts want two of them:

| What | How |
| --- | --- |
| Your own services | Instrument the app, send direct or via a collector |
| Managed AWS services (RDS, ELB, SQS) | CloudWatch metric stream, via the CloudFormation stack |
| Host metrics (CPU, memory, disk) | The collector's `hostmetrics` receiver |

## ECS and Fargate — collector sidecar

On Fargate you cannot run a node agent, so the collector goes in the task as a second
container. Your app talks to it over localhost and only the collector holds the key.

```yaml
receivers:
  otlp:
    protocols:
      http: { endpoint: 0.0.0.0:4318 }

processors:
  batch: { timeout: 5s }
  resourcedetection:
    detectors: [env, ecs]
    timeout: 2s

exporters:
  otlphttp/rocketgraph:
    endpoint: https://ingress.rocketlog.io
    headers:
      Authorization: "Bearer ${ROCKETGRAPH_API_KEY}"

service:
  pipelines:
    traces:  { receivers: [otlp], processors: [resourcedetection, batch], exporters: [otlphttp/rocketgraph] }
    metrics: { receivers: [otlp], processors: [resourcedetection, batch], exporters: [otlphttp/rocketgraph] }
    logs:    { receivers: [otlp], processors: [resourcedetection, batch], exporters: [otlphttp/rocketgraph] }
```

In the task definition, run the collector alongside your app:

```json
{
  "name": "otel-collector",
  "image": "public.ecr.aws/aws-observability/aws-otel-collector:latest",
  "secrets": [
    { "name": "ROCKETGRAPH_API_KEY",
      "valueFrom": "arn:aws:secretsmanager:us-east-2:<account-id>:secret:rocketgraph-api-key" }
  ],
  "essential": false
}
```

> **Note:** Two things worth copying: `essential: false` so a collector crash does not kill the task,
  and the key delivered through `secrets` from Secrets Manager rather than a plain
  environment variable readable by anyone with `ecs:DescribeTaskDefinition`.

Your app container then needs only:

```
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_SERVICE_NAME=my-service
```

## Lambda — the ADOT layer

```bash
aws lambda update-function-configuration \
  --function-name my-function \
  --layers "arn:aws:lambda:<region>:901920570463:layer:aws-otel-<runtime>-<arch>-ver-<version>:<n>" \
  --environment "Variables={
    AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler,
    OTEL_SERVICE_NAME=my-function,
    OTEL_EXPORTER_OTLP_ENDPOINT=https://ingress.rocketlog.io,
    OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf,
    OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer rg_live_xxxxxx
  }"
```

Layer ARNs are region- and runtime-specific and AWS revises them — get the current one
from the [ADOT Lambda docs](https://aws-otel.github.io/docs/getting-started/lambda)
rather than copying an ARN.

> **Warning:** `AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler` is what activates the layer. Attaching the
  layer without it is the most common reason a Lambda reports nothing.

Lambda freezes the environment when the handler returns, which can strand buffered spans
until the next invocation. For low-traffic functions, export synchronously:

```
OTEL_BSP_SCHEDULE_DELAY=100
OTEL_BSP_MAX_EXPORT_BATCH_SIZE=1
```

## EC2 — collector as a systemd service

```bash
sudo rpm -Uvh https://aws-otel-collector.s3.amazonaws.com/amazon_linux/amd64/latest/aws-otel-collector.rpm
sudo systemctl enable --now aws-otel-collector
```

Use the same config as the sidecar above, swapping the `ecs` detector for `ec2` and adding
the `hostmetrics` receiver for CPU, memory, disk and network.

## Managed services

Instrumentation only covers services you deploy. For RDS, ELB, SQS and Lambda platform
metrics, use the CloudFormation stack on the **Infrastructure → AWS** page of your
dashboard, which sets up a CloudWatch metric stream into the same ingress.

> **Note:** Metric streams do not cross regions — an account spanning `us-east-1` and `us-east-2`
  needs the stack deployed in both.
