ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [서비스메시] Istio, Envoy
    DevOps, 클라우드/Service Mesh 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를 공부해보기 시작했다.

     

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

    댓글

Designed by Tistory.