LabHub
はじめる
배우기 러닝패스 코스

Istio 実測ラボ

準備チェックは通るのに 500 を返す Pod

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

늘 500 을 내는 파드가 섞인 서비스에서 이상치 감지로 그 파드가 실제로 방출되는 것을 응답 수와 Envoy 의 엔드포인트 상태로 확인하고, 미러링과 장애 주입을 더해 그 설정들이 Envoy 의 무엇이 되는지 본다.

왜 중요한가

쿠버네티스의 준비 검사는 파드가 스스로 말하는 상태를 본다. 요청마다 500 을 내면서 준비 검사는 통과하는 파드는 엔드포인트에 계속 남는다. 이상치 감지는 실제로 받은 응답으로 판단하므로 그 파드를 빼낼 수 있지만, 그만큼 고장을 가리기도 한다. 방출을 눈으로 확인하고 숫자로 세는 법을 알아야 이 기능을 믿고 쓸 수 있다.

단계

  1. kubectl apply -f /opt/fixtures/istlab/resilience-app.yaml 로 재료를 올리고 파드가 모두 준비될 때까지 기다리세요. 그다음 client 에서 http://api/ 를 30번 부르고 /root/istlab-resilience/01-baseline.txtok=(200 수)와 err=(500 수)를 적으세요.
  2. /root/istlab-resilience/client.yaml 에 client 파드를 다시 적되 애너테이션 proxy.istio.io/configproxyStatsMatcher.inclusionRegexps: [".*outlier_detection.*"] 를 넣고, 기존 client 를 지운 뒤 이 파일로 다시 만드세요.
  3. /root/istlab-resilience/dr.yamlDestinationRule api(호스트 api.pay.svc.cluster.local)를 적어 적용하세요 — outlierDetection 으로 연속 5xx 2번(consecutive5xxErrors: 2), 검사 주기 2초, 기본 방출 시간 60초, 최대 방출 비율 50%. 그다음 30번씩 두 차례 부르고 /root/istlab-resilience/03-outlier.txterr_first=(첫 30번의 500 수)와 err_second=(둘째 30번의 500 수)를 적으세요.
  4. 방출이 걸려 있는 동안(3단계 뒤 60초 안에) istioctl proxy-config endpoints client.pay --cluster 'outbound|80||api.pay.svc.cluster.local' -o json 의 출력을 /root/istlab-resilience/04-endpoints.json 에 저장하세요. 60초가 지났다면 3단계처럼 30번 불러 다시 방출시킨 뒤 저장합니다.
  5. /root/istlab-resilience/vs.yamlVirtualService api(호스트 api.pay.svc.cluster.local)를 적어 적용하세요 — 목적지는 그대로 api.pay.svc.cluster.local, mirrorapi-v2.pay.svc.cluster.local, mirrorPercentage 100. 적용 뒤 x-request-idmirror-1·mirror-2·mirror-3 으로 붙여 세 번 부르고 /root/istlab-resilience/05-mirror.txtmirrored=(api-v2 사이드카 로그에 남은 그 세 id 의 수)와 client_saw_v2=(client 가 받은 본문 가운데 v2 가 있었으면 yes) 두 줄을 적으세요.
  6. /root/istlab-resilience/vs.yaml 의 VirtualService 앞쪽에 규칙 둘을 더하세요 — 헤더 x-chaos: abort 이면 fault.abort 로 503 을 100%, x-chaos: delay 이면 fault.delay 로 2초를 100% 넣고 목적지는 그대로입니다(미러를 단 기본 규칙은 맨 뒤에 둡니다). 적용 뒤 /root/istlab-resilience/06-fault.txtabort_code=(abort 요청의 상태 코드), abort_flag=(그 요청의 client 사이드카 접근 로그 응답 플래그), delay_seconds=(delay 요청에 걸린 시간을 소수점 버린 초) 세 줄을 적으세요.
  7. client 사이드카가 받은 설정에서 두 가지를 꺼내 /root/istlab-resilience/07-envoy.json 에 JSON 객체로 저장하세요 — outlier: istioctl proxy-config cluster client.pay --fqdn api.pay.svc.cluster.local -o json 의 첫 클러스터의 outlierDetection 객체 그대로, mirror_cluster: istioctl proxy-config routes client.pay --name 80 -o json 에서 api.pay.svc.cluster.local 가상 호스트의 마지막 라우트가 가진 requestMirrorPolicies[0].cluster 값.
  8. /root/istlab-resilience/08-report.md 에 다섯 줄 — errors_before=(1단계 err), errors_after_ejection=(3단계 err_second), ejected_ip=(4단계 파일에서 failedOutlierCheck 가 true 인 주소), mirror_reached_client=(5단계 client_saw_v2), abort_flag=(6단계) — 을 적고 그 아래 배운 것을 네 줄 이상 적으세요.

