[Istio] Envoy

xgro·2025년 4월 19일
0

Istio

목록 보기
3/17

📌 Notice

Istio Hands-on Study (=Istio)
직접 실습을 통해 Isito를 배포 및 설정하는 내용을 정리한 블로그입니다.

CloudNetaStudy 그룹에서 스터디를 진행하고 있습니다.

Gasida님께 다시한번 🙇 감사드립니다.

EKS 관련 이전 스터디 내용은 아래 링크를 통해 확인할 수 있습니다.



📌 Summary

  • Envoy는 Lyft에서 개발한 고성능 L7 프록시로, 현재는 CNCF 프로젝트로 활발히 사용되고 있음
  • 분산 환경에서 서비스 디스커버리, 로드 밸런싱, 보안, 트래픽 제어, 관찰성을 제공
  • 정적 설정과 동적 설정 방식이 있으며, 실습에서는 YAML 기반의 정적 구성 방식을 사용
  • Admin API를 통해 프록시 상태 및 설정을 실시간으로 확인 가능
  • retry_policy 등의 설정을 통해 네트워크 복원력 구현 가능
  • Istio에서는 Envoy를 사이드카 프록시로 활용해 모든 서비스 간 트래픽을 제어



📌 Overview

이번 블로그에서는 서비스 메시(Service Mesh)의 핵심 구성 요소인 Envoy Proxy에 대해 집중적으로 다뤘습니다.
Envoy가 왜 필요한지, 어떤 기능을 제공하는지, 어떻게 설정하고 실습해볼 수 있는지를 순차적으로 정리했습니다.

특히 정적 설정 기반으로 Envoy를 직접 실행하고, 실제 요청이 프록시되는 과정을 확인함으로써,
단순한 개념 이해를 넘어 실제 동작하는 모습을 체감하는 경험을 목표로 했습니다.



📌 Istio’s data plane: Envoy Proxy


👉 Step. 01 - Envoy는 왜 필요한가요?

서비스가 점점 분산 시스템 구조로 진화하면서, 마이크로서비스 아키텍처에서는 서비스 간의 통신이 이전보다 훨씬 복잡해졌습니다. 하나의 요청이 여러 서비스를 거쳐 처리되다 보니, 단순히 “요청을 보낸다”는 행위 뒤에 수많은 네트워크 문제가 숨어 있습니다.

예를 들어, 클라이언트가 어떤 서비스에 요청을 보내야 할 때 아래와 같은 질문들이 생깁니다:

  • 이 서비스 인스턴스는 정상적으로 작동 중인가요?

  • 어떤 인스턴스로 요청을 보내야 하죠?

  • 요청이 오래 걸리거나 실패하면 어떻게 해야 하죠?

이러한 문제들을 애플리케이션 코드 안에서 직접 해결하려면, 로직은 복잡해지고 유지보수는 더욱 어려워집니다. 이때 등장하는 것이 바로 Envoy Proxy입니다.


💡 Envoy의 탄생과 철학
Envoy는 미국의 차량 호출 서비스인 Lyft에서, 위와 같은 네트워크 복잡도를 애플리케이션 바깥에서 해결하기 위해 개발한 고성능 L7 프록시입니다.
2016년에 오픈소스로 공개되었고, 이듬해인 2017년에는 CNCF(Cloud Native Computing Foundation) 프로젝트로 채택되며 본격적인 생태계를 갖추게 되었습니다.

Envoy의 설계 철학은 두 가지로 요약할 수 있습니다:

  • 네트워크는 애플리케이션에게 투명해야 한다

  • 문제가 생겼을 때, 원인을 빠르게 파악할 수 있어야 한다

즉, 네트워크의 복잡성과 오류 처리를 프록시 계층에서 해결하고, 애플리케이션은 본연의 비즈니스 로직에만 집중할 수 있도록 하자는 목표입니다.


🧭 프록시는 무엇이며, Envoy는 어떻게 다른가요?

