개요
This chapter covers Extending Istio on the request path
- 엔보이 필터 이해하기
- 이스티오의 EnvoyFilter 리소스를 사용해 엔보이 직접 설정하기 Using Istio’s EnvoyFilter resource to configure Envoy directly
- 루아 Lua 를 사용해 요청 경로 커스터마이징하기 Using Lua to customize the request path
- 웹어셈블리 WebAssembly 를 사용해 요청 경로 커스터마이징하기 Using WebAssembly to customize the request path
들어가며
- 지금까지 살펴본 것처럼, 이스티오는 애플리케이션 네트워킹 기능으로 조직에 많은 가치를 제공할 수 있다.
- 그렇지만 이스티오를 채택하는 조직에는 이스티오의 기본 버전이 충족하지 못하는 제약이나 가정이 있을 가능성이 높다.
- 따라서 이런 제약을 잘 충족시키도록 이스티오 기능의 확장이 필요할 것이다. You will likely need to extend Istio’s capabilities to more nicely fit within these constraints.
- 3장에서 살펴봤고 이 책에서 내내 강조했듯이, 엔보이 프록시는 이스티오 서비스 메시의 기본 구성 요소다.
- 엔보이는 메시 내부 서비스 사이의 요청 경로에서 애플리케이션 인스턴스와 함께 있는 서비스 프록시다.
- 엔보이에 서비스의 애플리케이션 네트워킹을 간단하게 만들어줄 수 있는 중요한 기능 집합이 있기는 하지만, 맞춤형 통합 혹은 통합 최종 단계를 위해서는 엔보이를 강화해야 하는 상황에 직면할 수 있다. 다음은 그 확장의 예시들이다.
- 속도 제한 rate limiting 혹은 외부 인가 서비스와 통합 Integrating with rate limiting or external authorization services
- 헤더 추가, 제거, 수정 Adding, removing, or modifying headers
- 요청 페이로드를 보강하기 위해 다른 서비스 호출 Calling out to other services to enrich a request payload
- HMAC 서명/검증 같은 사용자 정의 프로토콜 구현 Implementing custom protocols like HMAC signing/verification
- 비표준 보안 토큰 처리 Non-standard security token handling
- 엔보이는 사용자에게 필요한 거의 모든 것을 제공할 수 있지만, 결국은 사용자의 특정 사용 사례에 맞게 맞춤 조정해야 한다.
- 이번 장에서는 요청 경로에서 이스티오를 확장하는 것을 다루는데, 이는 필연적으로 엔보이 확장을 의미하게 된다.
14.1 엔보이의 확장 기능
[강력 추천 공식 문서] Envoy Life of a Request - Docs
Request flow - Docs
- A TCP connection from downstream is accepted by an Envoy listener running on a worker thread.
- The listener filter chain is created and runs. It can provide SNI and other pre-TLS info. Once completed, the listener will match a network filter chain. Each listener may have multiple filter chains which match on some combination of destination IP CIDR range, SNI, ALPN, source ports, etc. A transport socket, in our case the TLS transport socket, is associated with this filter chain.
- On network reads, the TLS transport socket decrypts the data read from the TCP connection to a decrypted data stream for further processing.
- The network filter chain is created and runs. The most important filter for HTTP is the HTTP connection manager, which is the last network filter in the chain.
- The HTTP/2 codec in HTTP connection manager deframes and demultiplexes the decrypted data stream from the TLS connection to a number of independent streams. Each stream handles a single request and response.
- For each HTTP stream, an Downstream HTTP filter chain is created and runs. The request first passes through CustomFilter which may read and modify the request. The most important HTTP filter is the router filter which sits at the end of the HTTP filter chain. When decodeHeaders is invoked on the router filter, the route is selected and a cluster is picked. The request headers on the stream are forwarded to an upstream endpoint in that cluster. The router filter obtains an HTTP connection pool from the cluster manager for the matched cluster to do this.
- Cluster specific load balancing is performed to find an endpoint. The cluster’s circuit breakers are checked to determine if a new stream is allowed. A new connection to the endpoint is created if the endpoint’s connection pool is empty or lacks capacity.
- For each stream an Upstream HTTP filter chain is created and runs. By default this only includes the CodecFilter, sending data to the appropriate codec, but if the cluster is configured with an upstream HTTP filter chain, that filter chain will be created and run on each stream, which includes creating and running separate filter chains for retries and shadowed requests.
- The upstream endpoint connection’s HTTP/2 codec multiplexes and frames the request’s stream with any other streams going to that upstream over a single TCP connection.
- The upstream endpoint connection’s TLS transport socket encrypts these bytes and writes them to a TCP socket for the upstream connection.
- The request, consisting of headers, and optional body and trailers, is proxied upstream, and the response is proxied downstream. The response passes through the HTTP filters in the opposite order from the request, starting at the codec filter, traversing any upstream HTTP filters, then going through the router filter and passing through CustomFilter, before being sent downstream.
- If independent half-close is enabled the stream is destroyed after both request and response are complete (END_STREAM for the HTTP/2 stream is observed in both directions) AND response has success (2xx) status code. Otherwise the stream is destroyed when the response is complete, even if the request has not yet completed. Post-request processing will update stats, write to the access log and finalize trace spans.
- 다운스트림의 TCP 연결은 워커 스레드에서 실행 중인 Envoy 리스너가 수락합니다.
- 수신기 필터 체인이 생성되고 실행됩니다. SNI 및 기타 사전 TLS 정보를 제공할 수 있습니다. 완료되면 리스너는 네트워크 필터 체인과 일치합니다. 각 수신기에는 대상 IP CIDR 범위, SNI, ALPN, 소스 포트 등의 일부 조합에서 일치하는 여러 필터 체인이 있을 수 있습니다. 전송 소켓(이 경우 TLS 전송 소켓)이 이 필터 체인과 연결됩니다.
- 네트워크 읽기 시 TLS 전송 소켓은 추가 처리를 위해 TCP 연결에서 읽은 데이터를 해독된 데이터 스트림으로 복호화합니다
- 네트워크 필터 체인이 생성되고 실행됩니다. HTTP에 대한 가장 중요한 필터는 체인의 마지막 네트워크 필터인 HTTP 연결 관리자입니다.
- HTTP 연결 관리자의 HTTP/2 코덱은 TLS 연결에서 복호화된 데이터 스트림을 여러 개의 독립적인 스트림으로 디프레이밍하고 역다중화합니다. 각 스트림은 하나의 요청과 응답을 처리합니다.
- 각 HTTP 스트림에 대해 다운스트림 HTTP 필터 체인이 생성되어 실행됩니다. 요청은 먼저 요청을 읽고 수정할 수 있는 CustomFilter를 통과합니다. 가장 중요한 HTTP 필터는 HTTP 필터 체인의 끝에 있는 라우터 필터입니다. 라우터 필터에서 decodeHeaders가 호출되면 경로가 선택되고 클러스터가 선택됩니다. 스트림의 요청 헤더는 해당 클러스터의 업스트림 엔드포인트로 전달됩니다. 라우터 필터는 이를 위해 클러스터 관리자로부터 일치하는 클러스터에 대한 HTTP 연결 풀을 가져옵니다.
- 엔드포인트를 찾기 위해 클러스터별 부하 분산이 수행됩니다. 클러스터의 회로 차단기를 확인하여 새 스트림이 허용되는지 확인합니다. 엔드포인트의 연결 풀이 비어 있거나 용량이 부족한 경우 엔드포인트에 대한 새 연결이 만들어집니다.
- 각 스트림에 대해 업스트림 HTTP 필터 체인이 생성되어 실행됩니다. 기본적으로 여기에는 적절한 코덱으로 데이터를 전송하는 코덱필터만 포함되지만, 클러스터가 업스트림 HTTP 필터 체인으로 구성된 경우 재시도 및 섀도 요청에 대한 별도의 필터 체인을 생성하고 실행하는 것을 포함하여 각 스트림에서 해당 필터 체인이 생성 및 실행됩니다.
- 업스트림 엔드포인트 연결의 HTTP/2 코덱은 요청의 스트림을 단일 TCP 연결을 통해 해당 업스트림으로 가는 다른 스트림과 함께 멀티플렉싱하고 프레임을 구성합니다.
- 업스트림 엔드포인트 연결의 TLS 전송 소켓은 이러한 바이트를 암호화하여 업스트림 연결을 위한 TCP 소켓에 씁니다.
- 헤더와 선택적 본문 및 트레일러로 구성된 요청은 업스트림으로 프록시되고 응답은 다운스트림으로 프록시됩니다. 응답은 요청과 반대 순서로 코덱 필터에서 시작하여 업스트림 HTTP 필터를 통과한 다음 라우터 필터를 통과하고 CustomFilter를 통과한 후 다운스트림으로 전송됩니다.
- 독립적 하프 클로즈가 활성화된 경우 스트림은 요청과 응답이 모두 완료되고(HTTP/2 스트림의 경우 양방향에서 END_STREAM이 관찰됨) 응답이 성공(2xx) 상태 코드를 받으면 삭제됩니다. 그렇지 않으면 요청이 아직 완료되지 않았더라도 응답이 완료되면 스트림이 삭제됩니다. 요청 후 처리는 통계를 업데이트하고, 액세스 로그에 기록하고, 추적 범위를 마무리합니다.
들어가며 : 필터 확장
- 엔보이 프록시의 강점 중 하나는 확장할 수 있게 만들어졌다는 점이다.
- 엔보이의 API를 설계하는 데 많은 고민과 주의가 반영됐으며, 다른 사람들이 작성한 확장 기능도 인기를 얻게 해준 큰 이유 중 하나다.
- 엔보이를 확장할 수 있는 주요 방법은 필터 확장이다.
- 엔보이를 확장할 수 있는 지점이 어디인지, 무엇이 애플리케이션에 가장 큰 이점을 주는지 이해하려면 먼저 엔보이의 아키텍처를 이해해야 한다.
14.1.1 엔보이의 필터 체인 이해하기 Understanding Envoy’s filter chaining
- 3장에서는 그림 14.1과 같이 엔보이의 개념인 리스너, 루트, 클러스터를 소개헀다.
- 이것들이 고수준 개념임을 강조했지만, 이번 장에서는 더 자세히 다루기로 약속한 바 있다.
- 여기서는 리스너와, 필터 및 필터 체인을 사용해 리스너 모델을 확장하는 방법에 중점을 둔다.
- 엔보이의 리스너는 네트워킹 인터페이스에 포트를 열고 들어오는 트래픽 수신을 시작하는 방법이다. A listener in Envoy is a way to open a port on a networking interface and start listening to incoming traffic.
- 궁극적으로 엔보이는 네트워크 커넥션에서 바이트를 가져와 어떤 방식으로 처리하는 3계층과 4계층**(L3/L4) 프록시**다. Envoy is ultimately a layer 3 and layer 4 (L3/L4) proxy that takes bytes off a network connection and processes them in some way.
- 이것이 아키텍처의 첫 번째 중요 부분인 필터로 이어진다. This brings us to the first important part of the architecture: the filter.
- 리스너는 네트워크 스트림에서 바이트를 읽어들인 후 그림 14.2와 같이 다양한 필터 혹은 기능 단계들을 거쳐 처리한다. A listener reads bytes off the networking stream and processes them through various filters or stages of functionality.
- 엔보이에서 가장 기본적인 필터는 네트워크 필터로, 바이트 스트림에서 인코딩/디코딩 작업을 한다. Envoy’s most basic filters are network filters, which operate on a stream of bytes for either encoding or decoding.
- 스트림에서 필터가 둘 이상 순서대로 동작하도록 설정할 수 있는데, 이를 필터 체인이라고 부른다. 필터 체인을 사용하면 프록시의 기능을 구현할 수 있다. You can configure more than one filter to operate on the stream in a sequence called a filter chain, and these chains can be used to implement the functionality of the proxy.
- 예를 들어, 엔보이에는 대표적으로 다음과 같은 프로토콜용 네트워크 필터가 있다. network filters for the following protocols
- MongoDB
- Redis
- Thrift
- Kafka
- HTTP Connection Manager (HCM)
- 가장 많이 사용하는 네트워크 필터 중 하나가 HttpConnectionManager 이다.
- 이 필터는 바이트 스트림을 HTTP 기반 프로토콜(즉, HTTP 1.1, HTTP 2, gRPC, 최근에는 HTTP 3 등)의 HTTP 헤더, 바디, 트레일러로 변환하는 것에 관한 세부 사항을 추상화하는 역할을 하며, 그림 14.3에서 볼 수 있다. This filter is responsible for abstracting away the details of converting a stream of bytes into HTTP headers, body, and trailers for HTTP-based protocols (that is, HTTP 1.1, HTTP 2, gRPC, and recently HTTP 3, and so on) and is shown in figure 14.3.
- HttpConnectionManager 는 HTTP 요청을 처리할 뿐 아니라 헤더, 경로 접두사 path prefix 등 요청 속성을 바탕으로 한 요청 라우팅, 액세스 로깅, 헤더 조작도 처리한다.
- 또 HCM에는 필터 기반 아키텍처가 있어 HTTP 요청에서 동작하는 HTTP 필터를 순서대로 또는 연쇄로 설정하거나 구축할 수 있다. The HCM also has a filter-based architecture that allows you to build or configure HTTP filters into a sequence or chain of filters that operate on an HTTP request.
- 기본 HTTP 필터의 예시는 다음과 같다, 전체목록 - Docs
- Cross-origin resource sharing (CORS)
- Cross-site request forgery prevention (CSRF)
- ExternalAuth 외부 인증
- RateLimit 속도 제한
- Fault injection 결합 주입
- gRPC/JSON transcoding 트랜스코딩
- Gzip
- Lua 루아
- Role-based access control (RBAC) 역할 기반 접근 제어
- Tap
- Router 라우터
- WebAssembly (Wasm) 웹어셈블리
- HTTP 요청 하나에서 HTTP 필터가 순서대로 동작하도록 설정할 수 있다. HTTP filters can be configured in a sequence to operate on an HTTP request.
- HTTP 필터들은 요청을 업스트림 클러스터로 보내는 터미널 필터로 끝나야 한다. The HTTP filters must end in a terminal filter that sends the request to an upstream cluster.
- 이를 담당하는 HTTP 필터는 라우터 필터로 그림 14.14에 표시돼 있다. The HTTP filter responsible for this is the router filter, shown in figure 14.4.
- 라우터 필터는 요청을 적절한 업스트림 클러스터를 찾아 전달하며, 이때 타임아웃과 재시도 파라미터를 설정할 수 있다 - Docs The router filter matches requests to upstream clusters with configurable timeout and retry parameters.
- 또한 사용자는 엔보이의 핵심 코드를 변경하지 않고도 자신만의 필터를 직접 작성하고 프록시 위에 얹어 계층화할 수 있다. Users can also write their own filters and layer them on top of the proxy without having to change any of Envoy’s core code.
- 예를 들어, 이스티오의 프록시는 데이터 플레인용으로 엔보이 위에 필터를 추가해 커스텀 엔보이를 빌드한다. For example, Istio’s proxy adds filters on top of Envoy and builds a custom Envoy for its data plane.
- 그렇지만 이렇게 커스텀 엔보이 프록시 빌드를 도입하면, 유지 보수할 것이 많아질 수 있고 개발자가 C++를 사용해야 한다. However, this introduces a custom Envoy proxy build that can be a lot to maintain and require developers to use C++.
14.1.2 확장용 필터 Filters intended for extension
- 자신만의 필터를 C++로 작성해 프록시에 내장할 수 있지만, 이와 관련된 내용은 이 책의 범위를 벗어난다.
- 필터 작성 등과 같이 엔보이 바이너리 자체에 변경 사항을 컴파일하지 않고도 엔보이의 HTTP 기능을 확장하는 방법이 있는데, 이 경우 다음 HTTP 필터들을 사용한다. There are ways to extend Envoy’s HTTP capabilities, including writing filters, without compiling changes into the Envoy binary itself, by using the following HTTP filters:
- 외부 처리 External processing
- 루아 Lua
- 웹어셈블리 Wasm (WebAssembly)
- 이런 필터를 사용하면 외부 서비스를 호출하도록 설정하거나, 루아 스크립트를 실행하거나, 커스텀 코드를 실행해 HTTP 요청이나 응답을 처리할 때 HCM의 기능을 강화할 수 있다. With these filters, you can configure calls out to an external service, run a Lua script, or run custom code to enhance the capabilities of the HCM when processing HTTP requests or responses.
- 외부 서비스를 호출해 처리하는 부분에 대해 이야기할 때는 속도 제한 필터를 중점을 두겠다. For calling an external service for processing, we’ll focus on the rate-limiting filter.
- 또한 9장에서 다룬 것처럼 외부 인가를 요청할 수도 있다.
엔보이에는 범용 처리 목적으로 외부 서비스를 호출하기 위한 외부 처리 필터가 있다. 이 필터는 코드베이스에 존재하기는 하지만, 저술 시점에는 아무것도 하지 않는다. 우리는 전역 속도 제한 필터를 사용하는 등 외부 서비스를 호출하는 다른 방법에 초점을 맞춘다.
14.1.3 이스티오의 데이터 플레인 커스터마이징하기 Customizing Istio’s data plane
- 엔보이의 필터 아키텍처를 깊이 있게 이해한 것을 바탕으로, 이후 절들에서 다음 방법 중 하나를 사용해 엔보이 데이터 플레인의 기능을 확장한다.
- 엔보이 HTTP 필터를 이스티오 API에서 EnvoyFilter 리소스로 설정하기
- 속도 제한 서버 RLS, Rate-Limit Server 호출하기
- 루아 스크립트를 구현해 루아 HTTP 필터에 불러오기
- 웹어셈블리 HTTP 필터용 웹어셈블리 모듈(Wasm 모듈) 구현하기
- 엔보이의 필터를 직접 설정하는 방법을 이해해야 하며, 이를 위해 이스티오의 EnvoyFilter 리소스를 사용할 것이다.
- 이전 장들에서 이미 사용해봤지만, 여기서 더 자세히 살펴본다.
14.2 EnvoyFilter 리소스로 엔보이 필터 설정하기
[실습 환경 구성] k8s(1.23.17) 배포 & mypc 컨테이너
#
git clone https://github.com/AcornPublishing/istio-in-action
cd istio-in-action/book-source-code-master
pwd # 각자 자신의 pwd 경로
code .
# 아래 extramounts 생략 시, myk8s-control-plane 컨테이너 sh/bash 진입 후 직접 git clone 가능
kind create cluster --name myk8s --image kindest/node:v1.23.17 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000 # Sample Application (istio-ingrssgateway) HTTP
hostPort: 30000
- containerPort: 30001 # Prometheus
hostPort: 30001
- containerPort: 30002 # Grafana
hostPort: 30002
- containerPort: 30003 # Kiali
hostPort: 30003
- containerPort: 30004 # Tracing
hostPort: 30004
- containerPort: 30005 # Sample Application (istio-ingrssgateway) HTTPS
hostPort: 30005
- containerPort: 30006 # TCP Route
hostPort: 30006
- containerPort: 30007 # kube-ops-view
hostPort: 30007
extraMounts: # 해당 부분 생략 가능
- hostPath: /root/istio_gasida/istio-in-action/book-source-code-master # 각자 자신의 pwd 경로로 설정
containerPath: /istiobook
networking:
podSubnet: 10.10.0.0/16
serviceSubnet: 10.200.0.0/22
EOF
# 설치 확인
docker ps
# 노드에 기본 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
# (옵션) kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30007 --set env.TZ="Asia/Seoul" --namespace kube-system
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
## kube-ops-view 접속 URL 확인
open "http://localhost:30007/#scale=1.5"
open "http://localhost:30007/#scale=1.3"
# (옵션) metrics-server
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
kind docker network 에 테스트용 PC(실제로는 컨테이너) 배포
# kind 설치 시 kind 이름의 도커 브리지가 생성된다 : 172.18.0.0/16 대역
docker network ls
docker inspect kind
# mypc 컨테이너 기동 : kind 도커 브리지를 사용하고, 컨테이너 IP를 지정 없이 혹은 지정 해서 사용
docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity # IP 지정 실행 시
혹은 IP 지정 실행 시 에러 발생 시 아래 처럼 IP 지정 없이 실행
docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity # IP 지정 없이 실행 시
docker ps
# kind network 중 컨테이너(노드) IP(대역) 확인
docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}'
/mypc 172.18.0.100
/myk8s-control-plane 172.18.0.2
# 동일한 docker network(kind) 내부에서 컨테이너 이름 기반 도메인 통신 가능 확인!
docker exec -it mypc ping -c 1 172.18.0.2
docker exec -it mypc ping -c 1 myk8s-control-plane
docker exec -it myk8s-control-plane ping -c 1 mypc
[실습 환경 구성] (편리성 설정) MetalLB 배포
MetalLB 배포
# MetalLB 배포
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml
# 확인
kubectl get crd
kubectl get pod -n metallb-system
# IPAddressPool, L2Advertisement 설정 : MetalLB 파드(speaker) 배포 정상 완료 후 아래 설정 실행하자.
cat << EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default
namespace: metallb-system
spec:
addresses:
- 172.18.255.101-172.18.255.120
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default
namespace: metallb-system
spec:
ipAddressPools:
- default
EOF
# 확인
kubectl get IPAddressPool,L2Advertisement -A
샘플 애플리케이션 배포 후 확인
#
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
type: LoadBalancer
EOF
# 확인
kubectl get deploy,pod,svc,ep
kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
LBIP=$(kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
# 외부?에서 각 클러스터의 Service(LB)로 접속(인입) 확인 : TCP 80 포트 사용으로 편리하다!
docker exec -it mypc curl -s $LBIP
docker exec -it mypc curl -s $LBIP -v -I
# 확인 후 삭제
kubectl delete deploy,svc --all
[실습 환경 구성] istio 1.17.8 설치 - Docs , Install , profile
# myk8s-control-plane 진입 후 설치 진행
docker exec -it myk8s-control-plane bash
-----------------------------------
# (옵션) 코드 파일들 마운트 확인
tree /istiobook/ -L 1
혹은
git clone ... /istiobook
# istioctl 설치
export ISTIOV=1.17.8
echo 'export ISTIOV=1.17.8' >> /root/.bashrc
curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
# demo 프로파일 컨트롤 플레인 배포
istioctl install --set profile=demo --set values.global.proxy.privileged=true -y
# 보조 도구 설치
kubectl apply -f istio-$ISTIOV/samples/addons
# 빠져나오기
exit
-----------------------------------
# 설치 확인 : istiod, istio-ingressgateway, crd 등
kubectl get istiooperators -n istio-system -o yaml
kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
kubectl get cm -n istio-system istio -o yaml
kubectl get crd | grep istio.io | sort
# 실습을 위한 네임스페이스 설정
kubectl create ns istioinaction
kubectl label namespace istioinaction istio-injection=enabled
kubectl get ns --show-labels
# istio-ingressgateway 서비스 : nodeport 지정 변경 , externalTrafficPolicy 설정 (ClientIP 수집)
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}'
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 443, "targetPort": 8443, "nodePort": 30005}]}}'
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
kubectl describe svc -n istio-system istio-ingressgateway
# NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
kubectl patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
kubectl patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}'
kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'
kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'
# Prometheus 접속 : envoy, istio 메트릭 확인
open http://127.0.0.1:30001
# Grafana 접속
open http://127.0.0.1:30002
# Kiali 접속 : NodePort
open http://127.0.0.1:30003
# tracing 접속 : 예거 트레이싱 대시보드
open http://127.0.0.1:30004
들어가며 : tap 필터 (실습~)
- 이스티오의 데이터 플레인을 확장하는 첫 번째 단계는 엔보이의 기존 필터가 우리가 찾고 있는 확장 유형을 달성하는 데 충분하지 파악하는 것이다. The first step in extending Istio’s data plane is to figure out whether an existing filter in Envoy is sufficient to accomplish the type of extension we’re looking for.
- 필터가 존재하는 경우, EnvoyFilter 리소스를 사용해 이스티오의 데이터 플레인을 직접 설정할 수 있다. If one exists, we can use the EnvoyFilter resource to directly configure Istio’s data plane.
- 이스티오의 API는 일반적으로 특정 네트워크나 보안 시나리오에 초점을 맞춰 기저 underlying 엔보이의 설정을 추상화한다. Istio’s APIs generally abstract away the underlying Envoy configuration, focusing on specific networking or security scenarios.
- VirtualService, DestinationRule, and AuthorizationPolicy 같은 리소스는 모두 결국 엔보이 설정으로 변환되며, 필터 체인에서 특정 HTTP 필터를 설정하기도 한다. Resources like VirtualService, DestinationRule, and AuthorizationPolicy all end up getting translated to an Envoy configuration and potentially configure specific HTTP filters in a filter chain.
- 이스티오는 기저 엔보이에서 할 수 있는 모든 필터나 설정을 노출하려고 시도하지 않으며, 따라서 엔보이를 직접 설정해야 하는 상황이 있을 수 있다. Istio does not try to expose every possible filter or configuration for the underlying Envoy proxy, and there may be cases where we need to configure Envoy directly.
- 이스티오의 EnvoyFilter 리소스는 이스티오의 고수준 API에서 노출하지 않는 엔보이의 일부를 설정하거나 조정해야 하는 고급 사용 사례를 위한 것이다. Istio’s EnvoyFilter resource is intended for advanced use cases where a user needs to either tweak or configure a portion of Envoy not exposed by Istio’s higher-level APIs.
- 이 리소스는 엔보이의 거의 모든 것을 설정할 수 있으며(일부 제한 있음) 리스너, 루트, 클러스터, 필터가 포함된다. This resource can configure just about anything (with some limitations) in Envoy, including listeners, routes, clusters, and filters.
- EnvoyFilter 리소스는 이스티오의 고급 사용자용이며 비상 수단이다. The EnvoyFilter resource is intended for advanced usage of Istio and is a 'break glass' solution.
- 기저 엔보이 API는 이스티오 버전 간에 언제든 달라질 수 있으니, 배포하는 모든 EnvoyFilter 의 유효성을 반드시 확인해야 한다. The underlying Envoy API may change at any time between releases of Istio, so be sure to validate any EnvoyFilter you deploy.
- 이전 버전과의 호환성은 어떤 것도 가정해서는 안 된다. 이 API를 잘못 설정하면 이스티오 데이터 플레인 전체가 멈출 수도 있다. Do not assume any backward compatibility here. Bad configuration with this API can potentially take down the entire Istio data plane.
예제를 보고 어떻게 작동하는지 이해해보자. 사용할 서비스를 배포하자.
#
kubectl get envoyfilter -A
NAMESPACE NAME AGE
istio-system stats-filter-1.13 12m
istio-system stats-filter-1.14 12m
istio-system stats-filter-1.15 12m
istio-system stats-filter-1.16 12m
istio-system stats-filter-1.17 12m
istio-system tcp-stats-filter-1.13 12m
istio-system tcp-stats-filter-1.14 12m
istio-system tcp-stats-filter-1.15 12m
istio-system tcp-stats-filter-1.16 12m
istio-system tcp-stats-filter-1.17 12m
# 배포
kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction
kubectl apply -f services/webapp/kubernetes/webapp.yaml -n istioinaction
kubectl apply -f services/webapp/istio/webapp-catalog-gw-vs.yaml -n istioinaction
kubectl apply -f ch9/sleep.yaml -n istioinaction
# 확인
kubectl get gw,vs,dr -n istioinaction
NAME AGE
gateway.networking.istio.io/coolstore-gateway 50s
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/webapp-virtualservice ["coolstore-gateway"] ["webapp.istioinaction.io"] 50s
# 호출 확인 : mypc
EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP
docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog
# 신규 터미널 : mypc 에서 반복 접속
EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
while true; do docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog ; echo ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
# 호출 확인 : 자신의 호스트 PC
curl -s http://webapp.istioinaction.io:30000
curl -s http://webapp.istioinaction.io:30000/api/catalog
- webapp 서비스를 거쳐 흐르는 특정 요청을 디버깅할 도구로 데이터 플레인을 확장하고 싶다고 하자. Suppose we want to extend our data plane with tooling to debug certain requests that flow through the webapp service.
- 엔보이를 커스텀 필터로 확장할 수도 있지만, 충분히 살펴보니 이런 기능을 위한 tap 필터가 있음을 발견했다. We can extend Envoy with some custom filters, but if we look thoroughly enough, we see that a Tap filter exists for this type of functionality.
- 이스티오의 API가 이 필터를 노출하고 있지는 않으므로, webapp 서비스에 이 필터를 설정하려면 EnvoyFilter 리소스를 사용하면 된다. It is not exposed by Istio’s APIs, so we can use the EnvoyFilter resource to configure this filter for our webapp service.
- EnvoyFilter 리소스에 대해 알아야 할 첫 번째 사항은, 달리 지정하지 않은 한 네임스페이스의 모든 워크로드에 적용된다는 것이다. The first thing to know about an EnvoyFilter resource is that it applies to all workloads in the namespace for which it is declared, unless you specify otherwise.
- istio-system 네임스페이스에 EnvoyFilter 리소스를 만들었다면 메시의 모든 워크로드에 적용된다. If you create an EnvoyFilter resource in the istio-system namespace, it will be applied to all workloads in the mesh.
- 네임스페이스 내에서 커스텀 EnvoyFilter 설정을 적용할 워크로드를 좀 더 특정하고 싶다면, 예제에서 보겠지만 workloadSelector 를 사용하면 된다. If you want to be more specific about the workloads in a namespace to which the custom EnvoyFilter configuration applies, you can use a workloadSelector, as we’ll see in our example.
- EnvoyFilter 리소스에 대해 알아야 할 두 번째 사항은, 다른 이스티오 리소스가 모두 변환되고 설정된 후에야 적용된다는 것이다. The second thing to know about an EnvoyFilter resource is that it applies after all other Istio resources have been translated and configured.
- 예를 들어 VirtualService or DestinationRule 리소스가 있다면, 이 설정들이 먼저 데이터 플레인에 적용된다. For example, if you have VirtualService or DestinationRule resources, those configurations are applied to the data plane first.
- 마지막으로, EnvoyFilter 리소스로 워크로드를 설정할 때 각별히 주의해야 한다. Finally, you should take great care when configuring a workload with the Envoy Filter resource.
- 따라서 엔보이 명명 규칙과 설정 세부 사항을 잘 알아두자. 이는 정말 이스티오 API의 고급 사용법으로, 잘못 설정하면 메시를 마비시킬 수 있다. You should be familiar with Envoy naming conventions and configuration specifics. This really is an advanced usage of Istio’s API and can bring down your mesh if misconfigured.
- 이 예제에서는 그림 14.5와 같이 webapp 워크로드의 데이터 플레인을 거치는 메시지를 샘플링하도록 엔보이의 tap 필터를 설정하려고 한다 - Docs In our example, we want to configure Envoy’s tap filter to sample messages that go over the data plane for the webapp workload, as shown in figure 14.5.
- 요청이나 응답이 tap 필터를 지나 흐를 때마다 tap 필터는 그 요청/응답을 어떤 수신 에이전트로 스트리밍한다. 이 예제에서는 콘솔/CLI로 스트리밍한다. Every time a request or response flows over the tap filter, it streams it out to some listening agent. In this example, we stream it out to the console/CLI.
EnvoyFilter 리소스는 다음과 같다
# cat ch14/tap-envoy-filter.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: tap-filter
namespace: istioinaction
spec:
workloadSelector:
labels:
app: webapp # 워크로드 셀렉터
configPatches:
- applyTo: HTTP_FILTER # 설정할 위치
match:
context: SIDECAR_INBOUND
listener:
portNumber: 8080
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch: # 엔보이 설정 패치
operation: INSERT_BEFORE
value:
name: envoy.filters.http.tap
typed_config:
"@type": "type.googleapis.com/envoy.extensions.filters.http.tap.v3.Tap"
commonConfig:
adminConfig:
configId: tap_config
- EnvoyFilter 를 istioinaction 네임스페이스에 배포했다.
- workloadSelector를 사용해 이 설정을 적용할 워크로드를 구체적으로 지정했다.
- 다음으로 엔보이 설정에서 패치할 위치를 지정해야 한다.
- 이 예제에서는 인바운드 리스너(SIDECAR_INBOUDN)의 HTTP_FILTER가 되도록 지정했다.
- 앞서 언근한 바와 같이 리스너를 위한 네트워크 필터들이 있으며, 그중 하나가 HCM 이다.
- HCM에도 HTTP 요청을 처리하는 HTTP용 필터 체인이 있다.
- 또한 이 예제에서는 특정 리스너도 지정했는데, HCM을 8080 포트에 바인딩된 리스너를 지정했다.
- 마지막으로, 이 HCM HTTP 필터 체인에서 envoy.filters.http.router HTTP 필터를 골랐다.
- 이 필터를 고른 이유는, 다음 절에서 보겠지만 새로운 tap 필터를 이 router 필터 바로 앞에 배치할 것이기 때문이다.
- EnvoyFilter 리소스의 patch 부분에서 설정을 어떻게 패치할지 지정한다.
- 여기서는 앞서 설정 부분에서 선택한 필터 앞에 설정을 병합한다.
- 추가하는 필터인 envoy_filters.http.tap 은 HCM 필터 체인에서 envoy.filters.http.router 앞에 위치한다.
- tap 필터 설정의 구조를 명확하게 해야 하므로 명시적인 타입을 지정한다 - Docs
- 이 EnvoyFilter를 istioinaction 네임스페이스에 webapp 워크로드에 적용하자.
#
cat ch14/tap-envoy-filter.yaml
kubectl apply -f ch14/tap-envoy-filter.yaml
kubectl get envoyfilter -n istioinaction
NAME AGE
tap-filter 10s
#
docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction
docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006
docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 -o json
...
{
"name": "envoy.filters.http.tap",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.tap.v3.Tap",
"commonConfig": {
"adminConfig": {
"configId": "tap_config"
}
}
}
},
{
"name": "envoy.filters.http.router",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
}
}
...
tap 기능이 동작하는지 확인해보자. 2개의 터미널 창이 필요한다. 창 하나에서는 curl 로 tap 설정을 전달해 webapp 워크로드에서 tap을 시작하자.
# 터미널 1 : 포트 포워딩 설정 후 tap 시작
kubectl port-forward -n istioinaction deploy/webapp 15000 &
curl -X POST -d @./ch14/tap-config.json localhost:15000/tap
# 터미널 2 : 기존 반복 접속하는 것 활용
EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog
docker exec -it mypc curl -s -H "x-app-tap: true" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog
while true; do docker exec -it mypc curl -s -H "x-app-tap: true" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog ; echo ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
# 터미널 3 : 로그 확인
docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level http:debug
docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level tap:debug
kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
2025-05-18T05:59:47.704918Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:329 [C2100] new stream thread=31
2025-05-18T05:59:47.705028Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:1049 [C2100][S11845543740779751481] request headers complete (end_stream=true):
':authority', 'webapp.istioinaction.io'
':path', '/api/catalog'
':method', 'GET'
'user-agent', 'curl/8.7.1'
'accept', '*/*'
'x-app-tap', 'true'
...
2025-05-18T05:59:47.713852Z debug envoy tap external/envoy/source/extensions/filters/http/tap/tap_config_impl.cc:172 submitting buffered trace sink thread=31
2025-05-18T05:59:47.713860Z debug envoy tap external/envoy/source/extensions/common/tap/admin.cc:145 admin submitting buffered trace to main thread thread=31
...
- tap 을 시작한 창에 tap 출력이 표시돼야 한다. 이 출력은 헤더, 바디, 트레일러 등 요청에 대한 모드 정보를 준다.
- 엔보이 tap 필터와 이 필터를 이스티오에서 사용해 네트워크 전반의 요청을 디버깅하는 방법을 계속해서 알아보자.
14.3 외부 호출로 요청 속도 제한하기
들어가며 : 속도 제한 소개 - Docs Rate-limiting requests with external call-out
- 앞 절에서는 기본 HTTP 필터에 있는 기능으로 이스티오 데이터 플레인을 확장해봤는데, 외부 호출 기능으로 데이터 플레인을 확장하는 기본 필터들도 있다. In the previous section, we extended the Istio data plane with functionality that exists in an out-of-the-box HTTP filter. There are also out-of-the-box filters that enhance the data plane with functionality that exists as a call-out.
- 우리는 이 필터들을 사용해 외부 서비스를 호출하고, 요청을 계속할지 여부나 방법을 결정할 수 있는 어떤 기능을 수행하게 해본다. With these filters, we call out to an external service and have it perform some functionality that can determine how or whether to continue with a request.
- 이번 절에서는 특정 워크로드에서 서비스 측 속도 제한을 적용하기 위해 속도 제한 서비스를 호출하도록 이스티오 데이터 플레인을 설정하는 방법을 살펴본다. In this section, we explore how to configure Istio’s data plane to call out to a rate-limiting service to enforce service-side rate-limiting for a particular workload
- 이스티오가 데이터 플레인에 엔보이를 사용하는 것처럼, 속도 제한에 대한 특정 호출은 엔보이 HTTP 필터에서 나온다. Just as Istio uses Envoy for the data plane, the specific call-out for rate limiting comes from an Envoy HTTP filter.
- 엔보이에서 속도 제한을 하는 방법은 여러 가지가 있지만 (네트워크 필터, 로컬 속도 제한, 글로벌 속도 제한), 우리는 특히 글로벌 속도 제한 기능을 살펴본다. There are several ways to do rate limiting in Envoy (as a network filter, local rate limiting, and global rate limiting), but we specifically explore the global rate-limiting functionality.
- 글로벌 속도 제한을 사용하면 특정 워크로드의 모든 엔보이 프록시가 동일한 속도 제한 서비스를 호출하며, 이 속도 제한 서비스는 백엔드 글로벌 키-값 저장소를 호출한다. (그림 14.7 참조) With global rate limiting, you have all Envoy proxies for a particular workload calling the same rate-limiting service, which calls a backend global key-value store as shown in figure 14.7.
- 이 아키텍처를 사용하면 서비스의 복제본 개수에 상관없이 속도 제한이 적용되도록 할 수 있다. With this architecture, we can ensure that a rate limit is enforced regardless of how many replicas of a service exist.
- 엔보이 전역 속도 제한을 사용하면 속도 제한 서버를 호출해 특정 요청에 속도 제한을 적용해야 하는지 여부를 결정할 수 있다.
- 결정을 내리고자 요청 속성을 속도 제한 서버로 전송한다.
- 속도 제한을 하려면 엔보이 커뮤니티에서 제공하는 속도 제한 서버를 - Github 배포하거나, 더 정확히는 엔보이 속도 제한 API - Code 를 구현하는 속도 제한 서버를 배포해야 한다. To configure rate limiting, we need to deploy the rate-limit server, which comes from the Envoy community or, more accurately, a rate-limit server that implements the Envoy rate-limiting API.
- 이 서버는 백엔드 레디스 캐시와 통신하도록 설정돼 있으며, 레디스에 속도 제한 카운터를 저장한다 (원한다면 Memcached 를 사용할 수 있다). This server is configured to talk with a backend Redis cache and stores rate-limit counters in Redis (optionally, you can use Memcache).
- 따라서 속도 제한 서버를 배포하기 전에 기대하는 속도 제한 동작으로 설정해야 한다. Before we deploy the rate-limit server, we need to configure it with the expected rate-limiting behavior.
14.3.1 엔보이 속도 제한 이해하기 Understanding Envoy rate limiting
들어가며
- 엔보이 속도 제한 서버 RLS 를 구성하기 전에 속도 제한이 어떻게 작동하는지 이해해야 한다. Before configuring the Envoy rate-limit server (RLS), we need to understand how rate limiting works.
- 특히 엔보이의 HTTP 전역 속도 제한을 살펴볼 것인데, 이 속도 제한은 HTTP 필터로 존재하고 HCM에서 HTTP 필터 체인으로 설정해야 한다. We are specifically looking at understanding Envoy’s HTTP global rate limiting, which exists as an HTTP filter and needs to be configured into the HTTP filter chain on the HCM.
- 속도 제한 필터는 HTTP 요청을 처리할 때 요청에서 특정 속성을 가져오고 RLS로 보내 평가를 받는다. When the rate-limit filter processes an HTTP request, it takes certain attributes from the request and sends them out to the RLS for evaluation.
- 엔보이 속도 제한에서 이런 속성들 혹은 속성 그룹들을 가리켜 디스크립터 descripter 라고 한다. Envoy rate-limiting terminology uses the word descriptors to refer to attributes or groups of attributes.
- 요청이 디스크립터 또는 속성은 원격 주소일 수도, 요청 헤더일 수도, 목적지일 수도, 혹은 요청의 어떤 일반 속성 같은 것일 수도 있다. These descriptors, or attributes, of the request can be things like remote address, request headers, destination, or any other generic attributes about the request.
- 속성 제한 서버 RLS는 그림 14.8처럼 전송된 요청의 속성을 미리 정의한 속성 집합과 비교해 일치하는 속성의 카운터를 늘린다. The RLS evaluates the request attributes that have been sent as part of a request against a set of predefined attributes, as shown in figure 14.8, and increments counters for those attributes.
- 카운트할 속성은 트리 형태로 그룹화하거나 정의해 결정할 수 있다. The request attributes may be grouped or defined in a tree to determine what attributes should be counted.
- 속성 또는 속성 집합이 속도 제한 서버 정의와 일치하면 해당 제한의 횟수를 늘린다. If an attribute or set of attributes matches the RLS definitions, then the counts for those limits are incremented.
- 횟수가 임계값을 초과하면 해당 요청에는 속도 제한이 적용된다. If a count exceeds a threshold, that request is rate-limited.
엔보이 속도 제한 서버 설정하기 CONFIGURING THE ENVOY RATE-LIMIT SERVER
- 속성 카운터와 한도가 포함된 속도 제한 서버 설정을 만들어보자. Let’s configure the RLS with the attribute counters and limits.
- 우리 예제에서는 예제 조직에서 보유한 로열티 등급에 따라 특정 사용자 집단을 제한하고자 한다. For our example use case, we want to limit certain groups of users depending on what loyalty tier they have with our example organization.
- 요청의 로열티 등급은 x-loyalty 헤더를 검사해 판단할 수 있다. We can determine the loyalty tier in a request by examining the x-loyalty header.
- 골드 등급*(x-loyalty: gold)의 사용자 그룹에는 요청을 분당 10개까지 허용하고, 실버 등급(x-loyalty: silver)에는 요청을 분당 5개까지 허용한다. 또한 브론즈 등급(x-loyalty: bronze)*에는 요청을 분당 3개까지 허용한다. For a particular group of users in the gold tier (x-loyalty: gold), we allow 10 requests per minute.For silver (x-loyalty: silver), we allow five requests per minute; and for bronze (x-loyalty: bronze), we allow three requests per minute.
- 식별할 수 없는 로열티 등급의 경우, 분당 요청이 하나를 넘어가면 속도 제한이 처리를 제한한다. For a loyalty tier that cannot be identified, rate limiting will kick in after one request per minute.
- 요청이 디스크립터를 포착하는 속도 제한 서버 설정은 다음과 같이 표현할 수 있다.
# cat ch14/rate-limit/rlsconfig.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: catalog-ratelimit-config
namespace: istioinaction
data:
config.yaml: |
domain: catalog-ratelimit
descriptors:
- key: header_match
value: no_loyalty
rate_limit:
unit: MINUTE
requests_per_unit: 1
- key: header_match
value: gold_request
rate_limit:
unit: MINUTE
requests_per_unit: 10
- key: header_match
value: silver_request
rate_limit:
unit: MINUTE
requests_per_unit: 5
- key: header_match
value: bronze_request
rate_limit:
unit: MINUTE
requests_per_unit: 3
- 실제 요청 헤더를 직접 다루지 않고, 요청의 일부로 전송된 속성만 다루고 있음을 유의하자. 다음 절에서는 이 속성들을 정의하는 방법을 살펴볼 것이다.
- 앞서 언급했듯이, 속도 제한 서버 설정은 속도를 제한하기 위해 따라야 하는 규칙을 정의한다.
- 이스티오 데이터 플레인을 거쳐 요청이 처리될 때 속성들이 속도 제한 서버로 보내진다. 속성이 규칙 조건에 일치하면 그에 따라 처리를 제한한다.
요청 경로에 속도 제한 걸기* CONFIGURING THE REQUEST PATH FOR RATE LIMITING
- 속도 제한 서버 설정을 만들고 나면, 특정 요청에 대해 어떤 속성을 전송할 것인지 엔보이를 설정해야 한다.
- 엔보이 용어로는 이 설정을 특정 요청 경로에 취하는 속도 제한 조치 rate-limit action 라고 부른다.
- 예를 들어, catalog 서비스를 /items 경로로 호출하면 요청에 x-loyalty 헤더가 있는지와 어느 그룹에 속하는지를 포착하려고 한다.
- 적절한 속성 action 을 속도 제한 서버로 보내도록 설정하려면 특정 엔보이 루트 설정에 rate_limit 설정을 지정해야 한다.
- 이스티오에는 아직 전용 API가 없으므로(이 책을 저술하는 시점에는) EnvoyFilter 리소스를 사용해야 한다.
- catalog 서비스의 모든 경로에 속도 제한 조치를 지정하는 방법은 다음과 같다.
# cat ch14/rate-limit/catalog-ratelimit-actions.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: catalog-ratelimit-actions
namespace: istioinaction
spec:
workloadSelector:
labels:
app: catalog
configPatches:
- applyTo: VIRTUAL_HOST
match:
context: SIDECAR_INBOUND
routeConfiguration:
vhost:
route:
action: ANY
patch:
operation: MERGE
# Applies the rate limit rules.
value:
rate_limits: # 속도 제한 조치
- actions:
- header_value_match:
descriptor_value: no_loyalty
expect_match: false
headers:
- name: "x-loyalty"
- actions:
- header_value_match:
descriptor_value: bronze_request
headers:
- name: "x-loyalty"
exact_match: bronze
- actions:
- header_value_match:
descriptor_value: silver_request
headers:
- name: "x-loyalty"
exact_match: silver
- actions:
- header_value_match:
descriptor_value: gold_request
headers:
- name: "x-loyalty"
exact_match: gold
- 이제 이 규칙들을 속도 제한 서버와 함께 배포하고 데이터 플레인을 설정하는 방법을 살펴보자.
- k8s configmap 으로 규칙을 배포하고, 속도 제한 서버를 레디스 백엔드와 함께 배포하자.
#
tree ch14/rate-limit
ch14/rate-limit
├── catalog-ratelimit-actions.yaml
├── catalog-ratelimit.yaml
├── rls.yaml
└── rlsconfig.yaml
cat ch14/rate-limit/rlsconfig.yaml
cat ch14/rate-limit/rls.yaml
#
kubectl apply -f ch14/rate-limit/rlsconfig.yaml -n istioinaction
kubectl apply -f ch14/rate-limit/rls.yaml -n istioinaction
# 확인
kubectl get cm -n istioinaction catalog-ratelimit-config
kubectl get pod -n istioinaction
NAME READY STATUS RESTARTS AGE
ratelimit-99d5d9c5-q4zcm 1/1 Running 2 (25s ago) 34s
redis-6cf4ff9768-x97m8 1/1 Running 0 34s
...
- 엔보이가 속성을 속도 제한 서버로 보내게 해야 한다. 그래야 속도 제한 서버가 개수를 세어 속도 제한을 처리할 수 있기 때문이다.
- 이를 위해 그 역할을 하는 EnvoyFilter 리소스를 적용해보자.
# 기존에 반복 호출은 취소해두자
#
cat ch14/rate-limit/catalog-ratelimit.yaml
cat ch14/rate-limit/catalog-ratelimit-actions.yaml
kubectl apply -f ch14/rate-limit/catalog-ratelimit.yaml -n istioinaction
kubectl apply -f ch14/rate-limit/catalog-ratelimit-actions.yaml -n istioinaction
kubectl get envoyfilter -A
NAMESPACE NAME AGE
istioinaction catalog-ratelimit-actions 27s
istioinaction catalog-ratelimit-filter 28s
...
속도 제한 기능을 시험해보기 위해 sleep 앱을 배포하고 catalog 서비스를 호출하는 클라이언트를 시뮬레이션해보자.
# sleep 앱에서 catalog 서비스를 호출 시도 : 대략 1분에 한 번 정도 호출 성공! >> x-loyalty 헤더가 없을 때 속도 제한 값!
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://catalog/items -v
...
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://catalog/items -v
...
< HTTP/1.1 429 Too Many Requests
< x-envoy-ratelimited: true
...
# silver 헤더는?
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
...
#
docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'InboundPassthroughClusterIpv4'
docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'InboundPassthroughClusterIpv4' -o json | grep actions
docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||'
docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||' -o json | grep actions
docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||' -o json
...
"rateLimits": [
{
"actions": [
{
"headerValueMatch": {
"descriptorValue": "no_loyalty",
"expectMatch": false,
"headers": [
{
"name": "x-loyalty"
}
]
}
}
]
},
{
"actions": [
{
"headerValueMatch": {
"descriptorValue": "bronze_request",
"headers": [
{
"name": "x-loyalty",
"exactMatch": "bronze"
...
다음 실습을 위해 리소스 삭제
#
kubectl delete envoyfilter -n istioinaction --all
kubectl get envoyfilter -A
#
kubectl delete -f ch14/rate-limit/rlsconfig.yaml -n istioinaction
kubectl delete -f ch14/rate-limit/rls.yaml -n istioinaction
14.4 루아로 이스티오의 데이터 플레인 확장하기
Lua 소개 : 가벼운 명령형/절차적 언어로, 확장 언어로 쓰일 수 있는 스크립팅 언어를 주 목적으로 설계 - Wiki Extending Istio’s data plane with Lua
- 이미 존재하는 엔보이 필터를 설정해 이스티오의 데이터 플레인을 확장하는 것은 편리하지만, 추가하려는 기능이 기본 엔보이 필터에 없는 경우에는 어떻게 해야 할까? Extending Istio’s data plane by configuring existing Envoy filters is convenient, but what if the functionality we want to add doesn’t already exist as an out-of-the box Envoy filter?
- 요청 경로에 어떤 커스텀 로직을 구현하고 싶다면 어떻게 해야 할까? 이 절에서는 자체 커스텀 로직으로 데이터 플레인 동작을 확장하는 방법을 살펴본다. What if we want to implement some custom logic on the request path? In this section, we look at how to extend data-plane behavior with our own custom logic.
- 이전 절에서 엔보이에는 데이터 플레인 동작을 강화하기 위해 필터 체인에 추가할 수 있는 기본 필터가 많이 있음을 확인했다. 그중 하나가 루아 필터로, 루아 필터를 사용하면 루아 스크립트를 작성해 프록시에 주입함으로써 요청/응답 경로의 동작을 커스텀할 수 있다 (그림 14.9 참조) One of those is the Lua filter, which allows us to customize the behavior of the request or response path by writing Lua scripts and injecting them into the proxy.
- 이런 스크립트는 요청이나 응답의 헤더를 조작하고 바디를 조사하는 데 사용할 수 있다. 계속해서 루아 스크립트를 주입해 요청 경로의 처리를 변경하도록 EnvoyFilter 리소스를 사용해 데이터 플레인을 설정할 것이다. These scripts can be used to manipulate the headers and inspect the body of a request or response. We will continue to use the EnvoyFilter resource to configure the data plane to inject Lua scripts to change the processing of the request path.
루아 프로그래밍 언어 Lua programming language https://lua.org/ , https://luajit.org/
루아는 시스템 기능을 강화할 수 있는 강력하면서도 내장 가능한 스크립트 언어다.
Lua is a powerful, embeddable scripting language that can be used to enhance the capabilities of a system.
루아는 동적 타입의 인터프리터 언어로, 루아 가상머신이 제공하는 메모리 자동 관리 기능을 갖추고 있다 (엔보이에서는 LuaJIT이다).
Lua runs as a dynamically typed and interpreted language with automatic memory management provided by a Lua VM (in Envoy, this is LuaJIT).
요청 바디를 검사하면 프록시에서 스트림을 처리하는 방식에 영향을 줄 수 있다. 예를 들어 바디 전체를 메모리에 적재하는 작업을 수행할 수도 있는데, 이는 성능에 영향을 줄 수 있다. 루아 필터에 대한 엔보이 문서를 참조하자 https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter
실습 : 요청 경로의 동작을 커스텀 ⇒ 일부 실습은 실패!
- 요청 경로의 동작을 커스텀하는 일반적인 예를 들어본다. 들어오는 요청 모두를 A/B 테스트 그룹의 일부로 처리하고 싶다고 해보자.
- 어떤 그룹인지는 요청의 속성을 기반으로 런타임에서만 판단할 수 있다.
- 특정 요청이 어느 그룹에 속했는지 판단하려면 A/B 테스트 엔진을 호출해야 한다.
- 이 호출의 응답은 요청의 헤더로 추가해야 하며, 업스트림 서비스는 이 헤더를 사용해 A/B 테스트 목적에 맞는 라우팅 결정을 내릴 수 있다.
- 이 예제를 위해 몇 가지 보조 서비스를 배포해보자. 받은 요청의 헤더를 되돌려 보내는 샘플 httpbin 서비스를 배포할 것이다.
- 또한 샘플 A/B 테스트 버킷 서비스도 배포해보겠다. 이 서비스는 요청의 헤더를 평가해 요청이 속해야 하는 그룹을 나타내는 문자열을 반환한다.
#
cat ch14/bucket-tester-service.yaml
...
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: bucket-tester
version: v1
name: bucket-tester
spec:
replicas: 1
selector:
matchLabels:
app: bucket-tester
version: v1
template:
metadata:
labels:
app: bucket-tester
version: v1
spec:
containers:
- image: hashicorp/http-echo:1.0 # 수정 https://hub.docker.com/r/hashicorp/http-echo/tags
imagePullPolicy: IfNotPresent
name: bucket-tester
args:
- "-text=dark-launch-7"
ports:
- containerPort: 5678
name: http
protocol: TCP
securityContext:
privileged: false
#
kubectl apply -f ch14/httpbin.yaml -n istioinaction
kubectl apply -f ch14/bucket-tester-service.yaml -n istioinaction
# 확인
kubectl get pod -n istioinaction
NAME READY STATUS RESTARTS AGE
bucket-tester-688c598b47-86fbr 2/2 Running 0 25s
httpbin-85d76b4bb6-dz6b5 2/2 Running 0 2m56s
...
- 요청이나 응답 헤더를 조작하는 용도로 작성할 수 있는 루아 스크립트를 살펴보고, 이 사용 사례를 구현하는 방법도 살펴보자. Let’s look at a Lua script we can write to manipulate the request or response headers and how we can implement this use case.
- 엔보이에서는 루아 함수 envoy_on_reqest() 혹은 envoy_on_response() 를 구현해 요청과 응답 각각을 확인하고 조작할 수 있다. In Envoy, we can implement the envoy_on_request () or envoy_on_response() Lua function to inspect and manipulate the request and response, respectively.
- 루아 내에서 다른 서비스를 호출해야 한다면 엔보이가 제공하는 함수를 사용해야 한다 (RPC 호출을 하는데 범용 루아 라이브러리를 사용하면 안된다. 우리는 엔보이가 자체 논블로킹 non-blocking 스레딩 아키텍처로 호출을 올바르게 관리하길 원하기 때문이다). If we need to make a call to another service from within Lua, we have to use an Envoy-provided function (we should not use a general-purpose Lua library to make RPC calls, because we want Envoy to manage the call correctly with its non-blocking threading architecture).
- httpCall() 함수를 사용하면 외부 서비스와 통신할 수 있다. 다음 스크립트는 우리의 사용 사례를 구현한 것이다. We can use the http-Call() function to communicate with an external service. The following script implements our use case:
# cat ch14/lua-filter.yaml
...
function envoy_on_request(request_handle)
local headers, test_bucket = request_handle:httpCall(
"bucket_tester",
{
[":method"] = "GET",
[":path"] = "/",
[":scheme"] = "http",
[":authority"] = "bucket-tester.istioinaction.svc.cluster.local",
["accept"] = "*/*"
}, "", 5000)
request_handle:headers():add("x-test-cohort", test_bucket)
end
function envoy_on_response(response_handle)
response_handle:headers():add("istioinaction", "it works!")
end
...
- envoy_on_reqest() 함수를 구현하고, httpCall() 내장 함수를 사용해 외부 서비스와 통신하고 있다.
- 또한 응답을 받아 x-test-cohort 라는 헤더를 추가하고 있다. httpCall() 포함한 내장 함수는 문서 참조 - Docs
- 앞 절에서 했던 것처럼 이 스크립트를 EnvoyFilter 리소스에 추가할 수 있다.
# cat ch14/lua-filter.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: httpbin-lua-extension
namespace: istioinaction
spec:
workloadSelector:
labels:
app: httpbin
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
portNumber: 80
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.lua
typed_config:
"@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
inlineCode: |
function envoy_on_request(request_handle) # 아래 줄에 코드 입력
local headers, test_bucket = request_handle:httpCall(
"bucket_tester",
{
[":method"] = "GET",
[":path"] = "/",
[":scheme"] = "http",
[":authority"] = "bucket-tester.istioinaction.svc.cluster.local",
["accept"] = "*/*"
}, "", 5000)
request_handle:headers():add("x-test-cohort", test_bucket)
end
function envoy_on_response(response_handle) # 아래 줄에 코드 입력
response_handle:headers():add("istioinaction", "it works!")
end
- applyTo: CLUSTER
match:
context: SIDECAR_OUTBOUND
patch:
operation: ADD
value: # cluster specification
name: bucket_tester
type: STRICT_DNS
connect_timeout: 0.5s
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: bucket_tester
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
protocol: TCP
address: bucket-tester.istioinaction.svc.cluster.local
port_value: 80
workloadSelector 정의한 대로 이 필터를 httpbin 워크로드에 적용 후 httpbin 서비스 호출 확인! ⇒ 실습은 실패!
#
kubectl apply -f ch14/lua-filter.yaml
kubectl get envoyfilter -n istioinaction
# istio-proxy config 확인 내용 추가해두자
# httpbin 서비스 호출 확인!
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://httpbin.istioinaction:8000/ -v
...
< HTTP/1.1 503 Service Unavailable
< content-length: 39
< content-type: text/plain
< istioinaction: it works!
< date: Sun, 18 May 2025 07:59:34 GMT
< server: envoy
< x-envoy-upstream-service-time: 51
...
invalid header value for: x-test-cohort
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://httpbin.istioinaction:8000/headers
...
invalid header value for: x-test-cohort
# 정상 실습 시..
{
"headers": {
"Accept": "*/*",
"Content-Length": "0",
"Host": "httpbin.istioinaction:8000",
"User-Agent": "curl/7.69.1",
"X-B3-Sampled": "1",
"X-B3-Spanid": "1d066f4b17ee147b",
"X-B3-Traceid": "1ec27110e4141e131d066f4b17ee147b",
"X-Test-Cohort": "dark-launch-7" # A/B 테스트 서비스를 호출할 때 덧붙이는 새 헤더 x-test-cohort 가 보임
}
}
- 자세한 내용은 이 책 소스 코드에 있는 ch14/lua-filter.yaml 파일에서 살펴볼 수 있다.
- 이 예제에서는 데이터 플레인의 기능을 확장하기 위해 의도적으로 구축된 필터를 사용하는 방법을 알아봤다.
- 루아 스크립트 언어를 사용해 이 기능을 구현했으며, 내장 함수를 사용해 다른 함수를 호출했다.
- 다음 절에서는 웹어셈블리를 사용해 다른 언어로 우리의 커스텀 기능을 구현하는 방법을 다룬다.
- 다음 실습을 위해 리소스 삭제
#
kubectl delete envoyfilter -n istioinaction --all
kubectl get envoyfilter -A
#
kubectl delete -f ch14/httpbin.yaml -n istioinaction
kubectl delete -f ch14/bucket-tester-service.yaml -n istioinaction
14.5 웹어셈블리로 이스티오의 데이터 플레인 확장하기
들어가며 : 웹어셈블리로 새 엔보이 필터를 작성 - Docs Extending Istio’s data plane with WebAssembly
- 이 장에서 살펴보는, 요청 경로상에서 이스티오를 확장하는 마지막 방법은 웹어셈블리로 새 엔보이 필터를 작성하는 것이다. The last approach to extend Istio on the request path that we cover in this chapter is writing new Envoy filters with WebAssembly.
- 앞 절에서는 기존 엔보이 필터를 재사용하고 설정해 기본 이스티오 기능을 확장해봤으며, 요청 경로를 조작하는 우리의 자체 커스텀 스크립트를 주입하기도 했다.
- 이번 절에서는 우리의 자체 엔보이 필터를 구축하는 방법과 이스티오 데이터 플레인에 배포하는 방법을 살펴본다. In this section, we explore how we can build our own Envoy filters and dynamically deploy them to the Istio data plane.
https://istio.io/latest/blog/2020/deploy-wasm-declarative/
https://istio.io/latest/blog/2021/wasm-api-alpha/
https://istio.io/latest/blog/2021/wasm-progress/
https://istio.io/v1.17/docs/reference/config/proxy_extensions/wasm-plugin/
https://istio.io/v1.17/docs/ops/configuration/extensibility/wasm-pull-policy/
14.5.1 웹어셈블리 소개 Introducing WebAssembly
- 웹어셈블리 Wasm, WebAssembly 란 바이너리 명령 형식으로, 여러 가지 환경 간에 이식할 수 있는 것을 목표로 했으며 여러 프로그래밍 언어로 컴파일해 가상머신에서 실행할 수 있다. WebAssembly (Wasm) is a binary instruction format that is intended to be portable across environments and that can be compiled from many different programming languages and run in a VM.
- 본래 웹어셈블리는 브라우저의 웹 애플리케이션에서 CPU 집약적인 작업의 실행 속도를 높이고, 브라우저 기반 애플리케이션 지원을 자바스크립트 외의 언어로도 확장하기 위해 개발됐다 (그림 14.10 참조). Wasm was originally developed to speed up the execution of CPU-intensive operations for web apps in a browser and extend the support for browser-based apps to languages other than JavaScript.
- 웹어셈블리는 2019년에 W3C 권고 사항이 됐으며 모든 주요 브라우저에서 지원하고 있다. It became a W3C Recommendation in 2019 and is supported in all major browsers.
웹어셈블리는 모듈로 패키징된 커스텀 코드로, 웹 브라우저 같은 대상 호스트 안의 격리된 가상머신에서 안전하게 실행할 수 있다.
- 웹어셈블리는 저장 공간과 메모리를 적게 차지하고 거의 네이티브에 가까운 속도로 실행되도록 설계됐다. Wasm is intended to have a compact size and load footprint and execute at near-native speeds.
- 또한 호스트 애플리케이션(즉, 브라우저)에 내장돼도 안전한데, 메모리 안전 memory safe 하고 격리된 sandboxed 실행 환경(가상머신)에서 실행되기 때문이다. It is also safe to embed in host applications (that is, a browser) because it is memory safe and runs in a sandboxed execution environment (VM).
- 웹어셈블리 모듈은 호스트 시스템이 허용하는 메모리와 기능에만 접근할 수 있다. A Wasm module only has access to memory and functionality that the host system allows.
14.5.2 왜 엔보이에 웹어셈블리를 사용하는가? Why WebAssembly for Envoy?
- 네이티브 엔보이 필터를 직접 작성하는 데는 크게 두 가지 단점이 있다
- C++ 여야 한다.
- 변경 사항을 엔보이의 새 바이너리로 정적으로 빌드해야 하는데, 이는 사실상 엔보이 ‘커스텀’ 빌드다.
- 엔보이는 웹어셈블리 실행 엔진을 내장하고 있으므로, HTTP 필터를 포함해 엔보이의 다양한 영역을 커스터마이징하고 확장하는 데 사용할 수 있다. Envoy embeds a WebAssembly execution engine that can be used to customize and extend various areas of Envoy, including HTTP filters.
- 웹어셈블리가 지원하는 언어라면 어느 것이든 엔보이 필터로 작성해 그림 14.11 처럼 런타임에 프록시로 동적으로 불러올 수 있다. You can write Envoy filters in any language supported by Wasm and dynamically load them into the proxy at run time, as shown in figure 14.11.
- 즉, 이스티오에서 기본 엔보이 프록시를 계속 사용하면서 커스텀 필터를 런타임에 동적으로 불러올 수 있다는 것이다. This means you can keep using the out-of-the-box Envoy proxy in Istio and dynamically load your custom filters at run time.
How it works - Blog
- Set up local cache of Wasm extensions
- Pull desired Wasm extension into the local cache
- Mount the wasm-cache into appropriate workloads
- Configure Envoy with EnvoyFilter CRD to use the Wasm filter
Image fetcher in Istio agent - Blog
- Istio 1.9부터 Istio-agent는 EnvoyFiltersistio-agent 내부의 xDS 프록시와 Envoy의 확장 구성 검색 서비스(ECDS)를 활용하여 에 구성된 원격 HTTP 소스에서 가져온 Wasm 바이너리를 로드하는 안정적인 솔루션을 제공해 왔습니다. Istio 1.12의 새로운 Wasm API 구현에도 동일한 메커니즘이 적용됩니다. 원격 가져오기가 실패할 경우 Envoy가 잘못된 구성으로 인해 중단될 염려 없이 HTTP 원격 리소스를 안정적으로 사용할 수 있습니다.
- 또한, Istio 1.12는 이 기능을 Wasm OCI 이미지로 확장합니다. 즉, Istio 에이전트는 이제 Docker Hub, Google Container Registry(GCR), Amazon Elastic Container Registry(ECR) 등 모든 OCI 레지스트리에서 Wasm 이미지를 가져올 수 있습니다. 이미지를 가져온 후, Istio 에이전트는 Wasm 바이너리를 추출하여 캐시한 다음 Envoy 필터 체인에 삽입합니다.
Wasm module distribution via the Istio Agent - Blog
- Istio 1.9 이전에는 원격 Wasm 모듈을 프록시에 배포하기 위해 Envoy 원격 데이터 소스가 필요했습니다. 이 예시에서는 두 가지 EnvoyFilter리소스가 정의되어 있습니다. 하나는 원격 가져오기 Envoy 클러스터를 추가하는 리소스이고, 다른 하나는 HTTP 필터 체인에 Wasm 필터를 삽입하는 리소스입니다. 이 방법에는 단점이 있습니다. 잘못된 구성이나 일시적인 오류로 인해 원격 가져오기가 실패하면 Envoy가 잘못된 구성에 고정됩니다. Wasm 확장 프로그램이 fail closed 로 구성된 경우 , 잘못된 원격 가져오기로 인해 Envoy 서비스가 중단됩니다. 이 문제를 해결하려면 Envoy xDS 프로토콜을 근본적으로 변경 하여 비동기 xDS 응답을 허용해야 합니다.
- Istio 1.9는 istio-agent 내부의 xDS 프록시와 Envoy의 확장 구성 검색 서비스 (ECDS) 를 활용하여 기본적으로 안정적인 배포 메커니즘을 제공합니다 .
- istio-agent는 istiod에서 확장 구성 리소스 업데이트를 가로채고, 원격 페치 힌트를 읽어와 Wasm 모듈을 다운로드한 후, 다운로드된 Wasm 모듈의 경로로 ECDS 구성을 다시 작성합니다. 다운로드가 실패하면 istio-agent는 ECDS 업데이트를 거부하고 잘못된 구성이 Envoy에 도달하는 것을 방지합니다. 자세한 내용은 Wasm 모듈 배포 관련 문서를 참조하세요 .
14.5.3 웹어셈블리로 새로운 엔보이 필터 만들기 Building a new Envoy filter with WebAssembly
- 웹어셈블리로 엔보이 필터를 빌드하려면 어떤 언어를 사용하고 싶은지, 어떤 엔보이 버전을 사용 중인지, 그 버전에서 어떤 엔보이 ABI Abstract Binary Interace 를 지원하는지 등을 알아야 한다.
- 그러고 나서 적절한 언어 SDK를 골라 빌드 및 종속성 도구를 제대로 준비해야 한다.
- 이절에서는 Sola.io의 wasme 라는 오픈소스 개발자 도구를 사용해 엔보이용 웹어셈블리 필터를 만들고 빌드해본다.
- wasme 를 사용하면 엔보이 프로젝트용 웹어셈블리를 빠르게 부트스트랩하고 보일러플레이트 생성 boilerplate scaffolding 을 모두 자동화해준다.
- 그럼 웹어셈블리로 엔보이 필터 빌드를 시작하는 방법을 살펴보자.
- 이 책을 저술하는 시점에 엔보이 웹어셈블리 SDK가 있는 프래그래밍 언어는 다음 네 가지다.
- C++
- Rust
- AssemblyScript 어셈블리스크립트 (TypeScript 타입스크립트)
- TinyGo
- 이 절에서는 어셈블리스크립트 https://www.assemblyscript.org/ 를 사용해 웹어셈블리로 새 엔보이 필터를 빌드해본다.
- 어셈블리스크립트는 타입스크립트의 변형이므로 자바스크립트 개발자라면 익숙할 것이다.
- 어셈블리스크립트는 엔보이 필터를 구축하는 데 있어 C++의 훌륭한 대체재다.
엔보이의 웹어셈블리의 지원은 실험적인 것으로 간주되므로 바뀔 수 있다. 따라서 직접 만들어 엔보이에 배포하는 웹어셈블리 모듈은 운영 환경에 들어가기 전에 모두 철저히 테스트해볼 것을 권장한다.
14.5.4 meshctl 로 새 엔보이 필터 빌드하기 Building a new Envoy filter with the meshctl tool 버전 차이로 실습 불가능!
[공식 문서] Distributing WebAssembly Modules 실습 - Docs
- 소개
- Istio는 WebAssembly(Wasm)를 사용하여 프록시 기능을 확장할 수 있는 기능을 제공합니다.
- Wasm 확장성의 주요 장점 중 하나는 런타임에 확장 기능을 동적으로 로드할 수 있다는 것입니다.
- 이러한 확장 기능은 먼저 Envoy 프록시에 배포해야 합니다.
- Istio는 프록시 에이전트가 Wasm 모듈을 동적으로 다운로드할 수 있도록 허용하여 이를 가능하게 합니다.
- Wasm 모듈 구성
- 이 예시에서는 메시에 HTTP 기본 인증 확장을 추가합니다.
- 원격 이미지 레지스트리에서 기본 인증 모듈을 가져와 로드하도록 Istio를 구성합니다.
- 이 모듈은 API 호출 시 실행되도록 구성됩니다 /api.
- 원격 Wasm 모듈로 WebAssembly 필터를 구성하려면 WasmPlugin리소스를 생성하세요.
# 설정 전 호출 확인
EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog -v
#
kubectl explain wasmplugins.extensions.istio.io
...
#
kubectl apply -f - <<EOF
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: basic-auth
namespace: istio-system # 모든 네임스페이스에 영향
spec:
selector:
matchLabels:
istio: ingressgateway
url: oci://ghcr.io/istio-ecosystem/wasm-extensions/basic_auth:1.12.0
phase: AUTHN
pluginConfig:
basic_auth_rules:
- prefix: "/api"
request_methods:
- "GET"
- "POST"
credentials:
- "ok:test"
- "YWRtaW4zOmFkbWluMw=="
EOF
#
kubectl get WasmPlugin -A
NAMESPACE NAME AGE
istio-system basic-auth 21s
구성된 Wasm 모듈을 확인
# 자격 증명 없이 테스트
EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog -v
# 자격 증명으로 테스트
docker exec -it mypc curl -s -H "Authorization: Basic YWRtaW4zOmFkbWluMw==" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog -v
실습 완료 후 리소스 삭제
#
kubectl delete WasmPlugin -n istio-system basic-auth
Istio 에 Coraza (코라자) WAF 적용 - KrBlog
- Coraza is an open source, high performance, Web Application Firewall ready to protect your beloved applications - Home , Link
- Go 언어로 작성되었으며, ModSecurity SecLang 규칙 세트를 지원하고 OWASP 핵심 규칙 세트와 100% 호환됩니다.
- ⇲ Drop-in - Coraza는 부분 호환성이 있는 대체 엔진입니다.
트러스트웨이브OWASP ModSecurity 엔진을 지원하며 업계 표준 SecLang 규칙 세트를 지원합니다. - 🔥 보안 - Coraza는 OWASP CRS를 실행하여 OWASP Top Ten을 포함한 다양한 공격으로부터 웹 애플리케이션을 최소한의 오탐으로 보호합니다. CRS는 SQL 인젝션(SQLi), 크로스 사이트 스크립팅(XSS), PHP 및 Java 코드 인젝션, HTTPoxy, Shellshock, 스크립팅/스캐너/봇 탐지, 메타데이터 및 오류 유출 등 다양한 일반적인 공격 유형을 차단합니다.
- 🔌 확장성 - Coraza는 핵심 라이브러리로, 온프레미스 웹 애플리케이션 방화벽 인스턴스를 구축하기 위한 다양한 통합 기능을 제공합니다. 감사 로거, 지속성 엔진, 연산자, 액션 등 Coraza를 원하는 대로 확장할 수 있는 자체 기능을 생성하세요.
- 🚀 성능 - 대규모 웹사이트부터 소규모 블로그까지, Coraza는 성능 저하를 최소화하면서 부하를 처리할 수 있습니다. 벤치마크를 확인해 보세요.
- ﹡ 단순성 - 누구나 Coraza 소스 코드를 이해하고 수정할 수 있습니다. 새로운 기능으로 Coraza를 쉽게 확장할 수 있습니다.
- 플러그인 지원
- **Proxy WASM extension for proxies with proxy-wasm support (e.g. Envoy) - stable, still under development**
- Caddy Reverse Proxy and Webserver Plugin - stable, needs a maintainer
- HAProxy SPOE Plugin - preview
- Traefik Proxy Plugin - preview, needs maintainer
- Gin Web Framework Middleware - preview, needs maintainer
- Apache HTTP Server - experimental
- Nginx - experimental
- Coraza C Library - experimental
- Quick Start - Docs
- OWASP Core Ruleset - Docs
- Playground - Link
- 추천 영상
https://www.youtube.com/watch?v=SCEZGRvcxc8
- proxy-wasm filter based on Coraza WAF - Github
- Coraza 기반으로 구축되고 proxy-wasm ABI를 구현하는 웹 애플리케이션 방화벽 WASM 필터입니다.
- Envoy에서 직접 로드하거나 Istio 플러그인으로 사용할 수도 있습니다.
- WAF는 플러그 앤 플레이 방식의 보안 솔루션이 아닙니다. WAF가 효과적으로 작동하려면 보호하려는 환경과 트래픽에 맞춰 구성 및 튜닝이 필요합니다.
- 실제 운영 환경에서는 배포된 구성( @recommended-conf 및 @crs-setup-conf 참조 )을 완전히 숙지하고 사용된 규칙 세트에 대한 튜닝 단계를 수행하는 것이 좋습니다.
- OWASP 핵심 규칙 세트(CRS) 튜닝에 대한 자세한 내용은 " False Positives and Tuning" 가이드를 참조하십시오.
- Istio WasmPlugin 설정
#
kubectl apply -f - <<EOF
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: coraza-ingressgateway
namespace: istio-system
spec:
selector:
matchLabels:
istio: ingressgateway
url: oci://ghcr.io/corazawaf/coraza-proxy-wasm
phase: AUTHN
pluginConfig:
default_directives: default
directives_map:
default:
- Include @demo-conf
- SecDebugLogLevel 9
- SecRuleEngine On
- Include @crs-setup-conf
- Include @owasp_crs/*.conf
EOF
#
kubectl logs -n istio-system -l app=istio-ingressgateway | grep -i wasm
2025-05-18T09:22:39.344842Z info wasm fetching image corazawaf/coraza-proxy-wasm from registry ghcr.io with tag latest
#
kubectl get WasmPlugin -A
NAMESPACE NAME AGE
istio-system coraza-ingressgateway 9s
동작 확인
#
curl -s http://webapp.istioinaction.io:30000/api/catalog
# XSS (Cross-Site Scripting) 공격 시도 : REQUEST-941-APPLICATION-ATTACK-XSS
curl -s 'http://webapp.istioinaction.io:30000/api/catalog?arg=<script>alert(0)</script>' -IL
HTTP/1.1 403 Forbidden
# 로그 모니터링
kubectl logs -n istio-system -l app=istio-ingressgateway -f
2025-05-18T09:34:21.049522Z critical envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1157 wasm log istio-system.coraza-ingressgateway: [client "172.18.0.1"] Coraza: Access denied (phase 1). Inbound Anomaly Score Exceeded in phase 1 (Total Score: 20) [file "@owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "11347"] [id "949111"] [rev ""] [msg "Inbound Anomaly Score Exceeded in phase 1 (Total Score: 20)"] [data ""] [severity "emergency"] [ver "OWASP_CRS/4.0.0-rc2"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [hostname "10.10.0.10"] [uri "/api/catalog?arg=<script>alert(0)</script>"] [unique_id "ztBHScSiiDnOiwJfmOy"] thread=30
[2025-05-18T09:34:21.014Z] "HEAD /api/catalog?arg=<script>alert(0)</script> HTTP/1.1" 403 - - "-" 0 0 35 - "172.18.0.1" "curl/8.7.1" "27f43c69-359d-9d95-99cd-553081bb9346" "webapp.istioinaction.io:30000" "-" outbound|80||webapp.istioinaction.svc.cluster.local - 10.10.0.10:8080 172.18.0.1:65166 - -
# SQLI phase 2 (reading the body request)
curl -i -X POST 'http://webapp.istioinaction.io:30000/api/catalog' --data "1%27%20ORDER%20BY%203--%2B"
HTTP/1.1 403 Forbidden
# 로그 모니터링
kubectl logs -n istio-system -l app=istio-ingressgateway -f
2025-05-18T09:42:40.613124Z critical envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1157 wasm log istio-system.coraza-ingressgateway: [client "172.18.0.1"] Coraza: Access denied (phase 2). Inbound Anomaly Score Exceeded (Total Score: 10) [file "@owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "11358"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 10)"] [data ""] [severity "emergency"] [ver "OWASP_CRS/4.0.0-rc2"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [hostname "10.10.0.10"] [uri "/api/catalog"] [unique_id "GYSnZlurGTXFtujeNgl"] thread=34
[2025-05-18T09:42:40.575Z] "POST /api/catalog HTTP/1.1" 403 - - "-" 26 0 39 - "172.18.0.1" "curl/8.7.1" "c30d47e1-5631-99ae-91f2-5dfd8174b421" "webapp.istioinaction.io:30000" "10.10.0.16:8080" outbound|80||webapp.istioinaction.svc.cluster.local 10.10.0.10:44518 10.10.0.10:8080 172.18.0.1:63266 - -
...
프로메테우스 메트릭 확인 : curl -s localhost:8082/stats/prometheus | grep waf_filter
참고 영상
https://www.youtube.com/watch?v=QJJBYOKKui0
https://www.youtube.com/watch?v=47MxrFX1rRg
https://www.youtube.com/watch?v=NE9DvPwgNZc
Summary
- 엔보이의 내부 아키텍처는 리스너와 필터를 중심으로 구축됐다. Envoy’s internal architecture is built around listeners and filters.
- 즉시 사용할 수 있는 엔보이 필터가 많다. There are many out-of-the-box Envoy filters.
- 이스티오의 데이터 플레인(엔보이 프록시)을 확장할 수 있다. We can extend Istio’s data plane (Envoy proxy).
- 이스티오의 EnvoyFilter 리소스를 사용해 엔보이의 HTTP 필터 구조를 직접 구성하면 더 세밀한 설정이 가능하며, 이스티오의 API로는 노출되지 않는 엔보이의 부분들도 설정할 수 있다. Envoy’s HTTP filter architecture can be configured directly with Istio’s EnvoyFilter resource for more fine-grained configuration or to configure aspects of Envoy not exposed by Istio’s API.
- 속도 제한이나 tap 필터 같은 기능으로 서비스 간 통신에서 엔보이의 요청 경로를 확장할 수 있다. We can extend Envoy’s request path for service-to-service communication with functionality like rate limiting or the tap filter.
- 루아와 웹어셈블리를 사용하면 엔보이를 다시 빌드하지 않고도 데이터 플레인을 고급 수준에서 커스터마이징할 수 있다. Lua and Wasm are available for more advanced customizations to the data plane without having to rebuild Envoy.
실습 후 kind 삭제 : kind delete cluster --name myk8s && docker rm -f mypc
'스터디 > Istio Hands-on Study' 카테고리의 다른 글
Istio Hands-on Study [1기] - 9주차 - Ambient Mesh - 실습 (1) | 2025.06.06 |
---|---|
Istio Hands-on Study [1기] - 8주차 - VM Support & Istio Traffic Flow - 13장 (1) | 2025.05.31 |
Istio Hands-on Study [1기] - 7주차 - 조직 내에서 이스티오 스케일링하기 - 12장 (2) | 2025.05.25 |
Istio Hands-on Study [1기] - 6주차 - 데이터 플레인 트러블 슈팅하기 - 11장 (1) | 2025.05.18 |
Istio Hands-on Study [1기] - 6주차 - 데이터 플레인 트러블 슈팅하기 - 10장 (0) | 2025.05.18 |