이번에 Kubernetes에서 Canary 배포를 적용하면서 여러 가지 개념과 동작 원리에 대해 스스로 정리할 수 있는 기회가 있었다. 처음엔 단순히 Ingress에 canary-weight만 붙이면 되는 줄 알았는데, 막상 해보니까 생각보다 훨씬 깊은 구조와 동작 흐름이 숨어 있었다.
1. Ingress는 어떻게 트래픽을 나누는가?
처음엔 궁금했다. Ingress 리소스에 연결된 Service는 하나인데, 어떻게 요청이 Canary와 Stable로 나뉘지? 알고 보니 Ingress 리소스는 여러 개를 만들 수 있고, 동일한 host와 path를 가진 Ingress가 두 개 있을 때, 그중 하나에 nginx.ingress.kubernetes.io/canary: "true"와 같은 annotation을 붙이면 NGINX Ingress Controller가 이걸 인식해서 트래픽을 나눠준다.
기본 Ingress는 전체 요청을 Stable 서비스로 보내고, Canary Ingress는 특정 조건 (가중치, 헤더, 쿠키 등)에 따라 일부 요청만 Canary 서비스로 보낸다. 이 로직은 Kubernetes가 아니라 NGINX Ingress Controller 내부에서 작동한다.
2. Canary가 제대로 작동하려면 무엇이 필요할까?
Ingress만 나눈다고 되는 건 아니다. Service까지 따로 나눠야 진짜 Canary 배포가 가능하다. 왜냐하면 Ingress에서 아무리 분기해도 Service가 모든 파드를 바라보고 있다면 결국 트래픽은 Stable과 Canary 파드에 무작위로 흘러가게 된다.
따라서:
- Stable용 Service는 Stable 파드만 바라보는 selector (예: version: stable)
- Canary용 Service는 Canary 파드만 바라보는 selector (예: version: canary)
이렇게 완전히 분리해야 한다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: NAME_SPACE
name: NAME
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20" # 20%만 신규 파드로 전달
3. 가시적으로 Canary 트래픽을 확인하려면?
기존 파드나 응답을 수정할 수 없는 상황이라면 외부에서 어떤 요청이 Canary로 갔는지 확인하는 건 매우 어렵다.
하지만 Canary 파드만 살짝 수정할 수 있다면, 응답 헤더에 버전 정보를 넣거나, 콘솔에 System.out.println으로 Canary 요청을 찍어주는 방법이 가장 깔끔하다.
K6나 curl로 테스트하면서 헤더에 X-Canary: true를 넣으면 Canary Ingress를 타게 되는데, 이 때 Canary 파드에서 로그가 찍히는지 확인하면 된다.
실제로 이렇게 했을 때 Canary-weight 20% 설정이 정확히 반영되어, 대략 20~25% 수준으로 트래픽이 들어가는 걸 확인할 수 있었다.
kubectl logs POD/PodName -n NameSpace | grep "12345" | wc -l
4. 트래픽은 어떤 순서로 흘러갈까?
전체 트래픽 흐름은 다음과 같다:
- Client (curl, 브라우저 등)
- 외부 Load Balancer (ex. AWS ELB)
- NGINX Ingress Controller (Pod로 구동 중)
- Ingress 리소스 (조건 판단: 기본 or canary)
- Service (Stable or Canary)
- 파드 (Stable or Canary)
NGINX는 Ingress 리소스를 모두 읽고, 조건에 따라 트래픽을 분기하는 로직을 자체 설정 (nginx.conf)으로 만들어 처리한다.
5. Ingress와 IngressClass는 뭐가 다를까?
- Ingress는 요청을 어디로 보낼지 (host/path -> service) 를 정의하는 리소스
- IngressClass는 어떤 Ingress Controller가 이 Ingress를 처리할지를 결정하는 메타정보
하나의 클러스터에 여러 Ingress Controller가 있을 수 있기 때문에, ingressClassName을 지정해주는 게 중요하다. 그래야 의도한 Controller가 해당 Ingress를 처리하게 된다.
마무리
Canary 배포는 생각보다 섬세하게 설계되어야 하고, 단순히 annotation 하나로 끝나는 게 아니었다. 라벨, Selector, Service 분리, Ingress 구조 이해까지 제대로 갖춰야만 우리가 의도한 만큼의 트래픽 분산이 가능했다.
그리고 가장 중요한 건 "눈에 보이게" Canary가 동작하는지 확인할 수 있는 수단을 마련하는 것.
이걸 놓치면 진짜 Canary인지 아닌지도 모른 채 배포하게 될 수도 있다.
이번 경험을 통해 Kubernetes 위에서 Canary 배포가 어떻게 작동하는지, 그리고 실제 운영에선 어떤 점들을 신경 써야 하는지 몸소 느낄 수 있었다. 이건 단순 설정이 아니라 설계의 영역이었다.
'쿠버네티스(k8s)' 카테고리의 다른 글
[k8s] Kubernetes Canary 사용시 주의사항 (1) | 2025.04.11 |
---|---|
[k8s] Kubernetes Canary 배포 삽질기 (0) | 2025.04.07 |
[k8s] 쿠버네티스 환경에서 k6로 부하 테스트 해보기 + 리소스 사용량 확인까지 (1) | 2025.04.03 |
쿠버네티스에서 HPA와 VPA를 동시에 사용하면 안 되는 이유 (0) | 2024.08.14 |
쿠버네티스에서 QoS와 트래픽 손실 방지 방법 (0) | 2024.08.08 |