높은 처리량을 위한 Amazon ECS 로그 구성 - Amazon Elastic Container Service

높은 처리량을 위한 Amazon ECS 로그 구성

로그 처리량이 많은 시나리오의 경우, FireLens 및 Fluent Bit와(과) 함께 awsfirelens 로그 드라이버를 사용하는 것이 좋습니다. Fluent Bit은(는) 리소스를 효율적으로 사용하며 수백만 개의 로그 레코드를 처리할 수 있는 경량 로그 프로세서입니다. 하지만 대규모 환경에서 최적의 성능을 달성하려면 구성을 조정해야 합니다.

이 섹션에서는 시스템 안정성을 유지하고 데이터 손실이 없도록 보장하면서 높은 로그 처리량을 처리하기 위한 고급 Fluent Bit 최적화 기술을 다룹니다.

FireLens에서 사용자 지정 구성 파일을 사용하는 방법에 대한 자세한 내용은 사용자 지정 구성 파일 사용을(를) 참고하세요. 추가 예제는 GitHub의 Amazon ECS FireLens 예제를 확인하세요.

참고

workersthreaded와(과) 같은 이 섹션의 일부 구성 옵션에는 Fluent Bit 버전 3 이상을 위한 AWS이(가) 필요합니다. 사용 가능한 버전에 대한 자세한 내용은 Fluent Bit 릴리스에 대한 AWS을(를) 참고하세요.

파일 시스템 버퍼링 사용

기본적으로 Fluent Bit은(는) 모든 데이터를 메모리에 버퍼링합니다. 데이터가 출력으로 플러시되는 속도보다 빠르게 수집되면 버퍼가 가득 찹니다. 버퍼가 가득 차면 버퍼 스페이스가 확보될 때까지 입력 플러그인이 일시 중지되며, 이로 인해 백프레셔가 발생하고 애플리케이션 속도가 느려질 수 있습니다.

처리량이 많은 시나리오의 경우 파일 시스템 버퍼링을 사용하는 것이 좋습니다. Fluent Bit이(가) 버퍼링과 스토리지를 관리하는 방법에 대한 자세한 내용은 Fluent Bit 문서의 버퍼링 및 스토리지 섹션을 참고하세요.

파일 시스템 버퍼링은 다음과 같은 장점을 제공합니다:

  • 더 큰 버퍼 용량 – 일반적으로 디스크 스페이스를 메모리보다 훨씬 여유가 있습니다.

  • 지속성 - 버퍼링된 데이터는 Fluent Bit(이)가 재시작되어도 유지됩니다.

  • 점진적 성능 저하 - 출력 오류가 발생해도 데이터가 메모리를 고갈시키는 대신 디스크에 축적됩니다.

파일 시스템 버퍼링을 활성화하려면 사용자 지정 Fluent Bit 구성 파일을 제공하세요. 다음 예시는 권장 구성을 보여줍니다:

[SERVICE] # Flush logs every 1 second Flush 1 # Wait 120 seconds during shutdown to flush remaining logs Grace 120 # Directory for filesystem buffering storage.path /var/log/flb-storage/ # Limit chunks stored 'up' in memory (reduce for memory-constrained environments) storage.max_chunks_up 32 # Flush backlog chunks to destinations during shutdown (prevents log loss) storage.backlog.flush_on_shutdown On [INPUT] Name forward unix_path /var/run/fluent.sock # Run input in separate thread to prevent blocking threaded true # Enable filesystem buffering for persistence storage.type filesystem [OUTPUT] Name cloudwatch_logs Match * region us-west-2 log_group_name /aws/ecs/my-app log_stream_name $(ecs_task_id) # Use multiple workers for parallel processing workers 2 # Retry failed flushes up to 15 times retry_limit 15 # Maximum disk space for buffered data for this output storage.total_limit_size 10G

주요 구성 파라미터:

storage.path

Fluent Bit이(가) 디스크에 버퍼링된 청크를 저장하는 디렉터리입니다.

storage.backlog.flush_on_shutdown

이 기능이 활성화되면 Fluent Bit은(는) 종료 시 백로그에 있는 모든 파일 시스템 청크를 대상지로 플러시하려고 시도합니다. 이는 Fluent Bit이(가) 중지되기 전에 데이터 전달을 보장하는 데 도움이 되지만, 종료 시간이 늘어날 수 있습니다.

storage.max_chunks_up

