AWS 중급 #6 ALB / NLB와 ACM (HTTPS)

8 분 소요

#5 Route 53의 도메인이 가리키는 곳에는 거의 항상 로드 밸런서가 있습니다. AWS의 매니지드 로드 밸런서를 통틀어 ELB (Elastic Load Balancing) 라고 부르고, 그 안에 ALB / NLB / GWLB 세 가지 종류가 있습니다.

이 글에선 세 LB의 차이 → ALB의 Listener / Target Group / Health Check 흐름 → ACM으로 인증서를 받아 HTTPS를 다는 과정까지 한 줄로 정리합니다.

로드 밸런서가 푸는 문제 #

EC2 한 대 뒤에 도메인을 직접 꽂으면 다음이 깨집니다.

문제LB가 푸는 기능
EC2가 죽으면 사이트가 다운Health check로 죽은 인스턴스를 빼고 살아있는 쪽으로
트래픽 많아져 한 대로 부족여러 인스턴스에 부하 분산
AZ 장애 시 전면 다운Multi-AZ 분산
HTTPS 인증서 인스턴스마다 관리LB에서 한 번 끊어줌 (TLS termination)
카나리 / Blue-Green 배포Listener Rule로 비율 분산

운영에서는 거의 항상 Internet → ALB / NLB → EC2 / ECS / Lambda 패턴입니다.

ALB / NLB / GWLB: 세 가지 비교 #

ALB (Application LB)NLB (Network LB)GWLB (Gateway LB)
OSI 레이어L7 (HTTP / HTTPS)L4 (TCP / UDP / TLS)L3/L4 (IP)
라우팅 방식path / host / header / 메소드port만패킷 그대로
처리량좋음매우 빠름 (수백만 RPS)적합한 경우에 빠름
WebSocket-
HTTP/2✅ (TLS)-
고정 IP❌ (DNS)✅ (AZ마다 EIP)-
WAF 연동-
Cognito 인증-
Lambda target-
용도웹 / API게임 / IoT / TCP / gRPC보안 어플라이언스 (Firewall)

결정 가이드 #

LB 결정 트리
HTTP(S) ?
├── YES → ALB
│   └── 매우 높은 RPS (수십만+)? → ALB → 한계 시 NLB
└── NO →
    TCP/UDP ?
    ├── YES → NLB
    └── 외부 보안 어플라이언스 통과 → GWLB

99%의 일반 웹 워크로드는 ALB입니다. NLB는 게임 / IoT / 매우 높은 처리량 / 고정 IP가 필요한 경우에 씁니다.

ALB의 구조 #

