콘텐츠로 이동

Guance Amazon ECS 로그 수집


개요

Amazon Elastic Container Service(Amazon ECS)는 확장성이 뛰어난 고속 컨테이너 관리 서비스로, 클러스터에서 컨테이너를 손쉽게 실행, 중지 및 관리할 수 있습니다. 이러한 컨테이너는 자체 EC2 서버에서 실행하거나 AWS Fargate가 관리하는 서버리스 인프라에서 실행할 수 있습니다.
Fargate 시작 유형을 사용하는 태스크의 경우 컨테이너의 awslogs 로그 드라이버를 시작해야 하며, 컨테이너에서 실행되는 애플리케이션이 STDOUT 및 STDERR I/O 스트림으로 출력하는 로그는 CloudWatch Logs의 로그 그룹으로 전송됩니다. 그런 다음 Func를 통해 이러한 로그를 수집하고, Func는 EC2에 배포된 DataKit을 통해 로그를 Guance에 기록합니다.

이 문서의 로그 수집은 AWS Fargate가 관리하는 컨테이너를 대상으로 합니다.

이미지

전제 조건

여기서 사용하는 ECS 클러스터 이름은 cluster-docker입니다. 아래에서 샘플 로그 및 로그 그룹을 확인합니다. 「AWS」에 로그인하여 「Elastic Container Service」 - 「클러스터」 - 「cluster-docker」를 클릭합니다.

이미지

「서비스 이름」을 클릭합니다.

이미지

태스크로 이동합니다.

이미지

세부 정보 탭의 컨테이너 아래에서 로그 구성을 찾습니다.

이미지

「로그」 탭을 클릭하면 애플리케이션 로그가 표시됩니다. 다음으로 이 로그를 수집합니다.

이미지

작업 단계

Warning

예시에서 사용된 버전은 DataKit 1.4.18입니다.

1단계 AWS 구성

1.1 사용자 키

ECS 배포에 사용된 계정을 사용합니다. AWS에서 해당 사용자를 생성할 때 제공된 Access key IDSecret access key는 이후에 사용됩니다.

1.2 AWS 사용자 권한 설정

AWS IAM 콘솔에 로그인하여 사용자 아래에서 ECS가 있는 「사용자」를 찾은 다음 「권한 추가」를 클릭합니다.

이미지

「기존 정책 직접 연결」을 클릭하고, 「정책 필터」에서 CloudWatchLogsReadOnlyAccess, CloudWatchEventsReadOnlyAccess를 선택한 다음 「다음: 검토」를 클릭합니다.

이미지

2단계 Func 구성

2.1 환경 변수 구성

「Func」에 로그인하여 「개발」 - 「환경 변수」 - 「환경 변수 추가」를 선택합니다. 다음 3개의 환경 변수를 추가합니다.

  • AWS_LOG_KEY 값은 1.1단계의 AWS 사용자 Access key ID에 해당합니다.
  • AWS_LOG_SECRET_ACCESS_KEY 값은 1.1단계의 AWS 사용자 Secret access key에 해당합니다.
  • AWS_REGION_NAME 값은 AWS 사용자가 속한 REGION에 해당합니다.

이미지

2.2 커넥터 구성

「Func」에 로그인하여 「개발」 - 「커넥터」 - 「커넥터 추가」를 선택합니다.
여기서 ID는 반드시 DataKit으로 입력하고, 호스트는 DataKit이 설치된 주소, 포트는 DataKit의 포트입니다. (본 예시에서는 IP를 직접 사용하므로 프로토콜은 HTTP로 입력합니다.)
「연결 테스트」를 클릭했을 때 ✅가 반환되면 DataKit을 사용할 수 있습니다.

이미지

2.3 PIP 도구 구성

「Func」에 로그인하여 「관리」 - 「실험적 기능」을 선택하고 오른쪽에서 「PIP 도구 모듈 활성화」를 선택합니다.

이미지

왼쪽의 「PIP 도구」를 클릭하고 「Alibaba Cloud 미러」를 선택한 다음 boto3을 입력하고 「설치」를 클릭합니다.

이미지

2.4 스크립트 라이브러리

「Func」에 로그인하여 「개발」 - 「스크립트 라이브러리」 - 「스크립트 세트 추가」를 선택합니다. ID는 사용자 정의할 수 있으며 「저장」을 클릭합니다.

이미지

「AWS 로그 수집」을 찾은 다음 「스크립트 추가」를 클릭합니다.

이미지

ID를 입력합니다. 예시에서는 aws_ecs__log로 정의하고 「저장」을 클릭합니다.

이미지

「편집」을 클릭합니다.

이미지

다음 내용을 입력합니다.

