|
| 1 | +--- |
| 2 | +layout: blog |
| 3 | +title: '쿠버네티스에서 어려움 없이 gRPC 로드밸런싱하기' |
| 4 | +date: 2018-11-07 |
| 5 | +--- |
| 6 | + |
| 7 | +**저자**: William Morgan (Buoyant) |
| 8 | + |
| 9 | +**번역**: 송원석 (쏘카), 김상홍 (국민대), 손석호 (ETRI) |
| 10 | + |
| 11 | +다수의 새로운 gRPC 사용자는 쿠버네티스의 기본 로드 |
| 12 | +밸런싱이 종종 작동하지 않는 것에 놀란다. 예를 들어, 만약 |
| 13 | +[단순한 gRPC Node.js 마이크로서비스 |
| 14 | +앱](https://github.com/sourishkrout/nodevoto)을 만들고 쿠버네티스에 배포하면 어떤 일이 생기는지 살펴보자. |
| 15 | + |
| 16 | + |
| 17 | + |
| 18 | +여기 표시된 `voting` 서비스는 여러 개의 파드로 구성되어 있지만, 쿠버네티스의 CPU 그래프는 명확하게 |
| 19 | +파드 중 하나만 실제 작업을 수행하고 있는 것(하나의 파드만 트래픽을 수신하고 있으므로)을 |
| 20 | +보여준다. 왜 그런 것일까? |
| 21 | + |
| 22 | +이 블로그 게시물에서는, 이런 일이 발생하는 이유를 설명하고, |
| 23 | +[CNCF](https://cncf.io)의 서비스 메시(mesh)인 [Linkerd](https://linkerd.io) 및 서비스 사이드카(sidecar)를 활용한 |
| 24 | +gRPC 로드밸런싱을 쿠버네티스 앱에 추가하여, 이 문제를 쉽게 해결할 수 있는 방법을 설명한다. |
| 25 | + |
| 26 | +# 왜 gRPC에 특별한 로드밸런싱이 필요한가? |
| 27 | + |
| 28 | +먼저, 왜 gRPC를 위해 특별한 작업이 필요한지 살펴보자. |
| 29 | + |
| 30 | +gRPC는 애플리케이션 개발자에게 점점 더 일반적인 선택지가 되고 있다. |
| 31 | +JSON-over-HTTP와 같은 대체 프로토콜에 비해, gRPC는 |
| 32 | +극적으로 낮은 (역)직렬화 비용과, 자동 타입 |
| 33 | +체크, 공식화된 APIs, 적은 TCP 관리 오버헤드 등에 상당한 이점이 있다. |
| 34 | + |
| 35 | +그러나, gRPC는 쿠버네티스에서 제공하는 것과 마찬가지로 |
| 36 | +표준(일반)적으로 사용되는 연결 수준 로드밸런싱(connection-level load balancing)을 어렵게 만드는 측면도 있다. gRPC는 HTTP/2로 |
| 37 | +구축되었고, HTTP/2는 하나의 오래 지속되는 TCP 연결을 갖도록 설계되있기 때문에, |
| 38 | +모든 요청은 *다중화(multiplexed)*(특정 시점에 다수의 요청이 |
| 39 | +하나의 연결에서만 동작하는 것을 의미)된다. 일반적으로, 그것은 |
| 40 | +연결 관리 오버헤드를 줄이는 장점이 있다. 그러나, 그것은 또한 |
| 41 | +(상상할 수 있듯이) 연결 수준의 밸런싱(balancing)에는 유용하지 않다는 것을 의미한다. 일단 |
| 42 | +연결이 설정되면, 더 이상 밸런싱을 수행할 수 없기 때문이다. 모든 요청이 |
| 43 | +아래와 같이 단일 파드에 고정될 것이다. |
| 44 | + |
| 45 | + |
| 46 | + |
| 47 | +# 왜 HTTP/1.1에는 이러한 일이 발생하지 않는가? |
| 48 | + |
| 49 | +HTTP/1.1 또한 수명이 긴 연결의 개념이 있지만, |
| 50 | +HTTP/1.1에는 TCP 연결을 순환시키게 만드는 여러 특징들이 있기 때문에, |
| 51 | +이러한 문제가 발생하지 않는다. 그래서, |
| 52 | +연결 수준 밸런싱은 "충분히 양호"하며, 대부분의 HTTP/1.1 앱에 대해서는 |
| 53 | +더 이상 아무것도 할 필요가 없다. |
| 54 | + |
| 55 | +그 이유를 이해하기 위해, HTTP/1.1을 자세히 살펴보자. HTTP/2와 달리 |
| 56 | +HTTP/1.1은 요청을 다중화할 수 없다. TCP 연결 시점에 하나의 HTTP 요청만 |
| 57 | +활성화될 수 있다. 예를 들어 클라이언트가 'GET /foo'를 요청하고, |
| 58 | +서버가 응답할 때까지 대기한다. 요청-응답 주기가 |
| 59 | +발생하면, 해당 연결에서 다른 요청을 실행할 수 없다. |
| 60 | + |
| 61 | +일반적으로, 우리는 병렬로 많은 요청이 발생하기 원한다. 그러므로, |
| 62 | +HTTP/1.1 동시 요청을 위해, 여러 HTTP/1.1 연결을 만들어야 하고, |
| 63 | +그 모두에 걸쳐 우리의 요청을 발행해야 한다. 또한, 수명이 긴 HTTP/1.1 |
| 64 | +연결은 일반적으로 일정 시간 후 만료되고 클라이언트(또는 서버)에 의해 끊어진다. |
| 65 | +이 두 가지 요소를 결합하면 일반적으로 HTTP/1.1 요청이 |
| 66 | +여러 TCP 연결에서 순환하며, 연결 수준 밸런싱이 작동한다. |
| 67 | + |
| 68 | +# 그래서 우리는 어떻게 gRPC의 부하를 분산할 수 있을까?? |
| 69 | + |
| 70 | +이제 gRPC로 돌아가보자. 연결 수준에서 밸런싱을 맞출 수 없기 때문에, gRPC 로드 밸런싱을 |
| 71 | +수행하려면, 연결 밸런싱에서 *요청* 밸런싱으로 전환해야 |
| 72 | +한다. 즉, 각각에 대한 HTTP/2 연결을 열어야 |
| 73 | +하고, 아래와 같이, 이러한 연결들로 *요청*의 밸런싱을 맞춘다. |
| 74 | + |
| 75 | + |
| 76 | + |
| 77 | +네트워크 측면에서, L3/L4에서 결정을 내리기 보다는 L5/L7에서 결정을 |
| 78 | +내려야 한다. 즉, TCP 연결을 통해 전송된 프로토콜을 이해해야 한다. |
| 79 | + |
| 80 | +우리는 이것을 어떻게 달성해야할까? 몇 가지 옵션이 있다. 먼저, 우리의 애플리케이션 |
| 81 | +코드는 대상에 대한 자체 로드 밸런싱 풀을 수동으로 유지 관리할 수 있고, |
| 82 | +gRPC 클라이언트에 [로드 밸런싱 풀을 |
| 83 | +사용](https://godoc.org/google.golang.org/grpc/balancer)하도록 구성할 수 있다. 이 접근 방식은 |
| 84 | +우리에게 높은 제어력을 제공하지만, 시간이 지남에 따라 파드가 리스케줄링(reschedule)되면서 풀이 변경되는 |
| 85 | +쿠버네티스와 같은 환경에서는 매우 복잡할 수 있다. 이 경우, 우리의 |
| 86 | +애플리케이션은 파드와 쿠버네티스 API를 관찰하고 자체적으로 최신 상태를 |
| 87 | +유지해야 한다. |
| 88 | + |
| 89 | +대안으로, 쿠버네티스에서 앱을 [헤드리스(headless) |
| 90 | +서비스](/ko/docs/concepts/services-networking/service/#헤드리스-headless-서비스)로 배포할 수 있다. |
| 91 | +이 경우, 쿠버네티스는 서비스를 위한 DNS 항목에 |
| 92 | +[다중 A 레코드를 생성할 것이다](/ko/docs/concepts/services-networking/service/#헤드리스-headless-서비스). |
| 93 | +만약 충분히 진보한 gRPC 클라이언트를 사용한다면, |
| 94 | +해당 DNS 항목에서 로드 밸런싱 풀을 자동으로 유지 관리할 수 있다. |
| 95 | +그러나 이 접근 방식은 우리를 특정 gRPC 클라이언트로를 사용하도록 제한할 뿐만 아니라, |
| 96 | +헤드리스 서비스만 사용하는 경우도 거의 없으므로 제약이 크다. |
| 97 | + |
| 98 | +마지막으로, 세 번째 접근 방식을 취할 수 있다. 경량 프록시를 사용하는 것이다. |
| 99 | + |
| 100 | +# Linkerd를 사용하여 쿠버네티스에서 gRPC 로드 밸런싱 |
| 101 | + |
| 102 | +[Linkerd](https://linkerd.io)는 [CNCF](https://cncf.io)에서 관리하는 쿠버네티스용 |
| 103 | +*서비스 메시*이다. 우리의 목적과 가장 관련이 깊은 Linkerd는, 클러스터 수준의 권한 |
| 104 | +없이도 단일 서비스에 적용할 수 있는 |
| 105 | +*서비스 사이드카*로써도 작동한다. Linkerd를 서비스에 추가하는 |
| 106 | +것은, 각 파드에 작고, 초고속인 프록시를 추가하는 것을 의미하며, 이러한 프록시가 |
| 107 | +쿠버네티스 API를 와치(watch)하고 gRPC 로드 밸런싱을 자동으로 수행하는 것을 의미이다. 우리가 수행한 배포는 |
| 108 | +다음과 같다. |
| 109 | + |
| 110 | + |
| 111 | + |
| 112 | +Linkerd를 사용하면 몇 가지 장점이 있다. 첫째, 어떠한 언어로 작성된 서비스든지, 어떤 gRPC 클라이언트든지 |
| 113 | +그리고 어떠한 배포 모델과도 (헤드리스 여부와 상관없이) 함께 작동한다. |
| 114 | +Linkerd의 프록시는 완전히 투명하기 때문에, HTTP/2와 HTTP/1.x를 자동으로 감지하고 |
| 115 | +L7 로드 밸런싱을 수행하며, 다른 모든 트래픽을 |
| 116 | +순수한 TCP로 통과(pass through)시킨다. 이것은 모든 것이 *그냥 작동*한다는 것을 의미한다. |
| 117 | + |
| 118 | +둘째, Linkerd의 로드 밸런싱은 매우 정교하다. Linkerd는 |
| 119 | +쿠버네티스 API에 대한 와치(watch)를 유지하고 파드가 리스케술링 될 때 |
| 120 | +로드 밸런싱 풀을 자동으로 갱신할 뿐만 아니라, Linkerd는 응답 대기 시간이 가장 빠른 파드에 |
| 121 | +요청을 자동으로 보내기 위해 *지수 가중 이동 평균(exponentially-weighted moving average)* 을 |
| 122 | +사용한다. 하나의 파드가 잠시라도 느려지면, Linkerd가 트래픽을 |
| 123 | +변경할 것이다. 이를 통해 종단 간 지연 시간을 줄일 수 있다. |
| 124 | + |
| 125 | +마지막으로, Linkerd의 Rust 기반 프록시는 매우 작고 빠르다. 그것은 1ms 미만의 |
| 126 | +p99 지연 시간(<1ms of p99 latency)을 지원할 뿐만 아니라, 파드당 10mb 미만의 RSS(<10mb of RSS)만 필요로 하므로 |
| 127 | +시스템 성능에도 거의 영향을 미치지 않는다. |
| 128 | + |
| 129 | +# 60초 안에 gRPC 부하 분산 |
| 130 | + |
| 131 | +Linkerd는 시도하기가 매우 쉽다. 단지 —랩탑에 CLI를 설치하고— [Linkerd 시작 |
| 132 | +지침](https://linkerd.io/2/getting-started/)의 단계만 따르면 된다. |
| 133 | +클러스터에 컨트롤 플레인과 "메시" |
| 134 | +서비스(프록시를 각 파드에 주입)를 설치한다. 서비스에 Linkerd가 즉시 |
| 135 | +실행될 것이므로 적절한 gRPC 밸런싱을 즉시 확인할 수 있다. |
| 136 | + |
| 137 | +Linkerd 설치 후에, 예시 `voting` 서비스를 |
| 138 | +다시 살펴보자. |
| 139 | + |
| 140 | + |
| 141 | + |
| 142 | +그림과 같이, 모든 파드에 대한 CPU 그래프가 활성화되어 모든 파드가 |
| 143 | +—코드를 변경하지 않았지만— 트래픽을 받고 있다. 짜잔, |
| 144 | +마법처럼 gRPC 로드 밸런싱이 됐다! |
| 145 | + |
| 146 | +Linkerd는 또한 내장된 트래픽 수준 대시보드를 제공하므로, 더 이상 |
| 147 | +CPU 차트에서 무슨 일이 일어나고 있는지 추측하는 것이 필요하지 않다. 다음은 각 파드의 |
| 148 | +성공률, 요청 볼륨 및 지연 시간 백분위수를 보여주는 |
| 149 | +Linkerd 그래프이다. |
| 150 | + |
| 151 | + |
| 152 | + |
| 153 | +각 파드가 약 5 RPS를 얻고 있음을 알 수 있다. 또한, |
| 154 | +로드 밸런싱 문제는 해결되었지만 해당 서비스의 |
| 155 | +성공률에 대해서는 아직 할 일이 남았다는 것도 살펴볼 수 있다. (데모 앱은 독자에 대한 연습을 위해 의도적으로 |
| 156 | +실패 상태로 만들었다. Linkerd 대시보드를 사용하여 |
| 157 | +문제를 해결할 수 있는지 살펴보자!) |
| 158 | + |
| 159 | +# 마지막으로 |
| 160 | + |
| 161 | +쿠버네티스 서비스에 gRPC 로드 밸런싱을 추가하는 방법에 |
| 162 | +흥미가 있다면, 어떤 언어로 작성되었든 상관없이, 어떤 gRPC |
| 163 | +클라이언트를 사용중이든지, 또는 어떻게 배포되었든지, Linkerd를 사용하여 단 몇 개의 명령으로 |
| 164 | +gRPC 로드 밸런싱을 추가할 수 있다. |
| 165 | + |
| 166 | +보안, 안정성 및 디버깅을 포함하여 Linkerd에는 더 많은 특징 |
| 167 | +및 진단 기능이 있지만 이는 향후 블로그 게시물의 주제로 남겨두려 한다. |
| 168 | + |
| 169 | +더 알고 싶은가? 빠르게 성장하고 있는 우리 커뮤니티는 여러분의 참여를 환영한다! |
| 170 | +Linkerd는 [CNCF](https://cncf.io) 프로젝트로 |
| 171 | +[GitHub에 호스팅 되어 있고](https://github.com/linkerd/linkerd2), [Slack](https://slack.linkerd.io), |
| 172 | +[Twitter](https://twitter.com/linkerd), 그리고 [이메일 리스트](https://lists.cncf.io/g/cncf-linkerd-users)를 통해 커뮤니티를 만날 수 있다. |
| 173 | +접속하여 커뮤니티에 참여하는 즐거움을 느껴보길 바란다! |
0 commit comments