APISIX: etcd 작업을 HTTP에서 gRPC로 마이그레이션

Zexuan Luo

Zexuan Luo

February 10, 2023

Products

Apache APISIX의 HTTP 기반 etcd 작업의 한계

etcd가 2.x 버전일 때, 노출된 API 인터페이스는 HTTP 1(이하 HTTP)이었습니다. etcd가 3.x 버전으로 업그레이드된 후, 프로토콜을 HTTP에서 gRPC로 전환했습니다. gRPC를 지원하지 않는 사용자를 위해 etcd는 gRPC-Gateway를 제공하여 HTTP 요청을 gRPC로 프록시하여 새로운 gRPC API에 접근할 수 있게 했습니다.

APISIX가 etcd를 사용하기 시작했을 때, etcd v2 API를 사용했습니다. APISIX 2.0(2020)에서 etcd 요구 사항을 2.x에서 3.x로 업데이트했습니다. etcd의 HTTP 호환성은 버전 업데이트를 위한 노력을 절약해 주었습니다. 우리는 호출 방법과 응답 처리 코드만 수정하면 되었습니다. 그러나 몇 년 동안 etcd의 HTTP API와 관련된 몇 가지 문제를 발견했습니다. 여전히 미묘한 차이가 있습니다. gRPC-gateway가 있다고 해서 HTTP 접근을 완벽하게 지원할 수 있는 것은 아니라는 것을 깨달았습니다.

지난 몇 년 동안 etcd와 관련하여 직면한 문제 목록은 다음과 같습니다:

  1. gRPC-gateway가 기본적으로 비활성화됨. 유지 관리자의 부주의로 인해 일부 프로젝트에서 etcd의 기본 구성이 gRPC-gateway를 활성화하지 않았습니다. 따라서 문서에 현재 etcd가 gRPC-gateway를 활성화했는지 확인하는 지침을 추가해야 했습니다. https://github.com/apache/apisix/pull/2940 참조.
  2. 기본적으로 gRPC는 응답을 4MB로 제한합니다. etcd는 제공하는 SDK에서 이 제한을 제거했지만 gRPC-gateway에서는 제거하지 않았습니다. 공식 etcdctl(SDK를 기반으로 구축됨)은 잘 작동하지만 APISIX는 그렇지 않았습니다. https://github.com/etcd-io/etcd/issues/12576 참조.
  3. 동일한 문제 - 이번에는 동일한 연결에 대한 최대 요청 수와 관련된 문제입니다. Go의 HTTP2 구현에는 단일 클라이언트가 동시에 보낼 수 있는 요청 수를 제어하는 MaxConcurrentStreams 구성이 있으며, 기본값은 250입니다. 일반적으로 어떤 클라이언트가 동시에 250개 이상의 요청을 보낼까요? 그래서 etcd는 항상 이 구성을 사용했습니다. 그러나 모든 HTTP 요청을 로컬 gRPC 인터페이스로 프록시하는 gRPC-gateway는 이 제한을 초과할 수 있습니다. https://github.com/etcd-io/etcd/issues/14185 참조.
  4. etcd가 mTLS를 활성화한 후, etcd는 서버 인증서와 클라이언트 인증서로 동일한 인증서를 사용합니다: gRPC-gateway의 서버 인증서와 gRPC-gateway가 gRPC 인터페이스에 접근할 때의 클라이언트 인증서입니다. 인증서에 서버 인증 확장이 활성화되어 있지만 클라이언트 인증 확장이 활성화되어 있지 않으면 인증서 검증에서 오류가 발생합니다. 다시 한번, etcdctl로 직접 접근하면 잘 작동하지만(이 경우 클라이언트 인증서로 사용되지 않음) APISIX는 그렇지 않습니다. https://github.com/etcd-io/etcd/issues/9785 참조.
  5. mTLS를 활성화한 후, etcd는 인증서의 사용자 정보에 대한 보안 정책 구성을 허용합니다. 위에서 언급한 바와 같이, gRPC-gateway는 gRPC 인터페이스에 접근할 때 고정된 클라이언트 인증서를 사용하며, 처음에 HTTP 인터페이스에 접근할 때 사용된 인증서 정보를 사용하지 않습니다. 따라서 클라이언트 인증서가 고정되어 변경되지 않기 때문에 이 기능은 자연스럽게 작동하지 않습니다. https://github.com/apache/apisix/issues/5608 참조.

이 문제들을 두 가지로 요약할 수 있습니다:

  1. gRPC-gateway(및 HTTP를 gRPC로 변환하려는 다른 시도)는 모든 문제를 해결할 수 있는 만능 해결책이 아닙니다.
  2. etcd 개발자들은 HTTP에서 gRPC로의 방법에 충분히 중점을 두지 않습니다. 그리고 그들의 가장 큰 사용자인 Kubernetes는 이 기능을 사용하지 않습니다.

