DevOps, 클라우드/Service Mesh

[서비스메시] Istio, Envoy

Floodnut 2024. 7. 13. 18:24

서비스 메시, 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를 공부해보기 시작했다.

 

간단한 개념과 프록시 설정법만 확인해봤는데 버전 별 스펙이 다른데 설정할 값도 많아서 좀 신경써서 구축해야겠다 싶다.