실시간 ALB 로그 분석을 통한 사전 통합 복구: Datadog 모니터, 워크플로우 및 AWS Lambda 활용
Source: Dev.to
문제점
- 드라마: Google Calendar 이벤트가 심각한 문제를 일으키고 있었고, 어떤 사용자가 불편을 겪고 있는지 전혀 파악할 수 없었습니다.
- 원인: 오류는 대부분 우리를 무시하는 채널이나 API에 도달하지 못하는 이벤트와 연결되어 있었습니다.
- 가장 안 좋은 점: ELB/ALB에 접근 로그가 없어서 전혀 상황을 알 수 없었습니다.
해결 방안
접근 로그를 추가하면 어떨까요? 이미 Datadog을 사용하고 있으니 ALB/ELB 접근 로그를 Datadog에 연동하기로 했습니다.
이 프로젝트는 Pulumi를 사용해 만들었지만, 걱정하지 마세요—추가하면 됩니다. 아직 Pulumi를 사용해 본 적이 없다면 문서를 확인해 보세요. Pulumi는 Terraform과 비슷하게 여러 클라우드 제공자를 동일한 라이브러리/도구로 관리할 수 있게 해 주며, TypeScript를 사용합니다.
ALB/ELB 접근 로그 추가하기
접근 로그란?
접근 로그는 로드 밸런서에서 발생하는 모든 일을 기록한 종이‑흔적입니다. 각 로그 항목은 다음과 같은 정보를 담습니다:
- 요청이 도착한 시점(타임스탬프)
- 클라이언트 IP 주소
- 요청된 URL
- 응답 상태 코드(예:
200,404) - 요청 지연 시간
- 요청을 처리한 대상 인스턴스 ID
새 ALB/ELB를 위한 Pulumi 코드 예시
아래는 ALB, 타깃 그룹, 리스너, 리스너 규칙을 생성하는 최소 Pulumi 설정 예시입니다. (원본 프로젝트에서는 ALB가 이미 존재했지만, 여기서는 처음부터 만드는 방법을 보여줍니다.)
ALB 생성
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
const mainAlb = new awsx.lb.ApplicationLoadBalancer("main-alb", {
name: "main-alb",
subnetIds: vpc.publicSubnetIds,
securityGroups: [securityGroup.id],
internal: false,
accessLogs: {
bucket: lbLogs.id,
prefix: "access-logs-lb",
enabled: true,
},
tags: {
stage: environment,
managed: "true",
},
});
타깃 그룹 생성
const webhooksTargetGroup = new aws.lb.TargetGroup("webhooks-tg", {
port: 80,
protocol: "HTTP",
targetType: "ip",
vpcId: vpc.vpcId,
healthCheck: {
enabled: true,
healthyThreshold: 2,
unhealthyThreshold: 2,
interval: 10,
path: "/api/v1/health-check",
port: "traffic-port",
},
});
HTTPS 리스너
// HTTPS listener
const httpsListener = new aws.lb.Listener("httpsListener", {
loadBalancerArn: mainAlb.loadBalancer.arn,
port: 443,
protocol: "HTTPS",
defaultActions: [{
type: "fixed-response",
fixedResponse: {
contentType: "text/plain",
statusCode: "404",
messageBody: "Not Found",
},
}],
sslPolicy: "ELBSecurityPolicy-2016-08",
certificateArn: myCertificateArn,
});
// Additional certificates
new aws.lb.ListenerCertificate("webhooks-attachment", {
listenerArn: httpsListener.arn,
certificateArn: webhooksCertificateArn,
});
리스너 규칙
new aws.lb.ListenerRule("rule-webhooks", {
listenerArn: httpsListener.arn,
priority: 3,
actions: [{ type: "forward", targetGroupArn: webhooksTargetGroup.arn }],
conditions: [{ hostHeader: { values: [`webhooks.${route53ZoneName}`] } }],
});
이미 존재하는 ALB/ELB를 위한 Pulumi 코드 예시
로드 밸런서가 이미 존재한다면, 로그를 저장할 S3 버킷을 만들고 ALB에 로그를 활성화하기만 하면 됩니다.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// 1. ALB 접근 로그용 S3 버킷 생성
const albAccessLogsBucket = new aws.s3.Bucket("alb-access-logs", {
bucket: `alb-access-logs.${environment}.us-east-1.careops`,
acl: "private",
});
// ELB가 로그를 쓸 수 있도록 버킷 정책 추가
new aws.s3.BucketPolicy("albLogsBucketPolicy", {
bucket: albAccessLogsBucket.id,
policy: albAccessLogsBucket.arn.apply(bucketArn => JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Principal: { AWS: "arn:aws:iam::127311923021:root" }, // us-east-1 지역 ELB 서비스 계정
Action: ["s3:PutObject", "s3:GetBucketAcl"],
Resource: [bucketArn, `${bucketArn}/*`],
}],
})),
});
// 2. ALB에 접근 로그 활성화
new aws.lb.LoadBalancerAttributes("mainAlbAccessLogs", {
loadBalancerArn: mainAlb.loadBalancer.arn,
attributes: [
{ key: "access_logs.s3.enabled", value: "true" },
{ key: "access_logs.s3.bucket", value: albAccessLogsBucket.bucket },
{ key: "access_logs.s3.prefix", value: "main-alb/" },
],
});
이제 ALB/ELB 트래픽 로그를 저장하는 버킷이 준비되었습니다.
Note: ELB는 각 로드 밸런서 노드마다 5분마다 로그 파일을 생성합니다. 전달은 최종적으로 일관성을 갖지만, 트래픽이 많을 경우 동일 기간에 여러 로그 파일이 생성될 수 있습니다.
로그 파일 이름 패턴은 다음과 같습니다:
bucket[/prefix]/AWSLogs/aws-account-id/elasticloadbalancing/region/yyyy/mm/dd/
aws-account-id_elasticloadbalancing_region_app.load-balancer-id_end-time_ip-address_random-string.log.gz
예시:
s3://amzn-s3-demo-logging-bucket/logging-prefix/AWSLogs/123456789012/elasticloadbalancing/us-east-2/2022/05/01/
123456789012_elasticloadbalancing_us-east-2_app.my-loadbalancer.1234567890abcdef_20220215T2340Z_172.160.001.192_20sg8hgm.log.gz
로그를 Datadog으로 전달하기
로그를 Datadog에 전송하려면 S3 버킷에 새로운 객체가 생길 때마다 이를 처리하고 Datadog API로 전달하는 Lambda 함수가 필요합니다.
// 3. Datadog Forwarder Lambda 배포 (AWS SAM/CloudFormation 또는 수동 배포)
// Pulumi에서는 aws.cloudformation.Stack을 사용하거나 Lambda를 직접 배포할 수 있습니다.
const datadogForwarder = new aws.lambda.Function("datadogForwarder", {
runtime: aws.lambda.Runtime.Python39,
handler: "lambda_function.lambda_handler",
role: datadogForwarderRole.arn,
code: new pulumi.asset.AssetArchive({
".": new pulumi.asset.FileArchive("./datadog-forwarder"),
}),
environment: {
variables: {
DD_API_KEY_SECRET_ARN: datadogApiKeySecret.arn,
DD_SITE: "datadoghq.com",
DD_TAGS: `env:${environment}`,
},
},
});
// 4. 새로운 로그가 도착하면 Lambda를 트리거하도록 S3 이벤트 알림 설정
new aws.s3.BucketNotification("albLogsNotification", {
bucket: albAccessLogsBucket.id,
lambdaFunctions: [{
lambdaFunctionArn: datadogForwarder.arn,
events: ["s3:ObjectCreated:*"],
filterPrefix: "access-logs-lb/",
}],
});
이 구성을 사용하면 새로운 접근 로그 파일이 S3 버킷에 저장될 때마다 Lambda가 해당 파일을 Datadog으로 전달합니다. 그 결과 Datadog에서 모니터, 대시보드 및 자동 복구 워크플로우를 만들 수 있게 됩니다.