들어가며
EKS 위에 마이크로서비스를 올릴 때 가장 먼저 고민된 부분이 있었다.
"외부 트래픽을 어떻게 받고, 서비스끼리는 어떻게 통신시킬 것인가?"
단순히 "Ingress 하나 만들면 되는 거 아냐?" 라고 생각했다가, 선택지가 생각보다 많다는 걸 알게 됐다. 그리고 조합도 너무 많았다......
이 글은 각 게이트웨이를 비교하고, 최종적으로 ALB + Istio 를 선택한 이유와, 그로 인해 만들어진 트래픽 흐름을 정리한다.
1. 후보 기술 정리: 각 레이어에 뭐가 있는가
조합을 따져보기 전에 각 레이어에서 후보로 둘 만한 기술들의 특성을 먼저 정리한다. 트래픽이 인터넷에서 서비스 Pod까지 도달하려면 보통 세 개의 레이어를 거친다.
인터넷 → [① 로드밸런서] → [② 클러스터 내부 라우팅] → [③ 서비스 간 통신] → Pod① 외부 진입: 로드밸런서 (ALB vs NLB)
| 기술 | 레이어 | 장점 | 단점 |
|---|---|---|---|
| ALB | L7 | WAF·ACM 네이티브 연동, 경로/호스트 기반 라우팅, HTTPS 종단 가능 | 고정 IP 불가, 초저지연/대규모 동시 연결에 약함, TCP/UDP 처리 X |
| NLB | L4 | 초저지연, 고정 IP, 대규모 동시 연결, TCP/UDP 패스스루 | WAF 직접 연결 불가, HTTPS 종단·경로 라우팅을 LB 단에서 못 끝냄 |
② 클러스터 내부 라우팅 (Ingress / Gateway)
| 기술 | 위치 | 장점 | 단점 |
|---|---|---|---|
| Nginx Ingress | K8s Ingress | 자료 풍부, 구성 단순, 학습 곡선 낮음 | 2026-03 프로젝트 retire (EOL) — 보안 패치 중단. 그 외에도 어노테이션 의존, East-West 미지원 |
| Gateway API | K8s 표준 | K8s 공식 차세대 스펙, 인프라/앱 역할 분리(Gateway/HTTPRoute) | 메시 기능 없음(East-West 맹점), 구현체 성숙도 편차, 리소스 종류 증가 |
| Istio Gateway | Istio 전용 | Istio 메시와 통합 운영, VirtualService로 정교한 L7 제어 | Istio 종속, 사이드카 운영 부담, 표준 스펙은 아님 |
참고 (2026-05 시점):
ingress-nginx는 메인테이너 부족으로 2026년 3월 retire 됐고, 후속으로 발표됐던InGate도 함께 retire 됐다. K8s 커뮤니티는 Gateway API 로의 마이그레이션을 공식 권고한다. Ingress API 스펙 자체가 deprecated는 아니지만 feature-frozen 상태이므로, 신규 설계에서 Nginx Ingress는 사실상 후보에서 빠진다.
③ 서비스 간 통신: 서비스 메시
| 기술 | 장점 | 단점 |
|---|---|---|
| 메시 없음 | 운영 부담 0, 리소스 비용 최소 | mTLS·서킷브레이커·재시도·분산 추적을 모두 앱이 직접 구현 |
| Istio | 자동 mTLS, 트래픽 정책, 관측성(Kiali·Jaeger), 풍부한 생태계 | 사이드카 리소스·메모리 비용, 학습 곡선, 디버깅 난도 |
| Linkerd | 가벼운 사이드카, 단순성, 빠른 시작 | 기능 폭이 Istio보다 좁음 (Wasm 확장·고급 라우팅 일부 부재) |
참고: Gateway API / Istio Gateway는 외부에 노출되는 네트워크 엔드포인트가 아니다. 인터넷에서 클러스터로 트래픽이 들어오려면 ①의 로드밸런서가 반드시 앞에 있어야 한다.
이제 이 후보들을 조합하면 의미 있는 시나리오가 어떻게 나오는지 본다.
2. 선택지 정리: 뭘 조합할 수 있는가
조합 매트릭스
| # | 로드밸런서 | 내부 라우팅 | 서비스 메시 | 한 줄 요약 |
|---|---|---|---|---|
| 1 | ALB | Nginx Ingress | ✗ | 가장 단순한 구조. 서비스 간 제어 없음 |
| 2 | ALB | Gateway API | ✗ | K8s 표준 + WAF 가능. 메시 없어서 East-West 맹점 |
| 3 | ALB | Istio Gateway | Istio | L7이 ALB·Istio 양쪽에 중복. 현실적 절충안 |
| 4 | ALB | Gateway API | Istio | Gateway API + Istio 동시 운영 → 오버엔지니어링 |
| 5 | NLB | Istio Gateway | Istio | Istio 정석 구조. WAF 직접 연결 불가 |
| 6 | NLB | Gateway API | ✗ | 표준 기반, 단순 MSA. 메시 없음 |
3. 내 서비스에 무엇이 필요한가 (요구사항 분석)
| 요구사항 | 이유 |
|---|---|
| HTTPS + 인증서 자동 갱신 | 사용자 향 서비스 (ACM 활용) |
| WAF 연동 | 배달앱 = 결제 포함 → 앱 레벨 공격 방어 필요 |
| 서비스 간 암호화 (mTLS) | order → kitchen → delivery 내부 통신 보안 |
| 외부 노출 최소화 | kitchen / delivery는 외부에서 호출 불가해야 함 |
| 트래픽 제어 (서킷브레이커 등) | 주문 폭주 시 downstream 장애 전파 차단 |
판단: 외부 진입 레이어와 내부 서비스 메시 레이어 둘 다 필요하다.
4. 1단계 의사결정: 로드밸런서는 ALB냐 NLB냐
외부에서 클러스터로 트래픽이 들어오려면 로드밸런서가 반드시 필요하다. 그럼 ALB와 NLB 중 무엇을 쓸 것인가?
NLB의 강점이 필요한 상황인가?
NLB는 L4라서 TCP/UDP 레벨 처리에 강하고, 초저지연이 필요한 대규모 연결 유지에 적합하다. 게임 서버나 실시간 통신처럼 동시 연결 수가 폭발적으로 많은 서비스라면 NLB가 더 적절하다고 생각된다.
하지만 Shoong Delivery는 그 정도 규모가 아니다. REST API 기반이고, 동시 연결 수도 NLB의 강점이 빛날 만한 수준이 아니다.
ALB가 제공하는 것 중 내가 진짜 필요한 것
배달앱은 주문, 결제, 배달 주소 같은 개인정보와 결제 정보를 REST API로 다룬다.(아직 Shoong-Delivery에는 결제 로직이 없긴하지만 추후 개발 가능성 염두) 이런 데이터를 다루는 API는 SQL Injection, XSS, 비정상 요청으로 결제 로직 우회 같은 애플리케이션 레벨 공격에 노출된다. 이 방어는 L4에서는 불가능하고 L7에서만 가능하다.
| 항목 | NLB | ALB | 내 요구사항과 매칭 |
|---|---|---|---|
| WAF 연동 | ✗ | ✓ | 결제 데이터 → 앱 레벨 공격 방어 필수 |
| SSL 종단 (ACM) | ✗ | ✓ | 인증서 자동 갱신 운영 부담 ↓ |
| 경로 기반 라우팅 | ✗ | ✓ | /api/orders, /api/notification 분기 필요 |
| 고정 IP / 초저지연 | ✓ | ✗ | 배달앱 규모에선 불필요 |
결론: NLB가 필요할 만큼의 규모가 아니고, REST API 보안이 우선이므로 ALB 선택.
5. 2단계 의사결정: Gateway는 뭘 쓸 것인가
ALB는 정해졌다. 그럼 클러스터 내부 라우팅은? 여러가지 기술들이 있었고, 그 중 Nginx Ingress, Gateway API, Istio Gateway 3개로 추렸다.
사고 흐름: 요구사항이 선택을 강제한다
먼저 서비스 구성을 보자.
서비스 4~6개 (order, kitchen, delivery, notification, batch …)
서비스 간 직접 통신 존재 (order → kitchen → delivery)
개인정보/결제 데이터 취급(예정)서비스 간 통신이 있다는 게 핵심이다. 이 구조에서 발생하는 문제는 외부 라우팅으로는 해결되지 않는다.
서비스 간 통신 구간에서 생기는 문제
1. 결제 서비스 장애 → 주문 서비스로 전파(예정)
2. 통신 구간 평문 → 암호화 없음
3. 어느 구간에서 병목인지 추적 불가Nginx Ingress 탈락
외부 라우팅은 가능하지만 거기까지다. 서비스 간 통신은 무방비로 남는다. 고급 트래픽 제어도 어노테이션에 의존해서 구현체 종속이 강하다.
덧붙여
ingress-nginx프로젝트는 2026년 3월에 retire 되어 더 이상 보안 패치가 나오지 않는다. 결과적으로 기능 부족 이전에 유지보수 자체가 끝난 선택지가 됐다.
Gateway API 탈락
K8s 표준이라는 장점이 있고 외부 라우팅은 깔끔하게 해결한다. 하지만 서비스 메시 기능이 없다. 서비스 간 mTLS, 서킷브레이커, 분산 추적은 범위 밖이다. Gateway API만 쓰면 East-West 구간이 그대로 맹점으로 남는다.
Istio 선택
Istio는 외부 진입 라우팅(Gateway + VirtualService)과 서비스 간 통신 제어(사이드카 자동 mTLS, 서킷브레이커, 분산 추적의 hop span·trace context 전파)를 동시에 해결한다. 위에서 도출한 세 가지 문제를 전부 커버한다.
분산 추적은 사이드카만으로 완결되지 않는다. 앱 내부의 함수·DB·HTTP 호출 단위 span은 OTel SDK가 만들고, Istio 사이드카는 hop 단위 span과 trace context 전파를 담당하는 2-layer 구조다. 본 프로젝트는 4개 API 서비스에
@opentelemetry/sdk-node를 적용해 OTLP gRPC로 클러스터 내 OTel Collector에 송신한다. (각 서비스src/instrumentation.ts참조(https://github.com/shoong-delivery))
6. 3단계 의사결정: Gateway API + Istio 조합은?
여기서 한 번 더 고민이 생겼다. 요즘 권장되는 조합 중 하나가 Gateway API + Istio라는 것을 봤다. K8s 표준 스펙으로 외부 라우팅을 표현하고, 내부는 Istio가 처리하는 방식이다. 그럼 이걸 안 쓴 이유는?
L7 중복 문제
ALB → L7 (HTTP 라우팅, WAF, TLS 종단)
Gateway API → L7 (HTTP 라우팅)
Istio → L7 (HTTP 라우팅)L7이 세 번 중복된다. ALB 앞단에서 이미 경로 기반 라우팅이 가능한데, 그 뒤에 Gateway API로 한 번 더 L7 라우팅을 정의하면 사실상 같은 일을 두 번 한다.
Gateway API + Istio
Gateway API + Istio 조합은 역할 분리다.
인프라팀 → Gateway 리소스 (진입점 관리)
앱팀 → HTTPRoute 리소스 (라우팅 규칙)팀이 여러 개일 때 인프라팀과 앱팀이 각자 관심사를 분리해서 관리할 수 있다. 그리고 K8s 표준 스펙이라 나중에 Istio 대신 다른 구현체로 교체해도 스펙이 그대로 유지된다.
Shoong-Delivery 환경에선 의미가 없다
혼자 개발하는 포트폴리오다보니 인프라팀/앱팀 역할 분리가 의미 없고, 오히려 관리할 리소스 종류만 늘어난다. Gateway API + Istio는 단일 개발자 환경에서 진가를 발휘하지 못할 것 같다.
결론: Istio 리소스(Gateway, VirtualService, DestinationRule)로 외부/내부 트래픽을 통일해서 관리하는 게 더 단순하다. → Istio 단독 선택
7. 그래도 서비스 메시는 과스펙 아닌가?
Shoong Delivery 규모에 너무 과스펙이 아닌가하는 의문이 들었다. 서비스 4~6개 규모, 아직 결제 로직도 없는 포트폴리오에 Istio를 얹는 게 합당한가?
반론 1: 메시 없이 같은 보장을 얻으려면 더 비싸다
규모가 작아도 결제·개인정보 데이터가 흐르면 East-West 평문 통신은 리스크다. 메시 없이 같은 보장을 얻으려면 다음을 직접 구현해야 한다.
- 앱마다 TLS 인증서 발급·회전·신뢰 체인 관리 코드
- 서킷브레이커·재시도·타임아웃 로직을 SDK 단에서 구현 (서비스마다 언어/SDK 일관성 관리 필요)
서비스 4~6개에서 위를 모두 구현하면 결국 "직접 만든 메시"가 된다. 검증된 컴포넌트(Istio)로 위임하는 편이 오히려 단순할 것이라 판단했다.
반론 2: MSA의 진짜 비용은 "디버깅"이다
분리된 서비스 중 어디서 문제가 났는지 추적하기 어려운 게 MSA의 가장 큰 운영 비용이다. Kiali·Jaeger의 관측성은 규모와 무관하게 즉시 가치를 낸다. 오히려 작은 규모일수록 한 번 세팅으로 끝나서 ROI가 좋다.
반론 3: 포트폴리오의 목적은 "현실 운영 스택의 시연"이다
이 프로젝트는 "최소 비용으로 동작하는 앱"이 목표가 아니라 "실제 운영 환경에서 마주칠 DevOps 스택을 다뤄봤다"는 걸 보여주는 게 목표다. 메시가 필요 없는 규모라는 이유로 빼면, 그 학습 경험 자체가 없는 포트폴리오가 된다.
트레이드오프는 인정한다
- 리소스 비용: 사이드카가 Pod마다 수십 MB 메모리를 추가 소비한다. dev 환경에선 감내 가능. 진짜 비용 민감한 환경이라면 Istio ambient mode(사이드카리스)나 Linkerd 같은 경량 옵션이 더 나을지도 모른다.
- 학습 곡선: 한 번만 지불하면 되는 비용이고, 본 프로젝트의 학습 목표 자체와 일치한다.
- 디버깅 난도: Envoy 레이어가 추가되니 "어디서 막혔는지" 찾는 깊이가 한 단계 늘어난다. Kiali로 어느정도 상쇄되지 않을까라고 생각했다.
만약 진짜로 메시가 부담스럽다면
단순화 경로는 있다.
- Linkerd로 교체 — Istio보다 가볍고 단순. 단, 고급 트래픽 제어·생태계 폭은 좁다.
- 메시 없이 가는 길 — mTLS는 클러스터 신뢰 도메인 가정으로 포기. 앱 단 OTel SDK는 어차피 적용 중이라 유지되지만, 사이드카 hop span과 Kiali 서비스 그래프 같은 메시 레이어 관측성은 잃는다.
Shoong-Delivery는 1,2 대신 Istio를 택했다. 운영에서 가장 자주 보게 될 메시이기도 하고, 학습 및 시연 가치가 높다고 판단했기 때문이다.
8. 그래서 탈락한 조합들 (정리)
NLB + Istio (정석이지만 탈락)
- WAF를 직접 붙일 수 없음 → IP/포트 수준 방어만 가능 (SQL Injection, XSS 방어 안 됨)
- Istio Gateway에서 TLS termination + 인증서 관리까지 담당해야 함 → 운영 복잡도 증가
- 동시 연결 수가 많지 않은 서비스 규모에서 L4 pass-through의 장점이 없음
ALB + Gateway API (단순하지만 부족)
- 서비스 간 mTLS, 서킷브레이커를 지원하지 않음
- Istio를 추가로 선택하게 되면 어차피 아래 선택지로 이동
ALB + Gateway API + Istio (탈락)
- L7 라우팅이 ALB · Gateway API · Istio 세 곳에서 중복
- 역할 분리의 가치는 팀 구조가 있을 때 빛남, 단일 개발자 환경에선 관리 포인트만 늘어남
- 오버엔지니어링
9. 최종 선택: ALB + Istio
인터넷 → CloudFront (WAF · edge ACM) → ALB (TLS 종단 · ACM) → istio-ingressgateway → Istio VirtualService → 서비스ALB가 담당하는 것
- HTTPS 종단 (ACM 인증서 자동 갱신, ELBSecurityPolicy-TLS13)
- TargetGroupBinding으로 Pod IP 직접 등록 → 노드 hop 1단계 제거
WAF는 ALB 직접 attach가 아닌 앞단 CloudFront에 부착되어 있다.

앱 레벨 방어(SQLi·XSS·Bad Inputs 등)는 Edge 계층에서 1차로 차단되고, ALB는 TLS 종단과 클러스터로의 L7 라우팅에 집중한다.
Istio가 담당하는 것
- South-North: Gateway + VirtualService로 경로 기반 라우팅
- East-West: 사이드카 자동 mTLS — 메시 내부 통신은 모두 암호화 (앱 코드 0줄)
- DestinationRule로 서킷브레이커(connectionPool + outlierDetection) 적용
- kitchen / delivery에 VirtualService 없음 → 외부 호출 경로 자체가 없음
현재 PeerAuthentication은 mesh 기본값 PERMISSIVE 상태로 운영 중.
메시 내부 사이드카끼리는 auto-mTLS로 암호화되지만 평문 진입은 아직 차단되지 않는다.
STRICT으로의 점진 전환은 추후에 다룰 예정이다.
10. 실제 트래픽 흐름
아래 흐름이 실제로 동작 중임을 CLI와 Kiali 캡처로 함께 증명한다. 다이어그램 → 검증 캡처 순서로 본다.
South-North (외부 → 클러스터)
사용자
│ HTTPS
▼
ALB ─── TargetGroupBinding ip (NodePort 없음, Pod IP 직접)
│ HTTP (TLS 종단 완료)
▼
istio-ingressgateway ─── envoy sidecar
│
├─ VS /api/orders/* → order
├─ VS /api/notifications/* → notification
└─ VS *.internal.dev → argocd · grafana · kiali
(VS 없음: kitchen · delivery → 외부 호출 불가)검증 ① — ALB → Pod IP 직결 (NodePort 우회)
TargetGroupBinding의 targetType: ip 설정으로 ALB가 istio-ingressgateway Pod에 직접 트래픽을 보낸다. 노드 hop 1단계가 제거된다. 세 가지 출력을 한 화면에 모으면 다음이 증명된다.
- Target Group 자체가
ip모드 (TargetType: ip) - ALB에 등록된 IP가
10.0.12.30 kubectl로 확인한istio-ingressgatewayPod IP도10.0.12.30
→ 같은 IP가 AWS와 K8s 양쪽에 존재 = ALB가 노드를 거치지 않고 Pod로 직결.

검증 ② — VirtualService 라우팅 (kitchen/delivery 외부 노출 X)
shoong 네임스페이스의 VS 목록을 보면 dev-shoong-order, dev-shoong-notification 만 존재한다. kitchen·delivery에는 VS가 없으므로 게이트웨이가 라우팅할 경로 자체가 없고, 결과적으로 외부에서 호출 불가능하다.

East-West (서비스 간 자동 mTLS)
batch (CronJob)
│ 자동완료 (PATCH status)
▼
order ──[🔒 mTLS]──▶ kitchen ──[🔒 mTLS]──▶ delivery
│ │
PATCH status PATCH status
└──────[🔒 mTLS]──▶ notification포인트: 앱 코드는 평문 HTTP로 호출한다. sidecar ↔ sidecar 구간에서 Istio가 자동으로 mTLS를 처리하며, istiod가 인증서 발급·회전까지 담당한다.
검증 ③ — 전체 메시 트래픽 흐름 + mTLS 자물쇠
Kiali Graph(shoong 네임스페이스, Last 30m)로 실제 트래픽이 흐르는 모습. 빨간 표시 의미:
- 좌측 빨간 박스: 외부 진입점
istio-ingressgateway - 빨간 굵은 화살표:
order → kitchen → delivery → notification비즈니스 체인 (East-West) - 빨간 동그라미: 메시 내부 통신의 mTLS 🔒 자물쇠 (다수)
- 상단 빨간 박스 + "DB(RDS) - 메시 밖": PassthroughCluster — RDS로 빠지는 egress
→ 메시 내부 통신은 모두 mTLS로 암호화되고 있고 (PERMISSIVE auto-mTLS), DB는 메시 밖이라 PassthroughCluster로 잡힌다.

검증 ④ — 워크로드 디테일 (dev-shoong-order)
특정 워크로드를 들여다보면 사이드카 주입과 라이브 메트릭이 한 화면에 보인다.
- 좌측 Pods 섹션: 2개 Pod (replica=2 적용, 둘 다 healthy)
- 우측 미니 그래프: 라이브 트래픽 (rps + 평균 latency + 🔒)
- 그래프의 자물쇠: order ↔ batch ↔ kitchen 사이의 mTLS

Kiali의 Pod 정보 팝업에서 Istio Container: Not found로 보이는 건 아직 원인을 찾지 못했다.
트래픽 제어 — 서킷브레이커 (DestinationRule)
mTLS와 함께 East-West 구간의 또 다른 핵심 보호 장치. shoong 네임스페이스의 4개 서비스에 DestinationRule이 적용되어 있다.
connectionPool: 동시 연결·대기 큐 cap → 폭주 시 즉시 fail fast (장애 전파 차단)outlierDetection: 5xx 연속 5회 시 30초 격리, 최대 50% (replica=2 환경에서 한 쪽 Pod 격리 가능)- 앱 코드 변경 0줄 — DestinationRule manifest만으로 활성화

현재 운영 상태 — PERMISSIVE (점진 적용 중)
PeerAuthentication 미정의로 mesh 기본값 PERMISSIVE 운영 중. 메시 내부는 auto-mTLS로 암호화되지만 평문 진입도 허용된다. 운영 환경 STRICT 적용은 Istio 공식 마이그레이션 경로(PERMISSIVE → 호출자 식별 → namespace-level STRICT → AuthorizationPolicy)를 따라 단계적으로 전환 예정.
마치며
ALB + Istio 조합은 "현실적인 타협안"이다.
Istio 정석대로라면 NLB + Istio가 더 깔끔하지만, Edge 계층의 WAF(CloudFront)와 ALB의 ACM TLS 종단 등 AWS 매니지드 운영 편의성을 포기할 수 없었다
L7 라우팅 중복이라는 단점이 있지만, ALB는 외부 진입 보안에만 집중하고 Istio는 내부 서비스 메시에만 집중하도록 역할을 분리하니 구조가 명확해졌다.
선택 과정을 돌아보면 결국 두 가지가 핵심이었다.
- 로드밸런서 선택은 "내 서비스가 NLB의 강점이 필요할 만한 규모냐"의 답으로 갈렸다 → 아니오 → ALB
- Gateway 선택은 "서비스 간 통신 제어가 필요한가"의 답으로 갈렸다 → 예 → Istio
요구사항이 기술 선택을 강제하도록 흐름을 따라가니, 각 선택지에 명확한 근거가 붙었다.
참고 자료
'Project: Shoong-Delivery' 카테고리의 다른 글
| [자동화] terrafom apply 후 수동 작업 대신 자동화 스크립트 (0) | 2026.05.20 |
|---|---|
| [Terraform] shoong-delivery 테라폼 구조 및 회고 (0) | 2026.05.20 |
| [아키텍처] K8S 클러스터 아키텍처 구축기 (0) | 2026.05.18 |
| [아키텍처] CI/CD 파이프라인 아키텍처 구축기 (0) | 2026.05.18 |
| [아키텍처] AWS 인프라 아키텍처 구축기 (0) | 2026.05.18 |