Istio

istio deep dive: Sidecar Traffic Intercepting & Routing Process

dongb 2024. 1. 4. 10:46
반응형

개요

아래 그림은 istio 공식 페이지에서 제공하고 있는 bookinfo 데모 프로젝트 중 reviews.default.svc.cluster.local로 요청을 보낼 때 내부 사이드카 프록시가 트래픽을 인터셉트하고 라우팅하는 흐름, ratings로 향하는 아웃바운드 트래픽의 흐름을 보여준다.

Bookinfo 트래픽 흐름도

 

이 글에서는 envoy 프록시가 사이드카로 포함되어 있는 pod에 대해 인바운드 트래픽과 아웃바운드 트래픽이 어떻게 인터셉트 되고 처리 되는지 알아본다.

Iptable 분석

# minikube login for root
$ minikube ssh
docker@minikube:~$ sudo -i

# productpage pod의 프로세스 확인
root@minikube:~# docker top `docker ps|grep "istio-proxy_productpage"|cut -d " " -f1`
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
1337                346524              346435              0                   07:36               ?                   00:00:00            /usr/local/bin/pilot-agent proxy sidecar --domain default.svc.cluster.local --proxyLogLevel=warning --proxyComponentLogLevel=misc:error --log_output_level=default:info
1337                346665              346524              0                   07:36               ?                   00:00:00            /usr/local/bin/envoy -c etc/istio/proxy/envoy-rev.json --drain-time-s 45 --drain-strategy immediate --local-address-ip-version v4 --file-flush-interval-msec 1000 --disable-hot-restart --allow-unknown-static-fields --log-format %Y-%m-%dT%T.%fZ?%l?envoy %n %g:%#?%v?thread=%t -l warning --component-log-level misc:error --concurrency 2

# 사이드카 컨테이너의 네임스페이스로 접속 (위의 어떤 PID로도 가능)
root@minikube:~# nsenter -n --target 346524

# NAT 테이블 확인
root@minikube:~# iptables -t nat -L -v

# PREROUTING chain: 수신되는 모든 TCP 트래픽을 ISTIO_INBOUD 체인으로 점프하기 위해 대상 주소 변환에 사용
Chain PREROUTING (policy ACCEPT 58627 packets, 3518K bytes)
 pkts bytes target     prot opt in     out     source               destination
58627 3518K ISTIO_INBOUND  tcp  --  any    any     anywhere             anywhere

# INPUT chain: 들어오는 패킷과 non-TCP 트래픽을 처리하며 OUTPUT chain에서 계속됨
Chain INPUT (policy ACCEPT 58627 packets, 3518K bytes)
 pkts bytes target     prot opt in     out     source               destination

# OUTPUT chain: 모든 나가는 패킷을 ISTIO_OUTPUT chain으로 넘김
Chain OUTPUT (policy ACCEPT 4880 packets, 417K bytes)
 pkts bytes target     prot opt in     out     source               destination
  594 35640 ISTIO_OUTPUT  tcp  --  any    any     anywhere             anywhere

# POSTROUTING chain: 모든 패킷은 네트워크 카드를 떠날 때 먼저 POSTROUTING chain에 들어가야 하며, 커널은 패킷 대상에 따라 패킷을 전달할 지 여부를 결정
Chain POSTROUTING (policy ACCEPT 4880 packets, 417K bytes)
 pkts bytes target     prot opt in     out     source               destination