이 문제를 해결하기 위해 gRPC를 통해 etcd에 직접 접근해야 하며, 호환성을 위해 남겨진 gRPC-Gateway의 HTTP 경로를 거치지 않아도 됩니다.

gRPC로의 마이그레이션 도전 과제 극복

lua-protobuf의 버그

마이그레이션 과정에서 첫 번째 문제는 서드파티 라이브러리의 예상치 못한 버그였습니다. 대부분의 OpenResty 애플리케이션과 마찬가지로, 우리는 lua-protobuf를 사용하여 protobuf를 디코딩/인코딩합니다.

etcd의 proto 파일을 통합한 후, Lua 코드에서 가끔 "table overflow" 오류가 발생하며 크래시가 발생하는 것을 발견했습니다. 이 크래시는 신뢰할 수 있게 재현할 수 없기 때문에, 우리의 첫 번째 직감은 최소한의 재현 가능한 예제를 찾는 것이었습니다. 흥미롭게도, etcd의 proto 파일만 사용하면 이 문제를 전혀 재현할 수 없었습니다. 이 크래시는 APISIX가 실행 중일 때만 발생하는 것 같습니다.

일부 디버깅 후, proto 파일의 oneof 필드를 파싱할 때 lua-protobuf에서 문제가 발생하는 것을 발견했습니다. lua-protobuf는 파싱할 때 테이블 크기를 미리 할당하려고 시도하며, 할당된 크기는 특정 값에 따라 계산됩니다. 이 값이 음수가 될 가능성이 있었습니다. 그런 다음 LuaJIT는 할당할 때 이 숫자를 큰 양수로 변환하여 "table overflow" 오류를 발생시켰습니다. 저는 이 문제를 작성자에게 보고했고, 내부적으로 해결 방법이 포함된 포크를 유지했습니다.

lua-protobuf 작성자는 매우 반응이 빨랐고, 다음 날 수정을 제공하고 며칠 후 새 버전을 출시했습니다. lua-protobuf가 더 이상 사용되지 않는 proto 파일을 정리할 때 일부 필드를 정리하지 못해 oneof가 처리될 때 비합리적인 음수가 발생한 것으로 밝혀졌습니다. 이 문제는 가끔 발생했으며, etcd proto 파일만 사용할 때 재현할 수 없었던 이유는 이러한 필드를 정리하는 단계를 놓쳤기 때문입니다.

HTTP 동작과의 정렬

마이그레이션 과정에서 기존 API가 실행 결과를 정확히 반환하지 않고 응답 상태와 본문이 포함된 HTTP 응답을 반환한다는 것을 발견했습니다. 그리고 호출자는 HTTP 응답을 직접 처리해야 합니다.

응답이 gRPC인 경우, 처리 로직과 일치하도록 HTTP 응답 쉘로 감싸야 합니다. 그렇지 않으면 호출자는 새로운(gRPC) 응답 형식에 맞게 여러 곳에서 코드를 수정해야 합니다. 특히 기존의 HTTP 기반 etcd 작업도 동시에 지원해야 한다는 점을 고려할 때 더욱 그렇습니다.

HTTP 응답과 호환되도록 추가 레이어를 추가하는 것은 바람직하지 않지만, 이를 해결해야 합니다. 이 외에도 gRPC 응답에 대해 일부 처리가 필요합니다. 예를 들어, 해당 데이터가 없을 때 HTTP는 데이터를 반환하지 않지만 gRPC는 빈 테이블을 반환합니다. HTTP 동작과 일치하도록 맞춰야 합니다.

단기 연결에서 장기 연결로

HTTP 기반 etcd 작업에서 APISIX는 단기 연결을 사용하므로 연결 관리에 대해 고려할 필요가 없었습니다. 필요한 경우 새로운 연결을 시작하고 완료되면 닫으면 됩니다.

그러나 gRPC는 이를 할 수 없습니다. gRPC로 마이그레이션하는 주요 목적 중 하나는 멀티플렉싱을 달성하는 것이며, 각 작업마다 새로운 gRPC 연결을 생성하면 이를 달성할 수 없습니다. 여기서 gRPC-go에 감사해야 합니다. 연결이 끊어지면 자동으로 재연결할 수 있는 내장된 연결 관리 기능이 있기 때문입니다. 따라서 gRPC-go를 사용하여 연결을 재사용할 수 있습니다. 그리고 APISIX 수준에서는 비즈니스 요구 사항만 고려하면 됩니다.

