istio ingress gateway
서비스 메시로 요청을 보내고 싶은 사용자는 요청을 istio ingress gateway의 엔드포인트로 보낸다. 즉, 메시 클러스터의 ingress gateway는 요청을 가장 처음으로 받는 리소스이다.
ingress gateway이 무엇인지 알아보기 위해 먼저 쿠버네티스의 ingress와 ingress controller를 간단히 살펴보자.
kubernetes에서 ingress의 동작
ingress는 특정 path 나 host에 따라 요청을 특정 서비스로 보내는 규칙들을 정의하는 리소스이다.
여기에 정해진 규칙들을 기반으로 실제 라우팅을 하는 것은 ingress controller pod이다. nginx, kong과 같은 프로그램이 자주 사용된다.
ingress controller가 서비스로 요청을 보낼 때 과정을 좀 더 자세히 살펴보자.
클라이언트의 요청이 Ingress Controller를 통해 들어오면, Ingress의 규칙에 따라 요청은 특정 서비스의 DNS 이름으로 변환된다. 이 DNS 이름은 CoreDNS에 질의되며, CoreDNS는 API 서버를 통해 etcd에서 서비스 클러스터 IP를 검색한다.
받는 대상의 서비스 클러스터 IP는 node에 위치한 iptables의 규칙에 따라 실제 요청을 처리할 POD의 IP로 변경된다.
서비스 또는 서비스에 연결된 POD의 상태가 변할 때마다 control plane의 controller manager 내에 endpoints controller가 endpoints 리소스를 생성, 변경하고 이를 etcd에 저장한다. 그 후, kubelet에 요청을 하여 kube-proxy가 노드에 있는 iptables 규칙을 변경하도록 한다. (kube-proxy가 기본 모드(iptables)일 때)
쿠버네티스에서 서비스의 실체는 iptables이다.
ingress gateway
이제 istio의 ingress gateway를 보자.
istio에는 ingress gateway와 gateway라는 리소스가 있다.
ingress처럼 규칙을 나열한 리소스가 gateway, ingress controller처럼 pod으로써 배포되어 gateway의 규칙대로 실제 라우팅하는 리소스가 ingress gateway이다.
gateway는 기본적으로 L4 ~ L6 layer 규칙을 라우팅하는데 사용된다. 하지만 L7 layer 기반 다양한 규칙을 라우팅 할 수 있는 virtualService와 연결이 가능하다. 이로 인해 path, host에 그쳤던 ingress에 비해 gateway는 더욱 세밀한 라우팅 설정이 가능하다. 특히 gateway는 동일한 서비스에 연결된 복수의 pod 중 특정 pod에 라우팅이 가능하다는 특징을 가진다.
ingress gateway에서 동작하는 프로세스를 조회하면 pilot-agent와 envoy가 출력된다.
ingress gateway의 실체는 몇가지 기능이 활성화 된 envoy proxy라고 생각하면 된다.
pilot-agent는 이후에 설명
동작 원리의 차이
ingress controller와 ingress gateway 개념이 비슷해 보이지만 라우팅 할 pod의 IP를 찾는 과정에서는 차이를 보인다. 요약하자면 ingress controller는 라우팅 할 pod의 IP를 직접 알지 못하는 반면, ingress gateway는 그 IP를 직접 알고 있다.
어떻게 그 정보를 알고 있는지 살펴보자.
istio 메시 클러스터를 구성하면 istio control plane에 위치한 pilot은 etcd에 있는 여러 리소스 정보(virtualService 등)와 함께 endpoints 정보도 가져와 모두 메모리 내 데이터 구조에 저장시키고, iptables 및 envoy 구성을 생성한다.
pilot-agent는 pilot으로부터 정보를 받아 envoy를 부트스트랩 한다. 따라서 ingress gateway(envoy)는 메시 내부에 있는 모든 pod의 ip를 알고 있으므로 목적지 pod에 직접 요청을 전달할 수 있다.
ingress gateway pod이 저장하고 있는 엔드포인트 정보를 조회 해보면 메시에 있는 모든 pod의 IP를 저장하고 있음을 볼 수 있다. 추가적으로 envoy는 위 정보를 갖고 있기 때문에 POD에서 POD으로 트래픽을 보낼 때, ingress gateway를 거치는 hop 없이 바로 요청을 보낼 수 있다.
쿠버네티스의 pod은 CoreDNS를 거치는 홉이 생김
VirtualService
VirtualService는 istio에서 트래픽 라우팅 규칙을 정의하는 구성 요소이다.
pilot은 VirtualService 규칙을 기반으로 envoy에 대한 구성을 생성하고, 이 구성은 pilot-agent를 통해 envoy에 전달된다. 그 후, envoy는 이 구성을 사용해 트래픽을 올바르게 라우팅한다.
연결되어 있는 VirtualService와 gateway 규칙을 기반으로 ingress gateway의 iptables 라우팅 정보가 구성된 것을 확인할 수 있다.
DestinationRule
DestinationRule은 subset을 통해 동일한 서비스에 연결된 pod일지라도 구별하여 라우팅이 가능하도록 만든다.
일반적으로 쿠버네티스 환경에서 앱을 배포할 때, v1부터 v3까지 개발된 reviews POD을 동일한 서비스에 연결하면, 특정 POD로의 트래픽 라우팅이 어렵다. 각 POD은 고유한 IP를 할당받지만, 이들은 reviews 서비스 DNS 이름이라는 하나의 키에 할당되어 있다. 쿠버네티스는 round robin 방식을 통해 여러 IP중 하나의 IP를 반환하는데 이 방식은 특정 POD의 IP를 구별하지 못한다.
$ kubectl get endpoints |grep reviews
reviews 10.200.2.250:9080,10.200.3.0:9080,10.200.3.1:9080 29d
$ istioctl pc endpoint pod/istio-ingressgateway-6d5dcajj2-5dnja -n istio-system |grep reviews
10.200.2.250:9080 HEALTHY OK outbound|9080||reviews.default.svc.cluster.local
10.200.3.0:9080 HEALTHY OK outbound|9080||reviews.default.svc.cluster.local
10.200.3.1:9080 HEALTHY OK outbound|9080||reviews.default.svc.cluster.local
endpoints 리소스에서 조회되는 것처럼 reviews 서비스에는 3개의 POD IP가 매핑되어 있다.
위에서 알아본 대로 pilot은 endpoints 정보를 가져와서 envoy에 전달하므로, ingress gateway(envoy)에 저장되어 있는 endpoint 정보가 쿠버네티스의 것과 동일하다는 것을 알 수 있다.
따라서 reviews.demo.svc.cluster.local 로 요청을 보낼 때 이 3개의 IP 중 하나를 선택할 수 있는 방법이 없다.
그럼 DestinationRule을 적용하면 내부적으로 변화가 생기는지 알아보자.
kind: DestinationRule
apiVersion: networking.istio.io/v1beta1
metadata:
name: reviews
namespace: demo
spec:
host: reviews.demo.svc.cluster.local
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
$ kubectl get endpoints |grep reviews
reviews 10.200.2.250:9080,10.200.3.0:9080,10.200.3.1:9080 29d
$ istioctl pc endpoint pod/istio-ingressgateway-6d5dcajj2-5dnja -n istio-system |grep reviews
10.200.2.250:9080 HEALTHY OK outbound|9080|v2|reviews.default.svc.cluster.local
10.200.2.250:9080 HEALTHY OK outbound|9080||reviews.default.svc.cluster.local
10.200.3.0:9080 HEALTHY OK outbound|9080|v3|reviews.default.svc.cluster.local
10.200.3.0:9080 HEALTHY OK outbound|9080||reviews.default.svc.cluster.local
10.200.3.1:9080 HEALTHY OK outbound|9080|v1|reviews.default.svc.cluster.local
10.200.3.1:9080 HEALTHY OK outbound|9080||reviews.default.svc.cluster.local
쿠버네티스의 endpoints 리소스에는 변화가 없지만 envoy에 저장되어 있는 endpoint에 변화가 생긴 것을 확인할 수 있다. 즉, destinationRule이 생성되면 pilot은 기존 endpoint에 subset 이름을 기반으로 한 매핑 데이터를 추가한다.
실제로 서비스는 한 개이지만 envoy는 POD 당 하나의 서비스를 할당한 것과 동일하게 인식한다.
결국 subset 이름과 함께 service DNS 이름으로 라우팅하면 동일한 서비스에 연결된 POD이라도 구분해서 라우팅이 가능해진다.
Envoy 프록시
Envoy 프록시 삽입 여부에 따른 라우팅
DestinationRule을 통해 POD IP를 구분해서 저장할 수 있게 된다는 것까지 알아봤다.
reviews 앱에 아래의 destinationRule과 virtualService를 적용했을 때, productpage라는 앱에서 reviews앱으로 트래픽을 보내면 어떻게 되는지 알아보자.
kind: VirtualService
apiVersion: networking.istio.io/v1beta1
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
kind: DestinationRule
apiVersion: networking.istio.io/v1beta1
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
productpage POD에 envoy 프록시가 삽입되어 있다면 위의 virtualService 규칙이 적용되어 아래처럼 v1 reviews에만 트래픽을 보낸다.
만약 envoy 프록시가 삽입되지 않은 pod에서 destinationRule이 적용된 pod으로 요청을 보내면 어떻게 라우팅 될까?
이제 productpage에 envoy 프록시를 제거하고 동일하게 트래픽을 보내보자.
virtualService 규칙을 무시하고 모든 버전에 트래픽을 라우팅한다.
왜 Envoy 프록시 삽입 여부에 따라 동작이 다를까?
Envoy 프록시가 트래픽을 제어하는 방법
Envoy 프록시 삽입 여부에 따른 동작 차이를 이해하기 위해 Envoy 프록시가 POD에 삽입되면 어떤 일이 벌어지는지 알아보자.
Pod이 배포될 때 envoy 프록시가 삽입되어 있다면 initContainer로 istio-init 컨테이너가 실행되는데, 여기서 istio-iptables 스크립트를 실행한다. 이 스크립트는 pod의 iptables를 변경해서 들어오고 나가는 트래픽이 사이드카 프록시를 통과하도록 한다. 특히 outbound에 대한 iptables 규칙은 VirtualService 내용이 참고되어 pilot에서 생성된다.
이런 과정을 통해 특정 컨테이너로 향하는 트래픽도 envoy 프록시가 먼저 가로챌 수 있게 된다.
위의 예시를 다시 살펴보자.
productpage에 envoy 프록시가 없었기 때문에 pilot에게 받은 endpoint 정보가 없다. 그래서 일반적인 쿠버네티스의 트래픽처럼 CoreDNS에 질의하여 얻은 랜덤한 POD IP로 요청을 보낼 수 밖에 없다. VirtualService가 적용된 iptables도 반영되지 않았으므로 규칙에 따른 라우팅도 불가능하다.
같은 원리로 Ingress Controller가 요청을 받아 mesh 내의 리소스에게 전달하면, 그 과정(ingress controller to app)에서 라우팅이나 mTLS와 같은 istio의 기능이 동작하지 않는다. 따라서 기존 클러스터에 istio를 적용하려 할 때 다음과 같은 방법을 사용할 수 있다.
- ingress controller가 모든 요청을 ingress gateway로 보내도록 ingress 작성
- ingress controller를 제거
- ingress controller에 sidecar 삽입 후 gateway와 연결
Envoy 프록시가 없는 pod에서 있는 pod으로 보내는 방법
Envoy가 없는 pod에서 있는 pod으로 보내야 하는 상황에서도 VirtualService와 DestinationRule을 적용한 라우팅을 하고 싶다면 ingress gateway를 활용할 수 있다.
서비스 A는 istio 리소스 정보를 알지 못하지만 ingress gateway는 envoy 프록시를 기반으로 동작하기 때문에 그 정보를 알고 있다. 이 점을 이용해 서비스 A에서 ingress gateway를 통해 서비스 B로 요청을 보내면 원하는 라우팅을 수행할 수 있다. 하지만 추가적인 홉이 발생하기 때문에 추가적인 네트워크 지연이 발생할 수 있음에 주의해야 한다.
References
- https://jimmysong.io/en/blog/sidecar-injection-iptables-and-traffic-routing/#sidecar-pattern
- https://kubernetes.io/blog/2022/12/30/advancements-in-kubernetes-traffic-engineering/
- https://istio.io/latest/blog/2018/incremental-traffic-management/
- https://istio.io/latest/docs/setup/additional-setup/cni/
- https://ozt88.tistory.com/65
- https://yoo11052.tistory.com/193
- https://yoo11052.tistory.com/193
- https://coffeewhale.com/k8s/network/2019/05/11/k8s-network-02/
'Istio' 카테고리의 다른 글
istio deep dive: Sidecar Traffic Intercepting & Routing Process (0) | 2024.01.04 |
---|---|
Istio ingress gateway VS API Gateway (istio ver. 1.19) (0) | 2024.01.03 |
Traffic Management (1) | 2024.01.03 |
Canary deployment using Istio (1) | 2024.01.03 |
Istio addons on Minikube (0) | 2023.12.27 |