참고

고장 난 파드 하나가 섞인 서비스

kubectl apply -f /opt/fixtures/istlab/resilience-app.yaml 로 재료를 올리고 파드가 모두 준비될 때까지 기다리세요. 그다음 client 에서 http://api/ 를 30번 부르고 /root/istlab-resilience/01-baseline.txtok=(200 수)와 err=(500 수)를 적으세요.

api 서비스 뒤에는 파드가 셋이고 그중 api-bad 는 언제나 500 을 냅니다. 준비 검사가 없어서 쿠버네티스는 셋 다 Ready 로 보고 엔드포인트에 넣습니다 — 쿠버네티스의 눈에는 멀쩡한 파드입니다. 그래서 요청의 약 3분의 1 이 실패합니다. 파드 안의 셸에서 반복하려면 sh -c 'for i in $(seq 1 30); do …; done' 을 쓰세요.

보이지 않던 통계를 켠다

/root/istlab-resilience/client.yaml 에 client 파드를 다시 적되 애너테이션 proxy.istio.io/configproxyStatsMatcher.inclusionRegexps: [".*outlier_detection.*"] 를 넣고, 기존 client 를 지운 뒤 이 파일로 다시 만드세요.

Istio 는 사이드카가 내는 Envoy 통계 대부분을 기본으로 걸러 버립니다 — 파드 수천 개가 전부 내보내면 Prometheus 가 감당하지 못하기 때문입니다. 그래서 이상치 감지를 켜도 방출 횟수 같은 숫자가 보이지 않습니다. proxyStatsMatcher 는 파드 애너테이션으로 필요한 통계만 다시 살리는 장치이고, 프록시가 뜰 때 읽으므로 파드를 다시 만들어야 합니다. 잘 들어갔는지는 프록시 부트스트랩의 stats_config 에서 볼 수 있습니다.

이상치 감지로 고장 난 파드를 빼낸다

/root/istlab-resilience/dr.yamlDestinationRule api(호스트 api.pay.svc.cluster.local)를 적어 적용하세요 — outlierDetection 으로 연속 5xx 2번(consecutive5xxErrors: 2), 검사 주기 2초, 기본 방출 시간 60초, 최대 방출 비율 50%. 그다음 30번씩 두 차례 부르고 /root/istlab-resilience/03-outlier.txterr_first=(첫 30번의 500 수)와 err_second=(둘째 30번의 500 수)를 적으세요.

이상치 감지는 사이드카가 실제로 받은 응답으로 업스트림 하나하나를 판단합니다. 같은 파드가 연달아 5xx 를 내면 그 파드를 부하 분산 대상에서 한동안 빼냅니다(방출). 첫 차례에는 방출되기 전 몇 번의 500 이 보이고, 둘째 차례에는 0 이어야 합니다. 방출된 횟수는 2단계에서 살린 통계 …outlier_detection.ejections_enforced_total 로 셉니다 — kubectl -n pay exec client -c istio-proxy -- pilot-agent request GET stats | grep outlier.

Envoy 는 그 파드를 어떻게 적어 두었나

방출이 걸려 있는 동안(3단계 뒤 60초 안에) istioctl proxy-config endpoints client.pay --cluster 'outbound|80||api.pay.svc.cluster.local' -o json 의 출력을 /root/istlab-resilience/04-endpoints.json 에 저장하세요. 60초가 지났다면 3단계처럼 30번 불러 다시 방출시킨 뒤 저장합니다.

방출된 파드는 쿠버네티스 엔드포인트에서 빠지지 않습니다. 이 사이드카의 부하 분산표에서만 빠집니다. 그래서 kubectl get endpoints 에는 셋이 그대로 있고, Envoy 의 엔드포인트 목록에서 그 파드의 healthStatusfailedOutlierCheck: true 가 붙습니다. 표 모양으로 보면 OUTLIER CHECK 칸이 FAILED 입니다. 방출은 영구가 아니라 기본 방출 시간 뒤 다시 들어오고, 또 실패하면 더 길게 빠집니다.

운영 트래픽을 새 버전에 비춰 본다 — 미러링

