Client Side LB in gRPC
지난 몇 년 동안 마이크로 서비스의 성장과 함께 gRPC 는 이러한 소규모 서비스 간의 상호 통신에 대한 많은 인기를 얻었다. 인기를 얻은 이유 중 하나는 속도이다. HTTP / 1에서 구현된 REST Client는 요청을 전송할 때마다 연결을 생성하고, 응답이 오면 해당 연결을 끊는다. 이처럼 요청마다 connection을 생성하기 때문에 소모되는 비용이 많다. 하지만, gRPC는 HTTP 2.0 기반의 HTTP Streaming을 이용하여 통신한다. HTTP 2.0은 한번 연결된 HTTP 연결을 통신이 끝났을 때 끊지 않고, 장기간 유지하며 재사용할 수 있다. gRPC 또한 이를 이용하여 보통 하나의 연결을 맺어두고, 이 연결을 재사용하는 식으로 이용한다. 이 연결(TCP Connection)을 Channel이라는 개념으로 사용한다.
HTTP/2: Smarter at scale | Cloud Native Computing Foundation
위와 같이 커넥션을 재사용하는 것은 많은 장점을 가지고 있으나, 단점도 존재한다. 그 단점 중 하나가 오늘 다룰 예정인 로드 밸런싱에서 크게 발생한다.
Channel을 생성한 클라이언트에서 하나의 서버가 버티기 어려울 정도의 대량의 요청을 보내기 시작했다고 가정하자. 이 시점에 우리는 스케일 아웃을 통하여 이 문제를 해결하곤 한다. 하지만 gRPC에서는 한번 생성된 Channel을 재사용하려는 경향이 있기 때문에 여러 인스턴스를 준비하더라도 요청은 모두 하나의 인스턴스로 이동하게 될 것이다.
grpc client load balancing 구현하기 (grpc 번외편)
이런 문제를 해결하기 위하여 client side load balancing이 존재한다. client에서 알고리즘을 구현하고, 연결을 유지하며 load balancing을 하는 방식인데 gRPC에서 지원해주는 방식은 세가지로 나뉜다.
Client Side LB
Pick First LB
세팅 시, 따로 설정을 해주지 않는다면 기본적으로 Pick First LB가 적용된다.
동작
- Name Resolver에서 address 목록을 가져온다.
- 연결 가능한 address를 찾을 때까지 순서대로 한 번에 하나씩 연결 시도한다.
- address 중 하나에 연결할 수 있다면 Channel의 상태를 READY로 설정한다.
- 전송되는 모든 RPC가 해당 address로 전송된다.
- 이후 해당 address의 연결이 끊어진다면 Channel의 상태를 IDLE 상태로 변경한다.
- IDLE 상태에서 요청이 발생하면 go to 2.
- 연결할 수 있는 address가 없다면 back off에 설정된 만큼의 재시도를 거친다.
Round Robin LB
// in example
roundrobinConn, err := grpc.Dial(
fmt.Sprintf("%s:///%s",exampleScheme, exampleServiceName),
~~grpc.WithBalancerName("round_robin"),~~ // deprecated
grpc.WithInsecure(),
)
// recommended
roundrobinConn, err := grpc.Dial(
fmt.Sprintf("%s:///%s", exampleScheme, exampleServiceName),
grpc.WithDefaultServiceConfig(`{"loadBalancingConfig": [{"round_robin":{}}]}`), // This sets the initial balancing policy.
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
Round-Robin LB는 name resolver에서 address 목록을 가져오고 각 address에 대하여 subchannel을 생성한다. 위에서 언급했던 클라이언트가 연결 가능한 모든 서버와 연결을 하는 부분이다.
subchannel의 연결이 끊어질 때마다 적절한 back off로 다시 연결을 시도한다.
channel의 상태는 subchannel의 상태를 통하여 설정된다
READY → CONNECTING → IDLE 의 순위로 각 상태의 subchannel이 하나라도 있다면, 가장 높은 순위의 상태로 channel의 상태가 정해진다.
channel을 통하여 RPC가 전송이 될 떄, READY 상태인 subchannel을 순회하며 사용한다.
Look aside (xDS)
이 방법은 서버 인스턴스 간 트래픽을 분산하기 위하여 외부의 load balancing smart를 위한 LB server가 필요하다.
외부의 LB 서버에서 독립적인 로드 밸런싱 로직과 서버 인스턴스의 정보를 알고 있어 논리적이고 성능이 좋은 load balancing이 가능하다. 또한, client는 위의 round robin과 같이 연결을 유지하고 있어서 재사용 가능한 연결의 성능적 장점 또한 챙길 수 있다.
하지만, LB 서버가 필요하다는 단점이 있다. 즉, LB만를 위한 서버가 필요하며 이 서비스를 관리해야 한다. 유지 보수, 운영, 모니터링, 경고, 새로운 point of failure 등등 신경 써야 할 부분이 많이 늘어난다.
이전에는 grpc에서 제공하는 grpclb라는 라이브러리를 사용하여 Look aside loadbalancing을 수행했다고 한다. 하지만, 다양한 지원 플랫폼의 이유로 인하여 xDS 프로토콜을 사용하는 라이브러리를 통하여 수행하는 것이 권장되도록 바뀌었다. - grpclb는 deprecated
Test
설정
- 클라이언트에서 1초마다 요청이 발생한다.
- xds 서버는 10초마다 target server를 UpstreamPort를 순회하며 교체한다.
Pick First LB
- 첫 동작 시, 먼저 선언된 서버인 server 1부터 동작함.
- 4번 메세지가 간 시점에서 server 1을 종료함.
- server 2로 연결되어서 지속 동작
- 9번 메세지 이후 server 2도 종료.
- err에 의하여 종료
클라이언트가 연결을 처음 요청했을 시점부터 server 2만 살아 있었다면, 바로 server 2로 연결된다.
처음에 server 1로 연결되었다가 server 1이 죽으면 server 2로 연결된다. (Response 6)
이후 server 1이 다시 살아나고, server 2가 죽으면 다시 server 1로 연결된다. (Response 16)
Round-Robin LB
클라이언트가 n번 요청을 보낼 때, server 1, server 2 subchannel을 만들고 하나씩 보내는 것을 확인할 수 있다.
위의 예시처럼 처음부터 하나의 서버만 살아있거나, 하나의 서버가 도중에 죽는다면, 남아있는 READY 상태의 서버로 동작한다.
이후 server 1이 살아난다면 다시 subchannel을 만들고 하나씩 보낸다.
Look aside LB
상단의 설정과 같이 xDS 서버는 10초 이후에 보낼 서버를 server 2로 변경한다.
그로 인하여 Response 10부터 server 2에서 오는 것을 확인할 수 있다.
별 다른 로직을 구현해두지 않아서 server 1이 죽으면 위의 다른 LB 정책들과 다르게 바로 죽는다.
이를 위하여 xDS에서 지원하는 round_robin 로직을 추가적으로 사용하거나, 직접 원하는 방향으로 개발하여 사용할 수 있다.
중간에 xds서버가 죽으면 죽기 이전에 알던 서버로 계속 연결이 된다..?
결론
성능이 중요하고, 리소스가 되고, 구체적인 제어를 원하고 가능하다면 Look aside (xDS) 사용하는 것이 권장됨.
- xds로 넘어오면서 service mesh에도 강점을 얻음
아니면 connection 재사용과 로드밸런싱 효율성 및 클라이언트 제어 가능성을 고려해서
round robin vs proxy
위 테스트에 사용한 코드
https://github.com/Limm-jk/grpc_client_lb_example
Reference
HTTP/2: Smarter at scale | Cloud Native Computing Foundation
엔드 투 엔드 HTTP/2 지원을 통해 gRPC 워크로드를 뒷받침하는 Application Load Balancer
gRPC Load Balancing may not be that easy
https://github.com/evanj/grpclb_experiment
xDS
grpc-go/examples/features/xds at master · grpc/grpc-go
https://github.com/salrashid123/grpc_xds
https://github.com/salrashid123/grpc_xds
grpclb
grpc-proto/load_balancer.proto at master · grpc/grpc-proto
xDS GLB
proposal/A27-xds-global-load-balancing.md at master · grpc/proposal
grpc/connection-backoff.md at master · grpc/grpc
grpc/grpc_xds_features.md at master · grpc/grpc
'기타' 카테고리의 다른 글
Kafka 메세지를 안정적으로 다루는 방법 (Transaction Outbox Pattern) (1) | 2024.10.13 |
---|---|
git 별다줄 alias를 소개합니다. (0) | 2023.06.18 |
$0.005 per Elastic IP address not attached to a running instance per hour (2) | 2021.08.04 |
Grafana '/var/lib/grafana/plugins': Permission denied 오류 해결 (1) | 2021.02.06 |
Detection Algorithm (0) | 2021.01.09 |
댓글