공부하고 기록하는, 경제학과 출신 개발자의 노트

학습일지/Service Mesh

istioCon 2021 - What envoy hears when Istio Speaks

inspirit941 2023. 2. 3. 08:59
반응형

https://www.youtube.com/watch?v=nNTpfXSCLKs 

Understading the Data plane - What envoy hears when istio speaks

스크린샷 2023-01-30 오전 11 42 23

 

  • What is istiod
  • how it interacts with k8s / envoy
  • what is envoy / how envoy works
  • high-level tour of 'what an istio-generated envoy config actually looks like'

 

스크린샷 2023-01-30 오전 11 44 53

envoyproxy github을 보면 go-control-plane이 있음. envoyproxy를 관리하기 위한 표준 (canonical way). istiod is built on top of this (go-control-plane).

따라서 istiod를 커뮤니티에서 지칭하는 표현도 여러 가지.

  • pilot
  • discovery / istio discovery service...

스크린샷 2023-01-30 오전 11 49 27

 

istiod 컴포넌트를 간단하게 요약하면 위의 세 가지로 구분됨.

  • Citadel이 하는 일은 Know your peers 에서 다른 사람이 설명할 예정
  • Galley, pilot을 설명할 예정.

 

스크린샷 2023-01-30 오전 11 52 28

 

istiod에서 하는 일을 간단히 정리하면

  • Read from kubernetes
  • Write to Envoy.
    일종의 One-way pipe

Galley

service Discovery

스크린샷 2023-01-30 오후 1 28 51스크린샷 2023-01-30 오후 1 33 57

 

공식문서에는 'istio does not provide service discovery' 라고 쓰여 있는데, 이거 쓴 사람이 관련 내용을 뭔가 추가로 설명 좀 해줬으면 좋겠다.

istiod 컴포넌트의 galley가 하는 역할이 'discover services in k8s'이기 때문.

  • service discovery
  • service registry : keeps track of all the things that it's discovered

k8s API나 Conzul, zookeeper 같은 여러 discover services를 지원하고 있고, conzul이나 zookeeper같은 컴포넌트는 Mesh Configuration Protocol (MCP) 형태로 지원하고 있다.

  • 즉 k8s API / MCP를 통해 istiod와 통신하고, 연결되어 있는 컴포넌트의 정보를 istiod에 전달함.
  • 예컨대 클러스터 외부에 배포된 redis라 해도, mesh configuration protocol을 활용하면 service mesh로 접근할 수 있게 되는 것.

스크린샷 2023-01-30 오후 1 34 39스크린샷 2023-01-30 오후 1 34 44

 

istiod pod를 포트포워딩해서 debug/registryz 도메인으로 접근하면, istiod에서 discover / manage중인 service 정보를 확인할 수 있다.

serves Webhook Request

스크린샷 2023-01-30 오후 1 43 08

 

istio leverages k8s webhooks to provide some services it does. 사용하고 있는 Webhook 서비스는 크게 두 가지

  • Mutating Webhook : utilized to deliver pod injection
    • 예컨대 k8s api로 create a pod 요청을 보내면, webhook configuration causes k8s api to fire a request over to galley. -> munch the yaml, hand it back -> pod에 sidecar를 붙임.
  • Validating Webhook : CRD yaml을 istiod에 전달할 때, crd의 validation을 담당함

스크린샷 2023-01-30 오후 1 43 19

 

validationWebhookConfiguration에 정의된 내용을 보면

  • apiGroup에 정의된 object들 (config.istio.io / rbac.istio.io / security.istio.io / authentication.istio.io / networking.istio.io )
  • CREATE / UPDATE 요청이 들어오면
  • post payload가 clientConfig 하위에 정의한 service로 전달됨.
    • name: istiod
    • namespace: istio-system
    • path: /validate
    • port: 443

 

스크린샷 2023-01-31 오후 12 16 09

 

MutatingWebhookConfiguration의 sidecar inject 관련 로직

  • istio-injection: enabled 라는 label이 namspace에 존재할 경우
  • pod CREATE 요청이 들어올 때
  • clientConfig에 정의된 service로 요청을 보낸다
    • name: istiod
    • namespace: istio-system
    • path: /inject
    • port: 443

Pilot

스크린샷 2023-01-31 오후 12 36 13

 

Galley에서 제공한 service Discovery 정보를 토대로 envoy에 코드를 쓰는 컴포넌트.

스크린샷 2023-01-31 오후 12 20 32

 

istio에서 수행하는 kubernetes Resource -> Envoy Configuration 컴포넌트로의 변환.

