Skip to content

프록시 서버, 그거 어떻게 동작해야 하는데?

EnvyW6567 edited this page Dec 3, 2024 · 6 revisions

💬 배경

프록시 서버의 필요성

💡 최초의 아이디어

불편을 해결하기 위해 WatchDucks는 네임 서버의 동작에서 착안해 설치가 필요 없는 트래픽 분석 시스템을 구상하였습니다. 직접 네임서버를 개발해, 사용자의 요청에서 시작된 DNS Query에 응답하며 다양한 서비스의 트래픽을 확인하고자 하였습니다.

🚧 한계 발견

주제를 구체화하며 DNS Resolution 절차와 질의-응답 패킷 구조를 더 자세히 알아보았습니다. 그 결과 네임서버만으로는 저희가 원하는 동작을 구현할 수 없음을 깨달았습니다. 아래의 2가지 까닭이 있었습니다.

첫째, 환경에 따라 예측할 수 없는 DNS Resolver가 사용되며,
둘째, 캐싱으로 인해 TTL에 도달하기 전까지 트래픽은 도달하지 않는다.

✅ 해결 방안

이를 해결하기 위해 저희는 리버스 프록시 서버를 떠올렸습니다. 요청-응답을 그대로 포워딩하는 리버스 프록시 서버를 구현, 왓치덕스에 등록된 유효한 도메인이라면 네임서버는 언제나 프록시서버의 IP를 응답하는 것입니다.

이러한 이유로, 요청 ↔️ 응답을 그대로 포워딩하는 리버스 프록시 서버를 구현해야 한다고 판단했습니다.

🏛️ 서버 구조

프록시 서버의 역할

1. 프록시 서버는 요청-응답을 각각 Client와 User에게 그대로 전달한다.

저희 서버를 사용자(Client)가 가장 예민하게 생각할 수 있는 부분 중 하나가

2. 트래픽 수집에 필요한 로깅을 수행한다.

  • 어떤 로그 분석 결과를 보여줄 것인가?

    • 응답 시간
    • 트래픽 수
    • DAU
    • 응답 성공률
  • 어떤 정보를 수집할 것인가?

    • method
    • path
    • host
    • status_code
    • elapsed_time
    • user_ip

⚒️ 구현 맥락

최대한 가볍게 만들어야 한다.

Client들로부터 가장 많이 들은 질문 중 하나가 '프록시 서버를 거치면 느려지는 것 아닌가요?'라는 질문이었습니다.

client 서버가 할 일을 최소화하고, 빠른 응답을 할 수 있도록 만들기 위해 노력했습니다.

Fastify 선택

1. 높은 성능

  • Express와 비교했을 때 최대 2배 빠른 처리량을 보여줍니다.
  • 커스텀된 라우터 구현으로 요청 매칭 속도가 빠릅니다.

2. 비동기 처리 최적화

  • 모든 핸들러와 훅이 비동기로 동작하여 Node.js의 이벤트 루프 블로킹을 방지합니다.
  • Promise 기반 비동기 처리를 자연스럽게 지원합니다.
  • 플러그인 시스템도 비동기로 동작하여 초기화 과정의 성능을 보장합니다.

3. 확장성

  • 플러그인 아키텍처를 통해 기능 확장이 용이합니다.
  • @fastify/reply-from과 같은 프록시 관련 플러그인이 잘 구현되어 있습니다.
  • 미들웨어 시스템이 잘 설계되어 있어 커스텀 기능 추가가 쉽습니다.

위의 근거를 바탕으로, 모든 플러그인과 핸들러를 비동기 함수로 작성하여 노드의 이벤트루프가 차단되지 않게 하며 JSON 파싱 등을 제공하는 fastify를 선택하였습니다.

안전하게 만들어야 한다.

또한, '프록시 서버가 문제가 생기면, 저희 서버에도 문제가 생기는 것 아닌가요?'라는 질문이었습니다. 이에 따라, 두 가지 방안을 고안하여, 최대한 안전하고 많은 응답을 처리할 수 있도록 처리했습니다.

프록시 서버가 최대한 많은 응답을 처리할 수 있도록 해야 한다.

🎈 부하테스트를 진행 부하테스트를 진행하면서, 프록시 서버에서 Http 요청 헤더의 Host값을 읽어 MySQL 데이터베이스에 매핑된 IP를 쿼리하고 있다는 사실을 발견했습니다!
프록시 서버의 경우에는 들어온 요청에 대해 유효한 호스트에 대한 요청인지 확인을 하는 과정을 거치고 프록시하는 과정을 진행합니다.
즉, 매 요청마다 MySQL 데이터베이스에 쿼리 요청이 발생했고 요청당 1회의 쿼리가 발생했던 만큼 데이터베이스 부하가 심했다는 것을 알게되었습니다.

따라서, 이 부하를 캐싱을 통해 해결했습니다.

🎈 응답 로깅 블로킹 개선 로그를 수집하고 저장하는 로직이 동기 방식으로 구현된 부분을 발견했고 이를 비동기 논-블로킹 방식으로 변경했습니다

🌈 결과적으로, 프록시 서버의 효율을 많이 높일 수 있었습니다.

최종적으로, 위와 같이 초당 위 이미지와 같이 최고 1200~1500개의 request를 처리할 수 있는 상태가 되었습니다.

프록시 서버가 healthy한 상태인지 주기적으로 확인하고, unhealthy한 경우 원래 서버의 ip를 반환하게 해준다.

네임서버에서 비동기적으로 & 주기적으로 프록시 서버에 간단한 GET /health-check 요청을 보내며 상태를 확인합니다.

프록시 서버가 정상 동작 중이 아니라면 Redis에 캐싱하는 서비스 제공자의 원본 IP로 응답해, 보통의 네임서버와 같은 역할을 합니다.

🔗 참고자료

📚 개발 일지

개발일지 펼쳐보기
개발일지 펼쳐보기
개발일지 펼쳐보기
개발일지 펼쳐보기
개발일지 펼쳐보기
개발일지 펼쳐보기
개발일지 펼쳐보기

🗄️ 문서 보관함

📚 회의록
1주차
2주차
3주차
4주차
5주차
6주차
📆 데일리 스크럼
1주차
2주차
3주차
4주차
5주차
6주차
📝 그룹 회고
  • 1주차 그룹회고
  • 2주차 그룹회고
  • 4주차 그룹회고
  • 📝 멘토링 일지

    📒 릴리즈 노트

    🧑‍🧑‍🧒‍🧒 팀 및 커뮤니티

    About Team Watchducks

    📢 발표 자료

    1️⃣ 1주차: 기획 현황

    2️⃣ 2주차: 발표자료

    3️⃣ 3주차: 발표자료

    4️⃣ 4주차: 발표자료

    5️⃣ 5주차: 발표자료

    6️⃣ 6주차: 최종 발표 ✨

    Clone this wiki locally