APISIX의 etcd 작업은 두 가지 범주로 나눌 수 있습니다. 하나는 etcd 데이터에 대한 CRUD(추가, 삭제, 수정, 조회) 작업이고, 다른 하나는 제어 평면에서 구성을 동기화하는 것입니다. 이론적으로 이 두 etcd 작업은 동일한 gRPC 연결을 공유할 수 있지만, 책임 분리를 위해 두 연결로 분리하기로 결정했습니다. CRUD 작업의 연결은 APISIX가 시작 시와 시작 후에 별도로 처리해야 하므로, 새로운 연결을 얻을 때 if 문을 추가했습니다. 불일치가 있는 경우(즉, 현재 연결이 시작 시 생성되었지만 시작 후 연결이 필요한 경우), 현재 연결을 닫고 새로운 연결을 생성합니다. 구성 동기화를 위해 새로운 동기화 방법을 개발하여 각 리소스가 기존 연결 아래에서 etcd를 감시하는 스트림을 사용하도록 했습니다.

gRPC로의 마이그레이션의 이점

gRPC로 마이그레이션한 후의 명백한 이점 중 하나는 etcd를 운영하는 데 필요한 연결 수가 크게 줄어든다는 것입니다. HTTP를 통해 etcd를 운영할 때, APISIX는 단기 연결만 사용할 수 있었습니다. 그리고 구성을 동기화할 때 각 리소스는 별도의 연결을 가졌습니다.

gRPC로 전환한 후, 우리는 gRPC의 멀티플렉싱 기능을 사용할 수 있었고, 각 리소스는 완전한 연결 대신 단일 스트림만 사용합니다. 이렇게 하면 리소스 수가 증가해도 연결 수가 더 이상 증가하지 않습니다. APISIX의 향후 개발에서 더 많은 리소스 유형이 도입될 것을 고려할 때, 예를 들어 최신 버전 3.1에서는 secrets가 추가되었으며, gRPC를 사용하여 연결 수를 줄이는 것이 더 중요해질 것입니다.

gRPC를 사용하여 동기화할 때, 각 프로세스는 구성 동기화를 위해 하나(스트림 하위 시스템이 활성화된 경우 두 개)의 연결만 가집니다. 아래 그림에서 두 프로세스가 네 개의 연결을 가지고 있는 것을 볼 수 있으며, 그 중 두 개는 구성 동기화를 위해 사용되고, Admin API는 하나의 연결을 사용하며, 나머지 연결은 권한 있는 에이전트가 서버 정보를 보고하는 데 사용됩니다.

gRPC는 훨씬 적은 연결을 사용합니다

비교를 위해, 아래 그림은 다른 매개변수를 변경하지 않고 원래의 구성 동기화 방법을 사용할 때 필요한 22개의 연결을 보여줍니다. 또한 이러한 연결은 단기 연결입니다.

너무 많은 연결

이 두 구성의 유일한 차이점은 etcd 작업에 gRPC가 활성화되었는지 여부입니다:

  etcd:
    use_grpc: true
    host:
      - "http://127.0.0.1:2379"
    prefix: "/apisix"
    ...

연결 수를 줄이는 것 외에도, gRPC-gateway 대신 gRPC를 통해 etcd에 직접 접근하면 기사 초반에 언급된 mTLS 인증과 같은 일련의 아키텍처적 한계 문제를 해결할 수 있습니다. 또한 gRPC를 사용한 후 문제가 더 적을 것입니다. 왜냐하면 Kubernetes는 gRPC를 사용하여 etcd를 운영하기 때문입니다. 문제가 있다면 Kubernetes 커뮤니티에서 발견될 것입니다.

물론, gRPC 방법은 아직 비교적 새롭기 때문에 APISIX가 gRPC를 통해 etcd를 운영할 때 몇 가지 새로운 문제가 발생할 수 있습니다. 현재 기본값은 여전히 원래의 HTTP 기반 방법을 사용하여 etcd를 운영하는 것입니다. 사용자는 config.yaml에서 etcd 아래에 use_grpc를 true로 설정하여 gRPC 방법이 더 나은지 시도해 볼 수 있습니다. 우리는 또한 다양한 소스에서 피드백을 수집하여 gRPC 기반 etcd 작업을 개선할 것입니다. gRPC 접근 방식이 충분히 성숙해지면 기본 접근 방식으로 만들 것입니다.

APISIX를 최대한 활용하려면 API7이 필요합니다

당신은 Apache APISIX의 성능을 좋아하지만, 이를 관리하는 데 드는 오버헤드는 원하지 않습니다. 당신은 구성, 유지 관리 및 업데이트에 대해 걱정하지 않고 핵심 비즈니스에 집중할 수 있습니다.

우리 팀은 Apache APISIX 창시자 및 기여자, OpenResty 및 NGINX 코어 유지 관리자, Kubernetes 멤버 및 클라우드 인프라 산업 전문가로 구성되어 있습니다. 당신은 배후에서 최고의 인력을 얻을 수 있습니다.

자신 있게 개발을 가속화하고 싶으신가요? APISIX 지원을 극대화하려면 API7이 필요합니다. 당신의 요구에 기반한 APISIX 및 API 관리 솔루션에 대한 심층적인 지원을 제공합니다!

지금 문의하세요: https://api7.ai/contact.

Tags: