728x90
Istio’s data plane: Envoy Proxy
- 엔보이는 분산 시스템을 구축할 때 발생하는 어려운 애플리케이션 네트워킹 문제를 해결하고자 리프트가 개발
- 엔보이는 2016년 9월 오픈소스 프로젝트로 공개됐으며, 1년 후인 2017년 9월에는 CNCF에 합류
- 엔보이는 C++로 작성됐는데, 그 목표는 성능을 늘리는 것, 특히 높은 부하에서도 더 안정적이고 결정론적일 수 있도록 만드는 것
- 엔보이 2가지 중요 원칙
- 애플리케이션에게 네트워크는 투명해야 한다.
- 네트워크 및 애플리케이션 문제가 발생할 때는 문제의 원인을 파악하기 쉬워야 한다.
- proxy 역할: Client와 Server 중간에 중계자 역할 수행
- 프록시가 서비스 인스턴스 간에 로드 밸런싱을 처리
- 엔보이 프록시는 특히 서비스 디스커버리, 로드 밸런싱, 헬스 체크 같은 기능을 제공하기 위해 애플리케이션 요청 경로 중간에 삽입할 수 있는 애플리케이션 수준 프록시이지만, 엔보이는 그 이상의 기능을 제공(방화벽 기능)
- 기본 제공 프로토콜 외에도 다양한 프로토콜을 이해하도록 확장 가능
- 엔보이는 애플리케이션 수준 프로토콜을 이해할 수 있고, 애플리케이션 트래픽이 엔보이를 거쳐 흐르는 덕분에 통과하는 요청에 대한 텔레메트리를 수집할 수 있음
- 개발자가 네트워크 문제를 고려하지 않아도 되도록 설계
Envoy concepts as a high level
- 리스너 Listeners
- 애플리케이션이 연결할 수 있는 외부 세계로 포트를 노출
- 예를 들어 포트 80에 대한 리스터는 트래픽을 받고, 설정된 동작을 해당 트래픽에 적용
- 루트(라우트) Routes
- 리스너로 들어오는 트래픽을 처리하는 라우팅 규칙,
- 예를 들어 요청이 들어오고 `/catalog` 에 일치하면 그 트래픽을 catalog 클러스터로 전달
- 클러스터 Cluster
- 라우팅 규칭에 따라 도착하는 엔드포인트 집합
- 예를 들어 catalog-v1 과 catalog-v2 는 별도 클러스터일 수 있고,
루트는 catalog 서비스의 v1이나 v2로 트래픽을 보내는 방법에 대한 규칙을 지정할 수 있음
주요 기능
- 서비스 디스커버리(SERVICE DISCOVERY)
클라이언트 측 서비스 디스커버리 client-side service discovery를 구현하기 위해 런타임별로 전용 라이브러리를 사용할 필요 없이, 엔보이는 서비스 디스커버를 자동으로 수행할 수 있음 - 로드 밸런싱(LOAD BALANCING)
엔보이는 애플리케이션이 활용할 수 있는 고급 로드 밸런싱 알고리즘을 여러 가지 구현 - 트래픽 및 요청 라우팅(TRAFFIC AND REQUEST ROUTING)
엔보이는 HTTP 1.1과 HTTP 2 같은 애플리케이션 프로토콜을 이해할 수 있으므로 정교한 라우팅 규칙을 사용해 트래픽을 특정 백엔드 클러스터로 보낼 수 있음 - 트래픽 전환 및 섀도잉 기능(TRAFFIC SHIFTING AND SHADOWING CAPABILITIES)
엔보이는 비율 기반(즉, 가중치 적용) 트래픽 분할 splitting / 전환 shifting 을 지원 - 네트워크 복원력(NETWORK RESILIENCE)
엔보이에게 특정 종류의 복원력 문제를 맡길 수는 있지만, 그 파라미터를 설정하고 잘 조정하는 것은 애플리케이션의 책임이라는 점을 유의 - 메트릭 수집을 통한 관찰 가능성(OBSERVABILITY WITH METRICS COLLECTION)
엔보이의 목표 중 하나는 네트워크를 이해할 수 있게 만드는 것- 이 목표를 위해 엔보이는 다양한 메트릭을 수집
Configuring Envoy
- 엔보이는 JSON/YAML 형식 설정 파일로 구동
- 설정 파일은 리스너, 루트, 클러스터뿐 아니라 Admin API 활성화 여부, 액세스 로그 저장 위치, 트레이싱 엔진 설정 등 서버별 설정도 지정
- Static configuration 정적 설정
static_resources:
listeners: # (1) 리스너 정의
- name: httpbin-demo
address:
socket_address: { address: 0.0.0.0, port_value: 15001 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager # (2) HTTP 필터
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: # (3) 라우팅 규칙
name: httpbin_local_route
virtual_hosts:
- name: httpbin_local_service
domains: ["*"] # (4) 와일드카드 가상 호스트
routes:
- match: { prefix: "/" }
route:
auto_host_rewrite: true
cluster: httpbin_service # (5) 클러스터로 라우팅
clusters:
- name: httpbin_service # (6) 업스트림 클러스터
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
- Dynamic configuration 동적 설정
- 엔보이는 특정 API군을 사용해 다운타임이나 재시작 없이 설정을 실시간으로 업데이트할 수 있다.
- 올바른 디스커버리 서비스 API를 가리키는 간단한 부트스트랩 설정 파일만 있으면 나머지 설정은 동적으로 구성
- 엔보이는 동적 설정에 다음과 같은 API를 사용
- Listener discovery service (LDS): 엔보이가 자신이 어떤 리스너를 노출해야 한느지 쿼리할 수 있게 하는 API
- Route discovery service (RDS): 리스너 설정의 일부로, 사용할 루트를 지정한다. 정적 설정이나 동적 설정을 사용할 때 LDS의 부분집합
- Cluster discovery service (CDS): 엔보이가 클러스터 목록과 각 클러스터용 설정을 찾을 수 있는 API
- Endpoint discovery service (EDS): 클러스터 설정의 일부로, 특정 클러스터에 어떤 엔드포인트를 사용해야 하는지 지정한다. CDS의 부분집합
- Secret discovery service (SDS): 인증서를 배부하는 데 사용하는 API
- Aggregate discovery service (ADS): 나머지 API에 대한 모든 변경 사항을 직렬화된 스트림으로 제공한다. 이 API 하나로 모든 변경 사항을 순차적으로 가져올 수 있음
- 이 API들을 통틀어 xDS 서비스라고 부름
- 엔보이는 동적 설정에 다음과 같은 API를 사용
dynamic_resources:
lds_config: # Configuration for listeners (LDS) 리스너용 설정
api_config_source:
api_type: GRPC
grpc_services:
- envoy_grpc: # Go to this cluster for the listener API. 이 클러스터로 이동해 리스너 API를 확인하자
cluster_name: xds_cluster
clusters:
- name: xds_cluster # gRPC cluster that implements LDS. LDS를 구현하는 gRPC 클러스터
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
hosts: [{ socket_address: {
address: 127.0.0.3, port_value: 5678 }}]
기본 실습
- 엔보이는 C++로 작성돼 플랫폼에 맞게 컴파일
- 엔보이를 시작하기 가장 좋은 방법은 도커를 사용해 컨테이너를 실행하는 것
- 도커 이미지 가져오기 : citizenstig/httpbin 는 arm CPU 미지원
# 도커 이미지 가져오기
docker pull envoyproxy/envoy:v1.19.0
docker pull curlimages/curl
docker pull mccutchen/go-httpbin
docker pull citizenstig/httpbin
# 확인
docker images
- httpbin은 호출하는 엔드포인트에 따라 호출할 때 사용한 헤더를 반환하거나, HTTP 요청을 지연시키거나, 오류를 발생시키는 등의 서비스를 제공
- http://httpbin.org/headers 로 이동
- httpbin 서비스를 시작하면, 그 다음에는 엔보이를 시작하고 모든 트래픽이 httpbin 서비스로 가도록 프록시를 설정
- 클라이언트 앱을 시작해 프록시를 호출할 것
- httpbin 서비스 실행
/headers 엔드포인트를 호출하는데 사용한 헤더가 함께 반환
# 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" ] } }
- 엔보이 프록시를 실행
help 를 전달해 플래그와 명령줄 파라미터 중 일부 확인
# docker run -it --rm envoyproxy/envoy:v1.19.0 envoy --help ... --service-zone <string> # 프록시를 배포할 가용 영역을 지정 Zone name --service-node <string> # 프록시에 고유한 이름 부여 Node name ... -c <string>, --config-path <string> # 설정 파일을 전달 Path to configuration file
- 엔보이 실행 : 프록시를 실행하려고 했지만 유효한 설정 파일을 전달 X
# docker run -it --rm envoyproxy/envoy:v1.19.0 envoy [2025-04-21 18:21:39.356][1][info][main] [source/server/server.cc:342] envoy.dubbo_proxy.serializers: dubbo.hessian2 [2025-04-21 18:21:39.356][1][info][main] [source/server/server.cc:342] envoy.access_loggers: envoy.access_loggers.file, envoy.access_loggers.http_grpc, envoy.access_loggers.open_telemetry, envoy.access_loggers.stderr, envoy.access_loggers.stdout, envoy.access_loggers.tcp_grpc, envoy.access_loggers.wasm, envoy.file_access_log, envoy.http_grpc_access_log, envoy.open_telemetry_access_log, envoy.stderr_access_log, envoy.stdout_access_log, envoy.tcp_grpc_access_log, envoy.wasm_access_log [2025-04-21 18:21:39.356][1][info][main] [source/server/server.cc:342] envoy.filters.udp_listener: envoy.filters.udp.dns_filter, envoy.filters.udp_listener.udp_proxy [2025-04-21 18:21:39.356][1][info][main] [source/server/server.cc:342] envoy.retry_host_predicates: envoy.retry_host_predicates.omit_canary_hosts, envoy.retry_host_predicates.omit_host_metadata, envoy.retry_host_predicates.previous_hosts [2025-04-21 18:21:39.358][1][critical][main] [source/server/server.cc:112] error initializing configuration '': At least one of --config-path or --config-yaml or Options::configProto() should be non-empty [2025-04-21 18:21:39.358][1][info][main] [source/server/server.cc:855] exiting At least one of --config-path or --config-yaml or Options::configProto() should be non-empty
- 수정하고 앞서 봤던 예제 설정 파일을 전달
기본적으로 15001 포트에 단일 리스너를 노출하고 모든 트래픽을 httpbin 클러스터로 라우팅
admin: address: socket_address: { address: 0.0.0.0, port_value: 15000 } 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
- 엔보이 재시작
# cat ch3/simple.yaml # 터미널1 docker run --name proxy --link httpbin envoyproxy/envoy:v1.19.0 --config-yaml "$(cat ch3/simple.yaml)" # 터미널2 docker logs proxy [2025-04-12 08:25:15.455][1][info][config] [source/server/listener_manager_impl.cc:834] all dependencies initialized. starting workers [2025-04-12 08:25:15.456][1][info][main] [source/server/server.cc:804] starting main dispatch loo
- curl 로 프록시를 호출
- 프록시를 호출했는데도 트래픽이 httpbin 서비스로 정확하게 전송
- 새로운 헤더도 추가됐다.
- X-Envoy-Expected-Rq-Timeout-Ms
- X-Request-Id
# 터미널2 docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15001/headers { "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" ] } }
- 예상 요청 타임아웃을 1초로 설정
- 라우팅 규칙을 업데이트
- simple_change_timeout.yaml
- match: { prefix: "/" }
route:
auto_host_rewrite: true
cluster: httpbin_service
timeout: 1s
#
#docker run -p 15000:15000 --name proxy --link httpbin envoyproxy/envoy:v1.19.0 --config-yaml "$(cat ch3/simple_change_timeout.yaml)"
cat ch3/simple_change_timeout.yaml
docker run --name proxy --link httpbin envoyproxy/envoy:v1.19.0 --config-yaml "$(cat ch3/simple_change_timeout.yaml)"
docker ps
# 타임아웃 설정 변경 확인
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15001/headers
{
"headers": {
"Accept": [
"*/*"
],
"Host": [
"httpbin"
],
"User-Agent": [
"curl/8.13.0"
],
"X-Envoy-Expected-Rq-Timeout-Ms": [
"1000" 1000ms초 = 1초
],
"X-Forwarded-Proto": [
"http"
],
"X-Request-Id": [
"dbff822d-17df-4d8c-bd4d-9c9d6f890cff"
]
}
}
# 추가 테스트 : Envoy Admin API(TCP 15000) 를 통해 delay 설정
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:15001/delay/0.5
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15001/delay/1
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15001/delay/2
upstream request timeout
- Envoy’s Admin API
#
docker run --name proxy --link httpbin envoyproxy/envoy:v1.19.0 --config-yaml "$(cat ch3/simple_change_timeout.yaml)"
# 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 # 엔보이 통계(프로메테우스 레코드 형식)
- Envoy request retries 요청 재시도
- httpbin 요청을 일부러 실패시켜 엔보이가 어떻게 요청을 자동으로 재시작하는지 살펴보자.
- 먼저 retry_policy 를 사용하도록 설정 파일을 업데이트
- match: { prefix: "/" }
route:
auto_host_rewrite: true
cluster: httpbin_service
retry_policy:
retry_on: 5xx # 5xx 일때 재시도
num_retries: 3 # 재시도 횟수
#
docker rm -f proxy
#
cat ch3/simple_retry.yaml
docker run -p 15000:15000 --name proxy --link httpbin envoyproxy/envoy:v1.19.0 --config-yaml "$(cat ch3/simple_retry.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
...
- 엔보이는 업스트림 클러스터 httpbin 호출할 때 HTTP 500 응답을 받았다.
- 엔보이는 요청을 재시도했으며, 이는 통계값에 cluster.httpbin_service.upstream_rq_retry: 3 으로 표시
728x90
'2025_Istio Hands-on Study' 카테고리의 다른 글
2주차 - Envoy, Isto Gateway(3) (0) | 2025.04.22 |
---|---|
2주차 - Envoy, Isto Gateway(2) (0) | 2025.04.22 |
1주차 - Istio 소개, 첫걸음 (2) (0) | 2025.04.12 |
1주차 - Istio 소개, 첫걸음 (1) (0) | 2025.04.12 |