어떤 플랫폼을 사용하든 로깅은 빅데이터 처리, 통계, 감사, 고객 보고서 및 트랜잭션뿐만 아니라 클라이언트-서버 간 통신과 잠재적인 문제를 디버깅하는 데에도 필수적인 요구 사항인 경우가 많습니다.
이 블로그에서는 디버깅에서 로깅이 수행하는 역할을 살펴봅니다. 로깅은 인터넷 통신에서 흔히 발생하는 다양한 문제의 원인을 찾아내는 데 매우 유용한 도구입니다.

클라이언트와 웹 서버 간의 통신을 로깅하면 브라우저 버전, 클라이언트의 네트워크 및 액세스 중인 파일과 관련해 발생할 수 있는 문제를 디버깅하는 데 큰 도움이 됩니다. 하지만 로깅 대상은 여기에 그치지 않습니다. 무엇을 로깅할지는 플랫폼과 요구 사항에 따라 달라지며, 그 가능성은 무궁무진합니다.
NGINX의 로깅 아키텍처
리버스 프록시, 로드 밸런서 또는 콘텐츠 전송 네트워크(CDN)를 운영하면 클라이언트와 서버 간의 통신 흐름에 또 하나의 노드가 추가됩니다. 리버스 프록시처럼 데이터를 처리하는 중간 서버는 종종 예기치 않은 문제를 일으킵니다.
NGINX는 리버스 프록시로서 클라이언트와 웹 서버(또는 다른 애플리케이션 서버) 간의 연결을, 클라이언트의 연결을 종료하고 서버에 대한 새 연결을 생성하는 방식으로 두 개의 개별 연결로 전환합니다. 이렇게 연결이 두 개로 “분리”되면 로깅 컨텍스트도 두 개로 나뉘며, NGINX는 각 컨텍스트의 로깅을 다소 다른 방식으로 지원합니다.
- 클라이언트와 리버스 프록시 간의 트래픽에 대해 NGINX는 오류 로그와 액세스 로그를 모두 제공합니다. 액세스 로그에는 처리 이벤트와 오류가 아닌 작업이 기록됩니다.
error_log지시어를 사용하여 오류 로그를 활성화하고 기록할 오류의 심각도 수준을 설정할 수 있습니다.access_log지시어를 사용하여 액세스 로그를 활성화합니다. 관련log_format지시어를 사용하면 로그에 포함할 정보의 종류와 로그 항목의 형식을 사용자 지정할 수 있습니다. 로그는 파일이나 syslog 또는 둘 다에 기록할 수 있습니다. - 리버스 프록시와 웹 또는 애플리케이션 서버(NGINX에서는 이를 업스트림 서버라고 함) 간의 트래픽에 대해 NGINX는 오류 로그를 지원합니다. 그러나 이 트래픽에 대한 액세스 로깅은 지원하지 않습니다. 프록시 서버와 업스트림 서버 간의 오류가 아닌 이벤트를 확인하는 유일한 방법은 오류 로그의 심각도 수준을
debug로 설정하는 것입니다. 이 설정의 단점은 방대한 양의 데이터가 기록된다는 것입니다. 이로 인해 요청 처리 속도가 느려질 뿐만 아니라 매우 큰 파일이 생성되어 스토리지 공간이 빠르게 소진될 수 있습니다. (또한 디버깅 지원은 기본적으로 활성화되어 있지 않으므로configure명령에--with-debug인수를 지정하여 NGINX를 다시 컴파일해야 합니다.)
업스트림 로그를 위한 NGINX의 솔루션
CDN 제공업체로서 당사는 업스트림 서버가 리버스 프록시 서버 및 클라이언트와 제대로 통신하지 못하는 상황을 자주 겪습니다. 오류 로그의 debug 수준 메시지만으로는 업스트림 서버 문제를 해결하는 데 필요한 정보를 충분히 얻지 못할 때가 있습니다.
해결책은 곧 명확해졌습니다. 리버스 프록시 서버와 업스트림 서버 간의 통신에서 꼭 필요한 정보만 수집하는 함수를 추가해 업스트림 액세스 로그를 생성하는 것입니다.

