NGINX Plus를 활용한 엔터프라이즈 환경의 MQTT 배포 최적화

NGINX | 2023년 6월 05일

When announcing the NGINX Plus R29 릴리스를 발표하면서, 새롭게 추가된 기본 MQTT 메시지 파싱 지원 기능을 간략히 소개했습니다. 이 글에서는 이를 바탕으로 엔터프라이즈 환경에서 MQTT 배포를 최적화하도록 NGINX Plus를 구성하는 방법을 살펴보겠습니다.

MQTT란 무엇인가요?

MQTT는 메시지 큐잉 텔레메트리 전송을 의미합니다. MQTT는 매우 널리 사용되는 경량의 발행-구독 메시징 프로토콜로, 사물 인터넷(IoT) 또는 기기 간 통신(M2M) 장치와 애플리케이션을 인터넷으로 연결하는 데 적합합니다. MQTT는 대역폭이 낮거나 전력이 제한된 환경에서도 효율적으로 작동하도록 설계되어, 원격 클라이언트가 많은 애플리케이션에 이상적입니다. 가전제품, 자동차, 운송, 제조, 의료 등 다양한 산업에서 사용됩니다.

NGINX Plus MQTT 메시지 처리

NGINX Plus R29는 MQTT 3.1.1 및 MQTT 5.0을 지원합니다. 클라이언트와 브로커 사이에서 프록시 역할을 수행하여 핵심 시스템의 작업 부담을 덜고, 확장성을 간소화하며, 컴퓨팅 비용을 절감합니다. 특히 NGINX Plus는 MQTT CONNECT 메시지의 일부를 구문 분석하고 재작성하여 다음과 같은 기능을 지원합니다:

  • MQTT 브로커 부하 분산
  • 세션 지속성(클라이언트를 동일한 브로커에 다시 연결)
  • SSL/TLS 종료
  • 클라이언트 인증서 인증

MQTT 메시지 처리 지시문은 NGINX 구성 파일의 stream 컨텍스트에 정의해야 하며, ngx_stream_mqtt_preread_module
및 ngx_stream_mqtt_filter_module에서 제공합니다.

The preread 모듈은 NGINX의 내부 프록시 처리 전에 MQTT 데이터를 처리하므로, 파싱된 메시지 데이터를 기반으로 부하 분산 및 업스트림 라우팅을 결정할 수 있습니다.

The filter 모듈을 사용하면 수신된 CONNECT 메시지 내의 clientid, username, password 필드를 다시 작성할 수 있습니다. 이러한 필드를 변수와 복합 값으로 설정할 수 있어 구성 옵션이 크게 확장되며, NGINX Plus에서 민감한 장치 정보를 마스킹하거나 TLS 인증서의 고유 이름과 같은 데이터를 삽입할 수 있습니다.

MQTT 지시어 및 변수

이제 MQTT 배포를 최적화하고 특정 요구 사항을 충족하도록 NGINX 구성을 조정할 수 있는 여러 가지 새로운 지시어와 내장 변수를 사용할 수 있습니다.

사전 읽기 모듈 지시문 및 내장 변수

  • mqtt_preread – MQTT 구문 분석을 활성화하여 클라이언트 장치에서 보낸 CONNECT 메시지의 clientid 및 username 필드를 추출합니다. 이러한 값은 내장 변수를 통해 사용할 수 있으며, 세션을 해싱하여 부하 분산된 업스트림 서버에 할당하는 데 도움이 됩니다(아래 예시 참조).
  • $mqtt_preread_clientid – 디바이스에서 전송한 MQTT 클라이언트 식별자를 나타냅니다.
  • $mqtt_preread_username – 클라이언트가 인증을 위해 전송한 사용자 이름을 나타냅니다.