입력 내용
    import boto3
    import json
    import time
    scope_id='ecs_log'

    @DFF.API('aws_ecs log', timeout=500, api_timeout=180)
    def run(measurement, logGroupName, interval):
        print(measurement, logGroupName, interval)
        get_log_data(measurement, logGroupName, interval)
        # if data is not None:
        #     push_log(data)
        # else:
        #     print("None")


    def get_cron_time(interval, measurement):
        cache = DFF.CACHE.get('last_time_%s' %measurement,scope=scope_id)
        if cache == None:
            currentTime = int(round(time.time() * 1000))
            startTime = currentTime - int(interval) * 1000
            endTime = currentTime
        else:
            currentTime = int(round(time.time() * 1000))
            if currentTime - int(cache) > 10 * 60 * 1000:
                startTime = currentTime - int(interval) * 1000
                endTime = currentTime
            else:
                startTime = int(cache) + 1
                endTime = currentTime
        print(startTime, endTime)
        return  startTime, endTime

    def get_log_data(measurement, logGroupName, interval):
        logTime = get_cron_time(interval, measurement)
        startTime = logTime[0]
        endTime = logTime[1]
        isPush = False
        client = boto3.client(
            'logs',
            aws_access_key_id=DFF.ENV('AWS_LOG_KEY'),
            aws_secret_access_key=DFF.ENV('AWS_LOG_SECRET_ACCESS_KEY'),
            region_name=DFF.ENV('AWS_REGION_NAME')
        )# print(client.meta.config)
        try:
            nextToken = 'frist'
            logData = []
            while nextToken != '':
                if nextToken == 'frist':
                    nextToken = ''
                    response = client.filter_log_events(
                        logGroupName=logGroupName,
                        startTime=startTime,
                        endTime=endTime,
                        limit=1000,
                        #filterPattern="?ERROR ?WARN ?error ?warn",
                        interleaved=False
                    )
                else:
                    response = client.filter_log_events(
                        logGroupName=logGroupName,
                        startTime=startTime,
                        endTime=endTime,
                        nextToken=nextToken,
                        limit=1000,
                        #filterPattern="?ERROR ?WARN ?error ?warn",
                        interleaved=False
                    )
                try:
                    if len(response['events']) > 0:
                        data = []
                        lastTimeList = []
                        for i in response['events']:
                            # print("hii", i['logStreamName'])
                            log = {
                                'measurement': measurement,
                                'tags': {
                                    'logGroupName': logGroupName,
                                    'logStreamName': i['logStreamName'],
                                    'host': '127.0.0.1'
                                },
                                'fields': {
                                    'message': i['message'],
                                    'time': i['timestamp']
                                }
                            }
                            data.append(log)
                            lastTimeList.append(i['timestamp'])
                        push_log(data)
                        print("max %s"  % max(lastTimeList))
                        DFF.CACHE.set('last_time_%s' % measurement, max(lastTimeList), scope=scope_id, expire=None)
                        isPush = True
                    else:
                        DFF.CACHE.set('last_time_%s' % measurement, endTime , scope=scope_id, expire=None)
                    nextToken = response['nextToken']
                except:
                    nextToken = ''
        except Exception as  e:
            print('Error: %s' % e )
            return None
        if not isPush:
            DFF.CACHE.set('last_time_%s' % measurement, endTime , scope=scope_id, expire=None)

    def push_log(data):
        datakit = DFF.SRC('datakit')
        status_code, result = datakit.write_logging_many(data=data)
        if status_code == 200:
            print("total %d"  % len(data))
            print(status_code, result)
Warning
  • 위 내용 4행의 ecs_log 는 동일한 Func 내에서 고유해야 하므로 다른 문자로 변경할 수 있습니다.
  • 6행의 awc_ecs 는 방금 추가한 스크립트 세트 ID입니다.
  • 40, 41, 42행의 AWS_LOG_KEY, AWS_LOG_SECRET_ACCESS_KEY, AWS_REGION_NAME2.1단계의 환경 변수 이름에 해당합니다. 환경 변수 이름이 변경된 경우 해당 내용을 수정해야 합니다.

2.5 스크립트 테스트

아래 그림과 같이 「run」을 선택합니다. 두 번째 빨간 상자에 표시된 내용에서:

  • measurement 값에 ecs_log_source을 입력합니다. 이 값은 Guance 로그의 로그 소스에 해당합니다.
  • logGroupName 값은 전제 조건의 로그 구성에서 확인한 awslogs-group에 해당합니다.
  • interval 값은 수집 빈도에 해당하며, 예시에서는 60초입니다.

이미지

「실행」을 클릭하면 total 8이 출력되며, 이는 8개의 로그가 전송되었음을 의미합니다.

이미지

Guance」에 로그인하여 「로그」 모듈로 이동한 후 데이터 소스로 「ecs_log_source」를 선택하면 로그를 확인할 수 있습니다.

이미지

오른쪽 상단의 「게시」를 클릭합니다.

이미지

오른쪽 상단의 「편집 종료」를 클릭합니다.

이미지

2.6 자동 로그 수집

「Func」에 로그인하여 「관리」 - 「자동 트리거 구성」 - 「새로 만들기」를 선택하고 매개변수에 방금 실행한 내용을 입력합니다.

{
  "measurement": "ecs_log_source",
  "logGroupName": "/ecs/demo-task",
  "interval": 60
}

이미지

시간은 매분 또는 5분마다로 선택하고 「저장」을 클릭합니다.

이미지

「자동 트리거 구성」 목록에 「aws_ecs log」 레코드가 있습니다.

이미지

「최근 실행」을 클릭하여 실행 상태를 확인합니다.

이미지

문서 평가

이 페이지가 도움이 되었나요?