메모리에 남아 있는 청크의 수입니다. 기본값은 128개 청크이며, 각 청크가 최대 4~5MB를 사용할 수 있기 때문에 500MB 이상의 메모리를 소비할 수 있습니다. 메모리가 제한된 환경에서는 이 값을 낮추세요. 예를 들어 버퍼링에 사용할 수 있는 공간이 50MB라면, 이 값을 8~10개 청크로 설정합니다.

storage.type filesystem

입력 플러그인에 대해 파일 시스템 스토리지를 활성화합니다. 이름과 달리 Fluent Bit은(는) mmap을(를) 사용하여 청크를 메모리와 디스크 모두에 매핑하며, 성능 저하 없이 지속성을 제공합니다.

threaded true

Fluent Bit의 메인 이벤트 루프와 별개로 입력을 자체 스레드에서 실행합니다. 이렇게 하면 느린 입력이 전체 파이프라인을 차단하는 것을 방지합니다.

출력 구성 최적화

네트워크 문제, 서비스 중단, 대상지의 스로틀링 등으로 인해 로그가 전달되지 않을 수 있습니다. 적절한 출력 구성은 데이터 손실 없는 복원력을 보장합니다.

출력 플러시가 실패하면 Fluent Bit은(는) 해당 작업을 재시도할 수 있습니다. 다음 파라미터는 재시도 동작을 제어합니다:

retry_limit

레코드를 삭제하기 전까지의 최대 재시도 횟수입니다. 기본값은 1입니다. 프로덕션 환경의 경우 15회 이상으로 설정하는 것을 권장하며, 이는 지수 백오프를 통해 몇 분간의 장애 상황을 커버할 수 있습니다.

scheduler.base

재시도 간의 최소 초 단위 시간입니다. 10초를 권장합니다.

scheduler.cap

지수 백오프 사용 시 재시도 간의 최대 초 단위 시간입니다. 60초를 권장합니다.

workers

병렬 출력 처리를 위한 스레드 수입니다. 여러 워커를 사용하면 플러시를 동시에 수행할 수 있어, 많은 청크를 처리할 때 처리량이 향상됩니다.

[SERVICE] 섹션의 Grace 파라미터는 종료 시 버퍼링된 데이터를 플러시하기 위해 Fluent Bit이(가) 대기하는 시간을 설정합니다. Grace 기간은 컨테이너의 stopTimeout와(과) 연계하여 조정해야 합니다. Fluent Bit이(가) SIGKILL을(를) 받기 전에 플러시를 완료할 수 있도록 stopTimeout이(가) Grace 기간을 초과하도록 설정하세요. 예를 들어 Grace이(가) 120초라면, stopTimeout은(는) 150초로 설정합니다.

다음 예시는 높은 처리량 시나리오를 위해 권장되는 모든 설정이 포함된 전체 Fluent Bit 구성을 보여줍니다.

[SERVICE] # Flush logs every 1 second Flush 1 # Wait 120 seconds during shutdown to flush remaining logs Grace 120 # Directory for filesystem buffering storage.path /var/log/flb-storage/ # Limit chunks stored 'up' in memory (reduce for memory-constrained environments) storage.max_chunks_up 32 # Flush backlog chunks to destinations during shutdown (prevents log loss) storage.backlog.flush_on_shutdown On # Minimum seconds between retries scheduler.base 10 # Maximum seconds between retries (exponential backoff cap) scheduler.cap 60 [INPUT] Name forward unix_path /var/run/fluent.sock # Run input in separate thread to prevent blocking threaded true # Enable filesystem buffering for persistence storage.type filesystem [OUTPUT] Name cloudwatch_logs Match * region us-west-2 log_group_name /aws/ecs/my-app log_stream_name $(ecs_task_id) # Use multiple workers for parallel processing workers 2 # Retry failed flushes up to 15 times retry_limit 15 # Maximum disk space for buffered data for this output storage.total_limit_size 10G

안전성을 위해 다중 대상 로깅 사용

로그를 여러 대상으로 전송하면 단일 장애점을 제거할 수 있습니다. 예를 들어 CloudWatch Logs에 장애가 발생하더라도 로그는 여전히 Amazon S3에 도달합니다.

다중 대상 로깅은 다음과 같은 이점을 제공합니다. Amazon S3 출력 플러그인은 gzip 및 Parquet 형식과 같은 압축 옵션도 지원하여 스토리지 비용을 절감할 수 있습니다. 자세한 내용은 Fluent Bit 문서의 S3 압축 섹션을 참고하세요.

