트래픽 라우팅 이해하기
트래픽 라우팅 이해하기 (Understanding Traffic Routing)
Istio는 "투명 프록시(transparent proxy)"로 작동하는 것이 목표 중 하나예요. 이 페이지에서는 Istio가 트래픽을 라우팅하는 방식을 이해해서 메시에서 무슨 일이 일어나는지 파악하는 방법을 배워요.
출처: Istio 문서
본문
Istio의 목표 중 하나는 기존 클러스터에 투입할 수 있는 "투명 프록시"로 작동해서 트래픽이 이전처럼 계속 흐르게 하는 것이에요. 하지만 요청 로드 밸런싱 같은 추가 기능 덕분에 Istio가 일반적인 쿠버네티스 클러스터와 다르게 트래픽을 관리할 수 있는 강력한 방법들이 있어요. 메시에서 무슨 일이 일어나는지 이해하려면 Istio가 트래픽을 어떻게 라우팅하는지 이해하는 것이 중요해요.
프론트엔드와 백엔드
Istio의 트래픽 라우팅에는 두 가지 주요 단계가 있어요.
- "프론트엔드(frontend)"는 우리가 처리하는 트래픽의 유형을 어떻게 매칭하는지를 의미해요. 이는 트래픽을 라우팅할 백엔드를 식별하고 적용할 정책을 결정하는 데 필요해요. 예를 들어
http.ns.svc.cluster.local의Host헤더를 읽어 요청이httpService를 위한 것임을 식별할 수 있어요. 이 매칭이 어떻게 작동하는지에 대한 자세한 내용은 아래에서 찾을 수 있어요. - "백엔드(backend)"는 트래픽을 매칭한 후 어디로 보낼지를 의미해요. 위 예제를 사용하면 요청을
httpService를 대상으로 식별한 후 그 Service의 엔드포인트로 보내요. 하지만 이 선택이 항상 그렇게 간단하진 않아요. Istio는VirtualService라우팅 규칙을 통해 이 로직을 커스터마이즈할 수 있게 해줘요.
표준 쿠버네티스 네트워킹도 같은 개념을 가지지만, 훨씬 간단하고 일반적으로 숨겨져 있어요. Service가 생성되면 일반적으로 관련 프론트엔드(자동 생성된 DNS 이름, 예: http.ns.svc.cluster.local)와 서비스를 나타내는 자동 생성된 IP 주소(ClusterIP)가 있어요. 마찬가지로 백엔드도 생성돼요. Endpoints 또는 EndpointSlice는 서비스가 선택한 모든 파드를 나타내요.
프로토콜
쿠버네티스와 달리 Istio는 HTTP와 TLS 같은 애플리케이션 수준 프로토콜을 처리할 수 있어요. 이는 쿠버네티스에서 사용 가능한 것과는 다른 유형의 프론트엔드 매칭을 가능하게 해요.
일반적으로 Istio가 이해하는 프로토콜에는 세 가지 클래스가 있어요.
- HTTP: HTTP/1.1, HTTP/2, gRPC를 포함해요. TLS로 암호화된 트래픽(HTTPS)은 포함하지 않는다는 점을 기억하세요.
- TLS: HTTPS를 포함해요.
- 원시 TCP 바이트.
프로토콜 선택 문서는 Istio가 어떤 프로토콜을 사용할지 결정하는 방법을 설명해요.
"TCP"의 사용은 다른 맥락에서 UDP 같은 다른 L4 프로토콜을 구분하는 데 쓰이기 때문에 혼란스러울 수 있어요. Istio에서 TCP 프로토콜을 언급할 때는 일반적으로 이를 원시 바이트 스트림으로 취급하고 TLS나 HTTP 같은 애플리케이션 수준 프로토콜을 파싱하지 않는다는 뜻이에요.
트래픽 라우팅
Envoy 프록시가 요청을 받으면 어디로(만약 있다면) 전달할지 결정해야 해요. 기본적으로 이는 커스터마이즈되지 않는 한 요청된 원래 서비스로 가요. 이것이 어떻게 작동하는지는 사용된 프로토콜에 따라 달라져요.
TCP
TCP 트래픽을 처리할 때 Istio가 연결을 라우팅하는 데 사용할 수 있는 유용한 정보는 매우 적어요. 목적지 IP와 포트뿐이에요. 이 속성들을 사용해 의도된 Service를 결정해요. 프록시는 각 서비스 IP(<Kubernetes ClusterIP>:<Port>) 쌍에서 리슨하고 업스트림 서비스로 트래픽을 전달하도록 구성돼요.
커스터마이즈를 위해 TCP VirtualService를 구성할 수 있는데, 이는 특정 IP와 포트에 매칭하고 요청된 것과 다른 업스트림 서비스로 라우팅할 수 있게 해줘요.
TLS
TLS 트래픽을 처리할 때 Istio는 원시 TCP보다 약간 더 많은 정보를 사용할 수 있어요. TLS 핸드셰이크 동안 제시되는 SNI 필드를 검사할 수 있어요.
표준 Service의 경우 원시 TCP와 동일한 IP:포트 매칭이 사용돼요. 하지만 ExternalName 서비스처럼 Service IP가 정의되지 않은 서비스의 경우 SNI 필드가 라우팅에 사용돼요.
추가로 TLS VirtualService로 커스텀 라우팅을 구성해 SNI에 매칭하고 요청을 커스텀 목적지로 라우팅할 수 있어요.
HTTP
HTTP는 TCP와 TLS보다 훨씬 풍부한 라우팅을 허용해요. HTTP에서는 연결이 아니라 개별 HTTP 요청을 라우팅할 수 있어요. 또한 host, path, 헤더, 쿼리 파라미터 등 다양한 풍부한 속성을 사용할 수 있어요.
TCP와 TLS 트래픽은 Istio가 있든 없든 일반적으로 동일하게 동작하지만(라우팅을 커스터마이즈하는 구성이 적용되지 않았다고 가정), HTTP는 상당한 차이가 있어요.
- Istio는 개별 요청을 로드 밸런싱해요. 일반적으로 특히 gRPC와 HTTP/2 같은 장기 연결 시나리오에서 매우 바람직해요. 연결 수준 로드 밸런싱은 효과가 없기 때문이에요.
- 요청은 포트와 IP가 아니라 포트와
Host헤더를 기반으로 라우팅돼요. 이는 목적지 IP 주소가 실질적으로 무시된다는 뜻이에요. 예를 들어curl 8.8.8.8 -H "Host: productpage.default.svc.cluster.local"는productpageService로 라우팅돼요.
매칭되지 않는 트래픽
위에서 설명한 방법 중 하나로 트래픽을 매칭할 수 없다면 passthrough 트래픽으로 처리돼요. 기본적으로 이 요청들은 그대로 전달되며, 이는 Istio가 알지 못하는 서비스(ServiceEntry가 생성되지 않은 외부 서비스 같은)로 가는 트래픽이 계속 작동하도록 보장해요. 이 요청들이 전달될 때 상호 TLS는 사용되지 않고 텔레메트리 수집도 제한된다는 점을 기억하세요.
서비스 유형
표준 ClusterIP Service와 함께 Istio는 몇 가지 주의사항이 있지만 쿠버네티스 Service의 전체 범위를 지원해요.
LoadBalancer 및 NodePort Service
이 Service들은 ClusterIP Service의 상위 집합이며, 주로 외부 클라이언트의 접근을 허용하는 것과 관련돼요. 이 서비스 유형들은 지원되며 표준 ClusterIP Service와 정확히 동일하게 동작해요.
Headless Service
headless Service는 ClusterIP가 할당되지 않은 Service예요. 대신 DNS 응답에는 Service의 일부인 각 엔드포인트(즉 Pod IP)의 IP 주소가 포함돼요.
일반적으로 Istio는 Service 수준에서 작동하므로 각 Pod IP에 대한 리스너를 구성하지 않아요. 하지만 headless 서비스를 지원하기 위해 headless 서비스의 각 IP:포트 쌍에 리스너가 설정돼요. 예외는 HTTP로 선언된 프로토콜인데, 이는 Host 헤더로 트래픽을 매칭해요.
ExternalName Service
ExternalName Service는 본질적으로 DNS 별칭일 뿐이에요.
더 구체적으로 하기 위해 다음 예제를 고려해 보세요.
apiVersion: v1
kind: Service
metadata:
name: alias
spec:
type: ExternalName
externalName: concrete.example.com
매칭할 ClusterIP도 Pod IP도 없기 때문에 TCP 트래픽의 경우 Istio의 트래픽 매칭에 아무런 변화가 없어요. Istio가 요청을 받으면 concrete.example.com의 IP를 보게 돼요. 이것이 Istio가 아는 서비스라면 위에서 설명한 대로 라우팅돼요. 그렇지 않으면 매칭되지 않는 트래픽으로 처리돼요.
HTTP와 TLS의 경우 호스트네임으로 매칭하므로 상황이 조금 다르요. 대상 서비스(concrete.example.com)가 Istio가 아는 서비스라면 별칭 호스트네임(alias.default.svc.cluster.local)이 TLS 또는 HTTP 매칭에 추가 매치로 더해져요. 그렇지 않으면 변화가 없어 매칭되지 않는 트래픽으로 처리돼요.
ExternalName 서비스는 그 자체로 백엔드가 될 수 없어요. 대신 기존 Service에 대한 추가 프론트엔드 매치로만 사용돼요. VirtualService 목적지처럼 명시적으로 백엔드로 사용된다면 동일한 별칭이 적용돼요. 즉 alias.default.svc.cluster.local이 목적지로 설정되면 요청은 concrete.example.com으로 가요. 그 호스트네임이 Istio에 알려지지 않으면 요청은 실패해요. 이 경우 concrete.example.com에 대한 ServiceEntry가 이 구성을 작동하게 만들 거예요.
ServiceEntry
쿠버네티스 Service 외에도 Service Entry를 생성해 Istio가 아는 서비스 집합을 확장할 수 있어요. 이는 example.com 같은 외부 서비스로 가는 트래픽이 Istio의 기능을 얻도록 보장하는 데 유용할 수 있어요.
addresses가 설정된 ServiceEntry는 ClusterIP Service처럼 라우팅을 수행해요.
하지만 addresses가 없는 Service Entry의 경우 해당 포트의 모든 IP가 매칭돼요. 이는 같은 포트의 매칭되지 않는 트래픽이 올바르게 전달되는 것을 막을 수 있어요. 따라서 가능하면 이를 피하고, 필요할 때는 전용 포트를 사용하는 것이 가장 좋아요. HTTP와 TLS는 호스트네임/SNI를 기반으로 라우팅되므로 이 제약을 공유하지 않아요.
addresses 필드와 endpoints 필드는 종종 혼동돼요. addresses는 매칭될 IP를 의미하고, endpoints는 트래픽을 보낼 IP 집합을 의미해요.
예를 들어 아래 Service entry는 1.1.1.1에 대한 트래픽을 매칭하고, 구성된 로드 밸런싱 정책에 따라 요청을 2.2.2.2와 3.3.3.3으로 보내요.
addresses: [1.1.1.1]
resolution: STATIC
endpoints:
- address: 2.2.2.2
- address: 3.3.3.3