프록시(proxy)란 클라이언트와 서버 사이에 위치하여 요청과 응답을 중계하는 구성 요소입니다.
클라이언트는 어느 인스턴스가 정상인지, 어떤 포트로 요청을 보내야 하는지 몰라도 됩니다.
프록시는 클라이언트 대신 그 판단을 내리고 요청을 적절한 서비스 인스턴스로 전달해줍니다.

Envoy는 이러한 전통적인 프록시 역할을 하면서도, L7(Application Layer) 수준까지 이해하고 동작할 수 있는 지능형 프록시입니다.

  • HTTP, HTTP/2, gRPC 같은 애플리케이션 프로토콜을 해석할 수 있으며,

  • 요청 경로, 헤더, 쿼리값 등을 기반으로 정교한 라우팅과 제어가 가능합니다.

이는 단순한 L4 기반 프록시와 차별화되는 Envoy의 핵심적인 강점입니다.

🧩 Envoy는 어디에 배포될 수 있나요?

Envoy는 다양한 네트워크 위치에서 역할을 수행할 수 있습니다:

  • 에지 프록시(Edge Proxy): 클러스터 외부에서 들어오는 요청을 수용하는 진입점
  • 사이드카 프록시(Sidecar Proxy): 각 서비스 인스턴스와 함께 배포되어 서비스 간 트래픽 제어를 담당
  • 중앙 프록시(Shared Proxy): 여러 서비스가 공통으로 사용하는 프록시 형태

이런 유연한 배포 방식은 Envoy를 다양한 아키텍처 환경에서 쉽게 적용할 수 있도록 해줍니다.


🔍 왜 Envoy는 서비스 메시에서 핵심이 될까요?

Envoy는 단순히 요청을 전달하는 프록시가 아니라, 다음과 같은 고급 기능을 제공하는 애플리케이션 네트워크 플랫폼입니다.

  • 서비스 디스커버리: 인스턴스를 직접 지정하지 않아도 동적으로 탐색 가능
  • 로드 밸런싱: 다양한 알고리즘으로 트래픽을 효율적으로 분산
  • 헬스 체크 & 장애 우회: 오작동하는 인스턴스를 자동으로 우회
  • 보안: TLS/mTLS 지원, 인증서 관리
  • 관찰성(Observability): 메트릭, 로그, 분산 트레이싱을 기본 지원
  • 확장성: Wasm, Lua, C++ 기반의 커스텀 필터로 기능 확장 가능

이처럼 풍부한 기능을 갖춘 EnvoyIstio와 같은 서비스 메시 프레임워크에서 데이터 플레인의 핵심 요소로 사용됩니다.

실제로 Istio에서는 모든 서비스 앞에 Envoy를 사이드카로 붙여서 트래픽을 감시, 제어, 보호하고 있습니다.



👉 Step. 02 - Envoy의 주요 기능은 무엇인가요?

앞에서 Envoy가 왜 필요한지, 어떤 상황에서 유용한지 알아봤다면, 이번에는 실제로 Envoy가 제공하는 핵심 기능들을 살펴보겠습니다.
기능들은 아래처럼 개념적으로 나눠서 이해하면 훨씬 쉬워집니다.


🔌 1. 트래픽을 받아들이는 방식 - ListenerRoute

Envoy는 외부로부터 요청을 받기 위해 Listener라는 개념을 사용합니다.
Listener는 특정 포트(예: 80, 443 등)를 열고, 그 포트로 들어오는 트래픽을 수신합니다.

트래픽을 받은 이후에는, 이 요청을 어떻게 처리할지를 결정해야 하므로, Route라는 설정을 이용해 특정 경로로 들어온 요청을 어떤 서비스(Cluster)로 보낼지 지정합니다.

예시 흐름:

  • /api/products로 들어온 요청 → product-v1 클러스터로 전달

🧱 2. 백엔드를 구성하는 단위 - ClusterEndpoint

Envoy가 요청을 전달하는 대상은 Cluster라고 불리는 논리적인 집합입니다.
하나의 클러스터 안에는 여러 개의 실제 서비스 인스턴스(= Endpoint)가 포함됩니다.

즉, 클러스터는 트래픽을 분산시킬 수 있는 단위이며, Envoy는 이 클러스터 안에서 로드밸런싱을 수행합니다.

