AWS 기술 블로그

Amazon ECS에서 Spring Boot 애플리케이션 관찰 가능성(Observability) 구성하기

본 블로그는 ‘Amazon EKS에서 완전 관리형 서비스를 활용하여 Spring Boot 애플리케이션 관찰 가능성(Observability) 구성하기’ 블로그의 후속편입니다. 관찰가능성의 정의와 구성 요소는 위 게시글에서 확인하시기 바랍니다.

이 블로그에서는 AWS 관찰 가능성 도구인 AWS Distro for OpenTelemetry(ADOT)로 Trace 정보를 수집하고 애플리케이션 메트릭을 Amazon Managed Service for Prometheus(AMP)로 전송하여 Amazon Managed Grafana(AMG)에서 Amazon Elastic Kubernetes Service(EKS)환경과 애플리케이션에 대한 가시성을 확보하는 것을 알아보았습니다. 동일한 애플리케이션으로 AWS의 컨테이너 오케스트레이션 서비스인 Amazon ECS에서 ADOT, AMP, AMG, Amazon X-Ray를 활용하여 Observability 환경을 구축하는 방법에 대해 알아보겠습니다. 블로그에서 예제로 사용하는 애플리케이션은 OpenTelemetry Agent for Java 기반의 aws-otel-java-instrumentation 에이전트를 활용하여 Trace 데이터를 자동 계측하도록 실행하며 Spring Boot Actuator와 Micrometer 라이브러리를 활용하여 Metric을 노출합니다.

솔루션 개요

그림1. 솔루션 전체 아키텍처

Spring Boot 애플리케이션에서 수집할 Metric과 Trace는 ADOT Collector에 전송되거나 수집됩니다. 워크로드가 성장하면서 Metric 또는 Trace를 위한 ADOT Collector를 분리하는 것이 모범 사례이며 이러한 경우 ADOT Collector 사이드카는 2대로 구성될 수 있습니다. 이번 블로그에서는 관찰 가능성 확보를 하기 위한 기본적인 방법을 알아보기 위해 하나의 Collector로 구성합니다.

우선 Amazon ECS 환경에서 Spring Boot 애플리케이션을 실행하기 위해서는 작업 정의를 생성해야 합니다. 작업 정의에는 하나 이상의 컨테이너를 정의할 수 있으며 사이드카 컨테이너로 Spring Boot 애플리케이션에 대한 Metric과 Trace 정보를 수집하기 위해 그림 2과 같이 간편하게 체크 박스를 활성화하여 ADOT Collector 컨테이너를 생성할 수 있습니다.

그림2. ECS 작업정의에서 모니터링 활성화

선택할 수 있는 옵션으로는 Trace 정보를 ADOT Collector가 X-Ray로 전송할 수 있도록 Trace collection을 활성화할 수 있습니다. 또한 Metric 정보를 ADOT Collector가 Amazon Cloudwatch 또는 AMP로 전송할 수 있습니다. 이번 블로그에서는 Prometheus Metric을 수집하여 PrometheusRemoteWriteExporter로 AMP에 Metric을 수집합니다. 이제 단계별로 진행해보겠습니다.

1단계 : CloudWatch Logs로 로그 수집하기

먼저 spring-otel 작업 정의를 생성하겠습니다.

그림 3. ECS 작업 정의 생성

  • Spring Boot 컨테이너 이름 : spring
  • Image URI : Amazon ECR springboot 이미지의 URI

Amazon ECS에서 로그를 수집하는 방법으로 작업 정의에 Log Collection을 활성화하고 Amazon CloudWatch Logs 또는 Firelens로 AWS 관리형 서비스 및 타사 솔루션으로 전송할 수 있습니다. 현재 테스트 플랫폼은 FARGATE이며 awslogs 로그 드라이버를 사용하여 CloudWatch Logs로 전송해보도록 하겠습니다. awslogs 로그 드라이버는 stdout 및 stderr에서 로그를 캡쳐한 다음 PutLogEvents API를 통해 Amazon CloudWatch Logs에 업로드합니다. 이를 위해 Log collection을 활성화하고 필요한 정보를 작성합니다.

그림4. ECS 작업 정의 로그 설정

