1. circuit break란?
circuit break에 대해 다양한 관점이 있습니다. 이 글에서 말하는 circuit break는 쿠버네티스 pod 안정성 관점에서 circuit break입니다. pod 안전성 관점에서 circuit break는 pod가 죽지 않도록, 강제로 pod에 들어오는 요청을 거절합니다.
2. 왜 pod에 들어오는 요청을 거절할까요?
요청을 거절하는 이유는 쿠버네티스 관점에서 pod가 죽지 않게 보호하기 위해서입니다.
pod가 처리할 수 있는 요청수보다 많은 요청을 받으면, pod는 재실행 될 수 있습니다. pod의 cpu, memory가 100%를 넘게 사용하는 상황이 발생하고 OOM 또는 probe검사 실패로 pod가 재실행됩니다.
만약 모든 pod가 재실행되는 경우 서비스 장애로 이루어집니다. 그래서 운영자는 pod 재실행되지 않도록 안전하게 보호해야 하는데, 구현 방법 중 한개가 circuit break입니다.
비지니스 관점에서는 장애범위를 최소화합니다. pod가 다른 pod가 호출하고 그 pod가 다른 pod를 호출하는 것처럼 비지니스 호출이 꼬리를 물 때, 어느 한 곳에 장애가 일어나면 장애가 전파됩니다. 장애가 전파되면 디버깅이 어렵고 복구하기가 매우 힘듭니다. 그래서 미리 circuit break를 설정하여 장애범위를 축소시켜 향후 대처를 빠르게 합니다.
https://youtube.com/shorts/8a-J6N2pXZ8?si=rfccavLqW8Icmvj5
3. circuit break가 발생하는 상황
제가 경험했던 상황은 실행이 매우 느린 API호출이 짧은 기간에 많이 호출당했을 때 circuit break가 발생했습니다. API호출이 느리면 connection이 실행이 될 때까지 맺어지고, 짧은 시간에 많은 요청을 받으면 connection이 누적되어 circut break가 발생했습니다.
해결 방법은 API호출이 왜 느린지 분석하여 API호출을 빠르게 하는 것입니다. 코드 문제, 네트워크 문제, 데이터베이스 문제 등 원인은 여러가지 입니다.
4. 구현 방법
예제코드: https://github.com/choisungwook/istio_practice/tree/main/manifests/example-2-circuitbreak
circuit break 구현은 24.4기준으로 아직까지 쿠버네티스 자체 기능으로 지원하지 않습니다. 따라서 쿠버네티스가 아닌 외부 기능을 사용해야 하는데 대표적으로 istio입니다.
istio circuit break는 envoy trafficPolicy필드로 설정합니다. trafficPolicy는 트래픽흐름을 제어합니다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: example
spec:
host: "example.com"
trafficPolicy:
connectionPool:
생략
outlierDetection:
생략
connectionPool은 커넥션 풀 기반으로 요청을 거절합니다. 만약 설정한 커넥션을 넘으면 host로 오는 요청을 거절합니다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: circuit-break-example
spec:
host: "circuit-break-example"
trafficPolicy:
connectionPool:
tcp:
maxConnections: 50
http:
http1MaxPendingRequests: 50
maxRequestsPerConnection: 50
outlier detection(이상탐지)는 요청을 일정시간동안 거절할 때 사용합니다. 아래 예제에서는 host요청 응답이 5xx에러가 연속 7번 발생하면 3분동안, host로 오는 요청을 거절합니다. 요청 거절 시간은 baseEjectionTime으로 설정합니다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: example
spec:
host: "example.com"
trafficPolicy:
outlierDetection:
consecutive5xxErrors: 7
baseEjectionTime: 3m
...이하생략
5. 예제 1번: connection Pool
첫 번째 예제는 connection Pool로 circuit break를 구현합니다. connection pool 최대 갯수를 초과하면 host에 대한 요청을 거절합니다.
아래 설정은 http connection을 최대 50개로 제한합니다. connection이 50개가 넘으면 host에 대한 요청을 거절합니다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: circuit-break-example
spec:
host: "circuit-break-example"
trafficPolicy:
connectionPool:
tcp:
maxConnections: 50
http:
http1MaxPendingRequests: 50
maxRequestsPerConnection: 50
istio ingress pod와 pod의 istio-proxy컨테이너에서 에러 상세로그를 볼 수 있습니다. 로그를 보면 overflow키워드가 있습니다. connection이 overflow가 발생했다는 의미입니다.
kubectl logs {istio ingress pod} -n istio-systsem
connection개수가 줄어들면 host에 대한 요청을 허용하므로, http 200과 503응답이 번갈아 발생하는게 특징입니다.
6. 예제 2번: outlier detetion
첫 번째 예제는 outlier detection(이상탐지)로 circuit break를 구현합니다. 설정한 이상탐지 조건을 충족하면 일정시간 동안 host에 대한 모든 요청을 거절합니다.
아래 예제는 5xx http응답이 7번 발생하면 host에 대한 요청을 3분간 거절합니다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: example
spec:
host: "example.com"
trafficPolicy:
outlierDetection:
consecutive5xxErrors: 7
baseEjectionTime: 3m
istio ingress pod로그를 조회하면, circuit break가 적용된 pod가 unhealthy상태로 된 것을 확인할 수 있습니다.
kubectl logs {istio ingress pod} -n istio-systsem
7. 모니터링 방법
istio 모니터링 경험이 많이 없어 정확하지 않습니다. 제가 잠깐 공부하면서 생각한 방법은 큰범위에서 작은 범위로 점점 좁혀가는게 좋다고 느꼈습니다. 그리고 istio 메트릭과 istio pod로그를 같이 봐야 자세히 디버깅이 가능하다고 느꼈습니다.
7.1 큰 범위로 디버깅
istio는 요청에 대한 메트릭을 남깁니다. 어떤 출발지가 어떤 도착지로 요청을 했는지 그리고 요청에 대한 결과를 메트릭으로 남깁니다. 메트릭이름은 istio_requests_total입니다.
repsonse_code와 source_app, destination_service_name label로 어떤 요청이 실패했는지 조회할 수 있습니다.
sum(istio_requests_total{response_code=~"5[0-9]{2}"}) by (source_app, destination_service_name) != 0
7.2 좁은 범위 디버깅
outlierDetection(이상탐지)설정으로 생긴 circuit break는 실시간으로만 istioctl명령어로 디버깅 가능합니다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: example
spec:
host: "example.com"
trafficPolicy:
outlierDetection:
consecutive5xxErrors: 7
baseEjectionTime: 3m
istioctl proxy-status명령어를 사용하면 istio mesh에 속한 모든 istio-proxy 목록이 보입니다.
istioctl proxy-status
outlierDetection가 활성화되면 proxy 상태에 보입니다.
istioctl proxy-config endpoints {pod이름}.{namespace}
connectionPool설정으로 circuit break가 적용되었다면 envoy_cluster_upstream_rq_pending_overflow메트릭을 보면 됩니다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: circuit-break-example
spec:
host: "circuit-break-example"
trafficPolicy:
connectionPool:
tcp:
maxConnections: 50
http:
http1MaxPendingRequests: 50
maxRequestsPerConnection: 50
envoy_cluster_upstream_rq_pending_overflow 메트릭은 connection overflow가 발생하여 요청이 보류된 개수입니다.
envoy_cluster_upstream_rq_pending_overflow 메트릭은 제약사항이 있습니다. 출발지 host가 istio mesh이어야 메트릭이 보입니다. 만약 istio mesh에 속하지 않는 클라이언트가 출발지라면 메트릭이 보이지 않습니다.
참고자료
'전공영역 공부 기록' 카테고리의 다른 글
Amazon Managed Prometheus를 지금 사용하는건 시기상조같다. (0) | 2024.05.26 |
---|---|
EKS 클러스터 생성 시 부여되는 관리자 권한의 변화와 관리 방법 (0) | 2024.05.19 |
github action을 로컬에서 실행해보자 (0) | 2024.04.28 |
github action workflow로 cron을 실행할 때 주의사항 (0) | 2024.04.24 |
EKS IRSA 동작원리 톺아보기 (2) | 2024.04.13 |