저번 글에서 gRPC를 이용해 Envoy Proxy의 로그 및 매트릭을 전송 받는 기반을 다졌다.
이제 길을 닦아놨기 때문에 당연히 문을 열어야 된다.
Envoy Proxy가 gRPC 클라이언트 입장이기 때문에 서버를 구성해야 했다.
go언어로 AccessLog서버를 구성해봤다.
먼저 protobuf와 서비스 정의를 필요하기 때문에 공식 패키지를 임포트했다.
"github.com/envoyproxy/go-control-plane/envoy/service/accesslog/v3"
(accesslogs로 alias했다)
gRPC 구성 과정
추후 정리할 예정이지만, 간단하게 과정을 살펴보자면
1. tcp 리스너 생성
2. gRPC 서버, gRCP 서비스 생성
3. gRPC 서버에 서비스 등록 (컴파일된 protobuf 파일에 메소드 정의 되어 있음)
4. 서버를 생성해둔 tcp 리스너와 연결한다.
1. tcp 리스너 생성
다음과 같은 코드로 gRPC 서버에 등록할 tcp 리스너를 생성했다.
lis, _ := net.Listen("tcp", fmt.Sprintf("%s:%s", "0.0.0.0", "4317"))
해당 tcp 리스너를 통해 서버를 생성하면 모든 네트워크 인터페이스에서 들어오는 요청을 4317포트로 받을 수 있게 된다.
2. gRPC 서버 생성
gRPCServer := grpc.NewServer()
3. gRPC 서비스 생성
Envoy Proxy에서 제공하는 AccessLog를 받는 서비스를 생성해야 한다.
먼저 등록할 gRPC 서비스의 인터페이스가 담긴 구조체를 정의했다.
(이 또한 패키지에 정의 되어 있다)
type EnvoyAccessLogsServer struct {
accessLogs.UnimplementedAccessLogServiceServer
}
이 구조체를 이용해서 서비스를 생성하면 된다.
accessLogServer := &EnvoyAccessLogsServer{}
4. 서버에 서비스 등록
이제 생성해 둔 서버에 서비스를 등록 해야한다.
accessLogs.RegisterAccessLogServiceServer(gRPCServer, accessLogServer)
마찬가지로 임포트한 패키지 내에 정의되어 있는 등록 메소드를 사용하면 된다.
5. 서버와 리스너 연결
서버의 입구를 뚫기 위해 tcp 리스너를 등록해서,
특정 포트로 들어오는 요청들을 서버로 받아와야한다.
다음과 같이 서버와 연결하면 된다.
gRPCServer.Serve(colService)
하지만, 여기서 문제가 발생했다.
Serve()가 블록킹 함수이기 때문에 코드 흐름이 멈추게 된다.
모든 예시들은 이후 코드 동작이 없었기 때문에 따로 핸들링하지 않았고,
이 때문에 블록킹 함수인지 몰랐던 나는, 동작하지 않는 코드의 원인을 찾아 손디버깅을 열심히 했었다.
go언어의 경우 goroutine을 사용해 비동기로 실행하면 된다.
go gRPCServer.Serve(colService)
서비스 메서드 구현
이제 서버 생성은 완료 되었다.
요청을 받는 구멍을 뚫었지만,
들어오는 요청을 핸들링하는 부분이 없기 때문에 구현해야 한다.
구현해야 할 메서드 형태를 살펴보기 위해 또, 패키지 공식 문서를 살펴봤다.
서비스 인터페이스 내에 StreamAccessLogs라는 메서드가 정의되어 있었다.
해당 문서를 참고해서 메서드 코드를 직접 구현해야한다.
이번 글에선 요청을 리시브하는 순간까지만 정리해보도록 하겠다.
이것 역시 추후 정리하겠지만, gRPC의 연결 방식에는 4가지가 있다.
AccessLogs의 경우 메소드를 살펴보면, 클라이언트 스트리밍 형태임을 알 수 있다.
쉽게 말해서 연결이 성공한 후 서버의 응답 전까지 클라이언트는 계속해서 전송을 하는 형태이다.
func (evyAccLogs *EnvoyAccessLogsServer) StreamAccessLogs(stream accessLogs.AccessLogService_StreamAccessLogsServer) error {
for {
log, err := stream.Recv()
if err != nil {
log.Printf("Error receiving message: %v", err)
return err
}
///////////////////////
// 원하는 형태로 사용
///////////////////////
}
}
앞서 말했듯 클라이언트의 계속 된 요청을 리시브해야 되기 때문에 메서드 내에 무한 루프문을 사용했다.
그리고 스트림의 메서드인 Recv()를 이용해서 요청 내용을 받아올 수 있게 된다.
AccessLogs 서비스 인터페이스의 메서드를 정의한 것이기 때문에 따로 호출할 필요 없이,
해당 서비스로 요청이 들어오면 알아서 StreamAccesLogs()가 호출된다.
만약 요청을 받는 과정에서 에러가 발생하면, 스트림이 끊길 수 있도록 코드를 작성했다.
매트릭 서비스에 대해서도 똑같이 서비스를 생성하고, 등록해서 사용하면 된다.
추후 gRPC에 대해 공부한 내용을 정리해보도록 하겠다.
'클라우드 메모' 카테고리의 다른 글
[이스티오] Istio Envoy AccessLog 살펴보기 (2) | 2024.11.15 |
---|---|
[이스티오] Envoy Proxy와의 Stream이 끊겼을 때 (미완) (0) | 2024.11.14 |
[이스티오] Istio Envoy Proxy의 로그 및 매트릭 가져오기 (0) | 2024.11.12 |
PaaS vs Serverless (0) | 2024.09.05 |
리눅스 서버 인스턴스 디스크 마운트 (0) | 2024.08.17 |