caffeine-library / random

스터디 진행 외에 공유하고 싶은 주제에 대해 얘기합니다
0 stars 0 forks source link

[로그수집] ECS FireLens를 통한 로그 수집 구성 #22

Open binchoo opened 2 years ago

binchoo commented 2 years ago

AWS Firelens의 컨테이너 로그 수집

참고글

동작 원리를 배운 블로그 글

image

FireLens 설정 예제들

amazon-ecs-firelens-examples

ECS Task Definition JSON 예제가 정말 다양한 시나리오에 맞춰 제공되고 있음.

ECS의 로그 수집 관련 옵션

image

우선 AWS 콘솔을 들여다보면 ECS Task Definition을 생성할 때 로그 수집 관련 옵션이 제공된다.

Firelens쪽 옵션을 택하는 것만으로 CloudWatch · S3 · Kinesis · ElastiSearch 뿐만 아니라, Fluentbit 오픈소스가 지원하는 다양한 목적지로 수집 로그를 전송하는 게 가능해진다.

(Fluentbit란, Fluentd를 경량 개선한 버전이다.)

일단 아무 설명 없이 Firelens가 Fluentbit를 사용해 로그를 수집하는 아키텍처를 확인하자. 그림에 나타난 상호작용들을 훓어 보기 바란다.

image

도커의 로그 드라이버

image

도커 디먼기본 로그 드라이버를 통해 우리에게 컨테이너 응용이 출력한 로그를 보여줄 수 있다. 로그 드라이버란 여러 종류가 있고 변경 가능하며 제각기 다양한 옵션을 부여할 수 있다.

image

Fluentd 로그 드라이버는 로그 수집 파이프라인을 구성하는 오픈 소스 플랫폼인 Fluentd를 활용한다. 컨테이너 응용이 STDOUT에 출력한 로그를 Fluentd 디먼에게 전송해 준다.

Fluentd 로그 드라이버 실습

Fluentd 로그 드라이버의 동작을 실제로 목격하면 AWS FireLens의 동작도 쉽게 이해할 수 있으리라 생각한다. 따라서 간단하게 먼저 실습을 진행하자.

Fluentd 디먼 띄우기

Fluent 디먼이 동작하는 컨테이너를 내 호스트에 띄우기 위해 아래처럼 컨테이너를 실행하고자 한다. 하지만 이 컨테이너에게 주입해 줄 설정 파일을 먼저 준비 해야겠다.

docker run -it -p 24224:24224 -v /path/to/conf/test.conf:/fluentd/etc/test.conf -e FLUENTD_CONF=test.conf fluent/fluentd:latest

Fluentd 설정 파일 준비

Fluentd Configuration File은 입력 소스, 파서, 필터(레코드 트랜스포머), 라우터를 설정한다. 다양한 플러그인을 가져다 쓰는 것으로 커스터 마이징이 가능하다.

아래 test.conf 파일은 단순한 설정을 갖는다. 딱히 태그 필러링을 하지 않아서 모든 인풋 소스의 로그를 stdout으로 전달하는 것이다.

<source>
   @type forward
</source>

<match *>
   @type stdout
</match>

도커의 기본 로그 드라이버 변경

리눅스의 /etc/docker/daemon.json 파일은 도커 디먼 설정을 담는다. 윈도우는 C:\ProgramData\docker\config\daemon.json 파일이다.

이 파일에서 log-opts 찾아서 아래와 같이 객체를 작성한다.이제 도커 디먼이 기본적으로 Fluentd 로그 드라이버를 쓰게 된다. 방금 띄운 Fluentd 컨테이너 이름과 포트 매핑을 기입한다.

{
   "log-driver": "fluentd",
   "log-opts": {
     "fluentd-address": "fluentdhost:24224"
   }
 }

로그 생산용 컨테이너 실행

이제 간단한 nginx 서버를 nginxdemos/hello 이미지를 사용해 띄어보겠다.

사실 도커 디먼의 기본 드라이버를 바꾸지 않더라도, 컨테이서 수준에서 쓰는 로그 드라이버를 변경할 수 있다. 두 번째 명령이 그러하다.

