CloudWatch Log Group 로그를 S3로 Automatically하게 이전하기

· Infra, AWS, TroubleShooting

2021-2022 기간동안 스타트업에서 데브옵스 엔지니어로 일할 때 개인 노션에 트러블슈팅을 기록했던 내용을 블로그로 옮깁니다.

AS IS

TO BE

TRIED

내가 하려고 했던 작업을 분명 해본 사람이 있을거라 생각하고 찾아보니, 미디엄에 정확히 같은 내용의 글이 있었다.
Exporting Cloudwatch Logs automatically to S3 With a Lambda function
그래서 그대로 시도를 해봤는데 아래와 같은 에러가 출력됐다.

2022-06-09T00:44:50.137+09:00
[ERROR] ClientError: An error occurred (ThrottlingException) when calling the ListTagsLogGroup operation (reached max retries: 4): Rate exceeded
Traceback (most recent call last):
  File "/var/task/cloudwatch-to-s3.py", line 29, in lambda_handler
    response = logs.list_tags_log_group(logGroupName=log_group['logGroupName'])
  File "/var/runtime/botocore/client.py", line 391, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/var/runtime/botocore/client.py", line 719, in _make_api_call
    raise error_class(parsed_response, operation_name)
[ERROR] ClientError: An error occurred (ThrottlingException) when calling the ListTagsLogGroup operation (reached max retries: 4): Rate exceeded Traceback (most recent call last):   File "/var/task/cloudwatch-to-s3.py", line 29, in lambda_handler     response = logs.list_tags_log_group(logGroupName=log_group['logGroupName'])   File "/var/runtime/botocore/client.py", line 391, in _api_call     return self._make_api_call(operation_name, kwargs)   File "/var/runtime/botocore/client.py", line 719, in _make_api_call     raise error_class(parsed_response, operation_name)

오류 내용을 요약해보면, 짧은 시간에 AWS에 너무 많은 요청을 보냈다는 뜻인 것 같은데, 이는 Export 작업을 수행하는 Lambda 코드에서 발생한 것으로 보였다. (람다 관련 클라우드워치 로그였기 때문)

Trouble Shooting v1

Exporting Cloudwatch Logs automatically to S3 With a Lambda function

위 링크에서 올려놓은 코드를 분석해보았는데, 아래 부분에서 문제의 해답을 찾을 수 있었다. 비효율적으로 동작하고 있는 것으로 보였는데, 이 부분에서 AWS에 너무 많은 요청을 보내게 되면서 오류가 발생한 것 같았다. (해당 코드가 CW log-group 리소스가 적을 때는 문제가 없었겠지만, 회사처럼 리소스가 많은 곳에서는 모든 log-group에 대한 정보를 API에 매번 요청하면서 요청 횟수 초과가 발생한 듯하다.)

for log_group in log_groups:
    response = logs.list_tags_log_group(logGroupName=log_group['logGroupName'])
    log_group_tags = response['tags']
    if 'ExportToS3' in log_group_tags and log_group_tags['ExportToS3'] == 'true':
        log_groups_to_export.append(log_group['logGroupName'])

위에서 사용하는 log_groups 자체를 내가 export하고자하는 log_group으로 특정지어주면 되는 문제였다.

while True:
    # 기존 코드
    # response = logs.describe_log_groups(**extra_args)
    # 수정한 코드
    response = logs.describe_log_groups(logGroupNamePrefix="로그그룹명")
    log_groups = log_groups + response['logGroups']

Trouble Shooting v2

새로운 오류가 생겼다!

Error exporting 로그그룹명: 
InvalidParameterException('An error occurred (InvalidParameterException) 
when calling the CreateExportTask operation: GetBucketAcl call on the given bucket failed. 
Please check if CloudWatch Logs has been granted permission to perform this operation.')

또 열심히 검색을 해서 레퍼런스를 찾았다. Amazon S3로 Log Data 내보내는 방법. (Bucket Policy)

{
    "Sid": "CWLogsAcl",
    "Effect": "Allow",
    "Principal": {
        "Service": "logs.amazonaws.com"
    },
    "Action": "s3:GetBucketAcl",
    "Resource": "arn:aws:s3:::${aws_s3_bucket.로그그룹명.bucket}"
}

SOLVED

해결했다!