NGINX SSL: 복잡한 SSL 구성의 리소스 사용 최적화

이 포스트에서는 2024년의 덜 알려진 NGINX 프로젝트 중 하나를 조명하고자 합니다.
이 프로젝트는 오늘 발표된 NGINX 1.27.4 릴리스를 통해 완료되었습니다. 이 프로젝트는 많은 수의 SSL 컨텍스트를 포함한 설정에서 로드 시간과 리소스 사용량을 최적화하는 데 중점을 두었습니다.
이번 릴리스의 모든 업데이트를 보려면 nginx.org/en/CHANGES로 이동하세요.

목차

1. NGINX SSL 구성의 기본 문제
 1-1. NGINX 구성 블록의 독립 처리 방식
 1-2. NGINX SSL 컨텍스트 생성의 높은 비용
2. NGINX SSL 컨텍스트 상속을 통한 최적화
 2-1. proxy_ssl_* 지시어 상속 처리 방식
 2-2. 상속 최적화의 중단 조건
3. 대규모 구성 대응과 NGINX SSL 캐싱 전략
 3-1. 중복된 인증서 파싱 문제 해결
 3-2. 구성 주기 내 SSL 객체 캐싱 적용
4. NGINX SSL 성능 개선과 구성 리로드 최적화
 4-1. 고유 SSL 객체 수에 따른 성능 변화
 4-2. 리로드 시 기존 NGINX SSL 객체 재활용 조건
5. 변수 기반 SSL 로딩과 캐시 적용
 5-1. 변수 기반 SSL 인증서의 성능 이슈
 5-2. ssl_certificate_cache 지시어를 통한 개선

1. NGINX와 SSL 구성의 기본 문제

1-1. NGINX 구성 블록의 독립 처리 방식

NGINX는 구성 파일의 각 블록(server나 location 등)을 독립적으로 처리합니다. 각 블록은 상위 블록의 지시어 값을 상속받을 수 있지만, 복잡한 객체(예: OpenSSL의 SSL_CTX)는 기본적으로 새로 생성됩니다. 이는 복잡한 상속 로직을 피하고 구성 처리기를 단순하고 유지보수 가능하게 만들기 위한 설계입니다.

1-2. NGINX SSL 컨텍스트 생성의 높은 비용

SSL 컨텍스트 생성은 리소스를 많이 소모하는 작업입니다. 이를 매 location마다 반복하면 구성 로드 시간과 메모리 사용량이 기하급수적으로 증가할 수 있습니다. 따라서 2022년에 NGINX는 proxy_ssl_* 지시어가 현재 블록에서 재정의되지 않았을 경우, 상위 블록의 SSL_CTX를 그대로 사용할 수 있도록 최적화를 도입했습니다.

2. NGINX SSL 컨텍스트 상속을 통한 최적화

2-1. proxy_ssl_* 지시어 상속 처리 방식

이 최적화를 통해, proxy_ssl_certificate, proxy_ssl_certificate_key, proxy_ssl_trusted_certificate, proxy_ssl_verify 등이 상위 블록에 정의되어 있고 하위 블록에 재정의되지 않으면 SSL_CTX를 공유할 수 있게 됩니다. 이로 인해 인증서와 키, OpenSSL 구조체를 한 번만 생성하여 효율을 높일 수 있습니다.

location /a {
  proxy_ssl_certificate         client.crt;
  proxy_ssl_certificate_key     client.key;
  proxy_ssl_trusted_certificate upstream-ca.pem;
  proxy_ssl_verify              on;
  proxy_pass                    https://example.com/;

  location /a/test {
    proxy_ssl_name test.example.com;
    proxy_pass     https://test.example.com/;
  }
}

2-2. 상속 최적화의 중단 조건

하위 location 블록에 proxy_ssl_* 지시어가 복사되면 최적화는 중단됩니다. 지시어 값이 상위와 동일한지 확인하는 방식도 고려했지만, 이로 인해 모든 location 블록마다 부하가 발생하게 되어 제외되었습니다.

3. 대규모 구성 대응과 NGINX SSL 캐싱 전략

3-1. 중복된 인증서 파싱 문제 해결

2023년에는 거의 1분 동안 구성 로드에 걸리는 사례가 보고되었습니다. 이는 수많은 location 블록에서 동일한 인증서와 키를 반복해서 읽고 파싱한 결과였습니다. 이렇게 큰 구성은 다수의 사람이 관리하며 여러 파일로 나누어져 있어 재작성은 어려웠습니다.

3-2. 구성 주기 내 SSL 객체 캐싱 적용

NGINX 1.27.2부터는 구성 로드 중 동일한 SSL 파일이 한 번만 읽히도록 최적화되었습니다. OpenSSL의 참조 카운트 기능을 활용하여 인증서(X509), 키(EVP_PKEY), CRL 등을 재사용하며, 내부적으로 캐시를 도입해 객체를 중복 생성하지 않습니다.

4. NGINX SSL 성능 개선과 구성 리로드 최적화

4-1. 고유 SSL 객체 수에 따른 성능 변화