다중 대상 로깅은 다음과 같은 이점을 제공할 수 있습니다:

  • 중복성 - 한 곳의 대상지에 장애가 발생해도 로그는 다른 곳에 도달합니다.

  • 복구 - 한 시스템의 데이터 공백을 다른 시스템을 통해 재구성합니다.

  • 내구성 - 장기 보관을 위해 Amazon S3에 로그를 아카이빙합니다.

  • 비용 최적화 - .최근 로그는 보관 주기가 짧고 쿼리가 빠른 CloudWatch Logs에 보관하고, 모든 로그는 장기 보관을 위해 비용이 저렴한 Amazon S3 스토리지에 아카이빙합니다.

다음 Fluent Bit 구성은 CloudWatch Logs와 Amazon S3 모두에 로그를 전송합니다.

[OUTPUT] Name cloudwatch_logs Match * region us-west-2 log_group_name /aws/ecs/my-app log_stream_name $(ecs_task_id) workers 2 retry_limit 15 [OUTPUT] Name s3 Match * bucket my-logs-bucket region us-west-2 total_file_size 100M s3_key_format /fluent-bit-logs/$(ecs_task_id)/%Y%m%d/%H/%M/$UUID upload_timeout 10m # Maximum disk space for buffered data for this output storage.total_limit_size 5G

두 출력 모두 동일한 Match * 패턴을 사용하므로, 모든 레코드가 두 대상지에 각각 독립적으로 전송됩니다. 한 곳의 대상지에 장애가 발생하더라도 로그는 다른 곳으로 계속 흐르며, 실패한 플러시는 나중에 재시도할 수 있도록 파일 시스템 버퍼에 축적됩니다.

tail 입력 플러그인을 사용한 파일 기반 로깅 사용

로그 손실이 중요한 문제인 높은 처리량 시나리오에서는 대안적인 접근 방식을 사용할 수 있습니다. 애플리케이션이 로그를 디스크 파일에 쓰고, Fluent Bit이(가) tail 입력 플러그인을 사용하여 해당 파일을 읽도록 구성하는 방식입니다. 이 방식은 Docker 로깅 드라이버 레이어를 완전히 우회합니다.

tail 플러그인을 사용한 파일 기반 로깅은 다음과 같은 이점을 제공합니다:

  • 오프셋 추적 - tail 플러그인은 (DB 옵션을 사용하여) 데이터베이스 파일에 파일 오프셋을 저장할 수 있어, Fluent Bit 재시작 시에도 내구성을 제공합니다. 컨테이너 재시작 시 로그 손실이 발생하는 것을 방지하는 데 도움이 됩니다.

  • 입력 수준 버퍼링 - Mem_Buf_Limit을(를) 사용하여 입력 플러그인에서 직접 메모리 버퍼 제한을 구성할 수 있으며, 이를 통해 메모리 사용량을 더욱 세밀하게 제어할 수 있습니다.

  • Docker 오버헤드 방지 – 로그가 Docker의 로그 버퍼를 거치지 않고 파일에서 Fluent Bit(으)로 직접 전달됩니다.

이 방식을 사용하려면 애플리케이션이 stdout 대신 파일에 로그를 기록해야 합니다. 애플리케이션 컨테이너와 Fluent Bit 컨테이너 모두 로그 파일이 저장된 공유 볼륨을 마운트합니다.

다음 예시는 모범 사례가 적용된 tail 입력 구성을 보여줍니다.

[INPUT] Name tail # File path or glob pattern to tail Path /var/log/app.log # Database file for storing file offsets (enables resuming after restart) DB /var/log/flb_tail.db # when true, controls that only fluent-bit will access the database (improves performance) DB.locking true # Skip long lines instead of skipping the entire file Skip_Long_Lines On # How often (in seconds) to check for new files matching the glob pattern Refresh_Interval 10 # Extra seconds to monitor a file after rotation to account for pending flush Rotate_Wait 30 # Maximum size of the buffer for a single line Buffer_Max_Size 10MB # Initial allocation size for reading file data Buffer_Chunk_Size 1MB # Maximum memory buffer size (tail pauses when full) Mem_Buf_Limit 75MB

tail 입력 플러그인을 사용할 때는 다음 사항을 고려하세요:

  • 디스크 소진을 방지하기 위해 애플리케이션 로그에 대한 로그 로테이션을 구현하세요. 성능을 측정하기 위해 기본 볼륨 메트릭을 모니터링합니다.

  • 로그 형식에 따라 Ignore_Older, Read_from_Head 및 멀티라인 파서와 같은 설정을 고려하세요.

자세한 내용은 Fluent Bit 문서의 Tail 섹션을 참고하세요. 모범 사례는 AWS을(를) 위한 Fluent Bit 문제 해결 가이드의 Tail config with best practices를 참고하세요.