/root/istlab-resilience/vs.yamlVirtualService api(호스트 api.pay.svc.cluster.local)를 적어 적용하세요 — 목적지는 그대로 api.pay.svc.cluster.local, mirrorapi-v2.pay.svc.cluster.local, mirrorPercentage 100. 적용 뒤 x-request-idmirror-1·mirror-2·mirror-3 으로 붙여 세 번 부르고 /root/istlab-resilience/05-mirror.txtmirrored=(api-v2 사이드카 로그에 남은 그 세 id 의 수)와 client_saw_v2=(client 가 받은 본문 가운데 v2 가 있었으면 yes) 두 줄을 적으세요.

미러링은 요청을 복제해 한쪽으로 보내고 그 응답은 버립니다. 그래서 새 버전을 운영 트래픽으로 시험하면서 사용자에게는 영향을 주지 않습니다. 복제본은 원래 요청과 같은 x-request-id 를 가지므로, 미러 대상의 사이드카 로그에서 id 로 찾을 수 있습니다 — kubectl -n pay logs deploy/api-v2 -c istio-proxy. 복제본은 비동기로 가서 로그에 조금 늦게 찍힐 수 있습니다.

장애를 일부러 넣는다 — 헤더가 있을 때만

/root/istlab-resilience/vs.yaml 의 VirtualService 앞쪽에 규칙 둘을 더하세요 — 헤더 x-chaos: abort 이면 fault.abort 로 503 을 100%, x-chaos: delay 이면 fault.delay 로 2초를 100% 넣고 목적지는 그대로입니다(미러를 단 기본 규칙은 맨 뒤에 둡니다). 적용 뒤 /root/istlab-resilience/06-fault.txtabort_code=(abort 요청의 상태 코드), abort_flag=(그 요청의 client 사이드카 접근 로그 응답 플래그), delay_seconds=(delay 요청에 걸린 시간을 소수점 버린 초) 세 줄을 적으세요.

장애 주입은 클라이언트 쪽 사이드카가 요청을 업스트림에 보내기 전에 합니다. 그래서 abort 는 업스트림에 닿지 않고 응답 플래그가 FI(fault injected)로 찍힙니다. 헤더 조건을 걸어 두면 평소 트래픽은 그대로 두고 시험하는 요청만 망가뜨릴 수 있습니다. VirtualService 의 http 규칙은 위에서부터 처음 맞는 것이 쓰이므로 포괄 규칙은 맨 뒤에 둡니다. 걸린 시간은 curl -w '%{time_total}' 로 잽니다.

DestinationRule 과 VirtualService 는 Envoy 의 무엇이 되었나

client 사이드카가 받은 설정에서 두 가지를 꺼내 /root/istlab-resilience/07-envoy.json 에 JSON 객체로 저장하세요 — outlier: istioctl proxy-config cluster client.pay --fqdn api.pay.svc.cluster.local -o json 의 첫 클러스터의 outlierDetection 객체 그대로, mirror_cluster: istioctl proxy-config routes client.pay --name 80 -o json 에서 api.pay.svc.cluster.local 가상 호스트의 마지막 라우트가 가진 requestMirrorPolicies[0].cluster 값.

istiod 는 DestinationRule 의 consecutive5xxErrors·interval·baseEjectionTime·maxEjectionPercent 를 Envoy 클러스터의 outlierDetection 으로, VirtualService 의 mirror 를 라우트의 requestMirrorPolicies 로 옮깁니다. 이름이 조금씩 다르고(consecutive5xx), 시간은 "2s" 같은 문자열로 바뀝니다. 설정을 바꿨는데 동작이 이상하면 결국 이 번역 결과를 보게 됩니다.

메시가 무엇을 대신해 주었는지 적는다

/root/istlab-resilience/08-report.md 에 다섯 줄 — errors_before=(1단계 err), errors_after_ejection=(3단계 err_second), ejected_ip=(4단계 파일에서 failedOutlierCheck 가 true 인 주소), mirror_reached_client=(5단계 client_saw_v2), abort_flag=(6단계) — 을 적고 그 아래 배운 것을 네 줄 이상 적으세요.

값은 앞 단계 파일에서 옮기세요. 설명 줄에는 '쿠버네티스의 준비 검사와 메시의 이상치 감지가 각각 무엇을 보고 판단하는가' 를 적어 두세요 — 하나는 파드가 말하는 상태를, 하나는 실제로 받은 응답을 봅니다.