# 도커 디먼의 로그 드라이버를 사용함 (현재 Fluentd 로그 드라이버)
docker run -p 80:80 --log-opt fluentd-address=fluentdhost:24224 nginxdemos/hello

# 컨테이너 수준에서 로그 드라이버 설정함
docker run -p 80:80 --log-driver=fluentd --log-opt fluentd-address=tcp://fluentdhost:24224 nginxdemos/hello

Firelens 로그 수집 구조 설명

도커 드라이버와 Fluentd에 대해 간단히 실습했으니 AWS Firelens의 동작 과정을 더 잘 받아들일 수 있을 것이다.

image

image

Firelens 컨테이너의 Fluentd에게 커스텀 설정 주입

AWS Firelens가 기본 생성한 설정에 커스텀 설정을 덧붙여서 Fluentd에게 부여하고 싶다.

AWS 콘솔은 FireLens에 대한 저수준 설정을 다룰 수 없다. 더욱 저수준의 설정을 다루어야 한다면 ECS Task Definition의 JSON을 작성하거나 CloudFormation 템플릿을 이용해야 한다.

개요

AWF Firelens는 우리가 ECS Task Definition에 명세한 내용들을 토대로 자동으로 기본 설정 파일을 만들고, Fluentd가 사용하게끔 한다.

우리가 추가로 제공하는 설정 파일은, 기본 설정 파일보다 시간상 뒤늦게 들어오지만 Fluentd에게 정상 반영된다. 설정 파일에 명세된 영역의 순서가 뒤죽박죽이어도 기본 순서가 강제로 지켜지기 때문이다.

image

image

AWS 블로그 설명에 따르면, 아래의 영역 순으로 설정이 잘 반영된다.

  1. 로그 소스에 대한 설정 (Input 영역)
  2. ECS의 메타데이타들을 토대로 Transformer 설정 (Filter 영역)
  3. 우리가 제공한 설정 파일반영 (기본 설정 파일이 @include 한다고 함)
  4. 우리가 설정한 ECS Task Definition 내용대로 루터를 설정 (Route 영역)

커스텀 설정 파일 배치

image

AWS Firelens의 공식 문서에 따르면, 우리의 추가 설정 파일을 배치할 수 있는 방법이 두 가지이다.

커스텀 설정 파일 배치의 제약사항

image

AWS Firelens 예제 깃허브의 설명에 따르면, 이러한 제약사항을 알 수 있었다.

Task Definition JSON 작성

httpd 컨테이너의 로그를 볼 수 있게 태스크 정의를 만들어 보자.

로그는 키네시스 스트림에 전송하고자 한다. 그러려면 ECS Task Role은 kinesis 쓰기 권능을 갖고 있어야 한다.

이 예제는 앞서 소개한 Dockerfile 빌드를 수행하여 /extra.conf 파일이 aws-for-fluent-bit-customized 이미지에 패키징되어 있다고 가정한다.

{
    "family": "firelens-example-kinesis-data-stream",
    "taskRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/ecs_task_iam_role", # 태스크 롤
    "executionRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/ecs_task_execution_role",
    "containerDefinitions": [
        { # 여기는 Fluentd 컨테이너에 대한 설정임...
            "essential": true,
            "image": "aws-for-fluent-bit-customized:lastest", # MODIFIED
            "name": "log_router",
            "firelensConfiguration": {
                "type": "fluentbit"
                "options": { # ADDED
                    "config-file-type": "file", # ADDED
                    "config-file-value": "/extra.conf" # ADDED
                }
            },
            "logConfiguration": { 
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "firelens-container",
                    "awslogs-region": "us-west-2",
                    "awslogs-create-group": "true",
                    "awslogs-stream-prefix": "firelens"
                }
            },
            "memoryReservation": 50
        },
        { # 여기가 httpd 컨테이너에 대한 설정
             "essential": true,
             "image": "httpd",
             "name": "app",
             "logConfiguration": {
                 "logDriver":"awsfirelens",
                 "options": {
                    "Name": "kinesis_streams",
                    "region": "us-west-2",
                    "stream": "my-data-stream",
                    "retry_limit": "2"
                }
            },
            "memoryReservation": 100
        }
    ]
}

