19P by xguru 2020-08-10 | favorite | 댓글 3개

수천만 동시 커넥션, 초당 수백만 리퀘스트, 테라단위 대역폭을 사용중인 드랍박스가 Nginx 대비 Envoy의 장점을 잘 설명한 글

기존 : nginx(오픈소스 버전) + python2 + Jinja2 + YAML
ㅤ→ 하나만 바뀌어도 전체 재배포 필요
ㅤ→ 동적인 부분은 Lua로 개발
ㅤ→ 복잡한 로직은 Go 기반 프록시인 Bandaid 에서 처리

근 10년간 잘 동작했지만, 현재 환경에는 잘 맞지 않음
ㅤ→ 내부 및 외부(비공개) API들은 점차 REST에서 gRPC로 전환중이어서 프록시의 트랜스코딩 기능을 필요로 함
ㅤ→ Protocol Buffers 가 내부 서비스 정의 표준으로
ㅤ→ 모든 소프트웨어들은 언어 상관없이 Bazel 로 빌드 및 테스트
ㅤ→ 주요 인프라 프로젝트들의 오픈소스 커뮤니티에 직원들이 꽤 헤비하게 참여 중

Nginx는 운영적인 면에서도 유지하기에 비쌈
ㅤ→ Config 생성 로직이 너무 유연하고 YAML, Jinja2, Python에 나눠져 있음
ㅤ→ 모니터링이 Lua / Log 파싱 / 시스템 기반 모니터링 의 믹스
ㅤ→ 써드파티 모듈에 의존성이 높아지면서, 안정성/성능에 영향을 미치고 잦은 업그레이드에 의한 비용 발생
ㅤ→ nginx 자체의 배포 및 프로세스 관리는 다른 서비스와 많이 다름. syslog, logrotate 등 기본 시스템과 전혀 다른것에 많이 의존

그래서 10년만에 처음으로 Nginx 를 대체할 것을 찾기로 함

* 왜 Bandaid(드롭박스가 자체 개발한 Go 기반 프록시)로 안가고 ?
ㅤ→ Go 는 C/C++ 보다 리소스를 많이 먹음.
ㅤ→ GO 의 TLS 스택은 FIPS 지원 안함 ( 미국의 연방 정보 처리 표준 )
ㅤ→ 내부 도구여서 외부 커뮤니티의 지원이 불가

현재 : Envoy 기반 트래픽 인프라로 전환

----- Nginx 보다 Envoy 가 더 나았던 점 ------

* 성능 *

Nginx 의 아키텍처는 이벤트 드리븐 / 멀티프로세스. SO_REUSEPORT & EPOLLEXCLUSIVE 지원
이벤트 루프 기반이지만 완전한 넌블로킹은 아님. 파일 오픈/로깅 시엔 이벤트 루프가 중단될 수 있음. (aio, aio_write 및 Threadpool 을 활성화 하더라도)
이를 통해서 테일 지연이 발생하고 몇초단위 딜레이가 생기기도

Envoy 도 비슷하게 이벤트 드리븐 아키텍쳐 이지만, 프로세스가 아닌 쓰레드 기반
SO_REUSEPORT 지원(BPF 필터 지원), libevent 를 통한 이벤트 루프 지원
이벤트 루프에서 블로킹 IO 없음. 이벤트 로깅도 넌블로킹 방식으로 구현.

이론적으로는 비슷한 성능 특징을 보일 것 같이 보였고 실제 대부분 워크로드 테스트에서도 비슷.
다만, Nginx가 롱테일에서 지연시간이 더 컸음. I/O많을때 이벤트루프가 중단되었기 때문.

통계수집 없이는 Nginx가 Envoy와 비슷한 성능을 보이지만, 내부에서 사용중인 Lua 통계 수집도구가 high-RPS 테스트에서 Nginx를 3배 느리게 만듬. (이건 뮤텍스로 동기화되는 lua_shared_dict 때문 ). 드랍박스의 통계수집 방식에 문제가 있긴 하지만 이걸 효율적으로 재개발하는 건 포기했음. (Nginx 내부에 인스트루멘테이션 하면 나중에 업그레이드가 어려울것이라고 예상했기 때문)

하여튼 이런 이슈들이 Envoy 에서는 없었기 때문에, 이전하고 나서 기존 Nginx 가 사용중이던 서버의 최대 60%를 릴리즈 할 수 있었음.

* Observability *

무료버전 Nginx 는 stub status 모듈에 7가지 stat만 제공
이걸로는 당연히 부족했으므로, log_by_lua 핸들러를 붙여서 더 많은 stat을 제공하고 있었음.
또한 error.log 파서를 통해서 에러정보를 내보내고, nginx 내부 상태값을 내보내기 위한 별도 exporter 도 존재.

기본적인 Envoy 셋업은 프로메테우스 포맷으로 수천가지 다른 메트릭들을 제공
프록시 트래픽 정보부터 서버의 내부 상태 정보,
클러스터별/업스트림별/가상호스트별 통계값과 리스너별 TCP/HTTP/TLS 다운스트림 통계 정보 등등

이런 다양한 통계와 함께, Envoy 는 Tracing Provider를 플러그인 가능.
트래픽 팀 뿐만 아니라 어플리케이션 개발자들에게도 유용함.

마지막으로 Envoy는 gRPC를 통해 액세스 로그를 스트리밍 가능.
이러면 트래픽 팀의 syslog-to-hive 브리지를 지원하는 부담이 줄어듬.
커스텀 TCP/UDP 리스너를 붙이는 것보다 일반적인 gRPC서비스를 실행하는 것이 훨씬 쉽고 안전.

* Integration *

Nginx 의 통합은 매우 유닉스 스러움. Configuration이 매우 정적.
config 파일이나 TLS 인증서, allowlist/blocklist 등을 파일에 의존.
간단하고 하위호환이 되기 때문에 몇개의 쉘스크립트로 자동화는 가능하지만,
시스템이 커짐에 따라서 테스트가능여부 및 표준화가 점점 더 중요해짐.

Envoy는 이런 통합에 대해 자신만의 방법을 가지고 있음.
xDS라고 부르는 API를 제공해서 protobuf 와 gRPC 사용을 장려.
Envoy는 이런 xDS에 쿼리를 통해서 동적 리소스를 찾음.

- 이 xDS는 이제 Envoy 를 넘어서 Universal Data Place API(UDPA) 라는 이름으로 L4/L7 로드밸런서의 de facto 표준이 되려고 진화하고 있으며, 우리 경험으로는 이게 잘 되어가고 있음. Envoy께 아닌 Katran eBPF/XDP L4 로드밸런서에도 UDPA를 사용하려고 하는 중.

드랍박스는 내부적으로 gRPC를 통해서 서비스들이 연동하고 있기때문에 훨씬 좋음.

* Configuration *

Nginx 는 사람이 읽기 쉽다는 설정파일이라는 큰 장점을 가지고 있음.
하지만 이 장점은 점점 설정이 복잡해 지고 자동 생성되면서 그 장점을 잃어버림.
드랍박스는 Python2,Jinja2,YAML 등을 통해서 생성되다 보니 그러다보니 데이터 모델도 꼬이고 복잡.

Envoy는 설정에 대한 통합된 데이터 모델을 가지고 있음. 모든 설정값은 Protocol Buffer에 정의됨. 데이터 모델링 문제가 해결되며, 설정값에 타입 정보가 추가됨.
드랍박스 내부에서 protobuf 가 많이 쓰이기 때문에 통합을 쉽게함

* Extensibility *

Nginx 의 확장성을 위해서는 C모듈 작성이 필요함. 안전한 모듈 작성을 위해서는 시니어 개발자가 필요. 좀더 가벼운 모듈 개발을 위해서 Perl / JS 인터페이스도 제공하긴 하지만 매우 제한적임. 그래서 가장 일반적으로 쓰이는 방법이 lua-nginx-module 을 통한 것.

Envoy 의 주 확장 메커니즘은 C++ 플러그인을 통해서인데 문서화는 nginx 처럼 잘 되어있지는 않지만 매우 간단함. 이건 깔끔하게 잘 코멘트된 인터페이스, C++14 언어와 표준 라이브러리 등 덕분

