[서비스메시] Istio, Envoy
서비스 메시, Istio와 Envoy
서비스 메시는 주로 분산 아키텍처에서 각 소프트웨어 컴포넌트 간의 네트워킹을 관리한다.
- 특히 쿠버네티스 환경에서 마이크로서비스 간의 상호 연결을 관리하고, 그에 대한 가시성을 제공한다.
- 기본 쿠버네티스 기능만으로는 어렵다.
- 서비스 메시를 사용하면 네트워크 트래픽을 경유하는 다양한 로직을 구성할 수 있다.
- 이를 통해 원격 측정, 보안, 트래픽 관리 등 여러 기능을 구현 가능하다.
Istio는 각 파드에 프록시 컨테이너를 주입하여 네트워크 요청을 관리한다.
- 프록시는 네트워크 호출을 가로채어 서비스 메시의 로직을 적용하고, 이를 대상 컨테이너로 전달한다.
- 이 과정에서 Istio는 원격 측정 데이터를 수집하고, 호출 체인을 추적한다.
Istio의 구성 요소는 크게 데이터 플레인과 컨트롤 플레인으로 나뉜다.
- 데이터 플레인은 각 파드에 주입되는 프록시를 포함하며, 컨트롤 플레인은 Istio 시스템 자체를 실행하는 파드들로 구성된다.
- 최신 버전의 Istio에서는 Istio Daemon 파드가 대부분의 기능 담당한다.
Istio는 여러 서비스 메시 구현 중 하나로, 프록시를 이용하여 서비스 간 통신을 관리하는 역할을 한다.
- Istio는 Envoy 프록시를 기본 사이드카 컨테이너로서 구현하여, 특정 컨테이너가 클러스터 내 다른 컨테이너를 호출할 때 이를 프록시를 통해 라우팅한다.
- Istio는 Kubernetes의 커스텀 리소스 정의를 통해 Envoy를 설정하고 관리하며, 사용자는 일반적인 Kubernetes YAML을 사용하여 Istio를 구성할 수 있다.
- Istio를 사용하는 주된 이유는, Envoy를 직접 다루는 것보다 훨씬 편리하고 추상화된 서비스 메시 기능을 제공하기 때문이다.
Envoy Demo 구성 파일
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 9901 # Admin Endpoint Port
static_resources:
listeners:
- name: listener_0
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 10000 # 임의의 Envoy Listener Port(http)
filter_chains:
- filters:
- name: envoy.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
stat_prefix: ingress_http
route_config:
name: api_and_internal_split_routing
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match:
prefix: "/api"
route:
cluster: service_api
prefix_rewrite: "/"
host_rewrite: "www.google.com"
http_filters:
- name: envoy.router
clusters: # grouping 된 타겟, K8s Cluster가 아님
- name: service_api
connect_timeout: 10s
type: STRICT_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: service_api
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: www.google.com
port_value: 443
tls_context:
sni: www.google.com
- envoy 내장 필터 체인으로 envoy.http_connection_manager 를 사용한다.
- 이 필터 체인의 proto 스키마는 다음과 같다.
envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
- 해당 필터는 envoy 내장 TCP 필터이며 http에 대한 스펙을 정의하고 있다.
- 가상 호스트로 prefix-matching을 사용하고 있으며 reverse-proxy로 들어온 요청을 rewrite 하고 있다.
- 그래서 위 리버스 프록시에서 /api 로 접근하는 http 요청에 대해서 호스트를 www.google.com으로 라우팅해준다.
- 어드민 페이지는 요렇게 정의한 9901 포트로 접근해 확인할 수 있다.
Envoy v2와 v3 스펙 차이
1.16.x 버전까지의 envoy는 필터 체인에서 v2의 내장 tcp(http) 스키마를 사용할 수 있다.
- 데모에서 제공하는 http_connection_manager 가 v2의 구현체다.
- 근데, 1.17 부터는 v3를 extension 형태로 지원한다.
v2 정적 리소스 스펙
{
"codec_type": "...",
"stat_prefix": "...",
"rds": "{...}",
"route_config": "{...}",
"scoped_routes": "{...}",
"http_filters": [],
"add_user_agent": "{...}",
"tracing": "{...}",
"common_http_protocol_options": "{...}",
"http_protocol_options": "{...}",
"http2_protocol_options": "{...}",
"server_name": "...",
"server_header_transformation": "...",
"max_request_headers_kb": "{...}",
"idle_timeout": "{...}",
"stream_idle_timeout": "{...}",
"request_timeout": "{...}",
"drain_timeout": "{...}",
"delayed_close_timeout": "{...}",
"access_log": [],
"use_remote_address": "{...}",
"xff_num_trusted_hops": "...",
"internal_address_config": "{...}",
"skip_xff_append": "...",
"via": "...",
"generate_request_id": "{...}",
"preserve_external_request_id": "...",
"forward_client_cert_details": "...",
"set_current_client_cert_details": "{...}",
"proxy_100_continue": "...",
"upgrade_configs": [],
"normalize_path": "{...}",
"merge_slashes": "...",
"request_id_extension": "{...}"
}
v3 정적 리소스 스펙
{
"codec_type": ...,
"stat_prefix": ...,
"rds": {...},
"route_config": {...},
"scoped_routes": {...},
"http_filters": [],
"add_user_agent": {...},
"tracing": {...},
"common_http_protocol_options": {...},
"http_protocol_options": {...},
"http2_protocol_options": {...},
"server_name": ...,
"server_header_transformation": ...,
"scheme_header_transformation": {...},
"max_request_headers_kb": {...},
"stream_idle_timeout": {...},
"request_timeout": {...},
"request_headers_timeout": {...},
"drain_timeout": {...},
"delayed_close_timeout": {...},
"access_log": [],
"access_log_flush_interval": {...},
"flush_access_log_on_new_request": ...,
"access_log_options": {...},
"use_remote_address": {...},
"xff_num_trusted_hops": ...,
"original_ip_detection_extensions": [],
"early_header_mutation_extensions": [],
"internal_address_config": {...},
"skip_xff_append": ...,
"via": ...,
"generate_request_id": {...},
"preserve_external_request_id": ...,
"always_set_request_id_in_response": ...,
"forward_client_cert_details": ...,
"set_current_client_cert_details": {...},
"proxy_100_continue": ...,
"upgrade_configs": [],
"normalize_path": {...},
"merge_slashes": ...,
"path_with_escaped_slashes_action": ...,
"request_id_extension": {...},
"local_reply_config": {...},
"strip_matching_host_port": ...,
"strip_any_host_port": ...,
"stream_error_on_invalid_http_message": {...},
"strip_trailing_host_dot": ...,
"proxy_status_config": {...},
"append_x_forwarded_port": ...,
"append_local_overload": ...,
"add_proxy_protocol_connection_state": {...}
}
v2 http 라우터와 v3 http 라우터에는 차이가 존재한다.
우선 공식 docs에서 제공하는 v2 라우팅 스펙과 v3 라우팅 스펙를 보면 필터 설정에 대한 차이를 볼 수 있다.
v2: envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
v3: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
typed_config:
"@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
stat_prefix: ingress_http
route_config:
name: api_and_internal_split_routing
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match:
prefix: "/api"
route:
cluster: service_api
prefix_rewrite: "/"
host_rewrite: "www.google.com"
앞서 작성한 demo config에서의 차이를 보자.
v2 route-config에서는 host_rewite 를 통해서 라우팅할 호스트를 지정하고 있다.
이에 대한 공식 docs를 보면 다음과 같은 설명이 있다.
host_rewrite
(string) Indicates that during forwarding, the host header will be swapped with this value. Only one of host_rewrite, auto_host_rewrite, auto_host_rewrite_header may be set.
그니까… host_rewrite, auto_host_rewrite, auto_host_rewrite_header 중 하나의 필드를 통해서 호스트를 필수적으로 지정해야 한다.
그런데 v3 route-config에서는 host_rewrite 를 지원하지 않는다.
host_rewrite를 대체 할 수 있는 필드는 host_rewrite_literal, auto_host_rewrite, host_rewrite_header, host_rewrite_path_regex 뿐이다.
host_rewrite_literal
(string) Indicates that during forwarding, the host header will be swapped with this value. Using this option will append the x-forwarded-host header if append_x_forwarded_host is set. Only one of host_rewrite_literal, auto_host_rewrite, host_rewrite_header, host_rewrite_path_regex may be set.
라우팅 스펙이 변경되면서 내장된 필터 스펙도 변경된다.
우리는 아래의 처럼 envoy의 내장 router를 바로 사용했었다.
그렇지만, v3에서는 filter에 내장된 tcp 라우터 중 http 라우터를 선택하고 그 구현체를 직접 정의해야 한다.
# v2
http_filters:
- name: envoy.router
# v3
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
클러스터 스펙도 함께 확인해보자.
- 마찬가지로 tls_contex 에서 단순하게 Server Name Indication만 정의했던 v2다.
- 그렇지만, v3에서는 transport_socket 레벨에서 그 구현체를 정의하도록 변경되었다.
# v2
clusters: # grouping 된 타겟, K8s Cluster가 아님
- name: service_api
connect_timeout: 10s
type: STRICT_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: service_api
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: www.google.com
port_value: 443
tls_context:
sni: www.google.com
# v3
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: www.google.com
결론
컨테이너 오케스트레이션을 접하다보면 늘 서비스 디스커버리, 서비스 메시, 프록시가 함께 따라다니더라.
이번에 서비스 메시가 좀 궁금해져서 제일 많이 노출되는 Istio를 공부해보기 시작했다.
간단한 개념과 프록시 설정법만 확인해봤는데 버전 별 스펙이 다른데 설정할 값도 많아서 좀 신경써서 구축해야겠다 싶다.