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가 관리하는 컨테이너를 대상으로 합니다.
전제 조건¶
- 먼저 Guance 계정을 생성해야 합니다.
- DataKit 설치
- Func 휴대용 버전 설치
- ECS에서 실행 중인 Java 애플리케이션이 이미 있어야 합니다.
여기서 사용하는 ECS 클러스터 이름은 cluster-docker입니다. 아래에서 샘플 로그 및 로그 그룹을 확인합니다. 「AWS」에 로그인하여 「Elastic Container Service」 - 「클러스터」 - 「cluster-docker」를 클릭합니다.
「서비스 이름」을 클릭합니다.
태스크로 이동합니다.
세부 정보 탭의 컨테이너 아래에서 로그 구성을 찾습니다.
「로그」 탭을 클릭하면 애플리케이션 로그가 표시됩니다. 다음으로 이 로그를 수집합니다.
작업 단계¶
Warning
예시에서 사용된 버전은 DataKit 1.4.18입니다.
1단계 AWS 구성¶
1.1 사용자 키¶
ECS 배포에 사용된 계정을 사용합니다. AWS에서 해당 사용자를 생성할 때 제공된 Access key ID 및 Secret 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_NAME는 2.1단계의 환경 변수 이름에 해당합니다. 환경 변수 이름이 변경된 경우 해당 내용을 수정해야 합니다.
2.5 스크립트 테스트¶
아래 그림과 같이 「run」을 선택합니다. 두 번째 빨간 상자에 표시된 내용에서:
measurement값에ecs_log_source을 입력합니다. 이 값은 Guance 로그의 로그 소스에 해당합니다.logGroupName값은 전제 조건의 로그 구성에서 확인한awslogs-group에 해당합니다.interval값은 수집 빈도에 해당하며, 예시에서는 60초입니다.
「실행」을 클릭하면 total 8이 출력되며, 이는 8개의 로그가 전송되었음을 의미합니다.
「Guance」에 로그인하여 「로그」 모듈로 이동한 후 데이터 소스로 「ecs_log_source」를 선택하면 로그를 확인할 수 있습니다.
오른쪽 상단의 「게시」를 클릭합니다.
오른쪽 상단의 「편집 종료」를 클릭합니다.
2.6 자동 로그 수집¶
「Func」에 로그인하여 「관리」 - 「자동 트리거 구성」 - 「새로 만들기」를 선택하고 매개변수에 방금 실행한 내용을 입력합니다.
시간은 매분 또는 5분마다로 선택하고 「저장」을 클릭합니다.
「자동 트리거 구성」 목록에 「aws_ecs log」 레코드가 있습니다.
「최근 실행」을 클릭하여 실행 상태를 확인합니다.
























