https://www.youtube.com/watch?v=nNTpfXSCLKs
Understading the Data plane - What envoy hears when istio speaks
- 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'
envoyproxy github을 보면 go-control-plane이 있음. envoyproxy를 관리하기 위한 표준 (canonical way). istiod is built on top of this (go-control-plane).
따라서 istiod를 커뮤니티에서 지칭하는 표현도 여러 가지.
- pilot
- discovery / istio discovery service...
istiod 컴포넌트를 간단하게 요약하면 위의 세 가지로 구분됨.
- Citadel이 하는 일은 Know your peers 에서 다른 사람이 설명할 예정
- Galley, pilot을 설명할 예정.
istiod에서 하는 일을 간단히 정리하면
- Read from kubernetes
- Write to Envoy.
일종의 One-way pipe
Galley
service Discovery
공식문서에는 '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로 접근할 수 있게 되는 것.
istiod pod를 포트포워딩해서 debug/registryz 도메인으로 접근하면, istiod에서 discover / manage중인 service 정보를 확인할 수 있다.
serves Webhook Request
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를 붙임.
- 예컨대 k8s api로
- Validating Webhook : CRD yaml을 istiod에 전달할 때, crd의 validation을 담당함
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
MutatingWebhookConfiguration의 sidecar inject 관련 로직
- istio-injection: enabled 라는 label이 namspace에 존재할 경우
- pod CREATE 요청이 들어올 때
- clientConfig에 정의된 service로 요청을 보낸다
- name: istiod
- namespace: istio-system
- path: /inject
- port: 443
Pilot
Galley에서 제공한 service Discovery 정보를 토대로 envoy에 코드를 쓰는 컴포넌트.
istio에서 수행하는 kubernetes Resource -> Envoy Configuration 컴포넌트로의 변환.
사실 Pilot 자체는 크게 설명할 게 없음. 변환해주는 게 전부임.
중요한 건 how envoy responds to what pilot sends down.
Envoy
스크린샷 출처: Envoy Internals Deep Dive.
istio는 envoy 소스를 포크해서 쓰고 있음 (https://github.com/istio/proxy)
- istio 쪽에서 여러 custom functionality를 추가해 쓰고 있음
- 커스텀한 기능을 WebAssembly Extension (wasm) 로도 지원한다고 함. 일단은 커스텀한 코드 대부분 static c++로 개발되어 있음
Terminology
upstream / downstream
- Upstream: Where envoy connections come from.
- Downstream : Where envoy connections go to.
처음 접했을 때 용어가 쉽게 와닿지 않는데, 이렇게 연상하면 좀더 이해가 편하다.
- client에서 backend로 요청을 보내는 flow를 남쪽 -> 북쪽이라는 방향성으로 이해해보자.
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이 된다.
이외에도 Terminology를 이해시키려는 다양한 비유법이 있음. 위성에 비유하기도 함.
cluster, endpoint, listener, routes
envoy는 kubernetes가 아니므로, envoy에서 쓰이는 용어는 별개로 이해해야 함.
Cluster는 HostName, Endpoint는 ip address에 대응되는 개념이라고 이해하자.
- 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
envoy 문서에서 확인할 수 있는 filter chain 동작방식.
- 아무것도 정의하지 않은 default filter chain의 경우, connection close라는 기본 동작만 수행한다.
예컨대 간단한 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에 대입해보면 대략 아래와 같다.
envoy proxy가 있는 상황에서, source-app 에서 dest-app으로 요청을 전달하는 예시.
- 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) 로 트래픽 전달
실제 envoy Configuration json값은 위와 같다. istioctl proxy-config (istioctl pc
) 로 확인 가능.
istio는 filter chain을 구성할 때 istio.stats 라는 filter를 추가하게 되어 있다.
- 이 filter가 있기 때문에 istio metric을 수집하고 볼 수 있다.
- connection을 의미하는 'cx' suffix -> tcp stats filter가 생성.
- request를 의미하는 'rq' suffix -> http stats filter가 생성.
filter chain
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로 트래픽을 전달할 영역 (그림의 빨간색 작은 화살표 부분)은 따로 설명이 필요함.
트래픽이 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를 설명한 다이어그램도 있음.
Filter chain Match
트래픽이 들어왔을 때, http인지 TCP인지 등등... 을 구분하는 영역이 Filter Chain Match.
- FCM에서 트래픽을 HTTP로 분류하면, HCM (http connection manager) 으로 트래픽 전달.
- http 트래픽이 아니면, default 옵션은 TCP 트래픽으로 분류함. tcp proxy로 트래픽 전달
- FCM에서 트래픽 종류를 체크하고, http / tcp에 맞게 핸들링하기 전 istio.stats 필터를 통과함.
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하는지
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된다는 뜻.
- istio_inbound 트래픽도 마찬가지로 iptable chain을 통과한다.
- 외부로부터 오는 트래픽은 ISTIO_IN_REDIRECT라는 chain을 통과하는데,
--to-port 15006
이 들어가 있음. 즉 localhost:15006 으로 redirect된다.
- 외부로부터 오는 트래픽은 ISTIO_IN_REDIRECT라는 chain을 통과하는데,
sidecar의 listening process를 확인해보면, data plane에서 외부 트래픽을 받는 포트는 사실상 15001, 15006 두 개.
- 나머지는 Debug / stats용 port 등 별도의 목적으로 사용함
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를 복원해서 전달한다. (복원 로직까지는 설명하지 않음)
즉, 복원한 original destination ip / port (예시의 경우 10.4.0.3:8000)를 가지고, istio galley가 확보한 service discovery 결과들 중 일치하는 곳으로 트래픽을 전달함.
Extension / Plugins
커뮤니티에서 혼용되어 쓰이는데, 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
IstioCon에서 WebAssembly 관련 발표를 한 Yuval Kohavi의 envoy config 관련 코멘트 참고.
- Envoy Config is Not meant for people. it's meant for machines
무슨 말이냐면
공식문서에서 볼 수 있는 envoy config 예시는 이 정도지만
istio가 생성하는 envoy config 분량은 이 정도. default GKE 클러스터에 설치했을 때 기본적으로 만들어지는 분량. 대부분은 boilerplate 코드.
까만 선 기준으로
- 왼쪽 : 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 로직도 여기 있다.
색칠하지 않은 하얀 부분
- envoy가 최초로 떴을 때 확인하는 config. istiod / galley 등과 최초로 접속하기 위한 xds service를 정의한 곳
- xds_grpc service의 정의 부분을 보면 istiod.istio-system.svc endpoint를 가리키고 있는 걸 볼 수 있다.
'학습일지 > Service Mesh' 카테고리의 다른 글
istioCon 2022 - Egress Woes: Debugging External service Traffic in istio (0) | 2023.05.19 |
---|---|
istioCon 2022 - Understanding New istio Telemetry API (0) | 2023.03.20 |
ServiceMeshCon 2020 - istio Service Mesh Simplified Beyond a Single Cluster (0) | 2022.11.10 |
KubeCon 2019 - Istio Multi-Cluster Service Mesh Patterns Explained (0) | 2022.11.02 |
istio 개념 정리 (3) - mTLS Security (0) | 2022.10.29 |