기본적인 아이디어는 업스트림 서버로 요청이 전송될 때마다 NGINX가 함수를 호출하도록 하는 것입니다. 이렇게 하면 업스트림 로깅과 관련된 모든 로직을 함수 자체에 구현할 수 있습니다.
표준 ngx_http_upstream_module은 업스트림 요청을 처리하며, 이 모듈이 해당 함수를 호출하도록 해야 합니다. 현재 이 모듈은 이러한 기능을 지원하지 않으므로, 필요할 때 콜백을 실행할 수 있도록 패치했습니다.
로깅 자체는 저희가 작성한 별도의 모듈에서 처리하며, 이 모듈은 패치된 업스트림 모듈에 추가한 로그 콜백 기능을 사용합니다. 새 모듈은 로깅 기능을 구성하기 위한 새로운 upstream_log 지시어를 정의합니다. 이 지시어는 access_log 지시어와 동일한 파서를 사용하므로 데이터를 파일에 기록하거나 소켓을 통해 syslog 서버로 전송할 수 있습니다.
NGINX가 시작 중에 upstream_log 지시어를 nginx.conf에서 읽으면 다음 두 함수가 호출됩니다:
ngx_http_upstream_log_set_log는 지시어를 구문 분석하고 내부적으로ngx_log_t로그 구조체 자체를 준비할 때ngx_log_set_log를 사용합니다.ngx_http_upstream_log_init(구성 후 단계에서) 업스트림 모듈에 기본 로깅 함수를 등록합니다
이렇게 하면 요청을 업스트림으로 프록시해야 할 때 모든 준비가 완료된 상태가 됩니다. 업스트림 모듈은 업스트림 서버에 대한 연결을 시작하고, 패치를 통해 로깅 함수가 호출되어 요청 세부 정보가 기록되도록 합니다.
로그 형식은 업스트림 모듈에 하드코딩되어 있습니다. 필요하다면 log_format 지시어를 사용한 설정 기능을 추가할 수도 있지만, 현재 사용 사례에는 필요하지 않습니다.
업스트림에서 기록된 값
로깅 함수는 업스트림 서버와의 연결이 종료된 직후 호출됩니다. 이 함수의 인수는 현재 처리 중인 요청(ngx_http_ request_t 구조체)을 가리키는 포인터이므로, 함수가 구조체의 모든 데이터에 접근하여 기록할 수 있습니다. 특히 upstream 필드(ngx_http_upstream_t에 대한 포인터)는 업스트림 요청에 관한 데이터를 포함하므로 중요합니다. 특히 다음 항목에 주목합니다:
- 오리진 응답 상태 코드
- 요청 처리 시간
- 응답 헤더 또는 호스트 헤더의 HTTP 헤더 값
전체 요청 구조에 접근할 수 있기 때문에 이 모듈은 유연성을 갖추며, 매우 다양한 정보를 로그에 기록할 수 있습니다.
발생한 문제
처음에는 이 기능을 ngx_http_core_module에 구현했습니다. 프로토타입으로는 충분했지만, 향후 업데이트와 수정이 복잡해질 수 있어 그다지 깔끔한 해결책은 아니었습니다. 결국 업스트림 로그 솔루션에서 설명한 대로 업스트림 로깅 기능을 독립형 모듈로 분리했습니다.
물론 구현 과정에서 몇 가지 문제가 있었습니다. 특히 일부 코드에서 ngx_str_t 문자열을 잘못 처리했습니다. 예를 들어 C 라이브러리 함수인 sprintf를 ngx_snprintf 대신 사용했습니다. 이로 인해 정의되지 않은 데이터가 업스트림 로그에 기록되거나 워커 스레드에서 세그멘테이션 오류가 발생할 수도 있습니다. 이러한 문제는 Valgrind와 AddressSanitizer 같은 도구를 사용해 광범위한 디버깅과 테스트를 수행한 끝에 해결했습니다.
CDN77에서 사용되는 NGINX 기능
CDN77이 NGINX를 사용하는 주된 이유는 캐싱 기능 때문입니다. CDN 서버는 클라이언트와 웹 서버(업스트림 서버) 사이에 배치되는 노드로, 클라이언트의 요청을 전달하고 업스트림 서버에 적절한 파일을 요청합니다. 파일이 캐시되면 동일한 위치에서 같은 파일을 요청하는 다른 사용자에게도 해당 파일이 제공됩니다.
“로컬 제공 파일”은 NGINX가 요청을 받았을 때 서버 디스크에 있는 파일을 제공하도록 하기 위해 사용하는 기능 중 하나입니다.
안전한 캐싱을 위해서는 몇 가지 추가 기능과 구성이 필요합니다. 콘텐츠를 적절히 보호하기 위해 SSL(0‑RTT를 지원하는 TLS 1.3) 또는 특정 IP 주소용으로 생성할 수 있는 보안 토큰을 사용합니다.
그 밖에도 고객을 위한 사용자 지정 오류 페이지, NGINX의 기본 OCSP 스테이플링 구현, 필요한 PHP 프로세스 수를 줄이기 위한 PHP용 FastCGI 등의 기능을 사용합니다.
결론
업스트림 로깅은 다양한 문제를 디버깅하는 데 지금까지 큰 도움이 되었고 현재도 계속 도움이 되고 있을 뿐만 아니라, NGINX의 핵심 기능을 더 깊이 살펴볼 훌륭한 기회를 제공했으며 우리가 진행한 수많은 다른 프로젝트도 간소화해 주었습니다.
CDN77 소개
CDN77은 전 세계에서 콘텐츠를 더욱 효율적이고 편리하게 전송할 수 있도록 지원합니다. 30개 이상의 데이터 센터를 기반으로 전 세계 어디서나 콘텐츠를 효과적으로 캐싱하고 전송할 수 있습니다. 여기에는 웹사이트의 정적 콘텐츠, 소프트웨어 배포, 주문형 비디오(VoD), 그리고 전용 스트리밍 엔진을 사용해 HLS 또는 MPEG‑DASH와 같은 다양한 프로토콜로 제공하는 라이브 스트리밍이 포함됩니다.
CDN77을 시작하는 방법은 매우 쉽고 빠르며 간단합니다. 무료 체험판에 가입하고 CDN 리소스를 생성한 다음, 생성된 CDN URL 또는 맞춤형 CNAME 레코드를 사용하여 웹사이트나 스트리밍 솔루션과 연동하세요. 다양한 기능과 설정, 맞춤형 솔루션을 통해 CDN77은 고객의 요구 사항을 충족합니다.
작성자 소개
관련 블로그 게시물
시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.
F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.
Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.
진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.
NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.
Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.