정리하면:

  • Listener는 요청을 받고
  • Route는 라우팅 규칙을 결정하며
  • Cluster는 트래픽을 전달받고
  • Endpoint는 실제 요청을 처리합니다.

🧭 3. 서비스 디스커버리 - 자동으로 살아있는 서비스 찾기

Envoy는 외부 서비스 등록 시스템과 연동해 살아있는 서비스 인스턴스를 동적으로 찾을 수 있습니다.
이를 통해 애플리케이션은 직접 인스턴스를 관리하지 않아도 되고, Envoy가 알아서 서비스 디스커버리 API로 등록된 정보를 받아 사용합니다.

지원 예시:

  • Consul, Zookeeper, Eureka, Kubernetes Service 등

⚖️ 4. 다양한 로드 밸런싱 알고리즘

Envoy는 다음과 같은 다양한 로드밸런싱 전략을 기본으로 지원합니다:

  • Random
  • Round Robin
  • Least Request (가장 요청이 적은 인스턴스로 전송)
  • Weighted (가중치 기반)
  • Consistent Hashing (세션 유지에 유리함)

특히 Locality-aware Load Balancing 기능은, 가능하면 동일한 지역(zone)의 인스턴스를 우선 사용함으로써 레이턴시를 줄이고 안정성을 높입니다.


🔁 5. 트래픽 라우팅, 전환, 섀도잉

Envoy는 요청을 라우팅할 때 단순 경로 기반뿐 아니라 아래와 같은 고급 기능도 제공합니다:

  • 헤더 기반 라우팅: 특정 User-Agent를 가진 요청만 다른 클러스터로 전달
  • 비율 기반 트래픽 전환: 신규 버전에 전체 트래픽의 10%만 전달 (카나리 배포)
  • 섀도잉(Shadowing): 요청을 실제로 처리하는 대신, 복사본을 신규 서비스로 보내기 (성능 테스트용)

💪 6. 네트워크 복원력 기능

서비스 간 통신은 언제든 실패할 수 있기 때문에, Envoy는 여러 복원력 기능을 기본 제공합니다:

  • 재시도 (Retry): 5xx 오류 발생 시 자동으로 다시 요청
  • 타임아웃 설정: 응답이 늦으면 중단
  • 서킷 브레이커 (Circuit Breaker): 특정 임계치를 초과하면 요청 차단
  • Outlier Detection: 에러가 반복되는 인스턴스는 로드밸런싱 대상에서 제외

이 기능들은 네트워크 이슈로 인한 장애 전파를 줄이는 데 매우 중요합니다.


📈 7. 관찰 가능성 (Observability)

Envoy는 아래와 같은 메트릭을 수집하여 시스템 상태를 실시간으로 관찰할 수 있게 해줍니다:

  • 요청 수, 지연 시간, 재시도 횟수, 서킷 브레이커 활성화 횟수
  • Prometheus, Datadog, StatsD 등 다양한 시스템과 통합 가능

또한 Envoy는 OpenTracing, Zipkin, Jaeger와 연동하여 분산 트레이싱을 구현할 수 있습니다.

Envoy는 자체적으로 x-request-id, x-b3-* 등의 트레이싱 헤더도 자동 생성 및 전달합니다.


🔒 8. TLS/mTLS 통신 지원

  • Envoy는 TLS 종료(Termination)TLS 시작(Origination) 기능을 모두 지원합니다.
  • Istio와 함께 사용하면, 서비스 간 통신을 완전히 자동화된 mTLS 환경으로 만들 수 있습니다.
  • 인증서 발급과 회전은 Istiod가 자동으로 수행하며, Envoy는 이를 반영해 암호화 통신을 수행합니다.

⛔ 9. 속도 제한 (Rate Limiting)

  • Redis나 DB처럼 자원이 제한된 시스템에 무한정 요청이 몰리는 것을 방지하기 위해
  • Envoy는 요청 단위 또는 커넥션 단위 속도 제한 기능을 제공합니다.
  • 이는 외부 Rate Limiter와 연동하거나 자체 설정으로 구성할 수 있습니다.

