이번 튜토리얼은 Envoy 공식문서의 QuickStart를 쭉 따라해봤다.
Envoy 설치
각 운영체제 별로 설치방법은 공식문서에서 참조하기바란다.
나는 대표적으로 ubuntu-22.04인 jammy version으로 작성한다.
wget -O- https://apt.envoyproxy.io/signing.key | sudo gpg --dearmor -o /etc/apt/keyrings/envoy-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/envoy-keyring.gpg] https://apt.envoyproxy.io jammy main" | sudo tee /etc/apt/sources.list.d/envoy.list
sudo apt-get update
sudo apt-get install envoy
Envoy 설치확인
https://www.envoyproxy.io/docs/envoy/v1.32.0/start/quick-start/run-envoy
Envoy가 올바르게 설치되었는지 확인하기 위해서 version 명령어를 입력해보자.
envoy --version
demo-config 설정
잘 실행된다면 공식문서에서 제공하는 demo-config를 설정해보자.
일단 envoy-demo.yaml은 다음과 같다. 자세한 내용은 아래에 차근차근 설명하고 있다.
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 10000
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
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
host_rewrite_literal: www.envoyproxy.io
cluster: service_envoyproxy_io
clusters:
- name: service_envoyproxy_io
type: LOGICAL_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: service_envoyproxy_io
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: www.envoyproxy.io
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: www.envoyproxy.io
-c 혹은 --config-path 옵션을 통해서 configuration을 적용할 수 있다.
envoy -c envoy-demo.yaml
체크
기존 config에서 envoy listener로 10000번 포트를 할당했기 때문에 해당 포트를 호출하면 envoy 홈이 등장한다.
저는 공인 IP를 붙여서 접속도 해보았는데 이처럼 홈페이지가 등장한ㄷ.
curl -v localhost:10000
config overriding
기존 config 정보에 특정 부분만 덮어써서 반영하고 싶다면 --config-yaml 옵션을 사용하면된다.
예시파일(envoy-override.yaml)
해당 파일을 보면 localhost(127.0.0.1)의 9902 포트에 admin을 할당한다.
admin:
address:
socket_address:
address: 127.0.0.1
port_value: 9902
만약 도커로 설치했다면 address는 0.0.0.0으로 오픈해야한다.
override를 진행한다.
envoy -c envoy-demo.yaml --config-yaml "$(cat envoy-override.yaml)"
확인해보자.
curl -v localhost:9902
이렇게 admin 페이지를 할당할 수 있다.
Config 유효성 검사
validate 명령은 envoy의 서비스 적용이나 인터넷 연결 혹은 재시작 없이 config의 유효성을 검사한다.
--mode 옵션에서 validate 를 사용하고 config 파일의 경로를 입력한다.
envoy --mode validate -c <config yaml 파일명>
만약 이상이 없다면 'OK'와 함께 코드0, 부적격시 에러와 함께 코드 1을 반환한다.
Envoy 로깅
envoy의 기본 로그 경로는 /dev/stderr에 위치한다.
만약 새로운 경로를 지정하려면 --log-path 옵션을 사용한다.
mkdir logs
envoy -c envoy-demo.yaml --log-path logs/custom.log
Envoy 네트워킹
기본적으로 IPv4, IPv6를 모두 지원한다. 만약 사용하는 환경이 IPv6가 불가능한 환경이면 반드시 IPv6를 비활성화 할 것을 권장한다. 그 설정은 cluster config에서 dns_lookup_family의 값을 V4_ONLY로 해서 설정할 수 있다.
기존에 demo-config에 해당 내용이 동일하게 들어있다.
clusters:
- name: service_envoyproxy_io
type: LOGICAL_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: service_envoyproxy_io
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
Envoy 디버깅
Envoy를 디버깅 하기 위해서 로그레벨을 변경할 때는 -l or --log-level 옵션을 이용하면 된다.
다음과같이 총 7가지 레벨을 제공한다. default는 info이다.
-trace
-debug
-info (기본)
-warning/warn
-error
-critical
-off
또한 --component-log-level 옵션을 통해서 각 컴포넌트 별로 로그레벨을 설정할 수 있는데, 아래 예시는 upstream에는 debug레벨, connection은 trace 레벨, 나머지는 모두 설정이 없으므로 info 레벨로 표시되는 예시이다.
envoy -c envoy-demo.yaml -l off --component-log-level upstream:debug,connection:trace
이제 실질적으로 config 설정을 해보자
Static : configuration
https://www.envoyproxy.io/docs/envoy/v1.32.0/start/quick-start/configuration-static
static configuration vs dynamic
Static(정적) 설정은 말 그대로 변경사항을 적용하기위해 envoy를 재시작해야한다.
dynamic(동적)은 말 그대로 파일이나 api 요청을 통해 그대로 설정 값을 적용하는 즉시 적용된다.(별도의 재시작이 필요없다)
envoy의 static 설정에는 listeners와 static_resources를 가진 clusters로 설정한다.
(원하면 admin을 추가로 붙여 모니터링 할 수 있다)
static_resources
정적 설정을 할 경우 최상단에 이와 같이 정적 리소스를 정의하고 그 속에 listeners와 clusters를 둔다.
static_resources:
listeners:
Listeners
listeners의 포트는 10000입니다. address를 현재는 0.0.0.0으로 any open인데 원하는 아이피를 통제할 수 있습니다.
일반적으로 root path로 접근하면 www.envoyproxy.io로 rewrite되는 설정임을 직관적으로도 이해할 수 있습니다.
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 10000
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
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
host_rewrite_literal: www.envoyproxy.io
cluster: service_envoyproxy_io
clusters:
- name: service_envoyproxy_io
clusters
clusters는 www.envoyproxy.io 도메인에 443 포트로 전달합니다.
route:
host_rewrite_literal: www.envoyproxy.io
cluster: service_envoyproxy_io
clusters:
- name: service_envoyproxy_io
type: LOGICAL_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: service_envoyproxy_io
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: www.envoyproxy.io
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: www.envoyproxy.io
파일시스템에 의한 동적 configuration
https://www.envoyproxy.io/docs/envoy/v1.32.0/start/quick-start/configuration-dynamic-filesystem
파일 시스템에 의한 동적 설정은 xDS 프로토콜을 사용한다. 동적 설정이란 파일 시스템에서 파일이 변경되면 envoy가 이를 자동으로 식별하고 config를 반영한다.
참고로 해당 config 파일을 단순 edit 시에는 반영하지못하며, 파일의 이동(생성, 삭제, 이동) 등이 필요로 한다.
여기에는 2가지 설정을 해야 한다.
- node : 고유한 프록시 노드 식별
- dynamic_resources : Envoy가 동적 configuration의 위치를 확인하기 위한 곳
envoy-dynamic-filesystem-demo.yaml 파일에 대해서 제대로 확인해보자.
# envoy-dynamic-filesystem-demo.yaml
node:
cluster: test-cluster
id: test-id
dynamic_resources:
cds_config:
path: /var/lib/envoy/cds.yaml
lds_config:
path: /var/lib/envoy/lds.yaml
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 19000
node
node에는 cluster와 id가 모두 특정되어야 한다.
node:
cluster: test-cluster
id: test-id
dynamic_resources
xDS 설정의 각각 위치를 설정한다.
dynamic_resources:
cds_config:
path: /var/lib/envoy/cds.yaml
lds_config:
path: /var/lib/envoy/lds.yaml
resources - listeners
리스너 설정은 lds(Listener Discovery Service)인 lds_config를 통해서 연결됩니다.
우리가 중점으로 봐야할 것은 address.socket_address로 아까 리스닝할 ip와 port 입니다. 이것을 통해서 리스너를 설정합니다.
또한 아래 route_config를 통해서 domain과 prefix 등을 지정하고 host_rewrite_literal로 rewrite 라우팅 경로를 지정할 수 있습니다.
# envoy-dynamic-lds-demo.yaml
resources:
- "@type": type.googleapis.com/envoy.config.listener.v3.Listener
name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.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.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
route:
host_rewrite_literal: www.envoyproxy.io
cluster: example_proxy_cluster
resources - clusters
cluster 설정은 CDS(Cluster Discovery Service)의 cds_config로 지정할 수 있습니다.
아래 예시에서는 example_proxy_cluster라는 클러스터에서 endpoint로 envoyproxy.io를 프록시 하는 것을 확인할 수 있습니다.
# envoy-dynamic-cds-demo.yaml
resources:
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
name: example_proxy_cluster
type: STRICT_DNS
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options: {}
load_assignment:
cluster_name: example_proxy_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: www.envoyproxy.io
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: www.envoyproxy.io
이제 실제 동적 config를 확인하기 위해서 적용해보자.
# envoy-dynamic-filesystem-demo.yaml
node:
cluster: example_proxy_cluster
id: test-id
dynamic_resources:
cds_config:
path: /var/lib/envoy/cds.yaml
lds_config:
path: /var/lib/envoy/lds.yaml
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 19000
# /var/lib/envoy/lds.yaml
resources:
- "@type": type.googleapis.com/envoy.config.listener.v3.Listener
name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.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.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
route:
host_rewrite_literal: www.envoyproxy.io
cluster: example_proxy_cluster
# /var/lib/envoy/cds.yaml
resources:
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
name: example_proxy_cluster
type: STRICT_DNS
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options: {}
load_assignment:
cluster_name: example_proxy_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: www.envoyproxy.io
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: www.envoyproxy.io
해당 설정을 모두 마치고 접속해보면 아마 다음과 같은 오류가 발생할 것이다.
이는 dns_lookup_family: V4_ONLY를 넣지 않아서 그런 것인데, 만약 해당 옵션이 없어도 잘 되는 IPv6가 사용가능한 경우라면 포트를 변경하는 방식으로 실습해보기 바란다.
따라서 우리는 config를 수정하겠다. /var/lib/envoy/cds.yaml에 해당 설정을 넣는다.
# /var/lib/envoy/cds.yaml
resources:
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
name: example_proxy_cluster
type: STRICT_DNS
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options: {}
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: example_proxy_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: www.envoyproxy.io
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: www.envoyproxy.io
해당 설정을 넣은 후에 cds.yaml 파일을 단순히 edit하는 것으로는 설정이 안바뀌므로 파일 이름을 변경하도록 한다.
특히 만약 config 이력관리를 하기위해서 기존 cds 이름을 변경한 후 신규 cds를 넣는 것으로 하자.
mv cds.yaml cds-241020.yaml
touch cds.yaml
그리고 위에서 본 cds.yaml을 다시 넣는다.
그럼 정상적으로 envoy 사이트로 포워드 되는 것을 확인할 수 있다.
Envoy Admin interface
https://www.envoyproxy.io/docs/envoy/v1.32.0/start/quick-start/admin
다음은 envoy에서 제공하는 관리자 인터페이스에 대한 조작이다.
관리자 인터페이스는 config 설정, 통계, 서버 동작 변경, 특정 필터 규칙에 따른 트래픽 변경 등을 UI에서 쉽게 확인하고 진행할 수 있도록 만들어진 기능이다.
관리자 엔드포인트를 생성하는 것은 앞서 살펴본 것이므로 간략하게 소개하고 넘어간다.
admin
해당 설정에서는 당연하지만 address를 제한하는 것이 좋다. 예시에서는 0.0.0.0을 사용하는데 해당 액세스를 제한하도록 하는 것을 권장하고 19000포트를 사용하는 예시이다.
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 19000
stat_prefix
Envoy의 HttpConnectionManager는 반드시 stat_prefix에서 설정한다.
아래 예시는 ingress_http를 stat_prefix로 사용하는 리스너 설정이다.
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 10000
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
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
Admin endpoint : config_dump
config_dump 엔드포인트는 Envoy의 런타임 설정을 json 형식으로 볼 수 있다. 현재 로드된 여러 설정들의 정보를 확인할 수 있기 때문에 현재 설정 값을 확인하거나 오류를 식별하는데 유리하다.
l -s http://localhost:19000/config_dump | jq -r '.configs[] | .["@type"]'
예를 들어 dynamic_listener의 socket_address 정보를 확인하고 싶으면 다음과 같이 쿼리하면 된다.
curl -s http://localhost:9901/config_dump?resource=dynamic_listeners | jq '.configs[0].active_state.listener.address'
Admin endpoint : stats
통계 부분인데 envoy의 런타임 정보나 통계 정보 등을 admin을 통해 확인할 수 있다.
사용가능한 통계의 카테고리는 다음 명령어를 통해 조회할 수 있다.
curl -s http://localhost:19000/stats | cut -d. -f1 | sort | uniq
통계 엔드포인트는 다음과 같이 정규식 표현을 허용하고 있다.
curl -s http://localhost:19000/stats?filter='^http\.ingress_http'
Web UI
앞서 살펴본 바와 같이 해당 포트를 웹에서 접근하면 Web UI로 쉽고 간단하게 확인할 수 있다.
우리가 cli로 쿼리해도되지만 아까 확인한 정보들을 사실 웹 UI에서 간편하게 확인할 수 있다.
보면 stats에서 프로메테우스 형식의 log도 제공한다.
실제로 눌러서 확인도 가능하고 옵션을 간단하게 체크 표시도 할 수 있다.
Envoy 보안
Envoy에서는 다양한 트래픽 보안 기능을 제공한다. TLS(Transport Layer Security)를 Websocket 뿐 아니라 모든 유형의 HTTP 통신에 적용할 수 있다. 뿐만 아니라 JWT, RBAC, OAuth와 같은 인가/인증 프로토콜도 모두 지원하고 있다.
DownStreamTLSContext
DownStream의 TLS 통신을 위해서 config에서 listener 설정에 tranport_socket 항목을 추가해 tls 인증서를 지정한다.
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain:
filename: certs/servercert.pem
private_key:
filename: certs/serverkey.pem
UpStreamTLSContext
마찬가지로 UpStream의 TLS 통신을 위해 cluster 설정에도 transport_socket 항목을 추가한다.
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
(추가) UpStream에서의 인증서 검증
Upstream의 경우 TLS 서비스에 연결하면 기본적으로 인증서의 유효성을 검사하지 않는다. 만약 검사를 하고자 한다면 validation_context를 사용해 인증서 유효성 검사 방법을 지정할 수 있다. 먼저 인증서가 상호 신뢰할 수 있는 인증기관에서 발급되었는지 확인한다.
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
common_tls_context:
validation_context:
trusted_ca:
filename: certs/cacert.pem
여기에 인증서의 SAN(Subject Alternative Name)이 일치하는지도 확인하려면 다음과 같이 사용한다.
common_tls_context:
validation_context:
trusted_ca:
filename: certs/cacert.pem
match_typed_subject_alt_names:
- san_type: DNS
클라이언트 인증서 인증을 위한 mTLS 설정
Envoy는 mTLS를 이용해 클라이언트를 인증하기도 한다. require_client_certificate를 true로 지정해서 해당 기능을 켠다.
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
require_client_certificate: true # 해당부분
common_tls_context:
validation_context:
trusted_ca:
filename: certs/cacert.pem
match_typed_subject_alt_names:
- san_type: DNS
matcher:
exact: proxy-postgres-frontend.example.com
tls_certificates:
위에서 설명한 upstream의 인증서 유효성 검사와 비슷하게 match_typed_subject_alt_names에서 연결 클라이언트 인증을 추가로 제한할 수 있다.
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
require_client_certificate: true
common_tls_context:
validation_context:
trusted_ca:
filename: certs/cacert.pem
match_typed_subject_alt_names: # 해당부분
- san_type: DNS
matcher:
exact: proxy-postgres-frontend.example.com
tls_certificates:
클라이언트 인증서를 통해 mTLS 인증
Upstream에 연결할때 클라이언트 인증서를 사용하려면 다음과 같이 설정한다.
clusters:
- name: service_envoyproxy_io
type: LOGICAL_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: service_envoyproxy_io
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: www.envoyproxy.io
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
common_tls_context: # 해당부분
tls_certificates:
- certificate_chain:
filename: certs/clientcert.pem
이상으로 Envoy QuickStart 실습을 마칩니다.
'프로젝트&&스터디 > KANS2기' 카테고리의 다른 글
[KANS-7주차] Istio의 Egress Gateway (0) | 2024.10.20 |
---|---|
[KANS-7주차] Istio에서 Kubernetes Ingress Gateway 활용하기 (5) | 2024.10.19 |
[KANS-6주차] Envoy Gateway 사용하기 (0) | 2024.10.12 |
[KANS-6주차] istio Gateway API 활용하기 (1) | 2024.10.07 |
[KANS - 5주차] Service-LoadBalancer Type의 IPVS모드 (2) | 2024.10.06 |