Envoy 가 다른 웹서버와의 큰 차별점은 바로 WebAssembly(WASM)의 지원 여부.
이를 통해 Rust 같은 다양한 언어들을 통해서 확장 개발 하는걸 지원 가능.
아직 드랍박스에서 WASM을 안쓰지만, 언젠가 Go SDK for proxy-wasm 지원이 된다면 변경될 수도 있음

* Building and Testing *

Nginx는 기본적으로 커스텀 쉘 기반 설정 과 make 기반 빌드를 사용. 간단하고 훌륭하지만 이걸 Bazel로 빌드되는 모노레포에 연동하기엔 꽤 많은 노력이 들어감
Nginx 는 펄기반 integration 테스트가 있지만 유닛테스트는 없음.

Envoy 는 이미 빌드시스템이 Bazel 기반이고 간단하게 우리 모노레포에 연동했음.
gtest/gmock 기반 유닛테스트와 integration test framework 를 지원

* Security *

Nginx 코드는 매우 작고 외부 의존성도 작아서 보안상 취약점이 많지 않음.

Envoy 는 코드가 많아서 아무래도 공격할 지점이 많아 보임. 이를 위해서 Envoy 는 현대적인 보안 프랙티스에 많의 의존. AddressSanitizer, ThreadSanitizer, , MemorySanitizer 등을 사용.

* Features *

이 부분은 주관적인 의견이 많으므로 참고 할 것

Nginx는 처음에 아주 적은 리소스로 스태틱 파일을 서비스 하는 웹서버로 시작.
즉 static serving, caching, range caching 이 주 기능
프록시 관점에선 Nginx는 요즘 인프라에서 요구하는 기능들이 많이 부족.
백엔드와의 HTTP/2접속도 안되고, 멀티연결되는 gRPC 프록시도 안됨. gRPC 트랜스코딩도 불가 등.
오픈코어 방식의 라이센스 모델이라 몇몇 주요한 기능들이 "커뮤니티 버전"엔 빠져있음

Envoy 는 에초에 ingress/egress proxy로 시작했고, gRPC 로드가 많은 환경에서 많이 사용.
웹서비스 기능은 매우 초보적인 수준. 파일 서빙도 불가, 캐슁도 아직 만드는중이고 brotli 도 미지원 등
이런 환경을 위해서는 Envoy를 업스트림 클러스터로 쓰는 Nginx 셋업도 사용중
Envoy 가 HTTP 캐쉬 가능해지면 이런 스태틱 서빙 환경도 옮길 수 있을 것으로 예상

Envoy는 gRPC 관련 기능을 많이 지원
- gRPC proxying
- HTTP/2 to backends
- gRPC → HTTP bridge (+ reverse.)
- gRPC-WEB
- gRPC JSON transcoder

또한 Envoy는 outbound proxy 로도 사용 가능
- Egress Proxy
- Third-party software service discovery with Courier gRPC 라이브러리

* Community *

Nginx 개발은 중앙방식이고 대부분 숨겨져 있음.
Envoy 개발은 열려있고 탈중앙화. GitHub 이슈/PR로 진행되며 메일링/슬랙등을 통해서도 활발

----- 드롭박스의 현재 Migration 상태 -----

Nginx 와 Envoy 를 같이 반년째 운영하며 DNS를 통해 점진적으로 트래픽 이관중
아무 문제도 없이 이관 된 것은 아니며 작은 이슈는 있었지만, 심각한 장애는 없었음.
"unusual" 하거나 "non-RFC" 적인 행동들 때문에 겪었던 문제들에 대한 해결책도 정리(본문에 상세 내용이 있으니 참고하세요)

** 앞으로 할 일들 **

- HTTP/3 : Envoy 도 실험적으로 지원하기 시작. UDP 가속을 위해 리눅스 커널을 업그레이드 하면 실험해 볼 예정
- 내부 xDS 기반 로드밸런서와 Outlier Detection
- WASM 기반 Envoy 확장
- Bandaid (Go 기반 프록시) 를 Envoy로 교체
- Envoy Mobile 로 모바일 앱에도 Envoy 적용

좋은내용 간단하게
정리 해주셔서
감사합니다.

감사합니다. :)

상세한 정리와 친절한 의견까지 달아주셔서 이해에 큰 도움이 되었습니다. 감사합니다 :)