본문 바로가기
WIKI 기술 지식 베이스

회로 차단기

원문 보기 위키 갱신

회로 차단기 (Circuit Breakers)

회로 차단(circuit breaking)은 어떤 엔드포인트가 불건전(unhealthy)하다고 판단되면 Linkerd가 그 엔드포인트로의 요청 라우팅을 일시적으로 멈추고, 대신 해당 요청을 Service의 다른 레플리카로 보내는 강력한 기능이에요. 이 튜토리얼에서는 백엔드 레플리카가 불건전할 때 클라이언트 성공률을 높이도록 Service에 회로 차단을 켜는 방법을 살펴볼게요.

출처: Linkerd Circuit Breakers

본문

회로 차단은 어떤 엔드포인트가 불건전하다고 판단되면 Linkerd가 잠시 그 엔드포인트로의 요청 라우팅을 멈추고, 대신 그 요청을 Service의 다른 레플리카로 보내는 강력한 기능이에요.

이 튜토리얼에서는 백엔드 레플리카가 불건전할 때 클라이언트 성공률을 높이도록 Service에서 회로 차단을 켜는 방법을 볼게요.

Linkerd가 회로 차단을 어떻게 구현하는지 자세한 내용은 레퍼런스 문서를 참고해 주세요.

Linkerd 프로덕션 팁

이 페이지는 오픈소스 커뮤니티의 best-effort 지침을 담고 있어요. 미션 크리티컬 애플리케이션을 운영하는 프로덕션 사용자는 Linkerd 프로덕션 리소스를 숙지하고/또는 상용 Linkerd 제공업체와 연결하는 것이 좋아요.

사전 준비

이 가이드를 사용하려면 Kubernetes 클러스터가 필요해요.

  • Linkerd와 Linkerd-Viz. 아직 설치하지 않았다면 Installing Linkerd Guide를 따라 주세요.

데모 설정

한 경비원은 항상 진실을 말하고 다른 경비원은 항상 거짓을 말하는 수수께끼를 기억하시나요? 이 데모에는 항상 HTTP 200을 반환하는 good 포드와 항상 HTTP 500을 반환하는 bad 포드가 있어요. 그리고 이 두 포드를 포함하는 Service에 트래픽을 보내는 로드 생성기도 만들 거예요.

로드 생성에는 Slow-Cooker를, 백엔드 포드에는 BB를 사용할 거예요.

이 컴포넌트들을 클러스터에 추가하고 Linkerd 데이터 플레인에 포함시키려면 다음을 실행해요.

`cat ---
apiVersion: v1
kind: Namespace
metadata:
  name: circuit-breaking-demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: good
  namespace: circuit-breaking-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      class: good
  template:
    metadata:
      labels:
        class: good
        app: bb
    spec:
      containers:
      - name: terminus
        image: buoyantio/bb:v0.0.6
        args:
        - terminus
        - "--h1-server-port=8080"
        ports:
        - containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: bad
  namespace: circuit-breaking-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      class: bad
  template:
    metadata:
      labels:
        class: bad
        app: bb
    spec:
      containers:
      - name: terminus
        image: buoyantio/bb:v0.0.6
        args:
        - terminus
        - "--h1-server-port=8080"
        - "--percent-failure=100"
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: bb
  namespace: circuit-breaking-demo
spec:
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  selector:
    app: bb
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: slow-cooker
  namespace: circuit-breaking-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: slow-cooker
  template:
    metadata:
      labels:
        app: slow-cooker
    spec:
      containers:
      - args:
        - -c
        - |
          sleep 5 # wait for pods to start
          /slow_cooker/slow_cooker --qps 10 http://bb:8080
        command:
        - /bin/sh
        image: buoyantio/slow_cooker:1.3.0
        name: slow-cooker
EOF
`

이제 good과 bad 포드의 성공률을 살펴볼 수 있어요.

`> linkerd viz -n circuit-breaking-demo stat deploy
NAME          MESHED   SUCCESS      RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99   TCP_CONN
bad              1/1     6.43%   4.7rps           1ms           1ms           4ms          2
good             1/1   100.00%   5.9rps           1ms           1ms           1ms          3
slow-cooker      1/1   100.00%   0.3rps           1ms           1ms           1ms          1
`

여기서 good과 bad 디플로이먼트가 비슷한 양의 트래픽을 각각 받지만, good의 성공률은 100%인 반면 bad의 성공률은 매우 낮다는 걸 볼 수 있어요(헬스체크 프로브만 성공하고 있어요). 트래픽 생성기 관점에서 어떻게 보이는지도 살펴볼 수 있어요.

`> linkerd viz -n circuit-breaking-demo stat deploy/slow-cooker --to svc/bb
NAME          MESHED   SUCCESS       RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99   TCP_CONN
slow-cooker      1/1    51.00%   10.0rps           1ms           1ms           2ms          2
`

slow-cooker 관점에서 보면, slow-cooker가 Service로 보내는 요청의 약 50%가 실패하고 있어요. 회로 차단을 사용해 bad 포드로 가는 트래픽을 끊으면 이 문제를 개선할 수 있어요.

회로 끊기