# ISTIO_INBOUND chain: 15008, 15090, 15021, 15020 포트를 제외한 모든 인바운드 트래픽을 ISTIO_IN_REDIRECT chain으로 리디렉션. 위 포트로 전송되는 트래픽은 INPUT chain으로 전달
Chain ISTIO_INBOUND (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 RETURN     tcp  --  any    any     anywhere             anywhere             tcp dpt:15008
    0     0 RETURN     tcp  --  any    any     anywhere             anywhere             tcp dpt:15090
58165 3490K RETURN     tcp  --  any    any     anywhere             anywhere             tcp dpt:15021
  462 27720 RETURN     tcp  --  any    any     anywhere             anywhere             tcp dpt:15020
    0     0 ISTIO_IN_REDIRECT  tcp  --  any    any     anywhere             anywhere

# ISTIO_IN_REDIRECT chain: 모든 인바운드 트래픽을 local 15006 포트로 전달하여 사이드카로의 트래픽 차단
Chain ISTIO_IN_REDIRECT (3 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 REDIRECT   tcp  --  any    any     anywhere             anywhere             redir ports 15006

# ISTIO_OUTPUT chain
Chain ISTIO_OUTPUT (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 RETURN     all  --  any    lo      127.0.0.6            anywhere
    0     0 ISTIO_IN_REDIRECT  tcp  --  any    lo      anywhere            !localhost            tcp dpt:!15008 owner UID match 1337
    7   420 RETURN     all  --  any    lo      anywhere             anywhere             ! owner UID match 1337
  587 35220 RETURN     all  --  any    any     anywhere             anywhere             owner UID match 1337
    0     0 ISTIO_IN_REDIRECT  tcp  --  any    lo      anywhere            !localhost            tcp dpt:!15008 owner GID match 1337
    0     0 RETURN     all  --  any    lo      anywhere             anywhere             ! owner GID match 1337
    0     0 RETURN     all  --  any    any     anywhere             anywhere             owner GID match 1337
    0     0 RETURN     all  --  any    any     anywhere             localhost
    0     0 ISTIO_REDIRECT  all  --  any    any     anywhere             anywhere

# ISTIO_REDIRECT chain: 모든 트래픽을 사이드카 포트 15001로 리디렉션
Chain ISTIO_REDIRECT (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 REDIRECT   tcp  --  any    any     anywhere             anywhere             redir ports 15001

위의 chain들 중 ISTIO_OUTPUT chain을 표와 다이어그램으로 만들면 아래와 같다.

Rule target in out source destination
1 RETURN any lo 127.0.0.6 anywhere
2 ISTIO_IN_REDIRECT any lo anywhere !localhost owner UID match 1337
3 RETURN any lo anywhere anywhere !owner UID match 1337
4 RETURN any any anywhere anywhere owner UID match 1337
5 ISTIO_IN_REDIRECT any lo anywhere !localhost owner GID match 1337
6 RETURN any lo anywhere anywhere !owner GID match 1337
7 RETURN any any anywhere anywhere owner GID match 1337
8 RETURN any any anywhere localhost
9 ISTIO_REDIRECT any any anywhere anywhere

규칙 5, 6, 7은 각각 규칙 2, 3, 4를 확장한 것으로 유사한 목적으로 갖기 때문에 함께 설명한다.

위 규칙들은 순서대로 평가되며 out이 lo인 경우 트래픽의 대상이 로컬 pod임을 뜻한다. 즉, 외부로 전송되는 트래픽은 4, 7, 8, 9 규칙만 적용된다.

RETURN target

RETURN target은 해당 규칙이 지정될 때, 규칙 chain을 점프하고 iptables의 호출지점(OUTPUT)으로 돌아가 나머지 라우팅 규칙(POSTROUTING; 임의의 목적지 주소로 트래픽을 보내는 규칙, 직관적으로는 통과라고 이해하면 됨)을 계속 실행한다.

127.0.0.6 IP 주소

IP 127.0.0.6은 istio의 InboundPassthroughClusterIPv4 기본 값이며, 트래픽은 envoy 프록시에 진입한 후 이 IP로 바인딩된다.

아웃바운드 트래픽이 아웃바운드 핸들러를 통과하지 않고 pod의 애플리케이션 컨테이너로 전송될 수 있도록 하는 역할을 한다.

이 트래픽은 실제 아웃바운드 트래픽이 아니라 자신의 pod에 대한 엑세스이다.

ISTIO_OUTPUT chain

ISTIO_OUTPUT chain의 규칙들을 아래 표에서 정리해 보았다.

규칙 이름 목적 Bookinfo 트래픽 흐름도 상 위치 세부 정보
rule1 Envoy 프록시가 보낸 트래픽을 로컬 애플리케이션 컨테이너로 전달해 envoy 프록시 우회 6~7단계 • 127.0.0.6의 모든 요청이 체인을 통하지 않고 iptables의 호출지점(즉, OUTPUT)으로 전달돼 local pod 내의 컨테이너 등 임의의 대상으로 트래픽을 전송하는 POSTROUTING 규칙을 수행
• 이 규칙이 없을 경우 규칙 2를 실행하고 트래픽은 인바운드 핸들러로 다시 진입하여 데드 루프가 생성됨
rule 2, 5 • Envoy 프록시로부터의 인바운드 트래픽을 처리하지만, 로컬 호스트로의 요청은 처리하지 않음
• 후속 규칙을 통해 envoy 프록시의 인바운드 핸들러에 전달
• 이 규칙은 pod 내부의 서비스들끼리 통신하는 경우 적용
- 트래픽 대상이 localhost가 아니고 1337 UID(i.e. istio-proxy 유저, envoy 프록시)로부터 온 패킷일 경우 ISTIO_IN_REDIRECT chain을 통해 envoy 프록시의 인바운드 핸들러로 포워딩 됨
rule 3, 6 • pod 내부의 애플리케이션 컨테이너의 내부 트래픽이 통과하는 규칙
• 이 규칙은 컨테이너 내의 트래픽(pod 내에서 pod’s ip 또는 localhost에 대한 엑세스)에 적용
6~7단계 트래픽이 envoy 유저로부터 온 것이 아닐 경우, chain을 건너뛰고 OUTPUT을 거쳐 POSTROUTING을 통해 목적지로 바로 전달
rule 4,7 envoy 프록시로부터 온 아웃바운드 요청이 통과하는 규칙 14~15단계 트래픽이 envoy 유저로부터 만들어진 것일 경우, OUTPUT을 거쳐 POSTROUTING을 통해 목적지로 바로 전달
rule 8 pod에서 localhost로의 트래픽이 통과하는 규칙 - 요청의 목적지가 localhost일 경우, OUTPUT을 거쳐 POSTROUTING을 통해 localhost로 바로 전달
rule 9 다른 모든 트래핏은 최종적으로 envoy 프록시의 아웃바운드 핸들러에 도달한 후 ISTIO_REDIRECT에 포워딩 10~11단계 -

트래픽 흐름을 나타낸 논리 다이어그램

트래픽 라우팅 프로세스

인바운드 핸들러

인바운드 핸들러는 iptable에 의해 차단된 다운 스트림의 트래픽을 Localhost로 전달하고, pod 내의 애플리케이션 컨테이너에 대한 연결을 설정하는 역할을 한다.

pod 이름이 reviews-v1-86896b7648-sp59z 라고 가정할 때 istioctl proxy-config listener reviews-v1-86896b7648-sp59z --port 15006을 실행하면 아래와 같은 결과가 나온다.

ADDRESSES PORT  MATCH                                                                                           DESTINATION
0.0.0.0   15006 Addr: *:15006                                                                                   Non-HTTP/Non-TCP
0.0.0.0   15006 Trans: tls; App: istio-http/1.0,istio-http/1.1,istio-h2; Addr: 0.0.0.0/0                        InboundPassthroughClusterIpv4
0.0.0.0   15006 Trans: raw_buffer; App: http/1.1,h2c; Addr: 0.0.0.0/0                                           InboundPassthroughClusterIpv4
0.0.0.0   15006 Trans: tls; App: TCP TLS; Addr: 0.0.0.0/0                                                       InboundPassthroughClusterIpv4
0.0.0.0   15006 Trans: raw_buffer; Addr: 0.0.0.0/0                                                              InboundPassthroughClusterIpv4
0.0.0.0   15006 Trans: tls; Addr: 0.0.0.0/0                                                                     InboundPassthroughClusterIpv4
0.0.0.0   15006 Trans: tls; App: istio,istio-peer-exchange,istio-http/1.0,istio-http/1.1,istio-h2; Addr: *:9080 Cluster: inbound|9080||
0.0.0.0   15006 Trans: raw_buffer; Addr: *:9080                                                                 Cluster: inbound|9080||
  • address: downstream 주소
  • port: envoy listener가 수신 대기하는 포트
  • match: 요청에 사용되는 전송 프로토콜 또는 일치하는 downstream 주소
  • destination: 라우팅 목적지

위 결과를 통해 알 수 있는 것은 다음과 같다.

  • pod의 iptable은 envoy 프록시의 인바운드 핸들러가 트래픽을 수신할 수 있도록 트래픽을 15006 포트로 리디렉션
  • 인바운드 리스너에는 특정 규칙이 있어서, 9080 포트로 가는 트래픽은 특정 클러스터(inbound|9080||)로 라우팅됨
  • 0.0.0.0:15006/TCP에 대한 리스너인 virtualInbound는 match 규칙이 포함된 트래픽 또는 9080 포트에 대한 모든 인바운드 트래픽을 수신

위 내용을 json 형식으로 더 자세히 보려면 istioctl proxy-config listeners [pod 이름] --port 15006 -o json을 실행하면 된다.

[
    /*omit*/
    {
        "name": "virtualInbound",
        "address": {
            "socketAddress": {
                "address": "0.0.0.0",
                "portValue": 15006
            }
        },
        "filterChains": [
            /*omit*/
            {
                "filterChainMatch": {
                    "destinationPort": 9080,
                    "transportProtocol": "tls",
                    "applicationProtocols": [
                        "istio",
                        "istio-peer-exchange",
                        "istio-http/1.0",
                        "istio-http/1.1",
                        "istio-h2"
                    ]
                },
                "filters": [
                    /*omit*/
                    {
                        "name": "envoy.filters.network.http_connection_manager",
                        "typedConfig": {
                            "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
                            "statPrefix": "inbound_0.0.0.0_9080",
                            "routeConfig": {
                                "name": "inbound|9080||",
                                "virtualHosts": [
                                    {
                                        "name": "inbound|http|9080",
                                        "domains": [
                                            "*"
                                        ],
                                        "routes": [
                                            {
                                                "name": "default",
                                                "match": {
                                                    "prefix": "/"
                                                },
                                                "route": {
                                                    "cluster": "inbound|9080||",
                                                    "timeout": "0s",
                                                    "maxStreamDuration": {
                                                        "maxStreamDuration": "0s",
                                                        "grpcTimeoutHeaderMax": "0s"
                                                    }
                                                },
                                                "decorator": {
                                                    "operation": "reviews.default.svc.cluster.local:9080/*"
                                                }
                                            }
                                        ]
                                    }
                                ],
                                "validateClusters": false
                            },
                            /*omit*/
                        }
                    }
                ],
            /*omit*/
        ],
        "listenerFilters": [
        /*omit*/
        ],
        "listenerFiltersTimeout": "0s",
        "continueOnListenerFiltersTimeout": true,
        "trafficDirection": "INBOUND"
    }
]

 

인바운드 핸들러는 9080 포트에 대한 트래픽을 inbound|9080|| 클러스터로 라우팅하기 때문에 istioctl pc cluster reviews-v1-86896b7648-sp59z --port 9080 --direction inbound -o json를 실행하면 클러스터 구성을 확인할 수 있다.

[
    {
        "name": "inbound|9080||",
        "type": "ORIGINAL_DST",
        "connectTimeout": "10s",
        "lbPolicy": "CLUSTER_PROVIDED",
        "circuitBreakers": {
            "thresholds": [
                {
                    "maxConnections": 4294967295,
                    "maxPendingRequests": 4294967295,
                    "maxRequests": 4294967295,
                    "maxRetries": 4294967295,
                    "trackRemaining": true
                }
            ]
        },
        "upstreamBindConfig": {
            "sourceAddress": {
                "address": "127.0.0.6",
                "portValue": 0
            }
        },
        "commonLbConfig": {},
        "metadata": {
            "filterMetadata": {
                "istio": {
                    "services": [
                        {
                            "host": "reviews.default.svc.cluster.local",
                            "name": "reviews",
                            "namespace": "default"
                        }
                    ]
                }
            }
        }
    }
]
  • ORGINAL_DST 타입을 볼 수 있는데, 이는 트래픽을 원래 대상 주소(pod ip)로 전달하는 타입이다.
  • 원래 대상 주소는 현재 pod이므로 upstreamBindingConfig.sourceAddress.address는 127.0.0.6으로 설정된다.
  • 127.0.0.6에 대한 트래픽은 iptables의 ISTIO_OUTPUT chain의 규칙 1을 적용받는다.

아웃바운드 핸들러

reviews 서비스에서는 http://ratings.default.svc.cluster.local:9080/에 위치한 ratings 서비스로 HTTP 요청을 보내는데, 이때 Envoy 프록시의 아웃바운드 핸들러가 등장한다.

애플리케이션 컨테이너에서 나가는 요청은 iptables에 의해 가로채여 아웃바운드 핸들러로 전달된다. 이 핸들러를 통과한 후 가상 아웃바운드 리스너, 0.0.0.0_9080 리스너를 거쳐 Route 9080을 통해 상위 클러스터를 찾게 되는데, 이 과정에서 EDS(Endpoint Discovery Service)를 참고하여 엔드포인트를 찾아 라우팅 작업을 수행한다.

istioctl proxy-config routes reviews-v1-86896b7648-sp59z --name 9080 -o json으로 라우팅 구성을 확인할 수 있다.

[{
		...
	  {
	    "name": "ratings.default.svc.cluster.local:9080",
	    "domains": [
	        "ratings.default.svc.cluster.local",
	        "ratings",
	        "ratings.default.svc",
	        "ratings.default",
	        "10.98.49.62"
	    ],
	    "routes": [
	        {
	            "name": "default",
	            "match": {
	                "prefix": "/"
	            },
	            "route": {
	                "cluster": "outbound|9080||ratings.default.svc.cluster.local",
	                "timeout": "0s",
	                "retryPolicy": {
	                    "retryOn": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
	                    "numRetries": 2,
	                    "retryHostPredicate": [
	                        {
	                            "name": "envoy.retry_host_predicates.previous_hosts",
	                            "typedConfig": {
	                                "@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
	                            }
	                        }
	                    ],
	                    "hostSelectionRetryMaxAttempts": "5",
	                    "retriableStatusCodes": [
	                        503
	                    ]
	                },
	                "maxGrpcTimeout": "0s"
	            },
	            "decorator": {
	                "operation": "ratings.default.svc.cluster.local:9080/*"
	            }
	        }
	    ],
	    "includeRequestAttemptCount": true
	},
	...
]

Reference

반응형