Ssoon

[7주차] 요청 경로에서 Istio 확장 : Lua로 Istio의 Data Plane 확장 본문

Istio Hands-on Study [1기]

[7주차] 요청 경로에서 Istio 확장 : Lua로 Istio의 Data Plane 확장

구구달스 2025. 4. 23. 16:52

🚀 Lua로 Istio 데이터 플레인 확장하기


🛠️ Lua 필터란?

Lua와 Envoy의 통합

  • Lua는 가볍고 임베디드 시스템에 적합한 스크립팅 언어로, Envoy에서는 LuaJIT 가상 머신을 통해 실행됩니다.
    Envoy의 Lua 필터를 사용하면 요청(envoy_on_request) 또는 응답(envoy_on_response) 경로에서 헤더 조작, 본문 검사 같은 커스텀 로직을 구현할 수 있습니다.

요청 본문을 검사하면 프록시가 본문을 메모리에 완전히 버퍼링할 수 있어 성능에 영향을 줄 수 있습니다. 

  • Lua 스크립트는 EnvoyFilter 리소스를 통해 Envoy의 HttpConnectionManager(HCM) 필터 체인에 삽입됩니다. 이를 통해 기존 필터로는 구현할 수 없는 유연한 확장이 가능합니다.
Lua 필터는 Lua 스크립트를 통해 Envoy의 요청/응답 경로를 커스터마이징하며,
성능 주의가 필요합니다.

🔧 A/B 테스트를 위한 Lua 필터 설정

예제 시나리오

  • A/B 테스트를 위해 요청마다 동적으로 테스트 그룹을 결정하고, 이를 헤더로 추가하는 시나리오를 구현해 보겠습니다. 요청이 들어오면 외부 A/B 테스트 서비스(bucket-tester)를 호출해 그룹을 결정하고, 결과를 x-test-cohort 헤더로 추가합니다.

환경 설정

  • 테스트를 위한 서비스를 배포합니다:
$ kubectl apply -f ch14/httpbin.yaml -n istioinaction
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created

$ kubectl apply -f ch14/bucket-tester-service.yaml -n istioinaction
service/bucket-tester created
deployment.apps/bucket-tester created
  • httpbin: 요청 헤더를 에코하는 서비스
  • bucket-tester: 요청 헤더를 평가해 A/B 테스트 그룹을 반환
A/B 테스트 예제를 위해 httpbin과 bucket-tester 서비스를 배포합니다.

🛠️ Lua 스크립트 구현

Lua 스크립트 작성

  • Lua 필터는 envoy_on_request 함수를 구현해 요청을 처리합니다.
    외부 서비스 호출에는 Envoy의 httpCall 함수를 사용하며, 이는 Envoy의 비동기 스레드 아키텍처를 준수합니다.
    아래는 bucket-tester 서비스를 호출해 테스트 그룹을 추가하는 Lua 스크립트입니다:
function envoy_on_request(request_handle) #Envoy 필터가 요청을 받을 때 실행되는 함수를 정의
  local headers, test_bucket = request_handle:httpCall( 
  #Envoy 프록시가 다른 서비스에 HTTP 요청을 보내는 기능을 사용
  #request_handle:httpCall 메서드는 지정된 서비스에 HTTP 요청을 보내고, 응답 헤더와 본문을 반환
    "bucket_tester", #요청을 보낼 업스트림 서비스의 이름
    {
      [":method"] = "GET", #HTTP 메서드를 GET으로 설정
      [":path"] = "/", #요청 경로를 루트 경로("/")로 설정
      [":scheme"] = "http",
      [":authority"] = "bucket-tester.istioinaction.svc.cluster.local",
      #Host 헤더를 설정합니다.  이는 요청을 보낼 서비스의 주소
      ["accept"] = "*/*" #Accept 헤더를 설정하여 모든 미디어 타입을 허용
    },
    "", #요청 본문(body)을 빈 문자열로 설정
    5000 #요청 타임아웃을 5000밀리초(5초)로 설정
  )
  request_handle:headers():add("x-test-cohort", test_bucket)
  #httpCall 메서드로부터 받은 응답 본문(test_bucket)을 원래 요청의 헤더에 추가
end
  • httpCall: bucket-tester 서비스에 GET 요청을 보내고 응답을 test_bucket에 저장
  • headers():add: 응답 값을 x-test-cohort 헤더로 추가

일반 Lua 라이브러리로 RPC 호출을 하면 Envoy의 스레드 모델과 충돌할 수 있으므로,
httpCall 같은 Envoy 내장 함수를 사용해야 합니다.

Lua 스크립트는 httpCall로 외부 서비스를 호출하고,
결과를 헤더로 추가해 요청을 커스터마이징합니다.

🔍 EnvoyFilter로 Lua 필터 적용

EnvoyFilter 설정

  • Lua 스크립트를 Envoy의 HCM 필터 체인에 삽입하려면 EnvoyFilter 리소스를 사용합니다.
    아래는 httpbin 워크로드에 Lua 필터를 적용하는 설정입니다:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: webapp-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.filters.http.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
  • workloadSelector: app: httpbin 워크로드에 적용
  • applyTo: HTTP 필터를 80번 포트의 HCM에 삽입
  • operation: envoy.filters.http.router 앞에 Lua 필터 삽입

  • 설정을 적용합니다:
$ kubectl apply -f ch14/lua-filter.yaml
envoyfilter.networking.istio.io/httpbin-lua-extension created
EnvoyFilter를 사용해 Lua 스크립트를 HCM 필터 체인에 삽입하며,
특정 워크로드에 적용합니다.

🛠️ 테스트 및 검증

Lua 필터 동작 확인

  • httpbin 서비스에 요청을 보내 Lua 필터가 x-test-cohort 헤더를 추가하는지 확인합니다: 실패! => 확인필요invalid header value for: x-test-cohort 오류 메시지는 Envoy 프록시가 x-test-cohort라는 이름의 헤더에 할당된 값을 유효한 헤더 값 형식으로 인식하지 못했다는 것을 의미합니다. 
$ kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://httpbin.istioinaction:8000/ -v
* Host httpbin.istioinaction:8000 was resolved.
* IPv6: (none)
* IPv4: 10.200.3.30
*   Trying 10.200.3.30:8000...
* Connected to httpbin.istioinaction (10.200.3.30) port 8000
> GET / HTTP/1.1
> Host: httpbin.istioinaction:8000
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 503 Service Unavailable
< content-length: 39
< content-type: text/plain
< istioinaction: it works!
< date: Tue, 20 May 2025 02:10:10 GMT
< server: envoy
< x-envoy-upstream-service-time: 97
<
* Connection #0 to host httpbin.istioinaction left intact
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"
  }
}
  • x-test-cohort 헤더가 포함되어 있다면 Lua 필터가 정상적으로 동작하는 것입니다.
Lua 필터는 요청에 x-test-cohort 헤더를 추가하며,
curl로 테스트해 동작을 확인할 수 있습니다.

📌 핵심 요약

  • Lua 필터의 역할: Lua 스크립트를 사용해 Envoy의 요청/응답 경로에 커스텀 로직을 추가하며, 헤더 조작이나 외부 서비스 호출이 가능합니다.
  • A/B 테스트 예제: httpCall로 bucket-tester 서비스를 호출해 A/B 테스트 그룹을 결정하고, 결과를 x-test-cohort 헤더로 추가합니다.
  • EnvoyFilter 설정: Lua 스크립트를 HCM 필터 체인에 삽입해 httpbin 워크로드에 적용합니다.
  • 주의사항: 요청 본문 처리는 성능에 영향을 줄 수 있으며, Envoy의 내장 함수(httpCall)를 사용해야 스레드 안전성을 보장합니다.

 

Comments