FireLens로 직접 로깅

awsfirelens 로그 드라이버가 태스크 정의에 지정되어 있으면 Amazon ECS 컨테이너 에이전트는 다음 환경 변수를 컨테이너에 주입합니다.

FLUENT_HOST

FireLens 컨테이너에 할당된 IP 주소입니다.

참고

bridge 네트워크 모드에서 EC2를 사용하는 경우 FireLens 로그 라우터 컨테이너(컨테이너 정의에 firelensConfiguration 객체가 있는 컨테이너)를 다시 시작한 후 애플리케이션 컨테이너의 FLUENT_HOST 환경 변수가 부정확해질 수 있습니다. 이는 FLUENT_HOST가 동적 IP 주소이며 재시작 후 변경될 수 있기 때문입니다. 애플리케이션 컨테이너에서 FLUENT_HOST IP 주소로 직접 로깅하면 주소가 변경된 후 실패하기 시작할 수 있습니다. 개별 컨테이너를 재시작하는 방법에 대한 자세한 내용은 컨테이너 재시작 정책이 있는 Amazon ECS 작업의 개별 컨테이너 재시작 섹션을 참조하세요.

FLUENT_PORT

Fluent Forward 프로토콜이 수신 대기 중인 포트입니다.

이 환경 변수들을 사용하면 stdout에 기록하는 대신 Fluent Forward 프로토콜을 사용하여 애플리케이션 코드에서 Fluent Bit 로그 라우터로 직접 로그를 보낼 수 있습니다. 이 방식은 Docker 로깅 드라이버 레이어를 우회하며 다음과 같은 이점을 제공합니다:

  • 낮은 대기 시간 – 로그가 Docker의 로깅 인프라를 거치지 않고 Fluent Bit(으)로 직접 전달됩니다.

  • 구조화된 로깅 - JSON 인코딩 오버헤드 없이 구조화된 로그 데이터를 기본적으로 전송합니다.

  • 제어력 향상 - 애플리케이션이 자체적인 버퍼링 및 오류 처리 로직을 구현할 수 있습니다.

다음 Fluent 로거 라이브러리는 Fluent Forward 프로토콜을 지원하며 Fluent Bit(으)로 로그를 직접 보내는 데 사용할 수 있습니다.

Docker 버퍼 제한 구성

태스크 정의를 생성할 때 log-driver-buffer-limit에 값을 지정하여 메모리에 버퍼링되는 로그 라인의 수를 지정할 수 있습니다. 이는 Docker와 Fluent Bit 사이의 버퍼를 제어합니다. 자세한 정보는 Docker 설명서의 Fluentd logging driver(Fluentd 로깅 드라이버)를 참조하세요.

Docker에서 버퍼 메모리가 부족하여 새 메시지를 추가할 수 있도록 버퍼 메시지를 삭제할 수 있으므로 처리량이 많을 때 이 옵션을 사용합니다.

이 옵션을 사용할 때 다음 사항을 고려하세요:

  • 이 옵션은 플랫폼 버전 1.4.0 이상의 EC2 및 Fargate 유형에서 지원됩니다.

  • 이 옵션은 logDriverawsfirelens로 설정된 경우에만 유효합니다.

  • 기본 버퍼 제한은 1048576개의 로그 줄입니다.

  • 버퍼 제한은 로그 라인 0줄 이상, 536870912줄 미만이어야 합니다.

  • 이 버퍼에 사용되는 최대 메모리 양은 각 로그 줄의 크기와 버퍼 크기를 곱한 값입니다. 예를 들어 애플리케이션의 로그 라인이 평균 2KiB라면, 버퍼 제한을 4096으로 설정했을 때 최대 8MiB를 사용합니다. 작업 수준에서 할당된 총 메모리 양은 로그 드라이버 메모리 버퍼 외에도 모든 컨테이너에 할당된 메모리 양보다 커야 합니다.

다음 태스크 정의는 log-driver-buffer-limit을(를) 구성하는 방법을 보여줍니다.

{ "containerDefinitions": [ { "name": "my_service_log_router", "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:3", "cpu": 0, "memoryReservation": 51, "essential": true, "firelensConfiguration": { "type": "fluentbit" } }, { "essential": true, "image": "public.ecr.aws/docker/library/httpd:latest", "name": "app", "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "firehose", "region": "us-west-2", "delivery_stream": "my-stream", "log-driver-buffer-limit": "52428800" } }, "dependsOn": [ { "containerName": "my_service_log_router", "condition": "START" } ], "memoryReservation": 100 } ] }