필터 모듈 지시어

  • mqtt – MQTT 재작성이 활성화되어 있는지 여부를 지정합니다.
  • mqtt_buffers – 연결당 할당할 수 있는 MQTT 처리 버퍼의 최대 개수와 각 버퍼의 크기를 재정의합니다. 기본적으로 NGINX는 연결당 버퍼 수를 100개로 제한하며, 각 버퍼의 크기는 1k입니다. 일반적으로 이 설정이 성능에 가장 적합하지만, 특수한 상황에서는 조정이 필요할 수 있습니다. 예를 들어 MQTT 메시지가 길면 더 큰 버퍼 크기가 필요합니다. 특정 연결에서 짧은 시간 내에 대량의 MQTT 메시지를 처리하는 시스템은 버퍼 수를 늘리면 도움이 될 수 있습니다. 대부분의 경우 NGINX는 내부 메모리 풀에서 버퍼를 생성하므로 버퍼 매개변수를 조정해도 기본 시스템 성능에는 거의 영향을 미치지 않습니다.
  • mqtt_rewrite_buffer_size – MQTT 메시지를 구성하는 데 사용되는 버퍼의 크기를 지정합니다. 이 지시문은 더 이상 사용되지 않으며 NGINX Plus R30부터 폐기되었습니다.
  • mqtt_set_connect – 클라이언트에서 전송된 CONNECT 메시지의 매개변수를 다시 작성합니다. 지원되는 매개변수는 다음과 같습니다: clientid, username, password.

MQTT 예제

NGINX Plus로 MQTT 메시지를 처리할 때의 이점과 관련 모범 사례를 좀 더 자세히 살펴보겠습니다. 아래 예시에서는 포트 1883과 8883을 사용합니다. 포트 1883은 기본 비보안 MQTT 포트이고, 포트 8883은 기본 SSL/TLS 암호화 포트입니다.

MQTT 브로커 부하 분산

MQTT 디바이스의 일시적인 특성으로 인해 클라이언트 IP가 예기치 않게 변경될 수 있습니다. 이로 인해 디바이스 연결을 올바른 업스트림 브로커로 라우팅하기가 어려워질 수 있습니다. 이후 디바이스 연결이 한 업스트림 브로커에서 다른 브로커로 이동하면 브로커 간에 비용이 많이 드는 동기화 작업이 발생하여 지연 시간과 비용이 증가할 수 있습니다.

MQTT CONNECT 메시지의 clientid 필드를 파싱하면 NGINX는 업스트림 서비스 브로커에 대한 고정 세션을 설정할 수 있습니다. 이를 위해 clientid를 해시 키로 사용하여 백엔드의 브로커 서비스에 대한 연결을 유지합니다.

이 예제에서는 clientid를 세 개의 업스트림 브로커에 고정 세션을 설정하기 위한 토큰으로 사용하여 MQTT 디바이스 데이터를 프록시합니다. 업스트림 서버에 장애가 발생할 경우 해당 서버가 처리하던 트래픽이 나머지 서버에 이미 설정된 세션에 영향을 주지 않으면서 균등하게 분산되도록 consistent 매개변수를 사용합니다.

stream {      mqtt_preread on; 
     
      upstream backend {
          zone tcp_mem 64k;
          hash $mqtt_preread_clientid consistent;
    
          server 10.0.0.7:1883; # upstream mqtt broker 1
          server 10.0.0.8:1883; # upstream mqtt broker 2
          server 10.0.0.9:1883; # upstream mqtt broker 3 
      }
    
      server {
          listen 1883;
          proxy_pass backend;
          proxy_connect_timeout 1s;
      }
  }

NGINX Plus는 MQTT CONNECT 메시지의 사용자 이름 필드도 구문 분석할 수 있습니다. 자세한 내용은 ngx_stream_mqtt_preread_module 사양을 참조하세요.

SSL/TLS 종료

디바이스 통신을 암호화하는 것은 데이터 기밀성을 보장하고 중간자 공격을 방지하는 데 매우 중요합니다. 하지만 TLS 핸드셰이크, 암호화 및 복호화는 MQTT 브로커에 상당한 리소스 부담을 줄 수 있습니다. 이를 해결하기 위해 NGINX Plus는 브로커(또는 브로커 클러스터)의 데이터 암호화 작업을 대신 처리하여 보안 규칙을 간소화하고 브로커가 디바이스 메시지 처리에 집중할 수 있도록 합니다.

이 예에서는 NGINX를 사용하여 디바이스의 TLS 암호화 MQTT 트래픽을 백엔드 브로커로 프록시하는 방법을 보여 줍니다. ssl_session_cache 지시문은 약 20,000개의 SSL 세션을 저장할 수 있는 5MB 캐시를 정의합니다. NGINX는 proxy_connect_timeout 지시문에 정의된 대로 시간 초과가 발생하기 전까지 5초 동안 프록시된 브로커에 연결을 시도합니다.

stream {      server {
          listen 8883 ssl;
          ssl_certificate /etc/nginx/certs/tls-cert.crt;
          ssl_certificate_key /etc/nginx/certs/tls-key.key;
          ssl_session_cache shared:SSL:5m;
          proxy_pass 10.0.0.8:1883;
          proxy_connect_timeout 5s;
      }
  } 

