Skip to content

필사 모드: Envoy Proxy Deep Dive — xDS, Filter Chain, Service Mesh, Hot Restart 내부 완전 정복 (2025)

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

TL;DR

- **Envoy**는 Lyft가 2016년 오픈소스로 공개한 C++ L7 프록시. 현재 CNCF graduated 프로젝트, 서비스 메시의 사실상 표준 데이터 플레인.

- **3대 추상화**: **Listener** (수신), **Filter Chain** (처리), **Cluster** (업스트림 풀). 이 셋의 조합으로 거의 모든 프록시 시나리오 표현 가능.

- **xDS**: Envoy의 동적 설정 프로토콜. LDS(Listener), RDS(Route), CDS(Cluster), EDS(Endpoint), SDS(Secret). 모두 gRPC streaming으로 control plane에서 받아온다.

- **per-worker thread-local**: 각 워커 스레드가 설정 복사본을 가진다. 락 없음, CPU 선형 확장. 대신 메모리 중복.

- **Hot Restart**: 무중단 설정 업데이트. 새 프로세스가 옛 프로세스의 리스닝 소켓을 SCM_RIGHTS로 넘겨받고 점진적으로 교체.

- **HTTP Filter Chain**: 요청/응답에 순차 적용되는 필터들. 인증, 라우팅, rate limit, 관측성, 변환 등을 조합.

- **Cluster Manager**: Upstream 엔드포인트 풀 관리. Circuit breaker, outlier detection, health check, connection pool.

- **관측성 3종 세트**: Stats (Prometheus), Tracing (OpenTelemetry), Access logs.

- **서비스 메시**: Envoy + 컨트롤 플레인(Istio, Consul Connect, Gloo, Kuma). 각 Pod에 사이드카로 배포 → 트래픽 가시성, 보안, 정책.

- **WASM 필터**: 커스텀 로직을 Rust/Go/AssemblyScript로 짜서 런타임에 삽입. Envoy 재컴파일 없음.

1. Envoy는 왜 생겼는가

1.1 Lyft의 아픈 경험

2015년 Lyft는 **수십 개의 마이크로서비스**를 운영 중이었다. 각 서비스는 다양한 언어로 쓰였고(Python, Go, Java 등), 각자 HTTP 클라이언트 라이브러리를 사용했다.

문제:

- **일관되지 않은 재시도 정책**. Python은 X초 타임아웃, Go는 Y초. 엉망.

- **관측성 부재**. 각 서비스가 자기만의 로그 포맷.

- **TLS 구현 불일치**. 누구는 mTLS, 누구는 일반 TLS.

- **circuit breaker 구현 X**. 한 서비스 장애가 전체 연쇄 실패.

- **배포 마다 HTTP 스택 버그 재등장**.

전통적 접근: **모든 언어용 공통 라이브러리**를 만든다. 하지만 언어마다 재구현해야 하고, 버전 불일치 문제, 배포 동기화 어려움.

1.2 "Out-of-Process" 아키텍처

Matt Klein(Lyft)의 제안: "네트워크 스택을 **별도 프로세스**로 분리하자."

Application Code (Python/Go/Java/...)

localhost:9080

Envoy Sidecar

mTLS, 재시도, 로드밸런싱, 관측성, circuit breaker

Remote Service

장점:

- **언어 무관**: 모든 서비스가 같은 프록시를 공유.

- **배포 독립**: Envoy 업데이트가 애플리케이션 배포와 분리.

- **통일된 관측성**: 모든 트래픽이 Envoy를 거치므로 일관된 메트릭.

- **동적 설정**: 라우팅/정책 변경이 런타임에 가능.

이것이 **사이드카 패턴**의 원조. Istio, Linkerd, Consul Connect 모두 같은 아이디어.

1.3 오픈소스 공개 (2016)

Lyft는 Envoy를 2016년 9월 공개했다. 반응은 폭발적이었다:

- **Google/IBM**: Istio를 Envoy 기반으로 시작 (2017).

- **HashiCorp**: Consul Connect가 Envoy 채택.

- **AWS**: App Mesh의 데이터 플레인으로 Envoy.

- **CNCF**: 2018년 incubating, 2020년 graduated.

Matt Klein은 현재도 Lyft의 프린시펄 엔지니어이자 Envoy의 메인테이너.

1.4 2025년 현재

Envoy는:

- 전 세계 서비스 메시 배포의 **80%+** 데이터 플레인.

- Google 내부에서 **Cloud Gateway**로 사용 (수백만 QPS).

- Airbnb, Netflix, Pinterest 등 대형 테크 기업 전면 사용.

- 200+ 커밋/월 활발한 개발.

2. 아키텍처 개요

2.1 3대 개념

Envoy의 데이터 흐름을 이해하는 3개의 핵심 추상화:

Listener → Filter Chain → Cluster

(수신) (처리) (업스트림 풀)

example:

0.0.0.0:8080 → http_connection_manager → cluster "backend-v1"

├─ Router

├─ JWT Auth

├─ Rate Limit

└─ Fault Injection

2.2 Listener

Envoy가 **연결을 수신**하는 지점. TCP 소켓과 동등:

listeners:

- name: main

address:

socket_address:

address: 0.0.0.0

port_value: 8080

filter_chains:

- filters:

- name: envoy.filters.network.http_connection_manager

typed_config:

...

한 Envoy가 여러 listener를 가질 수 있다. 각각 다른 포트, 다른 프로토콜.

2.3 Filter Chain

Listener 위에서 일어나는 **처리 로직의 순차 파이프라인**.

두 종류:

**Network Filter**: 바이트 스트림 처리. TCP proxy, HTTP CM, Thrift proxy, Redis proxy, Postgres proxy 등.

