최근 실제로 gRPC를 사용할만한 큰 규모의 현업에서는 어떻게 gRPC를 운영하며 사용할지 궁굼했었다.
마침 당근테크 유튜브 채널에서 이를 다룬 영상이 있어 이를 정리해보았다.
당근 마켓의 현재
레버리지 사용하기
적은 인력으로 gRPC를 잘 사용하는 방법.
레버리지란?
- 지렛대(leverage)를 뜻한다. 도구를 사용하여 안정성과 성능을 극대화 시키는 것.
- HAProxy
- k8s(kubernetes)
- Istio
트래픽 플로우 이해하기
어디서 어떤 이슈가 발생했는지 확인할 수 있도록, 각 구간별 플로우를 전부 이해하고 매트릭 준비.
같은 환경을 공유하는 sre-test-server
마이크로서비스의 이슈를 파악하기 위해 Golang으로 작성한 SRE 테스트 서버를 이용.
필요한 도구들을 함께 도커에 마운트.
gRPC 인프라 구성
클러스트 변경을 대비해 앞단에 프록시 레이어 필요하였기 때문에 HAProxy를 사용.
내부적으로 이스티오 인그레스 게이트웨이와 서비스 매시를 활용하여 Pod끼리 gRPC로 통신.
서비스 메시지에 실패 발생 시, HAProxy를 타고 다시 gRPC 백엔드 풀을 이용하여 내부 서비스로 접근.
StandByActive 방식보다, k8s 자체가 멀티 ag에 떠있기 때문에 스위칭 하는 전략으로 사용 중.
gRPC
MSA 구조에서 통신으로 사용하는 비용이 큰데, 몇 ms라도 이득을 보면 좋다.
레이턴시 측면에서도 좋은 선택.
서비스 인터페이스 스키마도 잘 지원하고, 내장 기능으로 인증이나 암호화, 압축 등도 풍부히 사용 가능.
gRPC 클라이언트와 서버
- 채널이라는 핵심 컨셉 존재.
- HTTP/2의 스펙을 이용하여 컨셉을 구현.
- gRPC의 채널은 HTTP/2 엔드 포인트에 대한 버추얼 커넥션을 나타내고,
클라이언트가 gRPC 채널을 만들면 내부적으로는 서버와 HTTP 커넥션을 맺는다고 생각하면 된다. - RPC는 HTTP/2의 스트림으로 처리
- 메시지는 HTTP/2의 프레임이라는 단위로 처리
채널(Channel)
채널에는 많은 RPC 요청이 있을 수 있고, RPC에는 많은 메시지가 있을 수 있다.
HTTP/2의 스트림 기능은 여러 요청과 응답을 동시에 처리할 수 있게 해줌으로써, 하나의 커넥션에서 병렬 대화(Multi concurrent conversation)를 가능하게 한다.
gRPC 운영에서 중요한 부분은 채널이다.
- 채널은 논리적 연결: gRPC에서 채널은 클라이언트와 서버 간의 논리적인 연결을 나타내며, 여러 호출을 재사용할 수 있다.
- 버추얼 커넥션: 채널은 하나의 엔드포인트에 대한 버추얼 커넥션으로, 실제 인프라 레이어에서는 여러 HTTP/2 커넥션에 의해 지원될 수 있다.
- HTTP/2의 이점: gRPC는 HTTP/2의 멀티플렉싱을 활용하여 하나의 물리적인 커넥션에서 여러 스트림을 동시에 처리, 효율적인 네트워크 통신을 제공한다.
채널은, 사용자에게는 단일 연결처럼 보이지만, 실제로는 여러 물리적 연결을 통해 기능을 제공한다.
gRPC Client
gRPC 클라이언트는 생각보다 똑똑하며, 서버와의 통신을 관리하는 컴포넌트로 구성되어있다.
컴포넌트
1. 리졸버
기능: 주기적으로 타겟 DNS를 Resolve 하고 엔드 포인트를 갱신
목적: 서버의 IP 주소나 도메인이 변경될 경우에도 클라이언트가 최신 정보를 기반으로 서버에 연결할 수 있도록 한다.
2. 로드밸런서
기능: 커넥션 실패 시, 로드밸런서가 직전에 사용했던 Address List를 리졸버에서 가져와 재연결을 시도
- 실패한 연결을 재시도하고, 복원력 제공
- 클라이언트가 자체적으로 로드 밸런싱을 관리하여 큰 도메인 이슈 없이 작업 진행 가능
- 커넥션 풀에 대한 책임도 가져가주며, 효율적인 연결 관리 지원
gRPC Keepalive
gRPC의 Keepalive는 HTTP/2 핑을 통해 실제로 커넥션을 유지하는 역할을 한다. 이를 통해 연결의 상태를 주기적으로 확인하고, 문제가 있을 경우 적절한 조치를 취해야한다.
핑(Ping)
- 역할: HTTP/2 핑은 클라이언트와 서버 간의 연결 상태를 확인하는 데 사용된다.
- 연결 실패 처리: 핑에 대한 응답이 없으면 연결이 실패한 것으로 간주하고 커넥션을 닫는다.
연결 실패 후 처리
- 채널 유지: 커넥션이 닫히더라도 채널 자체는 닫히지 않는다.
- 커넥션 재생성: 채널이 열려 있는 한, 커넥션은 자동으로 재생성된다.
- 영향 최소화: 일시적인 이슈가 있더라도 채널에는 문제가 없으며, 연결이 재생성된다.
설정 옵션 Tips
- Keepalive 시간 설정: Keepalive 설정은 일반적으로 ELS(Echo Link Status) 아이들 타임아웃보다 작게 설정한다. 이를 통해 연결이 더 빠르게 감지되고 유지된다.
- MAX_CONNECTION_AGE: 커넥션의 최대 수명을 설정. 커넥션이 설정된 나이 이상이 되면 서버는 goaway 패킷을 클라이언트에 보내고, 클라이언트는 새로운 커넥션을 설정한다.
- 패킷 전송: goaway → fin ack → syn ack 절차를 통해 새로운 커넥션
- 적절한 사용: 불필요한 패킷 사용을 줄이기 위해 서비스 상황에 맞게 설정을 조정해서 사용하자
gRPC 요청과 응답
- gRPC 요청은 기본적으로 POST 요청만 받고 있다.
- Content-type이 application/grpc 가 아닐 경우, 415 Status code(지원되지 않는 미디어 유형)가 응답된다.
- 응답 메시지는 응답 헤더, length prefixed Message, Trailer 3가지
- 마지막 트레일러 헤더는 http 스펙으로 grpc-status와 grpc-message를 포함한다.
gRPC status code
어플리케이션 status 코드와 인프라 레벨 status 고민 코드가 따로 나뉘어져 있다.
해당 status 코드를 기반으로 SRE들은 전역화한다.
gRPC 로드밸런싱
- gRPC는 스트림을 사용하여 통신한다. 그래서 스트림 베이스의 로드밸런싱이 필요하며, L4가 아닌 L7 로드밸런싱이 필요하다.
- 하지만 쿠버네티스는 L4 로드밸런싱만 지원하며, 따라서 gRPC 구성에 잠재적인 문제가 발생할 수 있다.
- gRPC는 엔드포인트 리스트를 프록시들이 직접 관리한다. 그래서 큐브 프록시(k8s에서 네트워크 트래픽을 관리하는 컴포넌트)를 통해 관리된다고 생각하면 안된다.
당근의 상황
k8s의 gRPC 구성 설명 정리
- Istio 데몬: Istio는 데몬을 통해 네트워크 트래픽을 관리.
- 갤리(Galley) 컴포넌트: Kubernetes API를 사용하여 엔드포인트를 갱신함.
- 파일럿(Pilot) 및 서비스 메시: 갱신된 정보를 기반으로 동기화 작업을 수행.
- istioctl CLI 툴: 특정 타겟 클러스터의 DNS에서 엔드포인트를 확인하고 해시 상태까지 점검함.
Client LB에 의존해 Headless service 타입을 사용하기도 하지만, proxy를 이용해 메트릭 모니터링 및 네트워크 디버깅 이점을 누리는 것을 추천.
마치며
이후, 영상의 내용에서는 Proxy 관련 gRPC 적용 툴 및 팁을 소개하고, SRE가 집중해야하는 부분, 운영 방법 및 개발자들과의 상생 Tips이 정리되어 있다.
조금 더 깊은 SRE 이야기를 원한다면, 꼭 해당 영상을 시청하여 정리해보는 것을 추천한다.
'Server' 카테고리의 다른 글
gRPC(Google Remote Procedure Call)에 대해서 알아보자 (1) | 2024.06.10 |
---|---|
[CORS] ExposeHeader 설정에 대해 알아보자 (0) | 2023.01.10 |
[linux] /usr/bin/xauth: file /root/.Xauthority does not exist (0) | 2021.10.06 |
[Weblogic] Admin User Password 변경 (0) | 2020.07.28 |
[Weblogic] Failed to initialize the application "_auto_generated_ear_-1" due to error weblogic.management.DeploymentException (0) | 2020.07.07 |