Post

Envoy

Envoy 살펴보기

💡 KANS 스터디
CloudNet에서 주관하는 istio(Istio Hands-on Study) 1기 스터디입니다. 아래의 글은 스터디의 내용을 기반으로 작성했습니다.

스터디에 관심이 있으신 분은 CloudNet Blog를 참고해주세요.

들어가며

이번 주차에는 istio에서 프록시로 사용하는 엔보이의 구성방식에 대해 자세히 알아보고 Istio Gatway에 대해 자세히 알아볼 예정이다.

Envoy란

엔보이는 리프트가 개발한 프록시 프로젝트로, 2017년 9월에 CNCF에 합류했다. 엔보이의 주요 목표 중에 하나가 성능이었기에 C++로 작성되었고 특히 높은 부하에서도 안정적인 성능을 만드는 것이 목표였다고한다.

구성

  • 리스너(Listeners)
    • 애플리케이션이 연결할 수 있는 외부 세계로 포트를 노출한다. ex) 80포트로 들어오면 → A 라우팅 적용
  • 라우트(Routes)
    • 리스너로 들어오는 트래픽을 처리하는 라우팅 규칙
    • ex) 요청이 들어오고 /catalog 에 일치하면 그 트래픽을 catalog 클러스터로 보내는 식이다.
  • 클러스터(Cluster)
    • 엔보이가 트래픽을 라우팅할 수 있는 특정 업스트림 서비스, 예를 들어 catalog-v1 과 catalog-v2 는 별도 클러스터일 수 있고,
    • 루트는 catalog 서비스의 v1이나 v2로 트래픽을 보내는 방법에 대한 규칙을 지정할 수 있다.

핵심 기능

엔보이는 분산 시스템을 구축할 때 발생하는 어려운 애플리케이션 네트워킹 문제를 해결하기 위해 애플리케이션 외부에서 아래와 같은 기능을 제공한다.

  • 서비스 디스커버리
    • 애플리케이션이 애플리케이션 수준의 동작에 사용할 수 있는 프록시
    • 런타임 제어를 위해 동적 API 사용
  • 네트워크 복원력
  • Observability
  • 기본적인 프록시 기능(로드 밸런싱, 트래픽 및 라우팅, 트래픽 전환 및 새도잉 기능)

Envoy in action

환경 구성

실습을 위해선 Docker, Kind(kubernetes in docker) 설치가 필요합니다.

setup

istio in actions 코드 복사

1
2
git clone https://github.com/AcornPublishing/istio-in-action
cd istio-in-action/book-source-code-master

envoy 등 실습에 필요한 이미지 셋업

1
2
3
docker pull envoyproxy/envoy:v1.19.0
docker pull curlimages/curl # 클라이언트 역할
docker pull mccutchen/go-httpbin # 애플리케이션 역할

확인하면 아래와 같이 가져온 3개의 이미지를 볼 수 있다.

1
2
3
4
5
docker image ls
REPOSITORY             TAG       IMAGE ID       CREATED       SIZE
curlimages/curl        latest    e507f3e43db3   13 days ago   21.9MB
mccutchen/go-httpbin   latest    18fc7a0469d6   2 weeks ago   38.1MB
envoyproxy/envoy       v1.19.0   f48f130ac643   3 years ago   134MB

서비스 실행

이제 애플리케이션 역할을 담당하는 go-httpbin 서비스를 8000포트로 실행한다.

실행 후 확인하면 아래와 같은 결과가 나와야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker run -it --rm --link httpbin curlimages/curl curl -X GET http://httpbin:8000/headers
{
  "headers": {
    "Accept": [
      "*/*"
    ],
    "Host": [
      "httpbin:8000"
    ],
    "User-Agent": [
      "curl/8.13.0"
    ]
  }
}

envoy 배포