**HTTP Filter** (Network Filter인 HTTP CM 내부): HTTP 요청/응답 처리. Router, JWT, rate limit, 변환 등.

이 레이어링은 Envoy가 HTTP만이 아니라 **범용 L4/L7 프록시**가 될 수 있게 한다.

2.4 Cluster

**Upstream 서버의 논리적 그룹**. 실제로는:

- 엔드포인트 목록 (IP:port).

- 로드밸런싱 정책.

- Connection pool.

- Health check.

- Circuit breaker.

clusters:

- name: backend-v1

type: STRICT_DNS

connect_timeout: 0.25s

lb_policy: ROUND_ROBIN

load_assignment:

cluster_name: backend-v1

endpoints:

- lb_endpoints:

- endpoint:

address:

socket_address:

address: backend.example.com

port_value: 8080

2.5 전체 그림

┌─────────────┐

│ Listener │

└──────┬──────┘

┌──────▼─────────────────┐

│ Network Filter Chain │

│ ┌─────────────────┐ │

│ │ TLS 검증/해독 │ │

│ ├─────────────────┤ │

│ │ HTTP Conn Mgr │──────────────┐

│ └─────────────────┘ │ │

└────────────────────────┘ │

┌──────────────────────┐

│ HTTP Filter Chain │

│ ┌──────────────────┐ │

│ │ JWT Auth │ │

│ ├──────────────────┤ │

│ │ Rate Limit │ │

│ ├──────────────────┤ │

│ │ Router │ │

│ └──────────────────┘ │

└──────┬───────────────┘

┌──────────────────┐

│ Cluster: backend │

│ - LB │

│ - Conn Pool │

│ - Circuit Break │

└──────┬───────────┘

Upstream

3. xDS 프로토콜

Envoy의 가장 큰 혁신 중 하나. "설정 파일 재로드 + SIGHUP"에서 벗어나 **동적 설정**을 가능하게 한 프로토콜.

3.1 xDS란

**xDS** = **X** Discovery **S**ervice. X는 여러 가지:

- **LDS**: Listener Discovery Service. 리스너 목록.

- **RDS**: Route Discovery Service. HTTP 라우팅 테이블.

- **CDS**: Cluster Discovery Service. 클러스터 목록.

- **EDS**: Endpoint Discovery Service. 클러스터 멤버 목록.

- **SDS**: Secret Discovery Service. TLS 인증서/키.

- **ADS**: Aggregated Discovery Service. 위의 모든 것을 하나의 스트림으로.

Envoy는 이들을 **gRPC streaming**으로 control plane에서 받는다.

3.2 작동 원리

┌─────────────┐ ┌──────────────┐

│ Envoy │ │ Control Plane│

│ │ │ (Istio) │

│ │ DiscoveryRequest │ │

│ │ ──────────────────► │ │

│ │ │ │

│ │ DiscoveryResponse │ │

│ │ ◄────────────────── │ │

│ │ │ │

│ (apply config) │ │

│ │ ACK │ │

│ │ ──────────────────► │ │

└─────────────┘ └──────────────┘

Envoy는:

1. Control plane에 **DiscoveryRequest** 전송: "나 envoy-v1.30, 내 노드 ID는 X, 현재 version은 Y".

2. Control plane이 **DiscoveryResponse**로 답함: "여기 LDS 설정". 버전 포함.

3. Envoy가 설정 적용.

4. **ACK** 또는 **NACK** 송신.

gRPC streaming이라 양방향 지속 연결. Control plane이 변경을 push할 수 있다.

3.3 Resource Versioning

Envoy는 현재 적용된 버전을 기억한다. Control plane은 변경이 있을 때만 새 응답 전송:

Envoy: "내 LDS version은 v42"

Control plane: "변경 있음, 새 설정 v43 보냄"

Envoy: "v43 적용 완료, ACK"

만약 새 설정이 잘못되었으면 NACK, 이전 버전 유지.

3.4 ADS (Aggregated Discovery Service)

여러 xDS를 **단일 스트림**으로. 순서 보장이 필요할 때 중요:

- CDS를 먼저 받아야 → EDS가 그 cluster의 endpoint를 가리킬 수 있다.

- LDS → RDS → CDS → EDS 순서가 있다.

ADS는 이 순서를 제어 플레인이 명시할 수 있게 해준다. Istio는 기본 ADS.

3.5 Delta xDS

초기 xDS는 **State of the World**: 변경 시 **전체 리소스 목록** 전송. 1만 개 cluster가 있으면 한 개 변경에 1만 개 전송.

**Delta xDS**: 변경된 것만 보냄 (추가/수정/삭제). 큰 설정을 가진 거대 클러스터에 필수.

3.6 Control Plane 구현

대표적인 control plane들:

- **Istio Pilot** (istiod): K8s 리소스 → xDS.

- **Consul Connect**: Consul 카탈로그 → xDS.

- **Gloo**: 자체 API → xDS.

- **Contour**: Ingress/HTTPProxy → xDS.

- **go-control-plane**: Envoy 공식 라이브러리. 커스텀 구현에 사용.

커스텀 control plane을 만들고 싶다면 `go-control-plane`이 출발점.

4. Per-Worker Thread-Local 아키텍처

Envoy의 스레딩 모델은 설계 결정의 핵심이다.

4.1 문제: 프록시의 락 경합

네트워크 프록시는 수만 연결을 처리한다. 전통적 방식:

많은 워커 스레드 → 공유 상태 (설정, 통계, 라우팅 테이블)

락 경합, 캐시 라인 핑퐁

확장성이 코어 수에 linear하지 않음.

4.2 Envoy의 해결

각 워커 스레드가 **자기 복사본**을 가진다.

