실시간 ALB 로그 분석을 통한 사전 통합 복구: Datadog 모니터, 워크플로우 및 AWS Lambda 활용

발행: (2025년 12월 8일 오후 12:22 GMT+9)
6 min read
원문: Dev.to

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에서 모니터, 대시보드 및 자동 복구 워크플로우를 만들 수 있게 됩니다.

Back to Blog

관련 글

더 보기 »

왜 GitFlow는 인프라에서 실패하는가

TL;DR GitFlow의 장기 운영 피처 또는 환경 브랜치를 Terraform에 적용하면 상태 드리프트와 취약한 파이프라인이 자주 발생합니다. 애플리케이션 코드와 달리 Infra...