사실 Pilot 자체는 크게 설명할 게 없음. 변환해주는 게 전부임.

중요한 건 how envoy responds to what pilot sends down.

Envoy

스크린샷 2023-01-31 오후 12 40 29

 

스크린샷 출처: Envoy Internals Deep Dive.

istio는 envoy 소스를 포크해서 쓰고 있음 (https://github.com/istio/proxy)

  • istio 쪽에서 여러 custom functionality를 추가해 쓰고 있음
  • 커스텀한 기능을 WebAssembly Extension (wasm) 로도 지원한다고 함. 일단은 커스텀한 코드 대부분 static c++로 개발되어 있음

 

스크린샷 2023-01-31 오후 1 15 42

 

Terminology

upstream / downstream
  • Upstream: Where envoy connections come from.
  • Downstream : Where envoy connections go to.

 

스크린샷 2023-01-31 오후 1 24 43

 

처음 접했을 때 용어가 쉽게 와닿지 않는데, 이렇게 연상하면 좀더 이해가 편하다.

  • client에서 backend로 요청을 보내는 flow를 남쪽 -> 북쪽이라는 방향성으로 이해해보자.

스크린샷 2023-01-31 오후 1 26 36스크린샷 2023-01-31 오후 1 26 46

frontend에 envoy proxy가 붙어 있다면

  • frontend로 요청을 보내는 client의 트래픽은 Envoy 입장에서 downstream. 쉽게 말해 client는 downstream
  • client로 들어온 요청을 backend로 전달할 때의 트래픽은 Envoy 입장에서 upstream. 쉽게 말해 backend는 upstream

 

backend에 envoy proxy가 붙어 있다면

  • backend에 요청을 보내는 frontend는 envoy 입장에서 downstream
  • 로직상 다른 Api를 추가로 호출해야 한다면 해당 API는 upstream. 예시로는 Google API가 upstream이 된다.

 

스크린샷 2023-01-31 오후 1 32 07

 

이외에도 Terminology를 이해시키려는 다양한 비유법이 있음. 위성에 비유하기도 함.

cluster, endpoint, listener, routes

envoy는 kubernetes가 아니므로, envoy에서 쓰이는 용어는 별개로 이해해야 함.

스크린샷 2023-01-31 오후 1 33 54스크린샷 2023-01-31 오후 1 34 03

 

Cluster는 HostName, Endpoint는 ip address에 대응되는 개념이라고 이해하자.

 

스크린샷 2023-01-31 오후 1 34 14

 

  • Listener : What to accept / How to process it
    • 보통 tcp / udp, path for unix domain socket
    • envoy에서 사용하는 filter chain을 구성.
  • Routes : Where to send it
    • http traffic만을 담당함. 트래픽을 특정 backend로 forwarding. nginx 기능과 유사
    • i.e. 특정 header값 / uri값에 매칭되는 service로 트래픽 전달하는 식.

Operation

스크린샷 2023-02-01 오전 9 31 08

envoy 문서에서 확인할 수 있는 filter chain 동작방식.

  • 아무것도 정의하지 않은 default filter chain의 경우, connection close라는 기본 동작만 수행한다.

 

스크린샷 2023-02-01 오전 9 33 13스크린샷 2023-02-01 오전 9 33 21

예컨대 간단한 python webserver를 구현해서 로그를 찍는다고 할 때

  • client request -> python logic -> response로 이어지는 flow에서
    • somebody had to decode tcp byte stream
    • recognize that it's http + decode it
    • turn it into python data type...
      와 같은 작업이 필요함. envoy filter chain에 대입해보면 대략 아래와 같다.

스크린샷 2023-02-01 오전 9 36 05

 


스크린샷 2023-02-01 오전 9 38 31

 

envoy proxy가 있는 상황에서, source-app 에서 dest-app으로 요청을 전달하는 예시.

 

스크린샷 2023-02-01 오전 9 40 22스크린샷 2023-02-01 오전 9 40 58

 

  • istio의 galley 컴포넌트가 service discovery를 수행하면서 k8s service인 dest-app을 확인한다.
  • k8s service의 존재를 확인했다면, envoy는 위와 같은 filter chain을 구성한다.
    • source-app의 service vip (10.4.0.3_8000)
    • single link in a filter chain (envoy.tcp_proxy)
    • backend 서비스에 해당하는 envoy cluster (outbound|8000|dest-app.dest-app.svc.cluster.local) 로 트래픽 전달

 

스크린샷 2023-02-01 오전 10 05 43

 

실제 envoy Configuration json값은 위와 같다. istioctl proxy-config (istioctl pc) 로 확인 가능.

 

스크린샷 2023-02-01 오전 10 08 31

 

istio는 filter chain을 구성할 때 istio.stats 라는 filter를 추가하게 되어 있다.

  • 이 filter가 있기 때문에 istio metric을 수집하고 볼 수 있다.
    • connection을 의미하는 'cx' suffix -> tcp stats filter가 생성.
    • request를 의미하는 'rq' suffix -> http stats filter가 생성.
filter chain

스크린샷 2023-02-01 오전 10 20 09

 

Listener Filter

  • 트래픽이 제일 먼저 거쳐가는 곳.
  • 어떤 종류의 connection인지 확인하고, sidecar memory에 metadata를 저장함
    • TLS 트래픽인지, http 트래픽인지 등등
    • 어떤 종류의 트래픽인지에 따라 다음에 동작하게 될 Network Filter 종류가 달라짐

Network Filter

  • L7 Layer. 따라서 L7에서 할 수 있는 기능인 Connection pooling / smart routing 등이 가능함.
    • Http Network filter는 HCM (http connection manager) 으로 축약해서 많이 씀.
    • HCM에서는 decoding tcp bytes into real http constructs.
    • technically it's complete service. 커스텀 가능함.

Http Filter

  • Network filter의 subcategory이지만, 워낙 중요도와 사용빈도가 높기 때문에 별도로 분리.
    • collecting http stats, injecting headers, rate limiting / gzip compression / request routing / cors headers... 등 여러 기능이 http filter에 구현되어 있음.
    • 이 중 router filter는 보통 filter의 가장 마지막 단계에서 진행됨. router filter가 있어야 트래픽을 다음 destination으로 보낼 수 있기 때문.

모든 filter를 통과하고, 목적지가 될 envoy cluster로 트래픽을 전달할 영역 (그림의 빨간색 작은 화살표 부분)은 따로 설명이 필요함.

 

스크린샷 2023-02-01 오전 10 41 54

 

트래픽이 envoy로 들어와서 모든 filter를 거쳐가기까지의 과정: 그림의 연두색 화살표 부분.

  • Cluster means hostname
  • upstream means 'where connections go'

route filter가 어디로 트래픽을 전달할 것인지를 결정하고 나면

  • upstream service에 연결해달라고 cluster manager에게 요청을 보낸다.
  • cluster manager는 '어느 endpoint가 healthy한 상황인지'를 관리, load balancing / connection pooling 등을 담당함.

envoy docs에 Listener / Cluster Manager가 동작하는 flow를 설명한 다이어그램도 있음.

스크린샷 2023-02-02 오후 1 04 22

 

Filter chain Match

스크린샷 2023-02-02 오후 1 06 23스크린샷 2023-02-02 오후 1 12 51

 

트래픽이 들어왔을 때, http인지 TCP인지 등등... 을 구분하는 영역이 Filter Chain Match.

  • FCM에서 트래픽을 HTTP로 분류하면, HCM (http connection manager) 으로 트래픽 전달.
  • http 트래픽이 아니면, default 옵션은 TCP 트래픽으로 분류함. tcp proxy로 트래픽 전달
  • FCM에서 트래픽 종류를 체크하고, http / tcp에 맞게 핸들링하기 전 istio.stats 필터를 통과함.

 

스크린샷 2023-02-02 오후 1 08 57

 

envoy에서는 아래와 같은 json 파일로 확인 가능. filter chains 하위에 있는 filter_chain_match 구조는 아래와 같음.

"filter_chains": [
  {
    "filter_chain_match": { // 아래 application protocol에 일치하는 게 있으면
      "application_protocols": [ 
        "http/1.0",
        "http/1.1",
        "h2c"
      ]
    },
    "filters" : [ ... ] // 이 필터를 사용한다
  },
  ...
]
k8s pod의 inbound / outbound 요청을 envoy Sidecar에서 어떻게 interrupt하는지

스크린샷 2023-02-02 오후 1 50 49스크린샷 2023-02-02 오후 1 51 05스크린샷 2023-02-02 오후 2 23 33

 

istio sidecar가 포함된 pod에서 iptable 정보를 조회하면 위와 같다. istio 관련 값은 init container를 통해 pod에 추가된 것들. 관련 로직은 pilot-agent에서 확인할 수 있다.

  • istio output으로, outbound로 나가는 트래픽은 상단의 빨간 네모박스의 iptable chain을 통과함.
    • basically (not all traffic) anything that comes out of this pod (ISTIO_OUTPUT) needs to pass through this one chain (ISTIO_REDIRECT).
    • ISTIO_REDIRECT는 스크린샷 맨 마지막줄에 있음. --to-ports 15001. localhost:15001로 redirect된다는 뜻.

 

스크린샷 2023-02-02 오후 2 47 47

 

  • istio_inbound 트래픽도 마찬가지로 iptable chain을 통과한다.
    • 외부로부터 오는 트래픽은 ISTIO_IN_REDIRECT라는 chain을 통과하는데, --to-port 15006 이 들어가 있음. 즉 localhost:15006 으로 redirect된다.

 

스크린샷 2023-02-02 오후 2 49 57

 

sidecar의 listening process를 확인해보면, data plane에서 외부 트래픽을 받는 포트는 사실상 15001, 15006 두 개.

  • 나머지는 Debug / stats용 port 등 별도의 목적으로 사용함

스크린샷 2023-02-02 오후 2 59 56

 

envoy 공식문서에서도 깔끔하게 정리되어 있지는 않은 부분.

  • source app이 10.4.0.3:8000 목적지로 트래픽을 보낸다
  • destination ip / port는 iptable shaped mirror (envoy가 활용하는 iptable) 을 거치며 해당 pod의 sidecar (localhost:15001) 로 전달된다. 일종의 interrupt인 셈
  • envoy filter를 거쳐가며 동작을 수행한다
  • 로직이 끝나고 외부로 트래픽을 전달할 때 original dst 로 ip / port를 복원해서 전달한다. (복원 로직까지는 설명하지 않음)

 

스크린샷 2023-02-02 오후 3 19 42

 

즉, 복원한 original destination ip / port (예시의 경우 10.4.0.3:8000)를 가지고, istio galley가 확보한 service discovery 결과들 중 일치하는 곳으로 트래픽을 전달함.

Extension / Plugins

스크린샷 2023-02-01 오전 10 14 53

커뮤니티에서 혼용되어 쓰이는데, Extension과 plugin 둘 다 같은 말임.

  • All filters in filter chain are Extensions. 모든 종류의 Filter는 extension의 한 종류이지만
  • Not all extensions are filters. 모든 extension이 filter를 의미하는 건 아니다.
  • 위에 쓰여 있는 것들은 filter가 아닌, 다른 종류의 extensions; access loggers, retry implementation, tracers, resource monitors, credential provider

물론 일반적으로 data plane 이라고 하면 Filter chain을 말하는 편임. 핵심 기능이라서

Envoy Configuration

스크린샷 2023-02-02 오후 3 30 57

 

IstioCon에서 WebAssembly 관련 발표를 한 Yuval Kohavi의 envoy config 관련 코멘트 참고.

  • Envoy Config is Not meant for people. it's meant for machines
    무슨 말이냐면

 

스크린샷 2023-02-02 오후 3 32 34

 

공식문서에서 볼 수 있는 envoy config 예시는 이 정도지만

스크린샷 2023-02-02 오후 3 32 46

 

istio가 생성하는 envoy config 분량은 이 정도. default GKE 클러스터에 설치했을 때 기본적으로 만들어지는 분량. 대부분은 boilerplate 코드.

 

스크린샷 2023-02-02 오후 5 05 07

 

까만 선 기준으로

  • 왼쪽 : bootstrap. sidecar에 적용될 init container (pilot-agent) 에서 만들어진 것
  • 연두색: k8s node / pod 관련 정보 (pod name, k8s cluster version)
  • 노란색: istio Extensions (filter 등) 관련 정보
  • 왼쪽 주황색: bootstrapping prometheus metrics, healthcheck endpoints
  • 초록색: envoy - prometheus 연동, how to find istiod / zipkins 연동 관련 코드
  • 살구색: logging / tracing configuration
  • 분홍색: galley가 확인한 cluster 내부 services 정보. (k8s master node / apiserver 같은 거)
  • 오른쪽 주황색: listeners 관련 정보. header based routing이라던가... istio config이 envoy에 반영되는 부분
  • 빨간색: envoy routes. static / dynamic routes
  • 보라색: mTLS, certificate, terminating TLS 로직도 여기 있다.

 

스크린샷 2023-02-03 오전 8 51 18스크린샷 2023-02-03 오전 8 53 32

 

색칠하지 않은 하얀 부분

  • envoy가 최초로 떴을 때 확인하는 config. istiod / galley 등과 최초로 접속하기 위한 xds service를 정의한 곳
  • xds_grpc service의 정의 부분을 보면 istiod.istio-system.svc endpoint를 가리키고 있는 걸 볼 수 있다.
반응형