기존 istio in actions의 코드 중 설정파일을 아래와 같이 조금 수정한다.(ch3/simple.yaml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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

배포: 아래의 명령어를 통해 엔보이를 실행한다.

1
docker run --name proxy --link httpbin envoyproxy/envoy:v1.19.0 --config-yaml "$(cat ch3/simple.yaml)"

컨테이너를 잘올라온 것을 확인할 수 있고,

1
2
3
4
[root@ip-172-31-7-43 istio-in-action]# docker ps
CONTAINER ID   IMAGE                      COMMAND                  CREATED          STATUS          PORTS       NAMES
5df6f9b5e6db   envoyproxy/envoy:v1.19.0   "/docker-entrypoint.…"   5 minutes ago    Up 5 minutes    10000/tcp   proxy
37b25e0885d0   mccutchen/go-httpbin       "/bin/go-httpbin"        17 minutes ago   Up 17 minutes   8080/tcp    httpbin

curl로 프록시를 호출해도 httpbin 서비스로 잘 라우팅되는 것을 확인할 수 있다.

1
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15001/headers

image.png

envoy 기능 사용해보기

timout

아래의 static_resources.listeners 쪽 route 규칙에 타임아웃을 1초로 설정한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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
                  timeout: 1s
  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

이제 프록시에 접근하여 확인해보면, 아래와 같이 X-Envoy-Expected-Rq-Timeout-Ms 정보를 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@ip-172-31-7-43 istio-in-action]# 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"
    ],
    "X-Forwarded-Proto": [
      "http"
    ],
    "X-Request-Id": [
      "7e634235-ba15-4ead-b708-864a55f3d66e"
    ]
  }
}

실제 httpbin 서비스의 의도적인 딜레이된 요청을 호출해보자

아래의 명령어에서 delay/{시간(s)} 방식으로 딜레이를 설정할 수 있다.

1
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15001/delay/0.5

결과 확인하기

0.5초일 때는 정상적으로 응답을 받지만, 1초를 넘는 순간 timeout이 발생한 것을 볼 수 있다.

image.png

retry

아래의 static_resources.listeners 쪽 route 규칙에 retry 정책을 설정한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
                  retry_policy:
                    retry_on: 5xx  # 5xx 일때 재시도
                    num_retries: 3 # 재시도 횟수
  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

아래의 명령어를 통해 500에러 응답을 반환하는 서비스 경로로 요청한다.

1
docker run -it --rm --link proxy curlimages/curl curl -X GET http://proxy:15001/status/500

엔보이는 5xx 응답에대해 재시도를 3번 시도한다.

필자는 위의 명령어를 3번 실행(3번의 요청)을 진행했기에 아래와 같이 상태를 확인하면 retry 횟수가 9번임을 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@ip-172-31-7-43 book-source-code-master]# 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.upstream_rq_500: 9
cluster.httpbin_service.retry.upstream_rq_5xx: 9
cluster.httpbin_service.retry.upstream_rq_completed: 9
cluster.httpbin_service.retry_or_shadow_abandoned: 0
cluster.httpbin_service.upstream_rq_retry: 9
cluster.httpbin_service.upstream_rq_retry_backoff_exponential: 9
cluster.httpbin_service.upstream_rq_retry_backoff_ratelimited: 0
cluster.httpbin_service.upstream_rq_retry_limit_exceeded: 3
cluster.httpbin_service.upstream_rq_retry_overflow: 0
cluster.httpbin_service.upstream_rq_retry_success: 0
vhost.httpbin_local_service.vcluster.other.upstream_rq_retry: 0
vhost.httpbin_local_service.vcluster.other.upstream_rq_retry_limit_exceeded: 0
vhost.httpbin_local_service.vcluster.other.upstream_rq_retry_overflow: 0
vhost.httpbin_local_service.vcluster.other.upstream_rq_retry_success: 0

마치며

실제 엔보이 프록시를 docker로 띄어 여러 기능을 사용해봤다. 엔보이의 유연한 L7기능(애플리케이션 수준)과 복원성 덕분에 istio에서 프록시로 사용하고 있다. 특히 API를 통해 재실행없이 동적으로 설정 변경이 가능하다는 것도 큰 장점인 것 같다. 다음 편에서는 istio gateway에 대해 알아볼 예정이다.

This post is licensed under CC BY 4.0 by the author.