이때 ECS Task Execution Role에는 CloudWatch Logs를 새로 생성하거나 전송하기 위해 “logs:CreateLogGroup“, “logs:CreateLogStream”, “logs:PutlogEvents” 정책이 필요합니다. awslogs-create-group, awslogs-group, awslogs-region, awslogs-stream-prefix에 대한 Value를 입력합니다. 여기까지 로그 수집을 위한 설정을 완료했습니다.

2단계: aws-otel-collector 작업 정의 추가

1단계에서 구성한 Spring Boot 애플리케이션의 Trace 정보와 Metric을 수집하기 위해 ADOT Collector 컨테이너를 추가해야 합니다. 먼저 Amazon X-Ray로 Trace 정보를 전달하기 위해 “Use trace collection”을 활성화합니다. 다음으로 Metric을 AMP로 전달하기 위해 “Use metric collection”을 선택하고 AMP에 remotewrite를 위한 엔드포인트를 콘솔 또는 CLI로 생성합니다. AMP Workspace를 생성하면 콘솔에서 아래와 같은 화면을 볼 수 있습니다.

그림 5. 생성한 AMP의 Remote Write Endpoint URL

Trace와 Metric 체크 박스를 활성화하고 위에서 복사한 remote with URL을 Workspace remote write endpoint에 붙여넣습니다.

그림 6. ECS 작업 정의에 AMP Remote Write Endpoint 작성

이후, ADOT Collector 컨테이너가 AMP remotewrite 엔드포인트로 Metric 정보를 전송할 수 있도록 ECS Task Execution Role에 AmazonPrometheusRemoteWriteAccess 정책을 추가합니다. 여기까지 Spring Boot 컨테이너와 ADOT Collector 컨테이너에 대한 작업 정의를 생성했습니다.

3단계 : Metric 수집을 위해 ADOT Collector 설정

Spring Boot 애플리케이션에서는 Actuator 모듈을 사용하여 Metric을 수집합니다. 애플리케이션의 Metrics 수집 경로는 /prometheus/actuator로 prometheus scrape_config에 정의된 기본 metrics_path인 /metrics와는 상이하여 ADOT collector 파일에서 일부 변경이 필요합니다. 즉, 작업 정의에서 활성화하여 ADOT Collector의 구성 정보를 사용하면 기본값을 기준으로 설정되기 때문에 애플리케이션의 Metric 또는 Trace 노출 엔드포인트가 상이하다면 사용자 재정의가 필요합니다. 현재는 ADOT collector가 어떤 yaml 파일을 기준으로 구성되는지 살펴보겠습니다.

2단계에서 생성한 작업정의에 JSON 파일을 확인해보면 containerDefinitions는 아래와 같이 표시됩니다.

{
  "name": "aws-otel-collector",
  "image": "public.ecr.aws/aws-observability/aws-otel-collector:v0.36.0",
  "cpu": 0,
  "portMappings": [],
  "essential": true,
  "command": [
    "--config=/etc/ecs/ecs-amp-xray-prometheus.yaml"
  ],
  "environment": [
    {
      "name": "AWS_PROMETHEUS_SCRAPING_ENDPOINT",
      "value": "0.0.0.0:8080"
    },
    {
      "name": "AWS_PROMETHEUS_ENDPOINT",
      "value": "https://aps-workspaces.ap-northeast-2.amazonaws.com/workspaces/ws-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/api/v1/remote_write"
    }
  ],
  ...
}

“command”에서 ECS Task가 실행될 때 /etc/ecs/ecs-amp-xray-prometheus.yaml 파일로부터 Collector를 구성하고 있습니다. 작업 정의 생성 시 Trace와 Metric을 활성화했기 때문에 기본으로 ecs-amp-xray-prometheus.yaml 파일이 등록된 것을 알 수 있습니다. 아래 yaml 파일의 receiver와 exporter에서는 작업 정의에 환경 변수 2가지(AWS_PROMETHEUS_SCRAPING_ENDPOINT, AWS_PROMETHEUS_ENDPOINT)를 static_configs와 prometheus_remotewrite endpoint 값으로 오버라이드합니다.

receivers:
...
  prometheus:
    config:
      global:
        scrape_interval: 20s
        scrape_timeout: 10s
      scrape_configs:
        - job_name: "otel-collector"
          static_configs:
            - targets: [$AWS_PROMETHEUS_SCRAPING_ENDPOINT]
...
exporters:
...
  prometheusremotewrite:
    endpoint: $AWS_PROMETHEUS_ENDPOINT
    auth:
      authenticator: sigv4auth

제공되는 yaml 파일에는 prometheus metrics 수집 시 기본 경로인 /metrics를 수집하므로 metrics_path를 알맞게 재정의해야 합니다. 사용자 정의 구성 정보를 가져오기 위해 OpenTelemetry는 confmap Provider를 활용하여 제공된 URI로부터 구성 정보를 가져올 수 있습니다. 이를 위해 Amazon Simple Storage Service(S3)에 아래와 같이 수정한 config 파일을 업로드합니다.

...
    prometheus:
    config:
      global:
        scrape_interval: 20s
        scrape_timeout: 10s
      scrape_configs:
        - job_name: "otel-collector"
          static_configs:
            - targets: [$AWS_PROMETHEUS_SCRAPING_ENDPOINT]
          metrics_path: "actuator/prometheus"
...

이후 작업 정의에 새로운 revision을 JSON으로 생성하고 아래와 같이 S3 URI를 포함하도록 command를 수정하고 저장합니다.

{
  "name": "aws-otel-collector",
  "image": "public.ecr.aws/aws-observability/aws-otel-collector:v0.36.0",
  "cpu": 0,
  "portMappings": [],
  "essential": true,
  "command": [
    "--config",
    "s3://adot-collector-confmap.s3.ap-northeast-2.amazonaws.com/ecs-amp-xray-prometheus.yaml"
  ],
  "environment": [
    {
      "name": "AWS_PROMETHEUS_SCRAPING_ENDPOINT",
      "value": "0.0.0.0:8080"
    },
    {
      "name": "AWS_PROMETHEUS_ENDPOINT",
      "value": "https://aps-workspaces.ap-northeast-2.amazonaws.com/workspaces/ws-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/api/v1/remote_write"
    }
  ],
  ...
}

여기까지 Metric 수집을 위한 설정이 끝났습니다.

4단계 : Trace 수집을 위해 ADOT Collector 설정

Spring Boot 애플리케이션의 Trace 정보는 AWS X-Ray로 전달해야 합니다. ADOT Collector의 Receiver는 OpenTelemetry protocol(OTLP)을 사용해 gRPC 또는 HTTP로 Trace 데이터를 수신합니다. 이후 AWS X-Ray exporter는 Trace 정보를 AWS X-Ray 포맷으로 변환하여 X-Ray로 전달하게 됩니다. 이러한 설정은 ecs-amp-xray-prometheus.yaml 파일에서도 찾아볼 수 있습니다.

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
...
exporters:
  awsxray:
...
processors:
  batch/traces:
    timeout: 1s
    send_batch_size: 50
  batch/metrics:
    timeout: 60s
  resourcedetection:
    detectors:
      - env
      - system
      - ecs
      - ec2
... 

위 설정에 대해 간단하게 살펴보겠습니다.

  • receivers: ADOT Collector는 OTLP 프로토콜로 grpc 또는 HTTP를 통해 X-Ray 데이터를 수신합니다. 애플리케이션에서 데이터를 전송할 때 로그와 trace 정보를 연계하기 위해 로그 포맷에는 X-Ray의 Trace ID가 포함되어야 합니다.
  • exporters: ADOT Collector는 전달받은 OTLP 포맷의 Trace 데이터를 X-Ray 포맷으로 변환하고 X-Ray 서비스로 전달합니다. 리전 또는 로그 그룹과 같은 추가 속성도 지정할 수 있으며 지원되는 속성은 github를 참고하시기 바랍니다. 현재 ADOT 구성 파일에는 로그 그룹이 지정되어 있지 않지만 자동 계측을 사용할 경우 Dockerfile의 환경 변수(ENV)에 OTEL_RESOURCE_ATTRIBUTES 를 추가하여 Trace 정보에 로그 그룹을 지정할 수 있습니다. (참고)
  • processors: 프로세서는 ADOT Collector 파이프라인에서 사용되며 전달되는 데이터를 전처리하는데 사용됩니다. Batch Processor는 시간(timeout) 또는 사이즈(send_batch_size) 기반 배치 처리를 지원합니다. timeout을 0으로 지정할 경우 데이터는 즉시 전송되며 send_batch_szie는 무시됩니다. batch/traces 프로세서는 최대 배치 사이즈를 적용하기 위해 데이터 항목을 분할하지 않고 최대 50개의 Span, Metric 또는 최대 1초동안 버퍼링합니다.

2단계에서 X-Ray Metric 수집을 활성화했기 때문에 ADOT Collector 구성에 X-Ray receiver 및 exporter를 추가 정의 하지 않아도 됩니다. 이제 애플리케이션을 배포하고 수집된 로그, Metric, Trace 정보를 확인하겠습니다.

4단계: AMP에서 수집 확인하기

배포 이후 Spring Boot 애플리케이션에서는 로그(Cloudwatch Logs), Trace(AWS X-Ray), Metrics(AMP)를 전송하고 Grafana에서 수집된 내용을 확인할 수 있습니다. 우선 서비스를 배포하면 작업에는 spring, aws-otel-collector라는 이름의 컨테이너 2개가 올라온 것을 알 수 있습니다.

그림 7. ADOT Collector 사이드카 컨테이너 (aws-otel-collector) 확인

Tasks 탭 오른쪽에 Logs에서는 Spring 컨테이너와 aws-otel-collecto 컨테이너에서 발생하는 로그를 볼 수 있습니다. AMG에서도 마찬가지로 Cloudwatch를 데이터 소스로 제공하며 Cloudwatch Logs Insight 쿼리 구문을 사용하여 로그를 쿼리할 수 있습니다.

fields @timestamp, @message
| parse '* : *@*' as pre, trace_id
| filter ispresent(trace_id)
| sort @timestamp desc
| limit 100

그림 8. AMG에서 Cloudwatch Logs 쿼리

로그와 X-Ray Tace ID는 연계되어 확인할 수 있습니다. 아래에는 기본적인 내용으로 구성한 X-Ray 대시보드의 예시입니다. 발생한 Trace ID 또는 Cloudwatch Log를 선택하면 콘솔로 이동하여 확인할수도 있습니다.

그림 9. AMG에서 생성한 X-Ray 및 Cloudwatch Logs 예제 대시보드

마지막으로 ECS Task 메트릭 대시보드와 애플리케이션 메트릭 대시보드를 아래와 같이 표현할 수 있습니다.

그림 10. Spring Boot 애플리케이션 메트릭 예시 대시보드

그림 11. ECS Service 메트릭 예시 대시보드

리소스 정리하기

이번 블로그에서는 Amazon ECS에서 Spring Boot 애플리케이션의 관찰 가능성을 구축하기 위해 AWS 관리형 서비스를 사용하는 방법을 다루었습니다. Amazon ECS에서는 작업 정의 생성 시 체크 박스를 활성화하고 생성된 작업 정의의 command를 수정하여 간단하게 Trace 데이터와 Metric을 수집할 수 있습니다.

ECS Cluster는 생성하는 자체로는 비용이 발생하지 않으며 작업 정의를 실행시키는 플랫폼인 EC2 또는 Fargate의 컴퓨팅 비용이 발생합니다. 따라서 아래 명령어로 생성한 서비스를 제거합니다.

aws ecs delete-service --cluster ${CLUSTER_NAME} --service ${SERVICE_NAME}

서비스를 생성하면서 로드밸런서를 추가했다면 같이 제거하여 테스트 이후 불필요한 과금을 방지합니다.

결론

관찰 가능성 도구를 효과적으로 선택하는 것은 컨테이너 오케스트레이션 환경에서 서비스의 로그, 메트릭 및 추적 정보를 관리하는 핵심 요소입니다. 여러 오픈 소스 옵션이 존재하지만 AWS의 관리형 도구를 활용함으로써 별도의 스토리지 운영이나 확장성 및 가용성에 대한 걱정을 덜 수 있습니다. 관리형 서비스와 오픈 소스, 또는 타사 솔루션 간의 장단점을 고려하여 적절한 도구를 선택하는 것은 환경을 효과적으로 운영하는 데에 필수적입니다. 어떠한 궁극적인 목표나 비즈니스 요구 사항을 고려하여 최적의 선택을 찾는 것이 중요합니다.

Seongjin Ahn

Seongjin Ahn

안성진 Solutions Architect는 다양한 고객 인프라 운영 경험을 바탕으로 AWS 서비스의 효율적인 사용을 위해 Startup 고객에게 기술을 지원하는 역할을 합니다.