ALB의 구조
                    Route 53
                   ┌────────┐
                   │  ALB   │
                   │        │
                   └─┬──┬───┘
                     │  │
                Listener (port 443)
                     │  │
                     ▼  ▼
                Listener Rules
                  (path / host)
                     ├── /api/*  ────▶ Target Group A (api EC2 들)
                     ├── /admin/* ────▶ Target Group B (admin)
                     └── 기본 /     ────▶ Target Group C (web)
                                          ├── EC2 #1 (AZ a)
                                          ├── EC2 #2 (AZ b)
                                          └── EC2 #3 (AZ a)

핵심 구성 요소:

1) Listener: 받는 포트 #

ALB가 어느 포트로 트래픽을 받을지. 보통 80 (HTTP), 443 (HTTPS).

ALB + Listener 만들기
aws elbv2 create-load-balancer \
  --name my-alb \
  --subnets subnet-pubA subnet-pubB \
  --security-groups sg-alb \
  --scheme internet-facing \
  --type application

aws elbv2 create-listener \
  --load-balancer-arn <alb-arn> \
  --protocol HTTPS \
  --port 443 \
  --certificates CertificateArn=<acm-arn> \
  --default-actions Type=forward,TargetGroupArn=<tg-arn>

2) Target Group: 보낼 대상 #

Listener가 트래픽을 보낼 인스턴스 / IP / Lambda의 묶음. 포트와 프로토콜을 가진 그룹.

Target Group 만들기
aws elbv2 create-target-group \
  --name my-app-tg \
  --protocol HTTP \
  --port 8080 \
  --vpc-id vpc-... \
  --target-type instance \
  --health-check-protocol HTTP \
  --health-check-path /health \
  --health-check-interval-seconds 30 \
  --healthy-threshold-count 2 \
  --unhealthy-threshold-count 3

target-type의 의미:

타입설명
instanceEC2 인스턴스 ID. SG 지정에 매끄러움
ip임의 IP (VPC 안). ECS / Fargate의 자동 등록
lambdaLambda 함수. ALB → Lambda 패턴

3) Listener Rule: 라우팅 규칙 #

같은 Listener 안에서 path / host / header 조건에 따라 다른 Target Group으로 보냅니다.

Listener Rules의 모양
Priority  Condition                      Action
10        host = api.example.com         forward → tg-api
20        path = /admin/*                forward → tg-admin
30        path = /static/*               redirect → cloudfront.example.com
default   *                              forward → tg-web

규칙은 priority 순 (작은 게 먼저). 매치되면 멈춤.

Listener Rule 액션 #

  • forward. Target Group으로 (가중치로 여러 대상)
  • redirect. 다른 URL로 (HTTP → HTTPS 영구 리다이렉트의 정석)
  • fixed-response. 고정 응답 (메인터넌스 페이지)
  • authenticate-cognito / -oidc. 사용자 인증 후 통과
HTTP → HTTPS 리다이렉트의 정석
Listener (port 80)
  default action: redirect → HTTPS://#{host}#{path}#{query} (301)

Listener (port 443)
  default action: forward → tg-web

이 패턴이 운영의 표준. 모든 80 트래픽을 443으로 영구 보냅니다.

Health Check: 살아있는 대상만 #

Target Group의 Health Check가 죽은 인스턴스를 자동으로 빼고 살아나면 다시 넣어줍니다.

Health Check의 흐름
ALB ─ HTTP GET /health ──▶ EC2:8080
                       응답 200 ──▶ healthy (3회 연속)
                       응답 5xx ──▶ unhealthy (3회 연속) → 라우팅에서 제외

자주 쓰는 옵션:

옵션설명
HealthCheckPath/health, /healthz 같은 가벼운 경로
HealthCheckProtocolHTTP / HTTPS
HealthCheckIntervalSeconds30 (보통)
HealthyThresholdCount2~3회 연속 200 → healthy
UnhealthyThresholdCount2~3회 연속 실패 → unhealthy
Matcher.HttpCode200 또는 200-299

Health Check 경로 설계 #

좋은 /health 응답:

가벼운 health endpoint 예 (FastAPI)
@app.get("/health")
def health():
    return {"status": "ok"}

DB 체크까지 하는 deep health는 별도 경로에:

deep health
@app.get("/health/deep")
def deep_health(db: Session = Depends(get_db)):
    db.execute("SELECT 1")
    return {"status": "ok", "db": "ok"}

/health는 항상 가볍게, /health/deep은 모니터링 / 디버깅 용도로 둡니다. ALB가 deep health를 폴링하면 DB 부담이 큽니다.

Sticky Session: 같은 대상으로 #

특정 사용자의 요청을 항상 같은 인스턴스로 보내는 옵션.

Stickiness 켜기
aws elbv2 modify-target-group-attributes \
  --target-group-arn <tg-arn> \
  --attributes \
    Key=stickiness.enabled,Value=true \
    Key=stickiness.type,Value=lb_cookie \
    Key=stickiness.lb_cookie.duration_seconds,Value=86400

구성:

  • 세션을 메모리에 두는 옛 앱. 거의 안 만들어야 하지만 마이그레이션 과정에만 임시로
  • WebSocket 같은 장수명 연결. 자동 stickiness

운영에서는 세션을 외부 (Redis / DB)로 빼고 stateless로 두는 것이 정석입니다. Stickiness는 예외적인 경우에만 씁니다.

NLB의 용도 #

ALB가 못 푸는 경우에 NLB를 씁니다.

NLB의 강점 #

  • 고정 IP (AZ마다 EIP). 방화벽 화이트리스트 용도
  • 수백만 RPS 처리
  • TLS termination도 가능 (NLB가 TLS 풀고 평문으로 backend)
  • PrivateLink 지원 (서비스를 다른 VPC에 노출)

NLB의 약점 #

  • L4만. path / host / header 라우팅 안 됨
  • WAF 연동 직접 안 됨
  • 단일 Listener가 한 Target Group
NLB + TLS Listener
aws elbv2 create-load-balancer \
  --name my-nlb --type network \
  --subnets subnet-pubA subnet-pubB

aws elbv2 create-listener \
  --load-balancer-arn <nlb-arn> \
  --protocol TLS --port 443 \
  --certificates CertificateArn=<acm-arn> \
  --default-actions Type=forward,TargetGroupArn=<tg-arn>

ACM: 인증서 발급의 용도 #

**ACM (AWS Certificate Manager)**는 AWS 안의 ALB / NLB / CloudFront / API Gateway에서 쓰는 공개 SSL 인증서를 무료로 발급하고 자동 갱신해 줍니다.

인증서 요청 #

ACM 인증서 요청
aws acm request-certificate \
  --domain-name example.com \
  --subject-alternative-names "*.example.com" "api.example.com" \
  --validation-method DNS \
  --region ap-northeast-2

검증 방식:

  • DNS 검증 (권장). Route 53의 CNAME으로 자동 검증, 자동 갱신
  • Email 검증 (옛 방식). admin@example.com 등으로 메일 발송, 매년 수동

거의 항상 DNS 검증. Route 53을 같이 쓰면 콘솔이 “Create record in Route 53” 버튼으로 자동.

인증서의 규칙: 리전 일치 규칙 #

ACM 인증서는 리전 단위. ALB가 서울에 있으면 인증서도 서울에서 발급. 단, CloudFront는 항상 us-east-1 (#7).

리전 매핑
ALB (서울)         → ACM 인증서 (ap-northeast-2)
NLB (도쿄)         → ACM 인증서 (ap-northeast-1)
CloudFront         → ACM 인증서 (us-east-1) ← 항상
API Gateway (REST) → ACM 인증서 (해당 리전)
API Gateway (Edge) → ACM 인증서 (us-east-1)

자동 갱신 #

ACM 인증서는 기본 13개월 유효, 만료 60일 전부터 자동 갱신:

  • DNS 검증으로 만든 인증서: 완전 자동
  • Email 검증: 수동 (그래서 DNS 권장)

자동 갱신이 실패하는 경우:

  • DNS 검증 CNAME이 사라짐 → 도메인 풀이 실패
  • ALB가 인증서 사용 중이지만 도메인이 다른 곳으로 이동 → 검증 실패

ACM 콘솔에서 만료 알림 자동, CloudWatch 알람도 가능.

HTTPS 다는 한 줄 절차 #

도메인이 있고 ALB가 있다고 가정.

HTTPS 다는 절차
1. ACM에서 인증서 요청 (DNS 검증)
2. Route 53에 검증 CNAME 추가 (콘솔 한 번 클릭)
3. 인증서 ISSUED 대기 (수 분)
4. ALB Listener 443에 인증서 attach
5. Listener 80 → HTTPS redirect
6. Route 53의 도메인 → ALB Alias

이 6단계가 운영의 표준 패턴이며, 인증서를 매년 수동 갱신할 일도 없습니다.

Security Policy: TLS 버전 #

Listener의 Security Policy가 허용할 TLS 버전과 암호 모음을 정의합니다.

자주 쓰는 정책
ELBSecurityPolicy-TLS13-1-2-2021-06   ← 권장 (TLS 1.3, 1.2)
ELBSecurityPolicy-TLS-1-2-2017-01     ← 호환성 좋음
ELBSecurityPolicy-FS-2018-06          ← Forward Secrecy 강제

옛 클라이언트 (TLS 1.0, 1.1)를 끊고 싶다면 TLS13-1-2-2021-06. 매년 새 정책이 나오니 정기 점검.

Connection Draining: 우아한 종료 #

인스턴스를 수동으로 빼거나 ASG가 줄일 때 진행 중인 요청이 끊기지 않도록 기다려 주는 기능.

Connection Draining
ALB ─ 새 요청은 안 보냄 ─▶ EC2  (deregistration_delay = 300s)
        진행 중인 요청은 끝까지 처리

기본 300초. 보통 30~60초로 줄여도 충분. 너무 짧으면 진행 중 요청 끊김, 너무 길면 배포가 느림.

LB의 SG와 EC2의 SG #

#2에서 본 SG 패턴:

ALB ↔ EC2의 SG
ALB SG (sg-alb)
  Inbound:  443 ← 0.0.0.0/0
  Outbound: all

EC2 SG (sg-app)
  Inbound:  8080 ← sg-alb           ← ALB SG 자체
  Outbound: all

NLB는 다릅니다. NLB 자체는 SG가 없거나 (옛 NLB) 또는 한 SG만 (새 옵션). 그래서 EC2 SG가 NLB의 IP가 아닌 클라이언트 IP를 그대로 보게 됩니다. 운영에선 NLB 클라이언트 IP를 좁히기 위해 NACL이나 VPC 단 제어를 추가하기도 합니다.

자주 만나는 함정 #

1) ACM 인증서가 ISSUED 안 됨 #

원인 거의 99%:

  • 검증 CNAME이 Route 53에 안 들어감
  • CNAME이 잘못된 zone에 들어감 (example.com이 아니라 api.example.com의 zone)
  • TTL이 너무 길어서 전파 안 됨 → 5분 정도 기다림

2) CloudFront 인증서를 서울에서 발급 #

CloudFront는 us-east-1. 콘솔에서 받을 때 우측 상단을 N. Virginia로.

3) HTTP 80 Listener 안 만들고 HTTPS만 #

사용자가 http://example.com으로 들어오면 timeout. 80 Listener의 redirect to HTTPS가 표준.

4) Health check path가 / #

/가 무거운 경우 (DB 호출 등)면 ALB health check가 DB를 30초마다 폭격합니다. /health 같은 가벼운 경로를 별도로 둡니다.

5) Target Group의 포트 vs Listener 포트 #

ALB Listener는 443인데 Target Group의 EC2는 8080일 수 있습니다. 두 포트는 별개입니다. Target Group 포트가 EC2가 listen 중인 포트입니다.

6) 502 Bad Gateway #

ALB → EC2 응답이 깨질 때. 흔한 원인:

  • EC2의 keep-alive timeout < ALB idle timeout (60s 기본)
  • EC2가 응답하기 전에 끊김
  • EC2 SG가 ALB SG를 inbound에 안 받음
  • EC2가 8080에서 listen 안 함

7) Sticky session으로 부하 쏠림 #

특정 인스턴스에만 요청이 쌓여 한 대만 CPU 100%가 됩니다. Stickiness를 끄고 stateless로 전환하는 것이 해결책입니다.

8) Cross-Zone 끔 #

옛 ALB의 cross-zone 옵션이 꺼져 있으면 AZ 단위 균등 분배. 한 AZ의 인스턴스가 적은데 트래픽은 같은 양 → 부하 쏠림. Cross-zone load balancing 켜기 (ALB는 기본 켜짐, NLB는 비용 때문에 기본 꺼짐).

9) Idle timeout의 함정 #

ALB의 idle timeout (기본 60s)이 백엔드 / 클라이언트의 keep-alive보다 짧으면 502. 보통 백엔드 keep-alive > ALB idle.

정리 #

이번 글에서 잡은 것:

  • ELB = ALB / NLB / GWLB. 99% 일반 웹은 ALB
  • ALB: L7, path / host / header 라우팅, WAF 연동, Lambda target, Cognito 인증
  • NLB: L4, 고정 IP, 수백만 RPS, PrivateLink
  • ALB 흐름 = Listener (받는 포트) → Listener Rule (path/host) → Target Group (보낼 대상) → Health Check
  • Target Group의 target-type = instance / ip / lambda
  • Listener Rule의 action = forward / redirect / fixed-response / authenticate
  • HTTP → HTTPS redirect가 운영 표준 (80 Listener의 default action)
  • Health check는 가벼운 /health, deep은 별도 /health/deep
  • ACM = 무료 SSL 인증서 + 자동 갱신. DNS 검증 권장
  • 리전 매핑. ALB는 같은 리전, CloudFront는 항상 us-east-1
  • HTTPS 다는 6단계. 인증서 요청 → CNAME 검증 → ISSUED → Listener attach → 80 redirect → Route 53 Alias
  • Security Policy로 허용 TLS 버전 제한
  • 함정. 인증서 검증 실패, CloudFront 리전, 80 누락, health check 무거움, 포트 헷갈림, 502, sticky 쏠림, cross-zone, idle timeout

다음: CloudFront #

ALB까지는 잡았습니다. 마지막으로, 사용자에 가까이 캐시를 두는 단계, CloudFront입니다.

#7 CloudFront로 정적 사이트 배포에서는 Origin / Behavior / Cache Policy의 흐름, S3 + CloudFront 정적 호스팅 패턴, OAC로 S3를 안전하게 가리는 방법, 그리고 무효화 (invalidation) 까지 정리하겠습니다.

X