이벤트 로그 서버 구축 (4) - 데이터 적재 파트 1
1. 데이터 적재 파트
데이터 적재 파트는 수집된 데이터를 안정적이고 효율적으로 저장하기 위한 역할을 수행합니다. 이 파트에서는 AWS Kinesis Firehose를 통해 데이터를 스트리밍 방식으로 처리하고, AWS S3에 적재하여 이후 분석 및 활용이 가능하도록 합니다. 이 파트는 데이터의 배치 처리, 압축 및 변환 등을 수행합니다.
2. 사용할 AWS 서비스
AWS Kinesis Data Firehose
Kinesis Data Firehose는 AWS에서 제공하는 완전 관리형 데이터 스트리밍 서비스로, 데이터를 실시간으로 수집하여 저장소나 분석 도구로 전달할 수 있습니다. 데이터 압축(예: gzip)과 형식 변환(예: JSON → Parquet)을 지원하여 저장 공간을 최적화하며, Lambda와 연동해 데이터를 실시간으로 가공할 수 있습니다. 이 프로젝트에서는 Firehose를 활용해 Lambda에서 전달받은 데이터를 압축 및 가공한 뒤 S3에 저장합니다.
AWS S3
S3는 AWS의 객체 스토리지 서비스로, 데이터를 안전하게 저장하고 관리할 수 있는 고가용성과 확장성을 제공합니다. 다양한 스토리지 옵션을 통해 비용 효율성을 극대화할 수 있으며, 데이터 암호화 및 버전 관리를 지원하여 높은 수준의 보안을 제공합니다. 이 프로젝트에서는 Firehose가 적재한 데이터를 저장하는 스토리지 역할을 수행합니다.
3. AWS S3 버킷 생성
먼저, S3 버킷을 생성해야 합니다. S3는 Firehose가 전송한 이벤트 로그를 저장하는 스토리지 역할을 수행합니다. AWS Console에서 S3 버킷을 생성하는 과정을 단계별로 설명하고, 이를 Serverless Framework로 관리하는 방법에 대해 다뤄보겠습니다.
주요 설정
(1) 객체 소유권
"객체 소유권"은 S3 버킷에 저장된 객체의 소유권을 정의하는 옵션으로, 버킷 소유자와 객체 업로드 주체 간의 소유권 충돌을 방지하고 관리 정책을 단순화하는 데 사용됩니다. 여기서는 기본값인 “ACL 비활성화됨(권장)”을 사용합니다.
(2) 이 버킷의 퍼블릭 액세스 차단 설정
"이 버킷의 퍼블릭 액세스 차단 설정"은Amazon S3 버킷에 대한 퍼블릭 액세스를 방지하기 위한 옵션입니다. 이벤트 로그는 외부에 공개할 데이터가 아니기 때문에 기본값인 차단 설정을 활성화 합니다
(3) 버킷 버전 관리
“버킷 버전 관리”는 Amazon S3에서 객체의 변경 이력을 관리하고, 이전 버전을 보관할 수 있는 기능입니다. 이를 통해 데이터 손실을 방지하거나, 잘못된 변경을 되돌릴 수 있는 복원 옵션을 제공합니다. 여기서는 기본값이 “비활성화”를 사용합니다.
AWS Console에서는 위의 설정대로 버킷을 생성하면 됩니다. 하지만 저는 Serverless Framework를 사용하여 인프라 리소스를 코드로 관리하려고 합니다. Serverless Framework에서는 serverless.yml 파일의 resources 프로퍼티에 CloudFormation 문법으로 작성하여 S3 버킷을 정의할 수 있습니다. 더 많은 옵션과 세부 사항은 AWS CloudFormation S3 리소스 공식 문서를 참고하세요. 아래는 위의 설정대로 S3 버킷을 생성하는 코드입니다.
[serverless.yml]
...
resources:
Resources:
EventLogBucket:
Type: AWS::S3::Bucket
Properties:
# "버킷 이름" 부분
# 운영 환경과 개발 환경의 리소스를 구분하기 위해서 stage 옵션의 값을 사용합니다.
BucketName: event-log-${self:provider.stage}
# "객체 소유권" 부분
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
# "이 버킷의 퍼블릭 엑세스 차단 설정" 부분
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
# "버킷 버전 관리" 부분
VersioningConfiguration:
Status: "Suspended"
4. AWS Kinesis Data Firehose 생성
다음으로 Firehose를 생성해야 합니다. Firehose는 실시간으로 스트리밍 데이터를 처리하고 S3와 같은 저장소로 전송하는 역할을 수행합니다. AWS Console에서 Firehose를 생성하는 과정을 단계별로 설명한 후, 이를 Serverless Framework로 관리하는 방법을 설명하겠습니다.
Firehose 스트림을 생성하기 위해 AWS Console에서 접속하여 Kinesis → Amazon Data Firehose → Firehose 스트림 생성을 클릭하여 생성을 시작합니다.
주요 설정
(1) 소스 및 대상 선택
“소스 및 대상 선택”은 데이터를 어디로부터 받아서 어디로 전송할 것인지 설정하는 옵션입니다.
소스는 “Direct PUT”을 선택합니다. “Direct PUT”은 데이터를 Firehose로 직접 전달하는 방식입니다. 이 프로젝트에서는 Lambda에서 Firehose로 전송하기 때문에 이 방식을 사용합니다.
대상은 “Amazon S3”를 선택합니다. 이 프로젝트에서는 데이터 분석 시 Athena를 사용할 계획이며, Athena는 S3에 저장된 데이터를 대상으로 SQL 쿼리를 실행할 수 있기 때문에 데이터를 S3에 저장합니다.
(2) 레코드 변형 및 변환
“레코드 변형 및 변환”은 데이터를 원하는 형태로 변환하거나 압축을 설정할 수 있는 옵션입니다. 이 프로젝트에서는 이 옵션을 사용하지 않지만, 간단히 각 옵션에 대해 알아보겠습니다.
“AWS Lambda를 사용하여 소스 레코드 변형”은 AWS Lambda를 사용하여 Firehose에 전달된 소스 데이터를 실시간으로 변형하거나 전처리할 수 있습니다. 변형은 주로 데이터 필터링, 포맷 변경, 필드 추가/삭제 등의 작업에 활용됩니다.
“레코드 형식 변환”은 Firehose에 전달된 소스 데이터를 특정 형식으로 변환할 수 있는 기능을 제공합니다. 예를 들어, JSON 데이터를 Apache Parquet 또는 ORC 형식으로 변환하여 저장 공간을 절약하고 분석 성능을 향상시킬 수 있습니다.
“Amazon CloudWatch Logs에서 소스 레코드 압축 해제”는 Firehose가 CloudWatch Logs 데이터를 수집할 때 사용하는 옵션입니다. CloudWatch Logs는 데이터를 Gzip 형식으로 압축해 전달하므로, 이 옵션을 사용하면 Firehose가 데이터를 자동으로 압축 해제한 후 처리할 수 있습니다.
(3) 대상 설정
“대상 설정”은 Firehose가 데이터를 전송할 대상을 설정하는 옵션입니다.
“S3 버킷”은 데이터가 저장될 버킷을 의미합니다. 이전에 생성한 버킷을 선택합니다.
“새 줄 구분자”는 각 레코드를 구분하기 위해 줄바꿈 문자를 추가하는 옵션입니다. 여기서는 기본값인 "활성화되지 않음"을 사용합니다.
“동적 파티셔닝”은 데이터를 S3에 저장할 때, 데이터의 특정 속성을 기준으로 폴더를 자동으로 생성하고 데이터를 분류하는 기능입니다. 예를 들어, 이벤트 로그 데이터에서 event_date 필드를 기준으로 파티셔닝을 설정하면, 데이터가 아래와 같은 폴더 구조로 저장됩니다. 여기서는 기본값인 "활성화되지 않음"을 사용합니다.
s3://your-bucket-name/event_date=2025-01-01/
s3://your-bucket-name/event_date=2025-01-02/
“S3 버킷 접두사”는 S3에 데이터를 저장할 경로를 결정하는 옵션입니다. 기본값은 "yyyy/mm/dd/hh” (UTC) 입니다. 여기서는 접두사를 “events/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/”로 지정하여 , events 폴더 아래에 날짜별로 데이터를 파티셔닝합니다. 이렇게 설정하면 데이터를 구조적으로 관리할 수 있어 이후 분석이나 검색 작업이 훨씬 효율적입니다.
"S3 버킷 오류 출력 접두사”는 Firehose에서 데이터를 S3에 전송하는 중 오류가 발생했을 때, 실패한 데이터를 저장할 경로를 지정하는 옵션입니다. 기본값은 error/로 시작하며, 정확한 경로는 Firehose에서 자동으로 생성합니다. 이 값을 지정할 때는 오류의 유형을 나타내는 !{firehose:error-output-type}를 반드시 포함해야 합니다. 여기서는 “errors/!{firehose:error-output-type}/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/”로 지정합니다.
“S3 버킷 및 S3 오류 출력 접두사 시간대”는 접두사에 사용하는 시간의 기준 시간대를 지정하는 옵션입니다. 여기서는 기본값인 UTC를 사용합니다.
(4) 버퍼 힌트, 압축, 파일 확장자 및 암호화
“버퍼 힌트, 압축, 파일 확장자 및 암호화”는 Firehose에서 데이터를 S3로 저장할 때, 데이터 처리와 저장 방식을 세부적으로 설정하는 옵션들입니다. 이 프로젝트에서는 모두 기본값으로 사용하지만, 간단히 각 옵션에 대해 알아보겠습니다.
“S3 버퍼 힌트”는 Firehose가 데이터를 S3에 전송하기 전에 버퍼에 데이터를 수집하는 조건을 설정하는 옵션입니다. “버퍼 크기”는 버퍼가 저장할 수 있는 데이터의 최대 크기를 의미하며, 이 크기에 도달하면 데이터를 전송합니다.
“버퍼 간격”은 버퍼가 데이터를 전송할 시간 간격을 의미하며, 설정된 간격마다 데이터를 전송합니다.
“데이터 레코드 압축”은 Firehose가 데이터를 S3로 전송하기 전에 데이터를 압축하여 저장 공간을 절약할 수 있도록 하는 옵션입니다. GZIP, ZIP, Snappy, Hadoop-Compatible Snappy 등의 압축 형식을 지원합니다.
“파일 확장자 형식”는 S3에 저장되는 데이터 파일의 확장자 이름을 설정하는 옵션입니다. 기본값은 특정 확장자를 사용하지 않습니다. 나중에 S3에 저장된 데이터를 확인해보면 확장자가 없는 것을 확인할 수 있습니다.
“데이터 레코드 암호화”는 Firehose가 데이터를 S3로 전송하는 동안 데이터를 보호하기 위한 암호화 설정 옵션입니다. 이 설정은 전송 중 데이터를 암호화하는 것으로, S3에 저장된 데이터의 암호화를 의미하지는 않습니다.
AWS Console에서는 위의 설정대로 Firehose를 생성합니다. 하지만 S3와 마찬가지로, 인프라를 코드로 관리하기 위해 Serverless Framework의 serverless.yml 파일에 Firehose 관련 코드를 추가하였습니다. Firehose는 조금 더 복잡합니다. 그 이유는 Firehose가 데이터를 S3로 전송하기 위해 S3 버킷에 대한 접근 권한이 필요하기 때문입니다. 따라서 Firehose 설정은 S3 접근 권한에 대한 IAM 역할을 생성하는 부분과 Firehose Stream를 생성하는 부분이 추가됩니다. 더 많은 옵션과 세부 사항은 AWS CloudFormation Firehose 리소스 공식 문서를 참고하세요. 아래는 위의 설정대로 Firehose를 생성하는 코드입니다.
[serverless.yml]
...
resources:
Resources:
...
# Firehose (Role)
FirehoseRole:
Type: AWS::IAM::Role
Properties:
# "IAM 역할 이름"
# 운영 환경과 개발 환경의 리소스를 구분하기 위해서 stage 옵션의 값을 사용합니다.
RoleName: event-log-firehose-role-${self:provider.stage}
# 이 역할을 사용할 수 있는 주체를 설정합니다.
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: firehose.amazonaws.com # Firehose 서비스가 이 역할을 사용할 수 있도록 허용
Action: sts:AssumeRole # Firehose 서비스가 이 역할의 권한을 위임받아 사용할 수 있도록 설정
# 세부 정책을 설정합니다.
Policies:
- PolicyName: FirehoseS3Access
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow # S3에 대한 접근 권한을 허용
Action:
- s3:PutObject # S3에 객체를 업로드할 수 있는 권한
- s3:PutObjectAcl # 업로드된 객체의 ACL을 설정할 수 있는 권한
Resource:
- !Sub "${EventLogBucket.Arn}/*" # Firehose가 접근할 S3 버킷과 그 내부 객체들
# Firehose (Stream)
EventLogStream:
Type: AWS::KinesisFirehose::DeliveryStream
Properties:
# "스트림 이름"
# 운영 환경과 개발 환경의 리소스를 구분하기 위해서 stage 옵션의 값을 사용합니다.
DeliveryStreamName: event-log-stream-${self:provider.stage}
# "소스 및 대상 선택 -> 소스" 부분
DeliveryStreamType: DirectPut
# "대상 설정" 부분
ExtendedS3DestinationConfiguration:
Prefix: "events/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/"
ErrorOutputPrefix: "errors/!{firehose:error-output-type}/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/"
CustomTimeZone: Asia/Seoul
BucketARN: !GetAtt EventLogBucket.Arn
RoleARN: !GetAtt FirehoseRole.Arn
5. Serverless Framework로 리소스 배포
지금까지 작성된 serverless.yml 파일은 아래와 같습니다. 참고로 S3 버킷 이름은 글로벌 유니크해야 하므로, 다른 프로젝트나 계정과 충돌하지 않도록 적절히 변경해야 합니다.
[serverless.yml]
# "org" ensures this Service is used with the correct Serverless Framework Access Key.
org: <YOUR_ORG>
# "service" is the name of this project. This will also be added to your AWS resource names.
service: event-log-server
provider:
name: aws
runtime: nodejs20.x
region: ap-northeast-2
functions:
recordEventLog:
handler: src/handler.recordEventLog
events:
- httpApi:
path: /
method: POST
plugins:
- serverless-offline
resources:
Resources:
EventLogBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: event-log-${opt:stage}
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: "Suspended"
EventLogFirehoseRole:
Type: AWS::IAM::Role
Properties:
RoleName: event-log-firehose-role-${opt:stage}
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: firehose.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: FirehoseS3Access
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:PutObject
- s3:PutObjectAcl
Resource:
- !Sub "${EventLogBucket.Arn}/*"
EventLogFirehoseStream:
Type: AWS::KinesisFirehose::DeliveryStream
Properties:
DeliveryStreamName: event-log-stream-${opt:stage}
DeliveryStreamType: DirectPut
ExtendedS3DestinationConfiguration:
Prefix: "events/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/"
ErrorOutputPrefix: "errors/!{firehose:error-output-type}/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/"
CustomTimeZone: UTC
BucketARN: !GetAtt EventLogBucket.Arn
RoleARN: !GetAtt EventLogFirehoseRole.Arn
이제 Serverless Framework CLI로 배포를 실행합니다. resources 프로퍼티에서 opt:stage 를 사용하고 있기 때문에 --stage 옵션을 명시적으로 추가해야 합니다. 배포가 완료된 후 AWS Console에 접속하여 S3와 Firehose에 접속해보면 리소스가 생성되어 있는 것을 확인할 수 있습니다.
$ sls deploy --stage dev
만약 "Error: Unauthorized…" 와 같은 에러가 발생한다면, AWS 자격 증명이 제대로 설정되지 않았을 가능성이 있습니다. 이 경우, AWS CLI를 통해 자격 증명을 재설정하거나, 공식 문서 에서 안내하는 방법 중 하나를 참고하여 문제를 해결하시면 됩니다.
혹시, 인증을 완료했음에도 동일한 "Error: Unauthorized…" 에러가 반복된다면,~/.serverless, ~/.serverlessrc, 그리고 프로젝트 루트의 .serverless 폴더를 삭제한 후 serverless login 명령을 다시 시도해보세요. 저도 이 방법으로 문제를 해결할 수 있었습니다.
다음 글에서는
이번 글에서는 데이터 적재를 위한 AWS 서비스와 이를 Serverless Framework를 활용해 효과적으로 관리하는 방법을 알아보았습니다. 다음 글에서는 이벤트 로그를 Firehose로 전송하는 코드를 작성하고, 이를 실제로 배포하여 테스트하는 과정을 다뤄보겠습니다.
2025.01.04 - [이벤트 로그] - 이벤트 로그 서버 구축 (1) - 개요
2025.01.11 - [이벤트 로그] - 이벤트 로그 서버 구축 (2) - 데이터 수집 파트 1
2025.01.18 - [이벤트 로그] - 이벤트 로그 서버 구축 (3) - 데이터 수집 파트 2
2025.01.25 - [이벤트 로그] - 이벤트 로그 서버 구축 (4) - 데이터 적재 파트 1