image

더 참고할 만한 예제를 여기서 확인하시라.

CloudFormation 템플릿 작성

방금 명세했던 ECS Task Definition을 똑같이 CloudFormation으로 작성해 보겠다. AWS::ECS::TaskDefinition 자원의 ContainerDefinition에서 FirelensConfiguration을 작성하면 된다.

Type: AWS::ECS::TaskDefinition
Properties: 
  Family: firelens-example-kinesis-data-stream
  TaskRoleArn: xxxxx # 태스크를 위한 권능 - Kinesis:PutRecords을 허용해주기.
  ExecutionRoleArn: xxxxx # ECS Agent를 위한 권능 - EC2 Instance 수준의 권능과 같다.
  ContainerDefinitions: 
    - Essential: true # 도커 컨네티이어 설정 - Firelens Fluentd
      Image: aws-for-fluent-bit-customized:lastest
      Name: log_route
      FirelensConfiguration:
        Type: fluentbit # fluentbit | fluentd
        Options:
          config-file-type: file # s3 | file
          config-file-value: /extra.conf # objet's arn | filepath
      LogConfiguration:
        LogDriver: awslogs
        Options: 
          awslogs-group: firelens-container
          awslogs-region: us-west-2
          awslogs-create-group: true
          awslogs-stream-prefix: firelens
      MemoryReservation: 20
    - Essential: true # 도커 컨네티너 설정 - 로그 생산용 앱
      Image: httpd
      Name: app
      LogConfiguration:
        LogDriver: awsfirelens
        Options: 
          Name: kinesis_streams
          region: us-west-2
          stream: my-data-stream
          retry_limit: 2
      MemoryReservation: 20

의의

마이크로서비스 환경에서 겉잡을 수 없이 분산되어 있는 컨테이너로부터, 응용 로그를 수집하고, 의미있는 메타 정보를 부여해, 하나의 저장소에 통합하는 것은, 시스템 가시성에 있어 몹시 중요한 워크플로이다.

태틱과 스타일에 대한 고찰

AWS FireLens는 ECS 태스크마다 Fluentd 디먼이 올라간 컨테이너를 소개해준다.

즉, FireLens의 아키텍처 태틱은 1) 사이드카 컨테이너이며 각 응용 로그를 끌어 모은다는 컨테이너에 전반에 걸친 횡단 관심사를 해소한다.

뿐만 아니라, 2) 매니지드 서비스로서 컨테이너 프로비저닝을 AWS가 담당하고, 기본 설정이 쓸만해 커스터마이징 없는 상태로도 운용 가능하다.

또, Fluetbit의 3) 파이프&필터 아키텍처 스타일과 4) 플러그&플레이 스타일을 잘 활용해 로그에 다양한 메타데이터를 붙이고, 태그를 기반으로 필터링하며, 적절한 목적지로 라우팅한다. 이러한 로직들은 플러그인으로서 갈아 끼워지며, 설정 파일을 토대로 외부에서 명세되어 DI된다.

결과적으로, 오픈 소스가 지원하는 모든 목적지로 로그를 전송할 수 있게 우리의 선택 폭이 넓어졌다.

마치며

사람들은 시스템에 배치된 특정 솔루션에 매몰되는 경향이 있다. 이런 훌륭한 시스템을 볼 때는, 전체가 어떠한 스타일과 전술로 아키텍팅 되었는가, 이로 인해 어떤 요구사항이 해소되고 있는가를 기억하는 게 좋을 것이다.

예를 들어 Fluentbit는 EFK 스택으로 Visualization 워크플로를 구성할 수 있다. 그러나 Logstash의 ELK 스택이나, Kinesis Data Streams의 EKK 스택 등 다른 솔루션과 충분히 대치 가능하다.

이런 선택지들 앞에서 우선할 관심사는 솔루션의 이름이나 기능 요구사항이 아니다. 각자 뿌리를 두고 있는 전술과, 이로 인해 벌어지는 품질 요구사항 커버리지의 미묘한 차이다.