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
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이 발생한 것을 볼 수 있다.
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에 대해 알아볼 예정이다.