Linkerd는 *연속 실패 누적(consecutive failure accrual)*이라는 유형의 회로 차단을 지원해요. 이는 Linkerd 내부 로드 밸런서에서 각 엔드포인트의 연속 실패를 추적하는 방식으로 동작해요. 연속으로 너무 많은 실패가 발생하면 해당 엔드포인트를 잠시 무시하고 Linkerd는 남은 엔드포인트 사이에서만 로드 밸런싱해요. 백오프 기간이 지나면 엔드포인트를 다시 도입해 그 엔드포인트가 건강해졌는지 판단해요.

bb Service에 주석을 추가해 연속 실패 누적을 켜 봐요.

`kubectl annotate -n circuit-breaking-demo svc/bb balancer.linkerd.io/failure-accrual=consecutive
`

경고

회로 차단은 ServiceProfile과 호환되지 않아요. 주석이 달린 Service에 ServiceProfile이 정의되어 있다면, 그 ServiceProfile이 존재하는 한 프록시는 회로 차단을 수행하지 않아요.

Linkerd 진단 명령으로 실패 누적이 올바르게 구성되었는지 확인할 수 있어요. linkerd diagnostics policy 명령은 Linkerd가 Service로 트래픽을 보낼 때 사용할 정책을 출력해요. jq 유틸리티로 출력을 필터링해 실패 누적 부분만 볼게요.

`> linkerd diagnostics policy -n circuit-breaking-demo svc/bb 8080 -o json | jq '.protocol.Kind.Detect.http1.failure_accrual'
{
  "Kind": {
    "ConsecutiveFailures": {
      "max_failures": 7,
      "backoff": {
        "min_backoff": {
          "seconds": 1
        },
        "max_backoff": {
          "seconds": 60
        },
        "jitter_ratio": 0.5
      }
    }
  }
}
`

이 출력은 Linkerd가 bb Service와 통신할 때 ConsecutiveFailures 실패 누적을 사용한다는 뜻이에요. 또한 max_failures가 7이라는 건, 연속 실패 7번을 관찰하면 회로 차단기를 발동시킨다는 의미예요. 여기 나온 각 파라미터에 대해서는 이 글의 마지막에서 더 자세히 다룰게요.

회로 차단기가 적용된 지금 각 포드가 얼마나 많은 트래픽을 받는지 살펴봐요.

`> linkerd viz -n circuit-breaking-demo stat deploy
NAME          MESHED   SUCCESS       RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99   TCP_CONN
bad              1/1    94.74%    0.3rps           1ms           1ms           1ms          3
good             1/1   100.00%   10.3rps           1ms           1ms           4ms          4
slow-cooker      1/1   100.00%    0.3rps           1ms           1ms           1ms          1
`

bad 포드의 RPS가 크게 낮아진 것을 확인할 수 있어요. 회로 차단기가 slow-cooker에서 bad로 가는 거의 모든 트래픽을 멈춘 거예요.

이것이 slow-cooker에 어떤 영향을 미쳤는지도 볼 수 있어요.

`> linkerd viz -n circuit-breaking-demo stat deploy/slow-cooker --to svc/bb
NAME          MESHED   SUCCESS       RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99   TCP_CONN
slow-cooker      1/1    99.83%   10.0rps           1ms           1ms           1ms          4
`

이제 slow-cooker의 요청이 거의 모두 good 포드로 라우팅되어 성공하고 있어요!

회로 차단 튜닝

앞서 linkerd diagnostics policy 명령을 실행했을 때 봤듯이 연속 실패 누적은 여러 파라미터로 제어돼요. 이 파라미터들은 각각 기본값이 있지만 주석으로 수동 구성할 수 있어요.

  • balancer.linkerd.io/failure-accrual-consecutive-max-failures회로 차단기가 발동하기 전에 Linkerd가 관찰해야 하는 연속 실패 횟수(기본값: 7). 회로 차단이 더 쉽게 발동되길 원하면 더 낮은 값을, 트래픽이 덜 고르게 분산되는 대신 성공률을 높일 수 있어요. 회로 차단기가 너무 쉽게 발동해 건강한 엔드포인트에서 트래픽이 끊기는 경우에는 더 높은 값을 고려해 보세요.
  • balancer.linkerd.io/failure-accrual-consecutive-max-penalty엔드포인트가 복원되기 전에 회로 차단기가 발동 상태를 유지하는 최대 시간(기본값: 60s). 회로 차단기를 발동시킨 엔드포인트로 가는 트래픽을 줄이고 싶다면 더 긴 시간을, 엔드포인트가 다시 건강해진 후 발동된 회로 차단기가 더 빨리 복구되길 원한다면 더 짧은 시간을 고려해 보세요.
  • balancer.linkerd.io/failure-accrual-consecutive-min-penalty엔드포인트가 복원되기 전에 회로 차단기가 발동 상태를 유지하는 최소 시간(기본값: 1s). failure-accrual-consecutive-max-penalty와 비슷한 방식으로 조정하는 걸 고려해 보세요.
  • balancer.linkerd.io/failure-accrual-consecutive-jitter-ratio회로 차단기 백오프에 도입할 지터(jitter)의 양(기본값: 0.5). 이건 조정할 일이 거의 없겠지만, 많은 클라이언트가 동시에 회로 차단된 엔드포인트로 요청을 보내 뾰족한 트래픽 패턴이 생긴다면 늘리는 걸 고려해 볼 수 있어요.

실패 누적 구성에 대한 자세한 내용은 레퍼런스 문서를 참고해 주세요.

더 알아보기 (Learn more)