L7 로드 밸런싱과 URL 재작성
L7 로드 밸런싱과 URL 재작성 (L7 Load Balancing and URL re-writing)
Cilium Service Mesh는 CiliumEnvoyConfig CRD를 정의해서 Cilium 에이전트에 내장된 Envoy 컴포넌트의 구성을 설정할 수 있게 해줘요. 이 예제에서는 두 백엔드 서비스 사이에서 요청을 로드 밸런싱하는 Envoy 리스너를 구성하는 방법을 보여드릴게요.
본문
테스트 애플리케이션 배포하기 (Deploy Test Applications)
$ kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.20.2/examples/kubernetes/servicemesh/envoy/test-application.yaml
테스트 워크로드는 다음과 같이 구성돼요:
- 두 개의 client 디플로이먼트,
client와client2 - 두 개의 서비스,
echo-service-1과echo-service-2
이 파드들에 대한 정보를 확인해볼게요:
$ kubectl get pods --show-labels -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
client-7568bc7f86-dlfqr 1/1 Running 0 100s 10.0.1.8 minikube-m02 <none> <none> kind=client,name=client,pod-template-hash=7568bc7f86
client2-8b4c4fd75-xn25d 1/1 Running 0 100s 10.0.1.24 minikube-m02 <none> <none> kind=client,name=client2,other=client,pod-template-hash=8b4c4fd75
echo-service-1-97748874-4sztx 2/2 Running 0 100s 10.0.1.86 minikube-m02 <none> <none> kind=echo,name=echo-service-1,other=echo,pod-template-hash=97748874
echo-service-2-76c584c4bf-p4z4w 2/2 Running 0 100s 10.0.1.16 minikube-m02 <none> <none> kind=echo,name=echo-service-2,pod-template-hash=76c584c4bf
여기서 다음을 확인할 수 있어요:
client2만other=client레이블이 붙어 있어요. 이 레이블은 나중에CiliumNetworkPolicy정의에서 사용할 거예요.
client2의 파드 ID로 환경 변수를 만들어볼게요:
$ export CLIENT2=$(kubectl get pods -l name=client2 -o jsonpath='{.items[0].metadata.name}')
이제 Envoy 구성을 사용해서 echo-service-1과 echo-service-2 두 서비스 사이의 요청을 로드 밸런싱할 거예요.
Hubble로 트래픽 관찰 시작하기 (Start Observing Traffic with Hubble)
Setting up Hubble Observability에 나온 단계에 따라 클러스터에서 Hubble을 활성화해주세요.
두 번째 터미널을 시작한 다음 Hubble 포트 포워딩을 활성화하고 client2 파드에서 오는 트래픽을 관찰해볼게요:
$ kubectl -n kube-system port-forward deployment/hubble-relay 4245:4245 &
$ hubble observe --from-pod $CLIENT2 -f
client2에서 두 백엔드 서비스 각각에 대한 응답을 받을 수 있어야 해요:
$ kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/
$ kubectl exec -it $CLIENT2 -- curl -v echo-service-2:8080/
Hubble은 이 파드들 사이의 모든 흐름을 to/from-stack, to/from-overlay 또는 to/from-endpoint로 표시해요. 이 단계에서는 프록시로 또는 프록시에서 흐르는 것으로 표시되는 트래픽은 없어요. (단, 이 트래픽에 영향을 주는 Layer 7 정책이 이미 없는 경우를 가정해요.)
이 서비스들에서 존재하지 않는 URL /foo에 curl을 보내면 404 오류가 나는지 확인해볼게요:
$ kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/foo
$ kubectl exec -it $CLIENT2 -- curl -v echo-service-2:8080/foo
Layer 7 정책 추가하기 (Add Layer 7 Policy)
Layer 7 정책을 추가하면 이 트래픽의 경로에 Envoy 프록시가 들어오게 돼요.
$ kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.20.2/examples/kubernetes/servicemesh/envoy/client-egress-l7-http.yaml
$ kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.20.2/examples/kubernetes/servicemesh/envoy/client-egress-only-dns.yaml
백엔드 서비스에 요청을 보내볼게요 (둘 중 아무거나 괜찮아요):
$ kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/
$ kubectl exec -it $CLIENT2 -- curl -v echo-service-2:8080/foo
Layer 7 정책을 추가하면 Layer 7 가시성이 활성화돼요. 이제 Hubble 출력에 to-proxy 흐름이 포함되고, 레벨 7의 HTTP 프로토콜 정보(예: HTTP/1.1 GET http://echo-service-1:8080/)도 표시되는 걸 볼 수 있어요.
참고
Envoy는 일부 헤더를 정리(sanitize)할 수 있어요.
대신 Envoy가 이전 홉(previous hops)을 신뢰하게 만들어서 HTTP 헤더 중 일부를 재작성하지 못하게 할 수도 있어요. Helm 값 envoy.xffNumTrustedHopsL7PolicyIngress와 envoy.xffNumTrustedHopsL7PolicyEgress를 신뢰할 홉(hop) 수로 설정하면 이전 홉을 신뢰해요.
이그레스 정책에서 이전 홉은 소스 파드이고, 인그레스 정책에서는 소스 파드, "egress policy transparent proxy", Cilium Ingress Controller, Cilium Gateway API 또는 다른 Ingress 프록시/인프라일 수 있어요.
환경에 따라 이전 홉을 신뢰할 때의 보안 영향도 고려해보세요.
Layer 7 정책 적용 테스트하기 (Test Layer 7 Policy Enforcement)
이 정책은 / 경로로 가는 GET 요청만 허용하므로, 다른 URL로 가는 요청은 전부 드롭되는 걸 볼 수 있어요. 예를 들어 다음을 시도해보세요:
$ kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/foo
Hubble 출력에는 HTTP 요청이 드롭된다고 다음과 같이 표시돼요:
Jul 7 08:40:15.076: default/client2-8b4c4fd75-6pgvl:58586 -> default/echo-service-1-97748874-n7758:8080 http-request DROPPED (HTTP/1.1 GET http://echo-service-1:8080/foo)
그리고 curl은 403 Forbidden 응답을 보여줘야 해요.
Envoy 로드 밸런싱과 URL 재작성 추가하기 (Add Envoy load-balancing and URL re-writing)
CiliumClusterwideEnvoyConfig를 정의하는 envoy-traffic-management-test.yaml 파일을 적용해볼게요.
$ kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.20.2/examples/kubernetes/servicemesh/envoy/envoy-traffic-management-test.yaml
참고
이 Envoy 리소스들은 K8s에서 전혀 검증되지 않아요. 즉 Envoy 리소스에 오류가 있어도 그 오류는 이 CRD를 관찰하는 Cilium Agent에서만 보여요. 따라서
kubectl apply는 성공을 보고하지만, 노드 로컬 Envoy 인스턴스를 위한 리소스의 파싱/설치는 실패했을 수 있어요. 현재 이를 검증하는 유일한 방법은 오류와 경고에 대해 Cilium Agent 로그를 관찰하는 거예요. 또한 Cilium Agent는 클러스터 안의 충돌하는 Envoy 리소스에 대해 경고 로그를 출력해요.
참고
Cilium Ingress Controller는 내부적으로 필요한 Envoy 리소스를 구성해줘요. 충돌이 없는지 Envoy 리소스를 명시적으로 만들 때는 Cilium Agent 로그를 확인해주세요.
이 구성은 두 echo- 서비스 중 하나로 향하는 트래픽을 수신해서 다음을 수행해요:
- 두 백엔드
echo-서비스 사이에서 50/50으로 로드 밸런싱 - 경로
/foo를/로 재작성
이제 경로 재작성 덕분에 /foo에 대한 요청은 성공해야 해요:
$ kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/foo
하지만 네트워크 정책은 여전히 /로 재작성되지 않은 경로로 가는 요청을 막아요. 예를 들어 다음 요청은 패킷이 드롭되고 403 Forbidden 응답 코드가 나올 거예요:
$ kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/bar
### Output from hubble observe
Jul 7 08:43:47.165: default/client2-8b4c4fd75-6pgvl:33376 -> default/echo-service-2-76c584c4bf-874dm:8080 http-request DROPPED (HTTP/1.1 GET http://echo-service-1:8080/bar)
한 백엔드 서비스에 여러 번 요청을 보내보세요. Hubble 출력에서 대략 절반의 경우 다른 백엔드가 처리하는 걸 볼 수 있을 거예요.
예:
Jul 7 08:45:25.807: default/client2-8b4c4fd75-6pgvl:37388 -> kube-system/coredns-64897985d-8jhhn:53 L3-L4 REDIRECTED (UDP)
Jul 7 08:45:25.807: default/client2-8b4c4fd75-6pgvl:37388 -> kube-system/coredns-64897985d-8jhhn:53 to-proxy FORWARDED (UDP)
Jul 7 08:45:25.807: default/client2-8b4c4fd75-6pgvl:37388 -> kube-system/coredns-64897985d-8jhhn:53 dns-request FORWARDED (DNS Query echo-service-1.default.svc.cluster.local. AAAA)
Jul 7 08:45:25.807: default/client2-8b4c4fd75-6pgvl:37388 -> kube-system/coredns-64897985d-8jhhn:53 dns-request FORWARDED (DNS Query echo-service-1.default.svc.cluster.local. A)
Jul 7 08:45:25.808: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-1:8080 none REDIRECTED (TCP Flags: SYN)
Jul 7 08:45:25.808: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-1:8080 to-proxy FORWARDED (TCP Flags: SYN)
Jul 7 08:45:25.808: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-1:8080 to-proxy FORWARDED (TCP Flags: ACK)
Jul 7 08:45:25.808: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-1:8080 to-proxy FORWARDED (TCP Flags: ACK, PSH)
Jul 7 08:45:25.809: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-2-76c584c4bf-874dm:8080 L3-L4 REDIRECTED (TCP Flags: SYN)
Jul 7 08:45:25.809: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-2-76c584c4bf-874dm:8080 to-endpoint FORWARDED (TCP Flags: SYN)
Jul 7 08:45:25.809: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-2-76c584c4bf-874dm:8080 to-endpoint FORWARDED (TCP Flags: ACK)
Jul 7 08:45:25.809: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-2-76c584c4bf-874dm:8080 to-endpoint FORWARDED (TCP Flags: ACK, PSH)
Jul 7 08:45:25.809: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-2-76c584c4bf-874dm:8080 http-request FORWARDED (HTTP/1.1 GET http://echo-service-1:8080/)
Jul 7 08:45:25.811: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-1:8080 to-proxy FORWARDED (TCP Flags: ACK, FIN)
Jul 7 08:45:25.811: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-1:8080 to-proxy FORWARDED (TCP Flags: ACK)
Jul 7 08:45:30.811: default/client2-8b4c4fd75-6pgvl:57942 -> default/echo-service-2-76c584c4bf-874dm:8080 to-endpoint FORWARDED (TCP Flags: ACK, FIN)
더 알아보기 (Learn more)
- Envoy Traffic Shifting — 트래픽 비율을 점진적으로 전환하는 방법
- L7-Aware Traffic Management — L7 트래픽 관리
- CiliumEnvoyConfig CRD — Cilium Service Mesh 개요