Main Thread Worker 0 Worker 1 Worker 2

(설정 관리) ┌─────────┐ ┌─────────┐ ┌─────────┐

│ Listener│ │ Listener│ │ Listener│

│ Copy │ │ Copy │ │ Copy │

├─────────┤ ├─────────┤ ├─────────┤

│ Cluster │ │ Cluster │ │ Cluster │

│ Copy │ │ Copy │ │ Copy │

├─────────┤ ├─────────┤ ├─────────┤

│ Stats │ │ Stats │ │ Stats │

│ Copy │ │ Copy │ │ Copy │

└─────────┘ └─────────┘ └─────────┘

- **Connection**은 accept한 worker에 **고정**. 다른 worker로 이동 안 함.

- **Config 업데이트**: Main thread가 각 worker에게 "update your copy" 메시지 전달.

- **Stats 합산**: 읽기 시점에 각 worker의 값 합산 (atomic하지 않아도 됨).

4.3 장점

1. **락 없음**: 워커 스레드가 자기 copy만 건드린다.

2. **선형 확장**: 코어 추가 = 성능 증가.

3. **캐시 친화적**: 같은 데이터가 한 코어에서만 읽힘.

4.4 단점

1. **메모리 오버헤드**: 설정이 워커 수만큼 복사. 1만 cluster × 32 워커 = 32만 copy.

2. **업데이트 지연**: 모든 워커가 copy를 업데이트하려면 시간 필요.

3. **통계 합산 비용**: 읽기 시 순회.

4.5 TLS (Thread-Local Storage)

C++에서 `thread_local` 또는 Envoy의 `ThreadLocal::Slot` 추상화 사용. 각 워커가 자기 슬롯에 객체 저장, main thread가 "update" 메시지 보내면 해당 객체 교체.

// Main thread

tls_slot.runOnAllThreads([new_config](Object& obj) {

obj.update(new_config);

});

락 없음. 각 worker가 "내 차례"에 업데이트.

5. Hot Restart

설정 파일 변경 시 무중단 재시작.

5.1 왜 필요한가

Envoy 설정이 변경되면 재시작이 필요할 수 있다(예: 새 컴파일된 이진 배포). 전통적 방식:

1. 옛 프로세스 kill

2. 새 프로세스 start

3. 다운타임 = (start time)

수십 ms라도 고성능 서비스에서는 치명적.

5.2 Envoy의 Hot Restart

**두 프로세스 공존 + 소켓 공유**:

Time 0: Envoy v1 단독 실행, 모든 트래픽 처리

Time 1: Envoy v2 시작 (--restart-epoch 1)

→ v2가 v1에게 SCM_RIGHTS로 listener socket 요청

→ v1이 fd 전달

Time 2: v2 트래픽 처리 시작 (v1과 병행)

Time 3: v1이 drain timer 시작 (예: 15분)

→ v1의 기존 연결만 처리, 새 연결은 v2로

Time 4: v1의 기존 연결 종료 시 graceful shutdown

Time 5: v1 종료

5.3 SCM_RIGHTS

UNIX 도메인 소켓의 특수 기능. 프로세스 간 **파일 디스크립터 전달** 가능:

// 송신 측 (Envoy v1)

struct msghdr msg = {0};

struct cmsghdr *cmsg;

int fds[1] = { listener_fd };

char buf[CMSG_SPACE(sizeof(fds))];

msg.msg_control = buf;

msg.msg_controllen = sizeof(buf);

cmsg = CMSG_FIRSTHDR(&msg);

cmsg->cmsg_level = SOL_SOCKET;

cmsg->cmsg_type = SCM_RIGHTS;

cmsg->cmsg_len = CMSG_LEN(sizeof(fds));

memcpy(CMSG_DATA(cmsg), fds, sizeof(fds));

sendmsg(unix_socket, &msg, 0);

// 수신 측 (Envoy v2)

// recvmsg로 받은 fds가 v2 프로세스에서도 유효한 fd

v2가 같은 listening socket에 accept하면 **새 연결이 자동으로 v2로 간다** (OS가 여러 프로세스 중 하나를 선택).

5.4 Shared Memory

두 프로세스가 통계를 공유하려면? **shared memory**:

/dev/shm/envoy_shared_memory_0 (v1)

/dev/shm/envoy_shared_memory_1 (v2)

Envoy는 hot restart 시 stats 같은 일부 상태를 공유 메모리에 저장. v2가 v1의 stats를 이어받는다.

5.5 Drain Manager

v1이 종료될 때:

- **Drain sequence**: 새 연결은 v2로, 기존 연결은 graceful close.

- **Connection counting**: 열린 연결이 0 될 때까지 대기.

- **Max drain time**: 무한 대기 방지 (기본 1시간).

Hot restart는 Envoy의 **시그니처 기능** 중 하나. 많은 서비스 메시의 "무중단 설정 업데이트"가 실제로는 여기에 의존.

6. HTTP Connection Manager & Filter Chain

6.1 HCM 역할

**HTTP Connection Manager** (HCM)은 Network Filter 중 하나지만 가장 중요하다:

1. **HTTP 디코딩**: 원시 바이트 → HTTP 메시지.

2. **HTTP/1.1, HTTP/2, HTTP/3 지원**.

3. **HTTP Filter Chain 실행**.

4. **Router를 통해 cluster로 라우팅**.

5. **응답 인코딩**.

6.2 HTTP Filter 종류

Envoy에는 40+ 개의 HTTP filter가 내장:

**인증/인가**:

- `envoy.filters.http.jwt_authn`: JWT 검증.

- `envoy.filters.http.oauth2`: OAuth 2.0 흐름.