Dell PowerEdge M630 서버와 Ubuntu 24.04, OpenSSL 3.0.13 환경에서 테스트를 진행했습니다. nginx -t 명령을 통해 전체 구성을 로드한 뒤 종료하며, 명령 실행 횟수(perf stat의 retired instructions)와 메모리 사용량(mallinfo2) 차이를 측정했습니다.

테스트 구성에는 10,000개의 location 블록이 각각 proxy_ssl_certificate 지시어를 사용하는 구조가 포함되었으며, “고유성 비율(uniqueness percentage)”에 따라 성능 차이를 측정했습니다. SSL 객체 캐싱 덕분에 로딩 시간과 메모리 사용량이 고유 인증서 수에 비례하도록 최적화되었습니다.

4-2. 리로드 시 기존 NGINX SSL 객체 재활용 조건

NGINX는 리로드 시 전체 구성을 새로 생성하지만, 대부분의 구성과 SSL 객체는 그대로 유지됩니다. 이를 바탕으로 이전 구성의 SSL 객체를 재사용하는 최적화가 1.27.4에서 도입되었습니다. 다음 조건을 기반으로 재사용 여부가 결정됩니다:

  • data: 형식: 항상 상속
  • engine:… 형식: 항상 재로드
  • 일반 파일: mtime 및 inode로 비교 후 같으면 재사용
  • 메타데이터 없는 경우: 재로드 처리
  • 암호화된 키는 캐시 제외

또한 ssl_object_cache_inheritable off 지시어를 통해 캐시 상속을 비활성화할 수 있습니다. 단, 이 설정은 현재 구성을 다음 구성에서 상속 불가능하게 표시하는 방식이므로, 완전한 캐시 제거를 위해서는 두 번의 reload 또는 전체 재시작이 필요합니다.

5. 변수 기반 SSL 로딩과 캐시 적용

5-1. 변수 기반 SSL 인증서의 성능 이슈

NGINX 1.15.10에서는 변수 기반의 인증서 로딩 기능이 도입되어, SNI 기반으로 도메인마다 인증서를 동적으로 로드할 수 있게 되었습니다. 하지만 TLS 핸드셰이크당 20~30% 성능 저하가 있었으며, OpenSSL 3.0 이후에는 오버헤드가 더 커졌습니다.

5-2. ssl_certificate_cache 지시어를 통한 개선

이를 개선하기 위해 NGINX 1.27.4에서 다음과 같은 캐시 디렉티브가 도입되었습니다:

ssl_certificate_cache max=N [inactive=time] [valid=time];
proxy_ssl_certificate_cache max=N;

이 디렉티브는 SSL 인증서 및 키를 변수 기반으로 설정할 때 일정 시간 동안 캐싱하여 성능을 개선합니다. 설정은 http, server, location 블록 단위로 적용 가능하며, 비활성화도 가능합니다.

server {
  listen 443 ssl;
  ssl_certificate        /etc/ssl/$ssl_server_name.crt;
  ssl_certificate_key    /etc/ssl/$ssl_server_name.key;
  ssl_certificate_cache  max=64;

  location / {
    proxy_ssl_certificate        /etc/ssl/auth/$current_upstream.crt;
    proxy_ssl_certificate_key    /etc/ssl/auth/$current_upstream.key;
    proxy_ssl_certificate_cache  max=6 inactive=20s valid=1m;

    location /nocache {
      proxy_ssl_certificate_cache off;
    }
  }
}

최악의 조건(단일 워커, TLS 세션 재사용 비활성화, keepalive 비활성화)에서도 캐싱 적용 시 정적 인증서 설정과 거의 동일한 수준의 성능을 확보했습니다.

6. 결론

NGINX는 고성능 웹 서버 및 Reverse Proxy로서, SSL 인증서를 다수 사용하는 복잡한 구성에서도 최적의 성능을 유지할 수 있도록 지속적인 개선을 이어가고 있습니다. 이번 NGINX 1.27.4 릴리스에서는 다음과 같은 핵심 최적화가 반영되었습니다:

  • SSL 컨텍스트 상속을 통한 중복 생성 방지
  • 구성 주기 내 SSL 객체 캐싱을 통한 메모리 절감
  • 변경되지 않은 SSL 객체의 리로드 시 재사용
  • 변수 기반 SSL 인증서 로딩 시 캐시 적용으로 실시간 유연성 확보

이러한 기능은 대규모 서비스 구성이나 멀티도메인 운영 환경에서도 성능 저하 없이 보안 구성을 유지하는 데 큰 도움을 줍니다.

실제 운영 환경에서도 구성 최적화 전략을 적절히 활용하면, 서버 리소스를 절감하고 배포 효율을 극대화할 수 있습니다.

앞으로도 NGINX의 최신 기능을 꾸준히 적용하고, 테스트 환경을 통해 직접 성능 개선 효과를 체감해보려면 NGINX STORE 블로그 포스트를 통해 최신 소식을 받아보세요.

NGINX STORE를 통한 솔루션 도입 및 기술지원 무료 상담 신청

* indicates required