Istio 서비스 메시 · 카나리와 트래픽 분할 · 실습
가중치·헤더·미러링으로 카나리 굴리기
목표
가중치·헤더·미러링 세 가지 분할 방식을 상황에 맞게 골라 쓰고, 되돌릴 조건이 명시된 카나리 계획을 만들 수 있게 됩니다.
왜 중요한가
카나리의 본질은 "조금 준다"가 아니라 "언제 멈출지를 먼저 정한다" 입니다. 가중치를 올리는 일은 누구나 할 수 있지만, 나쁜 지표를 보고 되돌리는 판단은 사람에게 맡기면 흔들립니다. 그래서 단계마다 판단 기준을 적어 두고, 그 기준이 곧 자동화의 입력이 되게 만듭니다. 또 하나 중요한 것은 파드 개수와 트래픽 비율의 분리입니다. 메시에서는 새 버전을 다 띄워 놓고도 트래픽은 0% 로 둘 수 있고, 문제가 보이면 배포 없이 가중치만 0 으로 되돌릴 수 있습니다.
이 실습은 두 종류의 채점이 섞여 있습니다. 1·2·4·5번은 저장된 파일을 보고, 6·7·8번은 클러스터에 실제로 적용된 상태를 봅니다. 그래서 단계마다 파일을 따로 남겨야 하고, 하나를 계속 덮어쓰면 앞 단계가 다시 실패합니다. 각 vs-*.yaml 파일에는 VirtualService 문서 하나만 넣으세요(여러 문서를 한 파일에 담으면 채점이 첫 문서를 특정하지 못합니다).
단계
시작 전 준비. 실습 파드는 실습마다 새로 뜨므로 앞 실습의 메시 설정은 남아 있지 않습니다. kubectl get crd virtualservices.networking.istio.io 가 비어 있으면 istioctl manifest generate --set profile=minimal > /root/istio/manifest.yaml 후 kubectl apply -f /root/istio/manifest.yaml 을 두 번 실행하고, kubectl create ns mesh-lab && kubectl label ns mesh-lab istio-injection=enabled 로 네임스페이스를 만드세요. 6번은 살아 있는 파드를 라벨로 조회하므로 픽스처 워크로드의 파드가 Running 이 될 때까지 기다려야 합니다.
1. 먼저 /opt/lab/fixtures/istio/workload-v1v2.yaml 을 mesh-lab 에 적용하고, spec.subsets 에 v1(labels: {version: v1})·v2(labels: {version: v2})를 가진 DestinationRule reviews 를 적용해 두세요. 그다음 /root/istio/canary/vs-baseline.yaml 을 만드세요 — VirtualService 하나이며 spec.http[0].route 에 항목이 정확히 두 개, subset: v1 의 weight 는 100, subset: v2 의 weight 는 0 입니다.
2. /root/istio/canary/vs-90-10.yaml 을 만드세요. v1 은 weight: 90, v2 는 weight: 10 입니다(합은 100). 이 파일을 kubectl apply 하고 그 출력을 /root/istio/canary/out/applied-10.txt 에 저장하세요(configured/created/unchanged 중 하나가 보여야 합니다).
3. 가중치 합이 100 이 아닌 파일(예: /root/istio/canary/vs-bad.yaml 에 v1 90 / v2 20)을 만들어 istioctl validate -f 로 검사하고, 그 거부 메시지를 표준 에러까지 포함해 /root/istio/canary/out/weight-error.txt 에 저장하세요. 그리고 /root/istio/canary/out/weight-note.txt 에 가중치 합이 100 이어야 한다는 규칙을 적으세요.
4. /root/istio/canary/vs-header.yaml 을 만드세요. 헤더 x-canary 가 정확히 true 인 요청을 고르는 라우트를 앞쪽에 두고 목적지는 subset: v2, weight: 100 입니다. 그 뒤에 조건 없는 기본 라우트를 하나 더 두세요(조건부 라우트가 마지막이면 안 됩니다).
5. /root/istio/canary/vs-mirror.yaml 을 만드세요. spec.http[0].route 는 subset: v1 에 weight: 100 이고, 같은 항목에 mirror(host: reviews, subset: v2)와 mirrorPercentage.value: 10 을 두세요. 그리고 /root/istio/canary/out/mirror-note.txt 에 미러된 요청의 응답이 버려진다는 점과 쓰기 작업의 부작용 위험을 적으세요.
6. 먼저 vs-90-10.yaml 을 다시 적용해 v1·v2 를 모두 참조하는 상태로 만드세요. 그다음 DestinationRule reviews 의 subset 이름 v2 를 잠시 v2-canary 로 바꿔 적용하고 istioctl analyze -n mesh-lab 결과를 /root/istio/canary/out/analyze-mismatch.txt 에 저장하세요. 이름을 v2(labels version: v2)로 되돌려 적용한 뒤 다시 분석해 /root/istio/canary/out/analyze-clean.txt 에 저장하세요(여기에는 Error [ 가 없어야 합니다).
7. DestinationRule reviews 에 trafficPolicy.loadBalancer.consistentHash.httpHeaderName: x-user-id 를 추가해 적용하세요. 그리고 /root/istio/canary/out/sticky-note.txt(50바이트 이상)에 같은 사용자가 버전 사이를 왔다 갔다 하면 왜 문제가 되는지 적으세요.
8. /root/istio/canary/rollout.yaml 을 만드세요. 최상위에 stages 배열(5단계 이상)과 rollback 키가 있어야 합니다. 각 stage 에는 weight(v2 가중치)와 criteria(다음 단계로 넘어갈 판단 기준)가 모두 있어야 하고, weight 는 0 으로 시작해 100 으로 끝나며 중간에 줄어들면 안 됩니다. 마지막으로 실제 VirtualService reviews 를 v1 50 / v2 50 으로 적용하세요(spec.http 의 마지막 항목 기준). istioctl analyze -n mesh-lab 결과는 /root/istio/canary/out/final-analyze.txt 에 저장하고 오류가 없어야 합니다.
참고
- 실습 파드는 실습마다 새로 뜨므로 앞 실습의 클러스터 상태는 남아 있지 않습니다. 그래서 메시 설정을 매니페스트로 남겨 두는 것이 곧 재현 가능성입니다. 이 실습이 단계마다 파일을 따로 남기라고 요구하는 이유도 같습니다 — 클러스터는 사라져도 파일은 남습니다.
kubectl get pods -n mesh-lab -l app=reviews --show-labels로 파드의version라벨이 실제로 붙어 있는지 먼저 확인하세요. 6번 채점이 이 라벨로 파드를 찾습니다.- 3번의
istioctl validate는 실패로 끝나므로istioctl validate -f 파일 > 출력 2>&1 || true처럼 저장하세요. - 6번에서 subset 이름을 바꾸면 VirtualService 가 참조하는
v2가 사라져 분석이 즉시 잡아냅니다. 라벨만 바꾸는 것보다 확실한 실험입니다. - 8번의
rollout.yaml은 Istio 리소스가 아니라 여러분이 설계한 계획 파일입니다. 형식은 자유지만stages·weight·criteria·rollback키 이름은 그대로 쓰세요. - 흔한 실수 1: 파일 하나를 계속 고쳐 쓰는 것. 단계마다 다른 파일이어야 앞 단계 채점이 유지됩니다.
- 흔한 실수 2: 5번에서 v1 의
weight: 100을 생략하는 것. 목적지가 하나면 생략해도 동작은 하지만, 미러링이 실제 응답을 바꾸지 않는다는 사실을 매니페스트에 명시적으로 남겨 두는 편이 안전합니다.
단계 8개
- 100/0 시작점 매니페스트 만들기
- 10% 를 새 버전으로 옮기기
- 가중치 합이 틀렸을 때 무슨 일이 나는지 보기
- 내부 테스터만 새 버전으로 보내기
- 응답을 버리는 미러링 걸기
- subset 과 파드 라벨의 불일치 만들고 고치기
- 같은 사용자를 한 버전에 고정하기
- 단계별 카나리 계획 세우고 중간까지 진행하기