- `envoy.filters.http.ext_authz`: 외부 인가 서비스 호출.

- `envoy.filters.http.rbac`: RBAC 정책.

**트래픽 제어**:

- `envoy.filters.http.router`: 최종 라우팅 (반드시 마지막에 와야).

- `envoy.filters.http.ratelimit`: Rate limit (외부 서비스).

- `envoy.filters.http.local_ratelimit`: 로컬 rate limit.

- `envoy.filters.http.fault`: Fault injection (지연, HTTP 에러).

**변환**:

- `envoy.filters.http.header_to_metadata`: 헤더 → 동적 메타데이터.

- `envoy.filters.http.lua`: Lua 스크립트로 커스텀.

- `envoy.filters.http.wasm`: WASM 모듈.

**관측성**:

- `envoy.filters.http.tap`: 요청/응답 캡처.

- `envoy.filters.http.tracer`: 분산 추적.

**프로토콜 변환**:

- `envoy.filters.http.grpc_json_transcoder`: gRPC ↔ REST.

- `envoy.filters.http.grpc_web`: gRPC-Web.

6.3 필터 실행 순서

http_filters:

- name: envoy.filters.http.jwt_authn # 1. 먼저 토큰 검증

- name: envoy.filters.http.rbac # 2. 권한 체크

- name: envoy.filters.http.ratelimit # 3. 레이트 리밋

- name: envoy.filters.http.fault # 4. 장애 주입 (테스트 시)

- name: envoy.filters.http.router # 5. 최종 라우팅 (반드시 마지막)

각 필터는:

- **Decode path** (요청 처리): `decodeHeaders` → `decodeData` → `decodeTrailers`.

- **Encode path** (응답 처리): `encodeHeaders` → `encodeData` → `encodeTrailers`.

필터가 `StopIteration`을 반환하면 뒤 필터 실행 안 됨. 예: JWT 검증 실패 시 401 응답.

6.4 Router

`envoy.filters.http.router`는 특별하다:

- **반드시 마지막** 필터.

- Route 테이블을 조회해 **cluster 선택**.

- 선택된 cluster에 요청 전송.

- 재시도, timeout, 외부 연결 관리.

6.5 Routing Rules

routes:

- match:

prefix: "/api/v1/"

route:

cluster: api-v1

timeout: 10s

retry_policy:

retry_on: 5xx

num_retries: 3

- match:

prefix: "/api/v2/"

route:

weighted_clusters:

clusters:

- name: api-v2-stable

weight: 90

- name: api-v2-canary

weight: 10

- match:

prefix: "/"

route:

cluster: frontend

매칭 기준:

- **Path**: prefix, exact, regex.

- **Headers**: 특정 헤더 값.

- **Query params**.

- **Connection props**: TLS SNI, source IP.

6.6 Direct Response

어떤 경로에 즉시 응답 (upstream 호출 없이):

- match:

path: "/health"

direct_response:

status: 200

body:

inline_string: "OK"

헬스 체크, 유지보수 모드에 유용.

7. Cluster Manager

Upstream 연결 관리의 심장.

7.1 Cluster Types

**STATIC**: 엔드포인트를 설정에 하드코딩.

load_assignment:

endpoints:

- lb_endpoints:

- endpoint:

address:

socket_address:

address: 10.0.0.1

port_value: 8080

**STRICT_DNS**: DNS로 엔드포인트 해석. 짧은 캐시(설정 가능).

type: STRICT_DNS

load_assignment:

endpoints:

- lb_endpoints:

- endpoint:

address:

socket_address:

address: backend.example.com # DNS 이름

**LOGICAL_DNS**: DNS 결과 중 첫 IP만 사용. 덜 정확하지만 가볍다.

**EDS** (Endpoint Discovery Service): xDS로 엔드포인트 동적 업데이트. Istio 기본.

type: EDS

eds_cluster_config:

eds_config:

ads: {}

**ORIGINAL_DST**: iptables redirect 등으로 프록시에 도착한 패킷의 원래 목적지로 포워딩. 투명 프록시.

7.2 Load Balancing

Envoy는 여러 LB 알고리즘:

- **ROUND_ROBIN**: 기본. 순차 순환.

- **LEAST_REQUEST**: 활성 요청이 가장 적은 엔드포인트.

- **RING_HASH**: Consistent hashing. 세션 고정.

- **RANDOM**: 무작위.

- **MAGLEV**: Google Maglev 해싱. Ring hash보다 균등하고 빠름.

- **LOAD_BALANCING_POLICY_CONFIG**: 사용자 정의.

7.3 Subset Load Balancing

Cluster 안에서 **부분집합** 선택. 예: 특정 버전, 특정 지역.

lb_subset_config:

fallback_policy: ANY_ENDPOINT

subset_selectors:

- keys: [version, region]

호출 시:

route:

cluster: backend

metadata_match:

filter_metadata:

envoy.lb:

version: v2

region: us-east

이로써 같은 cluster 내에서 "v2 + us-east"만 선택 가능. Canary 배포, A/B 테스트.

7.4 Connection Pool

각 cluster는 **connection pool**을 유지한다:

- HTTP/1.1: 여러 연결, 각 연결이 한 번에 하나의 요청.

- HTTP/2: 적은 연결(보통 1개), 많은 스트림 멀티플렉싱.

- HTTP/3: 유사.

- Raw TCP: 1:1 연결.

Pool 파라미터:

- `max_connections`: 최대 연결 수.

- `max_pending_requests`: 대기 큐.

- `max_requests`: 연결당 최대 요청 (HTTP/2).

Pool이 포화되면 요청이 대기 또는 거부.

7.5 Circuit Breaker

Upstream 과부하 시 **트래픽 차단**:

circuit_breakers:

thresholds:

- priority: DEFAULT

max_connections: 1024

max_pending_requests: 1024

max_requests: 1024

max_retries: 3

임계치 초과 시 새 요청은 503 즉시 반환. Upstream이 회복할 시간을 준다.

7.6 Outlier Detection

**자동으로 나쁜 엔드포인트 제외**:

outlier_detection:

consecutive_5xx: 5

interval: 10s

base_ejection_time: 30s

max_ejection_percent: 50

의미:

- 5xx가 5번 연속 → 30초간 제외.

- 10초마다 검사.

- 최대 50% 엔드포인트까지 제외 가능 (안전 한도).

이것이 **passive health check** — 실제 트래픽으로 판단. **Active health check**도 별도 가능:

health_checks:

- timeout: 1s

interval: 10s

unhealthy_threshold: 3

healthy_threshold: 3

http_health_check:

path: /health

주기적으로 `/health` GET → 3번 실패 시 제외.

8. 관측성

Envoy의 약속: **"모든 트래픽이 Envoy를 거치므로 관측성을 통일한다"**.

8.1 Statistics

Envoy는 수천 개의 stat을 제공한다.

**카운터 (Counter)**: 증가만.

cluster.backend.upstream_rq_total: 12345

cluster.backend.upstream_rq_2xx: 11000

cluster.backend.upstream_rq_5xx: 45

**게이지 (Gauge)**: 현재 값.

cluster.backend.upstream_cx_active: 128

**히스토그램 (Histogram)**: 분포.

cluster.backend.upstream_rq_time:

p50: 15ms, p95: 80ms, p99: 200ms

Prometheus 형식으로 export:

http://envoy_admin:9901/stats/prometheus

8.2 Access Logs

모든 요청을 로그로:

access_log:

- name: envoy.access_loggers.file

typed_config:

path: /var/log/access.log

log_format:

text_format_source:

inline_string: "[%START_TIME%] %REQ(:METHOD)% %REQ(:PATH)% %RESPONSE_CODE% %DURATION%\n"

또는 JSON:

log_format:

json_format:

start_time: "%START_TIME%"

method: "%REQ(:METHOD)%"

path: "%REQ(:PATH)%"

status: "%RESPONSE_CODE%"

duration: "%DURATION%"

upstream_host: "%UPSTREAM_HOST%"

8.3 Distributed Tracing

Envoy가 trace 헤더 전파 + 자신의 span 생성.

tracing:

provider:

name: envoy.tracers.opentelemetry

typed_config:

grpc_service:

envoy_grpc:

cluster_name: otel-collector

지원 backend:

- Jaeger

- Zipkin

- Datadog

- OpenTelemetry (OTLP)

- SkyWalking

- LightStep

**자동으로 span 생성**: Envoy가 요청을 받으면 trace ID 생성(없으면) 및 span 시작. Upstream으로 전달할 때 헤더에 삽입.

8.4 Tap Filter

요청/응답을 **실시간 캡처**:

- name: envoy.filters.http.tap

typed_config:

common_config:

admin_config:

config_id: my_tap

Admin API로 trigger:

curl -X POST http://envoy:9901/tap?config_id=my_tap \

-d '{"config_id":"my_tap","tap_config":{"match_config":{"http_request_headers_match":{"headers":[{"name":":path","exact_match":"/api/v1/users"}]}},"output_config":{"sinks":[{"streaming_admin":{}}]}}}'

`/api/v1/users` 요청이 올 때마다 전체 메시지가 HTTP 스트림으로 전송. 디버깅에 강력.

8.5 Admin Interface

Envoy는 기본으로 **admin interface**를 제공 (보통 port 9901):

- `/stats`: 통계 덤프.

- `/clusters`: cluster 상태.

- `/config_dump`: 현재 설정 전체.

- `/listeners`: 리스너 목록.

- `/certs`: 인증서 정보.

- `/healthcheck/fail`: 수동 fail(drain용).

- `/hot_restart_version`: hot restart 정보.

- `/runtime`: 런타임 플래그.

보안: admin은 **localhost에만** 바인딩 권장. 인증 없음(성능 이슈).

9. mTLS와 보안

9.1 서비스 메시의 mTLS

전통적으로 mTLS는 복잡했다. 인증서 발급/갱신/배포가 어려웠다. 서비스 메시는 이를 **자동화**:

CA (예: Istio의 Citadel)

↓ 각 workload에게 단기 인증서 발급

↓ SPIFFE SVID 형식

Envoy (sidecar)

↓ 인증서 사용

연결 상대방과 mTLS

9.2 SDS (Secret Discovery Service)

Envoy가 인증서를 **동적으로** 받는 메커니즘.

tls_context:

common_tls_context:

tls_certificate_sds_secret_configs:

- name: server_cert

sds_config:

api_config_source:

api_type: GRPC

grpc_services:

- envoy_grpc:

cluster_name: sds_server

인증서 만료 전에 control plane이 새 인증서 push → Envoy가 즉시 교체. 다운타임 없음.

9.3 SPIFFE / SPIRE

**SPIFFE**: Secure Production Identity Framework for Everyone. 표준 ID 형식.

spiffe://cluster.local/ns/default/sa/myservice

**SPIRE**: SPIFFE 구현체. workload의 신원을 인증하고 SVID 발급.

Istio는 SPIFFE 호환 ID를 사용 → 다양한 환경에서 상호운용.

9.4 Authorization

**RBAC 필터**:

- name: envoy.filters.http.rbac

typed_config:

rules:

action: ALLOW

policies:

"api-access":

permissions:

- url_path:

path:

prefix: "/api/"

principals:

- authenticated:

principal_name:

exact: "spiffe://cluster.local/ns/default/sa/frontend"

"`frontend` 서비스 계정이 `/api/` 경로에 접근 허용". 서비스 메시 정책의 기본.

10. WASM Filter

Envoy의 확장성은 필터 기반이지만, 새 필터를 추가하려면 C++로 짜고 Envoy를 재컴파일해야 했다. **WASM**이 이를 바꿨다.

10.1 WASM Filter Basic

Rust로 작성 예:

use proxy_wasm::traits::*;

use proxy_wasm::types::*;

#[no_mangle]

pub fn _start() {

proxy_wasm::set_log_level(LogLevel::Info);

proxy_wasm::set_http_context(|_, _| -> Box<dyn HttpContext> {

Box::new(MyHttpFilter)

});

}

struct MyHttpFilter;

impl HttpContext for MyHttpFilter {

fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {

if let Some(token) = self.get_http_request_header("Authorization") {

self.set_http_request_header("X-User", Some(&decode_token(&token)));

}

Action::Continue

}

}

컴파일:

cargo build --target wasm32-unknown-unknown --release

Envoy 설정:

- name: envoy.filters.http.wasm

typed_config:

config:

vm_config:

code:

local:

filename: "/etc/envoy/my_filter.wasm"

10.2 장점

- **언어 독립**: Rust, Go, AssemblyScript, C++.

- **Hot Reload**: 재시작 없이 필터 교체.

- **샌드박스**: 필터 크래시가 Envoy 크래시 되지 않음.

- **배포 분리**: 필터를 별도 저장소에서.

10.3 단점

- **성능**: 네이티브 C++ 필터보다 약간 느림 (수 μs 차이).

- **디버깅 어려움**: WASM 스택 트레이스 제한.

- **기능 제약**: 일부 Envoy API는 WASM에서 호출 불가.

주요 사용처: Solo.io의 Gloo, Istio의 WasmPlugin, Kong의 Gateway.

11. 서비스 메시 실제 배포

11.1 Istio 아키텍처

┌──────────────────────────────────┐

│ istiod (Control Plane) │

│ - Pilot (xDS) │

│ - Citadel (CA) │

│ - Galley (config validation) │

└──────────────────────────────────┘

↑ xDS gRPC

┌─────────────┴────────────────────┐

│ Data Plane │

│ │

│ ┌────────────┐ ┌────────────┐ │

│ │ Pod A │ │ Pod B │ │

│ │ ┌────────┐ │ │ ┌────────┐ │ │

│ │ │Envoy │ │ │ │Envoy │ │ │

│ │ ├────────┤ │ │ ├────────┤ │ │

│ │ │App │ │ │ │App │ │ │

│ │ └────────┘ │ │ └────────┘ │ │

│ └────────────┘ └────────────┘ │

└──────────────────────────────────┘

- `istiod`가 K8s 리소스(Service, VirtualService, DestinationRule)를 Envoy xDS로 변환.

- 각 Pod에 사이드카 Envoy 주입.

- 모든 Pod 간 트래픽이 Envoy를 거침.

11.2 Sidecar Injection

Istio는 K8s admission webhook으로 Pod 생성 시 Envoy 컨테이너 추가:

spec:

containers:

- name: my-app

image: my-app:v1

- name: istio-proxy # 자동 추가

image: istio/proxyv2:1.20

traffic redirect iptables 규칙으로 설정

또한 **init container**로 iptables 규칙 설정:

모든 pod의 inbound/outbound 트래픽 → localhost:15006 (Envoy)

11.3 전통적 vs Ambient Mesh

**Sidecar Mesh** (전통): 각 Pod에 Envoy. 오버헤드 있지만 격리 완벽.

**Ambient Mesh** (Istio 2023+): 사이드카 없음. 노드당 ztunnel + 필요 시 waypoint proxy.

Pod A (no sidecar)

ztunnel on Node A (L4 + mTLS)

ztunnel on Node B

Pod B

L7 기능이 필요하면 **waypoint proxy**를 명시적으로 배포. 경량화 + 필요할 때만 full proxy.

11.4 Consul Connect

HashiCorp의 대안. Consul의 서비스 카탈로그를 Envoy xDS로 제공.

Consul Server

↓ xDS

Consul Agent (per node)

↓ xDS

Envoy Sidecar

Istio보다 가볍지만 기능 적음.

11.5 AWS App Mesh

AWS 매니지드 서비스 메시. control plane은 AWS, data plane은 Envoy (ECS/EKS에 배포).

App Mesh Service (AWS) → xDS → Envoy (EC2/ECS/EKS/EKS Fargate)

AWS와의 통합이 좋지만 멀티 클라우드는 제한.

12. 실무 튜닝

12.1 워커 수

concurrency: 4 # worker 스레드 수 (기본 CPU 코어 수)

- 너무 적으면 CPU 병목.

- 너무 많으면 메모리 + 스케줄링 오버헤드.

- 일반적으로 `CPU 코어 수`가 좋은 시작점.

12.2 Connection Pool 튜닝

circuit_breakers:

thresholds:

- priority: DEFAULT

max_connections: 2048

max_requests: 16384 # HTTP/2 스트림

max_pending_requests: 2048

업스트림 부하에 맞춰 조정. `upstream_cx_overflow` stat 확인.

12.3 Buffer Size

per_connection_buffer_limit_bytes: 32768 # 32 KB

큰 요청/응답에는 늘려야 함. 너무 크면 OOM 위험.

12.4 Timeout

route:

timeout: 15s

idle_timeout: 300s

- `timeout`: 요청 전체 시간.

- `idle_timeout`: 연결이 idle 상태로 얼마나 유지.

12.5 Retry Policy

retry_policy:

retry_on: "5xx,reset,connect-failure"

num_retries: 3

per_try_timeout: 2s

retry_back_off:

base_interval: 0.1s

max_interval: 1s

주의: 재시도가 upstream 부하를 키울 수 있다. **retry budget**:

retry_budget:

budget_percent: 20

min_retry_concurrency: 3

"현재 활성 요청의 20% 이내에서만 retry 허용."

12.6 흔한 성능 문제

**CPU 포화**: WASM 필터, Lua, 과도한 필터 체인.

→ 측정 후 최적화.

**메모리 증가**: 큰 stats, 설정 복사본, connection pool.

→ stats matcher로 불필요한 stat 비활성화.

**레이턴시 spike**: GC 없지만 allocator(tcmalloc) 스파이크, 락, DNS 해석.

→ `per_connection_buffer_limit_bytes` 조정, STRICT_DNS 대신 EDS.

13. 대안 프록시

13.1 NGINX

- **장점**: 매우 안정적, 오래된 생태계, 풍부한 모듈.

- **단점**: 동적 설정 제한, C/Lua 모듈만, 서비스 메시 지원 제한.

- **용도**: 정적 웹사이트, 리버스 프록시, Ingress.

13.2 HAProxy

- **장점**: 매우 빠름, 뛰어난 L4/L7 로드 밸런싱.

- **단점**: xDS 미지원(변환 필요), observability 덜 풍부.

- **용도**: L4 로드 밸런서, TLS terminator.

13.3 Traefik

- **장점**: 쉬운 설정, K8s 통합.

- **단점**: 성능 Envoy 대비 낮음, 커스터마이즈 제한.

- **용도**: 간단한 Ingress.

13.4 Pingora (Cloudflare)

2022년 Cloudflare가 발표. Rust로 작성. NGINX를 대체 중.

- **장점**: Rust의 안전성, 높은 동시성, Cloudflare 규모 검증.

- **단점**: 아직 오픈소스 생태계 형성 중.

- **용도**: Cloudflare 내부 + 2024년부터 오픈소스.

13.5 Caddy