클라이언트 ID 대체

보안상의 이유로 MQTT 브로커의 데이터베이스에 클라이언트를 식별할 수 있는 정보를 저장하지 않도록 선택할 수 있습니다. 예를 들어 디바이스가 MQTT CONNECT 메시지의 일부로 일련번호나 기타 민감한 데이터를 전송할 수 있습니다. 디바이스 식별자를 클라이언트로부터 수신한 알려진 다른 정적 값으로 대체하면 NGINX Plus가 프록시하는 브로커에 연결하려는 각 디바이스에 대해 대체 고유 키를 설정할 수 있습니다.

이 예에서는 디바이스의 클라이언트 SSL 인증서에서 고유 식별자를 추출하여 MQTT 클라이언트 ID를 마스킹하는 데 사용합니다. 클라이언트 인증서 인증(상호 TLS)은 ssl_verify_client 지시어로 제어합니다. 이 지시어를 on 매개변수로 설정하면 NGINX는 클라이언트 인증서가 신뢰할 수 있는 인증 기관(CA)에서 서명되었는지 확인합니다. 신뢰할 수 있는 CA 인증서 목록은 ssl_client_certificate 지시어로 정의합니다.

stream {      mqtt on; 
    
      server {
          listen 8883 ssl;
          ssl_certificate /etc/nginx/certs/tls-cert.crt;
          ssl_certificate_key /etc/nginx/certs/tls-key.key;
          ssl_client_certificate /etc/nginx/certs/client-ca.crt;
          ssl_session_cache shared:SSL:10m;
          ssl_verify_client on;
          proxy_pass 10.0.0.8:1883;
          proxy_connect_timeout 1s;
          
          mqtt_set_connect clientid $ssl_client_serial;
      }
  }

인증 자격 증명으로서의 클라이언트 인증서

MQTT 클라이언트를 인증하는 일반적인 방법 중 하나는 클라이언트 인증서에 저장된 데이터를 사용자 이름으로 사용하는 것입니다. NGINX Plus는 클라이언트 인증서를 구문 분석하고 MQTT 사용자 이름 필드를 다시 작성하여 백엔드 브로커의 작업 부담을 줄일 수 있습니다. 다음 예에서는 클라이언트 인증서의 주체 고유 이름(Subject Distinguished Name, Subject DN)을 추출하여 MQTT CONNECT 메시지의 사용자 이름 부분에 복사합니다.

stream {      mqtt on; 
     
      server {
          listen 8883 ssl;
          ssl_certificate /etc/nginx/certs/tls-cert.crt;
          ssl_certificate_key /etc/nginx/certs/tls-key.key;
          ssl_client_certificate /etc/nginx/certs/client-ca.crt;
          ssl_session_cache shared:SSL:10m;
          ssl_verify_client on;
          proxy_pass 10.0.0.8:1883;
          proxy_connect_timeout 1s;
          
          mqtt_set_connect username $ssl_client_s_dn;
      }
  } 

NGINX Plus MQTT CONNECT 메시지 재작성에 대한 전체 사양은 ngx_stream_mqtt_filter_module 사양을 참조하세요.

지금 시작하세요

향후 NGINX Plus의 MQTT 기능에는 다른 MQTT 메시지 유형의 파싱뿐만 아니라 다음과 같은 기능을 지원하기 위한 CONNECT 메시지의 심층 파싱이 포함될 수 있습니다:

  • 추가 인증 및 액세스 제어 메커니즘
  • 요청이 지나치게 많은 클라이언트의 요청 속도를 제한하여 브로커 보호
  • 메시지 텔레메트리 및 연결 메트릭

NGINX Plus를 처음 사용하신다면 30일 무료 평가판을 신청하여 MQTT를 시작해 보세요. 또한 여러분에게 가장 중요한 기능에 대한 의견도 듣고 싶습니다. 댓글로 의견을 남겨 주세요.


공유
태그: F5 NGINX, Tech

관련 블로그 게시물

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
NGINX | 2023. 11. 30.

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric

이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
NGINX | 2023. 03. 23.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법

앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
NGINX | 2021. 09. 27.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션

개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX | 2021. 07. 06.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법

NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX | 2021. 06. 30.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리

NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
NGINX | 2015. 03. 25.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유

Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.