🧩 10. 확장성 - Filter 기반 구조

Envoy의 가장 큰 장점 중 하나는 기능을 플러그인처럼 확장할 수 있다는 점입니다.

  • 필터(Filter)라는 형태로 요청을 전처리/후처리 가능
  • 기본은 C++ 기반이지만, Lua, WASM(WebAssembly) 기반의 동적 확장도 지원
  • 예: 사용자 인증 필터, 로깅 필터, 트래픽 변경 필터 등 자유롭게 구성 가능

이처럼 Envoy는 단순한 트래픽 중계자가 아니라, 현대적인 마이크로서비스 네트워크의 중추 역할을 수행하는 강력한 도구입니다.



👉 Step. 03 - Envoy는 어떻게 설정할까요?

앞서 Envoy가 얼마나 다양한 기능을 제공하는지 살펴봤습니다. 하지만 이런 기능들을 어떻게 활성화하고 제어할 수 있을까요?
답은 바로 설정(configuration)에 있습니다. Envoy는 YAML 또는 JSON 기반의 설정 파일을 통해 모든 기능을 선언적으로 구성합니다.

Envoy 설정은 크게 정적 설정(Static)동적 설정(Dynamic)으로 나뉩니다. 각각의 방식이 어떤 차이를 가지며 어떤 상황에서 사용되는지 자세히 알아보겠습니다.


✅ 정적 설정 (Static Configuration)

정적 설정은 말 그대로 애플리케이션 실행 시점에 고정된 설정을 파일로 지정하는 방식입니다. 설정 파일에는 다음과 같은 정보들이 포함됩니다:

  • 어떤 포트를 열 것인지 (listener)
  • 어떤 URL 경로를 어떤 서비스로 보낼지 (route)
  • 트래픽을 전달할 클러스터와 엔드포인트는 무엇인지 (cluster)
  • 메트릭, 로그, 트레이싱 설정

예를 들어 다음과 같은 구성은 15001 포트로 들어온 모든 요청을 httpbin 서비스로 전달하는 설정입니다:

static_resources:
  listeners:
  - name: httpbin-demo
    address:
      socket_address: { address: 0.0.0.0, port_value: 15001 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          http_filters:
          - name: envoy.filters.http.router
          route_config:
            name: httpbin_local_route
            virtual_hosts:
            - name: httpbin_local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route:
                  auto_host_rewrite: true
                  cluster: httpbin_service

  clusters:
  - name: httpbin_service
    connect_timeout: 5s
    type: LOGICAL_DNS
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: httpbin
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: httpbin
                port_value: 8000

이 구성에서는 다음과 같은 동작이 이루어집니다:

  • 15001 포트에 바인딩된 Listener가 요청을 수신
  • 모든 요청을 하나의 Route로 매핑
  • Route는 httpbin_service라는 Cluster로 트래픽을 전달
  • Cluster는 내부적으로 DNS를 통해 실제 엔드포인트(httpbin:8000)를 조회
  • 로드밸런싱은 ROUND_ROBIN 방식으로 수행

정적 설정은 단순한 테스트나 로컬 개발 환경에서 유용하지만, 서비스가 동적으로 변경되는 환경에서는 유연성이 떨어집니다.


✅ 동적 설정 (Dynamic Configuration)

실제 운영 환경에서는 서비스 인스턴스가 동적으로 늘어나거나 줄어들고, 설정도 자주 바뀌게 됩니다.
이때 Envoy는 xDS API라는 표준 API 묶음을 통해 런타임 중에도 설정을 실시간으로 갱신할 수 있습니다.

xDS에는 다음과 같은 하위 API가 포함됩니다:

  • LDS (Listener Discovery Service): 어떤 Listener를 노출할지
  • RDS (Route Discovery Service): 어떤 Route를 사용할지
  • CDS (Cluster Discovery Service): 어떤 Cluster가 존재하는지
  • EDS (Endpoint Discovery Service): 각 Cluster가 어떤 엔드포인트를 포함하는지
  • SDS (Secret Discovery Service): TLS 인증서 등 보안 관련 리소스
  • ADS (Aggregated Discovery Service): 모든 xDS 변경을 하나의 스트림으로 처리

Envoy는 이러한 API들을 통해 설정 변경 사항을 주기적으로 polling하지 않고, 서버로부터 스트리밍 방식으로 푸시 받습니다.

📌 예: 동적 LDS 구성

dynamic_resources:
  lds_config:
    api_config_source:
      api_type: GRPC
      grpc_services:
      - envoy_grpc:
          cluster_name: xds_cluster

clusters:
- name: xds_cluster
  connect_timeout: 0.25s
  type: STATIC
  lb_policy: ROUND_ROBIN
  http2_protocol_options: {}
  hosts:
  - socket_address:
      address: 127.0.0.3
      port_value: 5678

이 설정은 Envoy가 xds_cluster라는 gRPC API 서버를 통해 리스너 정보를 받아오도록 구성하는 예입니다.


Istio와의 연계: ADS의 활용

Istio는 Envoy의 xDS API 중에서도 특히 ADS를 중심으로 구성되어 있습니다.
Istio의 컨트롤 플레인인 istiod는 Kubernetes 리소스 (예: VirtualService)를 읽어, 이를 xDS 포맷으로 변환해 사이드카 프록시(Envoy)에게 실시간으로 전달합니다.

이를 통해 수많은 Envoy 인스턴스들의 설정을 자동으로 일관되게 관리할 수 있으며, 사용자는 Kubernetes CRD만으로도 복잡한 네트워크 제어를 구성할 수 있습니다.


✅ 정리

설정 방식특징활용 예시
정적 설정YAML로 직접 정의로컬 테스트, 실습
동적 설정xDS API 사용프로덕션 환경, Istio 연동

Envoy 설정의 구조와 흐름을 이해하면, 이후 Istio에서의 동작 원리를 보다 명확히 이해할 수 있습니다.



👉 Step. 04 - 실습: Envoy를 정적 설정으로 실행해보기

이번 단계에서는 EnvoyYAML 설정 파일 기반의 정적 구성 방식으로 직접 실행해보는 실습을 진행합니다.
실습의 전체 흐름은 다음과 같습니다:

  1. httpbin 서비스를 띄운다
  2. Envoy 프록시를 띄운다 (설정 파일로)
  3. 프록시를 통해 httpbin으로 요청을 보낸다
  4. 결과를 확인하고 구성의 의미를 되짚는다

✅ 1. httpbin 컨테이너 띄우기

우선 테스트용 서비스로 많이 쓰이는 httpbin을 Docker로 띄웁니다.

# mccutchen/go-httpbin 는 기본 8080 포트여서, 책 실습에 맞게 8000으로 변경
# docker run -d -e PORT=8000 --name httpbin mccutchen/go-httpbin -p 8000:8000
docker run -d -e PORT=8000 --name httpbin mccutchen/go-httpbin 
docker ps

# curl 컨테이너로 httpbin 호출 확인
docker run -it --rm --link httpbin curlimages/curl curl -X GET http://httpbin:8000/headers
{
  "headers": {
    "Accept": [
      "*/*"
    ],
    "Host": [
      "localhost:8000"
    ],
    "User-Agent": [
      "curl/8.7.1"
    ]
  }
}
  • httpbin은 다양한 HTTP 요청을 테스트할 수 있는 간단한 웹 서버입니다.
  • 위 명령어로 로컬 포트 8000에 httpbin을 실행합니다.


✅ 2. Envoy 설정 파일 작성하기

아래는 envoy.yaml이라는 이름으로 저장할 Envoy 설정 파일입니다.
이 설정은 15001 포트로 들어온 요청을 모두 httpbin 서비스로 라우팅합니다.

# envoy.yaml

static_resources:
  # 요청을 수신할 리스너 구성
  listeners:
  - name: httpbin-demo
    address:
      socket_address: { address: 0.0.0.0, port_value: 15001 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          http_filters:
          - name: envoy.filters.http.router
          route_config:
            name: httpbin_local_route
            virtual_hosts:
            - name: httpbin_local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route:
                  auto_host_rewrite: true
                  cluster: httpbin_service

  # 프록시가 트래픽을 전달할 클러스터 정의
  clusters:
  - name: httpbin_service
    connect_timeout: 5s
    type: LOGICAL_DNS
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: httpbin
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: host.docker.internal  # Mac/Windows용 Docker에서 로컬호스트 접근
                port_value: 8000

📌 설정 포인트 요약

  • listener: Envoy가 요청을 수신할 포트 (15001)
  • http_connection_manager: HTTP 요청을 처리하는 필터
  • route: 모든 요청(prefix: "/")을 httpbin_service 클러스터로 라우팅
  • cluster: DNS 이름(host.docker.internal)을 통해 실제 서비스로 연결
  • ROUND_ROBIN: 요청을 여러 인스턴스로 분산 (현재는 하나만 사용)

✅ 3. Envoy 컨테이너 실행하기

위에서 작성한 envoy.yaml 설정 파일을 기반으로 Envoy 컨테이너를 실행합니다.

# 터미널 1
docker run --name proxy --link httpbin envoyproxy/envoy:v1.19.0 --config-yaml "$(cat envoy.yaml)"
  • 15001 포트는 외부 요청을 수신하는 포트입니다.
  • 9901은 Envoy의 Admin API가 열리는 포트로, 상태 확인이나 설정 조회에 사용됩니다.
  • 설정 파일은 -v 옵션을 이용해 컨테이너 내부로 복사됩니다.

✅ 4. 요청 테스트

이제 실제로 Envoy를 통해 httpbin 서비스에 요청을 보내보겠습니다.

# 터미널 2
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15001/headers

정상적으로 설정되었다면 아래와 같은 JSON 응답이 출력됩니다:

{
  "headers": {
    "Accept": [
      "*/*"
    ],
    "Host": [
      "httpbin"
    ],
    "User-Agent": [
      "curl/8.13.0"
    ],
    "X-Envoy-Expected-Rq-Timeout-Ms": [
      "15000" # 15000ms = 15초
    ],
    "X-Forwarded-Proto": [
      "http"
    ],
    "X-Request-Id": [
      "8d08bd8e-7899-42e1-bf74-7a3381a2494a"
    ]
  }
}

📌 X-Request-Id, X-Forwarded-Proto 등의 헤더는 Envoy가 프록시로서 자동으로 추가한 정보입니다.


✅ 5. Admin API로 현재 상태 확인하기

Envoy는 /stats, /config_dump, /clusters 등 다양한 엔드포인트를 통해 현재 설정 및 상태를 조회할 수 있는 Admin API를 제공합니다.

예시:

# admin API로 Envoy stat 확인 : 응답은 리스너, 클러스터, 서버에 대한 통계 및 메트릭
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15000/stats

# retry 통계만 확인
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15000/stats | grep retry
cluster.httpbin_service.circuit_breakers.default.rq_retry_open: 0
cluster.httpbin_service.circuit_breakers.high.rq_retry_open: 0
cluster.httpbin_service.retry_or_shadow_abandoned: 0
cluster.httpbin_service.upstream_rq_retry: 0
cluster.httpbin_service.upstream_rq_retry_backoff_exponential: 0
cluster.httpbin_service.upstream_rq_retry_backoff_ratelimited: 0
cluster.httpbin_service.upstream_rq_retry_limit_exceeded: 0
cluster.httpbin_service.upstream_rq_retry_overflow: 0
cluster.httpbin_service.upstream_rq_retry_success: 0
...

# 다른 엔드포인트 일부 목록들도 확인
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15000/certs # 머신상의 인증서
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15000/clusters # 엔보이에 설정한 클러스터
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15000/config_dump # 엔보이 설정 덤프
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15000/listeners # 엔보이에 설정한 리스너
docker run -it --rm --link proxy curlimages/curl curl -X POST http://proxy:15000/logging # 로깅 설정 확인 가능
docker run -it --rm --link proxy curlimages/curl curl -X POST http://proxy:15000/logging?http=debug # 로깅 설정 편집 가능
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15000/stats # 엔보이 통계
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15000/stats/prometheus # 엔보이 통계(프로메테우스 레코드 형식)

Admin API는 실시간 상태를 확인하거나, 동적으로 라우팅/필터 구성을 점검할 때 매우 유용합니다.


✅ 6. Retry 테스트

이번에는 httpbin/status/500 엔드포인트를 활용하여, 5xx 오류에 대한 재시도 정책이 작동하는지 확인합니다.
이를 위해 설정 파일을 다음과 같이 수정합니다:

routes:
- match: { prefix: "/" }
  route:
    auto_host_rewrite: true
    cluster: httpbin_service
    retry_policy:  # 재시도 정책 설정
      retry_on: "5xx"
      num_retries: 3

설정 적용 후 Envoy 컨테이너를 재시작하고 다음 명령을 실행합니다:

#
docker rm -f proxy

#
cat envoy.yaml
docker run -p 15000:15000 --name proxy --link httpbin envoyproxy/envoy:v1.19.0 --config-yaml "$(cat envoy.yaml)"
docker run -it --rm --link proxy curlimages/curl curl -X POST http://proxy:15000/logging?http=debug

# /stats/500 경로로 프록시를 호출 : 이 경로로 httphbin 호출하면 오류가 발생
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15001/status/500

# 호출이 끝났는데 아무런 응답도 보이지 않는다. 엔보이 Admin API에 확인
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15000/stats | grep retry
...
cluster.httpbin_service.retry.upstream_rq_500: 3
cluster.httpbin_service.retry.upstream_rq_5xx: 3
cluster.httpbin_service.retry.upstream_rq_completed: 3
cluster.httpbin_service.retry_or_shadow_abandoned: 0
cluster.httpbin_service.upstream_rq_retry: 3
...

정상적으로 설정되었다면, Envoy는 httpbin_service로 최대 3번까지 요청을 재시도하게 됩니다.


✅ 정리

이번 실습에서는 다음과 같은 핵심 흐름을 확인했습니다:

항목설명
프록시 설정YAML 기반의 정적 설정으로 Listener/Route/Cluster 구성
요청 테스트프록시를 통해 httpbin으로 HTTP 요청을 전달
Admin API프록시 상태와 구성 확인 가능 (/stats, /clusters, /config_dump)
Retry 정책5xx 오류에 대해 지정 횟수만큼 재시도 가능



👉 Step. 05 - Envoy는 왜 Istio에 적합할까요?

지금까지 Envoy를 독립적으로 구성하고 요청을 프록시하는 과정을 실습해보았습니다.
이번에는 Istio와 같은 서비스 메시(Service Mesh) 아키텍처에서 Envoy가 어떻게 통합되고, 왜 핵심 컴포넌트로 선택되었는지를 알아봅니다.


✅ 서비스 메시 구조에서의 데이터 플레인

서비스 메시란 마이크로서비스 간의 통신을 애플리케이션 코드가 아닌 인프라 레벨에서 제어하기 위한 아키텍처입니다.

서비스 메시의 구성 요소는 크게 두 가지입니다:

  • 컨트롤 플레인(Control Plane): 정책 설정, 인증 관리, 라우팅 규칙 전달 등을 담당
  • 데이터 플레인(Data Plane): 실제 트래픽을 전달하고, 정책에 따라 행동하는 네트워크 계층

Envoy는 바로 이 데이터 플레인의 중심에서, 애플리케이션과 외부 세계 사이의 트래픽을 제어하는 프록시로 사용됩니다.


Istio에서의 Envoy 역할

Istio는 컨트롤 플레인인 istiod를 통해 전체 설정을 관리하며,
각 Pod 옆에 배치된 Envoy를 통해 트래픽을 실제로 관찰, 제어, 보호합니다.

  • Istio는 Kubernetes 리소스(예: VirtualService, DestinationRule)를 분석하여
  • 해당 내용을 Envoy가 이해할 수 있는 xDS API 형태로 변환
  • 이를 istiod가 각 Envoy 인스턴스에 푸시(ADS 방식)

결과적으로 Envoy는 정책을 반영한 라우팅, 보안, 로깅, 트레이싱, 제한 제어 등 모든 통신 관련 기능을 실행하게 됩니다.


Istio + Envoy의 동작 흐름

서비스 메시 환경에서 요청이 처리되는 흐름을 정리하면 다음과 같습니다:

  1. 사용자가 애플리케이션에 요청을 보냅니다.
  2. 요청은 Pod 내부에 함께 배포된 Envoy 사이드카 프록시로 먼저 전달됩니다.
  3. Envoy는 Istio의 설정을 바탕으로:
    • 어떤 경로로 보낼지 (VirtualService)
    • 어떤 정책을 적용할지 (AuthorizationPolicy, PeerAuthentication)
    • 재시도, 타임아웃, 장애 우회 설정 등 포함
  4. 최종적으로 다른 서비스의 Envoy로 요청을 전달합니다.

모든 이 과정은 애플리케이션 코드 수정 없이, 설정만으로 제어됩니다.


Envoy가 선택된 이유

Istio는 수많은 L7 프록시 중에서 Envoy를 선택했습니다. 그 이유는 다음과 같습니다:

  • L7 프로토콜 지원: HTTP, HTTP/2, gRPC 등 현대적인 트래픽 제어 가능
  • xDS API 기반 동적 설정: Istio가 실시간으로 프록시 설정을 배포 가능
  • mTLS 지원: Pod 간 통신 암호화를 자동화 가능
  • 분산 트레이싱: Zipkin, Jaeger 연동을 통한 트래픽 흐름 시각화
  • 고성능 C++ 기반 아키텍처: 대규모 서비스에 적용 가능한 처리량 확보
  • 확장성: 필터 체인을 통한 유연한 로직 삽입 (WASM, Lua, C++)

Envoy를 이해하면 Istio가 보인다

Istio를 학습하는 과정에서 Envoy를 이해하는 것은 단순한 선택이 아니라 필수에 가깝습니다.
왜냐하면 Istio가 수행하는 거의 모든 기능이 Envoy 위에서 실행되기 때문입니다.

즉,

"Istio는 설정을 생성하고,
Envoy는 그것을 실행한다."

라고 말해도 과언이 아닙니다.


✅ 정리

구성 요소역할
istiod (컨트롤 플레인)정책 생성, Envoy 구성 전달
Envoy (데이터 플레인)트래픽을 실제로 전달하고 제어
xDS APIIstio ↔ Envoy 간 설정 전송 포맷
사이드카 프록시각 Pod 옆에 배치되어 트래픽 감시/제어

Envoy의 원리를 이해하면, Istio의 동작 방식은 더 이상 복잡하거나 추상적인 개념이 아닙니다.



📌 Conclusion

Envoy는 단순한 트래픽 중계자가 아닙니다.
네트워크의 복잡성을 서비스 코드 바깥으로 분리해주고, 정책 기반의 제어·보안·관측을 가능하게 해주는,
현대적 마이크로서비스 환경에 꼭 필요한 지능형 프록시 플랫폼입니다.

특히 Kubernetes 환경에서는 Istio와 결합되어 서비스 메시의 데이터 플레인으로 동작하면서,
전체 서비스 간 통신 흐름을 일관되고 안전하게 관리하는 기반이 되어줍니다.

다음 블로그에서는 Envoy와 함께 사용되는 Istio Ingress Gateway를 실습하며,
외부 트래픽이 클러스터 내부로 어떻게 유입되는지를 구체적으로 살펴볼 예정입니다.

profile
안녕하세요! DevOps 엔지니어 이재찬입니다. 블로그에 대한 피드백은 언제나 환영합니다! 기술, 개발, 운영에 관한 다양한 주제로 함께 나누며, 더 나은 협업과 효율적인 개발 환경을 만드는 과정에 대해 인사이트를 나누고 싶습니다. 함께 여행하는 기분으로, 즐겁게 읽어주시면 감사하겠습니다! 🚀

0개의 댓글