- **장점**: 자동 HTTPS (Let's Encrypt 자동), Go로 작성.

- **단점**: xDS 미지원, 성능 Envoy 대비 낮음.

- **용도**: 작은 서비스, 자동 TLS가 필요한 경우.

14. 학습 리소스

**공식 문서**:

- https://www.envoyproxy.io/docs — 권위 있는 reference.

- https://www.envoyproxy.io/learn — 학습 자료.

**블로그**:

- Matt Klein의 블로그 (Envoy 창시자).

- Istio 블로그.

- Solo.io 블로그 (Gloo 개발).

**책**:

- "Envoy Proxy: Comprehensive Guide" (비공식이지만 좋음).

- "Istio Up and Running" — Lee Calcote.

- "Cloud Native Patterns" — Cornelia Davis.

**영상**:

- EnvoyCon 발표들 (YouTube).

- KubeCon의 Envoy/Istio 세션.

**코드**:

- GitHub envoyproxy/envoy — C++ 코드.

- envoyproxy/go-control-plane — custom control plane 개발.

15. 요약 — 한 장 정리

┌─────────────────────────────────────────────────────┐

│ Envoy Cheat Sheet │

├─────────────────────────────────────────────────────┤

│ 3대 추상화: │

│ Listener (수신) → Filter Chain → Cluster │

│ │

│ xDS: │

│ LDS (Listener) → RDS (Route) → │

│ CDS (Cluster) → EDS (Endpoint) │

│ SDS (Secret), ADS (Aggregated) │

│ gRPC streaming, version-based │

│ │

│ Filter Chain: │

│ Network Filter (TCP, HTTP CM, Thrift, Redis) │

│ ↓ │

│ HTTP Filter (JWT, RBAC, Rate Limit, Router) │

│ │

│ Thread Model: │

│ Main thread + N worker threads │

│ Per-worker thread-local config │

│ 락 없음, 선형 확장 │

│ Connection fixed to accepting worker │

│ │

│ Cluster: │

│ Types: STATIC, STRICT_DNS, EDS, ORIGINAL_DST │

│ LB: RR, LR, RING_HASH, MAGLEV │

│ Subset LB (version + region 등) │

│ Circuit breaker, Outlier detection │

│ Active + Passive health check │

│ │

│ Hot Restart: │

│ v1 + v2 병행 실행 │

│ SCM_RIGHTS로 listener socket 전달 │

│ Shared memory로 stats 공유 │

│ Drain manager로 graceful shutdown │

│ │

│ 관측성: │

│ Stats (Prometheus, Statsd) │

│ Access log (text or JSON) │

│ Distributed tracing (OTel, Jaeger, Zipkin) │

│ Tap filter (실시간 캡처) │

│ Admin API (port 9901) │

│ │

│ 서비스 메시: │

│ Istio: istiod 컨트롤 + Envoy sidecar │

│ Consul Connect: Consul + Envoy │

│ App Mesh: AWS + Envoy │

│ Ambient: sidecarless, ztunnel + waypoint │

│ │

│ WASM: │

│ Rust/Go/AS로 필터 작성 │

│ Hot reload, sandbox │

│ │

│ 튜닝: │

│ concurrency (worker 수) │

│ Connection pool │

│ Buffer size │

│ Timeout / retry / circuit breaker │

└─────────────────────────────────────────────────────┘

16. 퀴즈

**A.** **락 없이 코어 수에 선형 확장**. 전통적 프록시는 여러 워커가 공유 상태(설정, 통계, 라우팅 테이블)를 락으로 보호해야 해서 코어가 많아질수록 캐시 라인 경합이 커진다. Envoy는 각 워커가 설정의 **전체 복사본**을 가지고, 한 연결은 accept한 워커에 고정된다. 설정 업데이트는 main thread가 각 워커에게 "update your copy" 메시지로 전달(TLS = Thread-Local Storage). 대가는 메모리 오버헤드(워커 수만큼 복사) 하지만 "CPU가 프록시의 병목, 메모리는 싸다"는 트레이드오프가 타당하다.

**A.** **동적, 점진적, 원자적 설정 업데이트**. 설정 파일 방식은 (1) SIGHUP로 재로드 필요, (2) 파일 배포 인프라 필요, (3) 수백 개 프록시에 동시 업데이트 어려움, (4) 부분 업데이트 불가. xDS는 gRPC streaming으로 control plane이 push → Envoy가 즉시 적용 + ACK/NACK. 버전 기반이라 잘못된 설정은 rollback. 대규모 서비스 메시에서 "수천 개 Envoy의 설정을 5초 안에 업데이트" 같은 요구를 만족한다. 이것이 Envoy가 NGINX 같은 기존 프록시와 결정적으로 다른 점.

**A.** **파일 디스크립터를 프로세스 간 전달**. UNIX 도메인 소켓의 특수 기능으로, 한 프로세스의 fd를 다른 프로세스에게 넘길 수 있다. Envoy는 이를 써서 새 프로세스(v2)가 옛 프로세스(v1)의 listening socket을 그대로 이어받게 한다. 커널 관점에서는 "두 프로세스가 같은 소켓을 공유" → accept()를 둘 다 호출 가능 → 새 연결은 v2로, 옛 연결은 v1이 drain 후 종료. 이 메커니즘 없이는 v2가 listen()을 새로 해야 하고 잠깐이나마 포트 충돌이 생긴다. 무중단 배포의 핵심 OS 기능.

**A.** **Circuit breaker**는 "업스트림에 얼마나 많이 요청을 쏠지"의 제한 — max_connections, max_requests 등 초과 시 새 요청 즉시 거부. 업스트림을 보호하고 이쪽 Envoy의 자원도 보호. **Outlier detection**은 "어느 엔드포인트가 나쁜지" 탐지 — 연속 5xx 기준으로 특정 엔드포인트를 load balancing 풀에서 일시 제외. Circuit breaker는 cluster 전체 수준, Outlier는 개별 엔드포인트 수준. 둘을 조합해서 "cluster 과부하 시 차단 + 특정 broken 노드 자동 제거"를 동시에 달성. 큰 분산 시스템에서 필수.

**A.** **Kubernetes admission webhook + iptables redirect**. (1) Pod 생성 요청이 오면 Istio의 **mutating admission webhook**이 YAML을 가로채 Envoy 컨테이너(`istio-proxy`)를 자동 추가. (2) **Init container**가 iptables 규칙을 설정해 Pod의 모든 inbound/outbound 트래픽을 Envoy로 redirect (port 15006 inbound, 15001 outbound). (3) 애플리케이션은 이 사실을 모르고 평소대로 동작하지만 모든 바이트가 Envoy를 거친다. 결과: 애플리케이션 코드 변경 없이 mTLS, 관측성, 정책 enforcement. 단점은 매 Pod에 Envoy 메모리(100-200MB)와 작은 CPU 오버헤드.

**A.** **사이드카의 리소스 오버헤드와 운영 복잡도**. 사이드카 메시는 Pod당 Envoy → 1000 Pod면 Envoy 1000개 (각 150MB). Envoy 업그레이드 시 전체 Pod 재시작 필요(애플리케이션에 영향). Ambient는 L4(mTLS, 라우팅)를 노드당 하나의 `ztunnel`(경량, Rust)로, L7(필터, 라우팅)은 **필요할 때만** waypoint proxy로 분리. 장점: (1) 리소스 대폭 감소(노드당 하나), (2) 앱 재시작 없이 메시 업그레이드, (3) L7 기능이 필요 없는 서비스는 더 가벼움. 단점: 격리 약함, 아직 성숙도 낮음. 2024+ 프로덕션 채택 시작.

**A.** **VM 오버헤드와 호스트 경계**. WASM은 샌드박스 VM에서 돌아가며 (1) JIT 컴파일이 LLVM보다 덜 공격적이라 네이티브 코드가 약간 느리고, (2) Envoy의 C++ API를 호출하려면 **WASM ↔ 호스트 경계**를 넘어야 해서 함수 호출당 수십 ns 오버헤드, (3) 문자열/버퍼를 WASM 선형 메모리 ↔ Envoy 메모리 사이에 복사해야 함. 측정 결과 보통 네이티브 대비 1.5-2배 느리지만, 대부분 마이크로초 단위라 실제 레이턴시 차이는 무시 가능. 대신 얻는 것: 언어 독립, hot reload, 크래시 격리. 트레이드오프가 거의 항상 WASM 쪽에 유리하다는 것이 2025년의 컨센서스.

이 글이 도움이 됐다면 다음 포스트도 확인해 보세요:

- "Load Balancer Internals L4/L7 Deep Dive" — Envoy 이전의 로드 밸런서 기술.

- "Service Discovery Deep Dive" — Consul, etcd, Eureka와 xDS의 연관성.

- "HTTP/3 & QUIC Deep Dive" — Envoy가 지원하는 최신 프로토콜.

- "WebAssembly Deep Dive" — WASM 필터 구현 배경.

현재 단락 (1/743)

- **Envoy**는 Lyft가 2016년 오픈소스로 공개한 C++ L7 프록시. 현재 CNCF graduated 프로젝트, 서비스 메시의 사실상 표준 데이터 플레인.

작성 글자: 0원문 글자: 22,122작성 단락: 0/743