엔터프라이즈 LLM 서빙을 위한 NeMo Microservices 아키텍처

Reading Time: 12 minutes

LLM을 실제 서비스 환경에 적용해 운영하다 보면, 시간이 지날수록 체감 성능이 서서히 저하되는 현상을 피하기 어렵습니다. 이는 모델의 지식이 과거의 학습 데이터에 머무르는 반면, 사용자의 질의 패턴과 서비스 환경은 계속해서 변화하기 때문입니다. 이러한 데이터 드리프트(Data Drift) 간극이 커질수록 응답 정확도와 활용 가치는 자연스럽게 떨어지게 됩니다. 이 문제를 해결하기 위한 핵심적인 접근이 바로 LLMOps입니다. LLMOps는 운영 중 발생하는 로그와 사용자 피드백을 지속적으로 수집하고, 이를 바탕으로 모델을 지속적으로 튜닝(Fine-tuning)하거나 지식을 업데이트하여 최신 상태로 유지하는 체계입니다. Kafka, Spark, MLflow, vLLM, Airflow 와 같은 검증된 오픈소스 도구들을 조합해 이러한 파이프라인을 직접 구축하는 것도 가능하지만, 엔터프라이즈 환경에서 이를 직접 구현하려면 각 도구에 대한 높은 숙련도가 요구될 뿐만 아니라, 복잡한 아키텍처로 인해 인프라 확장과 유지보수에 과도한 비용이 발생하게 됩니다. 또한, 장애 발생 시 원인 파악이 어렵고 보안·컴플라이언스 대응이 까다롭다는 현실적인 벽에도 부딪히게 됩니다.

결국 LLM을 장기적으로 안정 운영하기 위해서는 개별 도구를 이어 붙이는 방식이 아니라, 데이터 처리부터 학습, 평가, 추론, 보안까지 하나의 흐름으로 제공하는 통합 솔루션이 필요하며, NVIDIA NeMo Microservices는 이러한 요구에 대한 현실적인 해답이 될 수 있습니다.

NeMo Microservices

앞서 논의된 오픈소스 기반 접근 방식의 복잡성과 운영 부담을 해결하기 위한 엔터프라이즈급 해답은 바로 NVIDIA NeMo Microservices입니다. 이 솔루션은 데이터 처리부터 모델 튜닝, 평가, 배포, 그리고 안전성 제어에 이르는 LLMOps 전체 파이프라인을 독립적이면서도 유기적인 마이크로서비스 형태로 제공합니다. 이를 통해 기업은 복잡한 기술 스택을 개별적으로 조립하는 대신, 검증된 프레임워크 안에서 전체 파이프라인을 효율적으로 구축하고 운영할 수 있습니다. NeMo Microservices가 제공하는 엔드투엔드 워크플로우는 다음과 같은 핵심 컴포넌트로 구성됩니다.

  • Data Curator & Data Designer를 활용한 데이터 처리: 데이터셋 정제(필터링, 마스킹) 및 품질 향상을 위한 합성 데이터 생성
  • NeMo Customizer를 활용한 모델 튜닝: 리소스 프로비저닝, 워크로드 분산, 결과 모델 아티팩트 저장 등 전체 파인튜닝 라이프사이클을 조율하는 API 기반 마이크로서비스
  • NeMo Evaluator를 활용한 모델 평가: 파인튜닝된 모델의 성능을 벤치마크 또는 커스텀 데이터셋으로 객관적으로 검증 NIM (NVIDIA Inference Microservices)을 활용한 모델 추론: 최적화된 고성능 추론 엔진을 통해 모델을 서비스로 배포
  • Guardrails를 활용한 안전성 및 제어: 모든 요청과 응답을 가로채 단순한 콘텐츠 필터링을 넘어 복잡하고 문맥 인지적인 정책을 적용할 수 있는 안전 및 제어 계층
  • Argo Workflows를 활용한 파이프라인 자동화 : 위 모든 컴포넌트를 템플릿 기반으로 묶어 전체 파이프라인 자동화

이러한 NeMo Microservices는 네이버 클라우드 플랫폼 환경에 배포되어 강력한 시너지를 창출합니다.

NeMo Microservices on NAVER Cloud Platform 아키텍처

네이버 클라우드 플랫폼의 검증된 매니지드 서비스와 NVIDIA NeMo Microservices를 결합하면, 데이터 수집부터 모델 서빙, 안전성 제어(Guardrail)까지 단절 없는 엔터프라이즈 LLMOps 파이프라인을 구축할 수 있습니다.

본 아키텍처는 데이터 준비(Data Prep), 모델 최적화(Customization), 서비스 배포(Serving)의 전 과정을 유기적으로 연결하도록 설계되었습니다.

주요 구성 요소와 역할은 다음과 같습니다.

1. 데이터 파이프라인

LLM의 성능은 양질의 데이터에 달려 있습니다. 네이버 클라우드 플랫폼의 데이터 서비스를 활용하여 안정적인 수집 및 처리 환경을 구성합니다.

  • Data Stream (실시간 수집): 외부 시스템 및 사용자 피드백 로그를 실시간으로 수집하는 관문입니다. 별도의 Kafka 클러스터 구축 없이 토픽 생성만으로 즉시 사용 가능하며, 대규모 트래픽을 안정적으로 처리합니다.
  • Data Flow (데이터 정제 ETL): 수집된 Raw Data를 학습 가능한 포맷(JSONL 등)으로 변환합니다. 완전 관리형 Spark 서비스로, 복잡한 인프라 관리 없이 데이터 정제, 필터링, 구조화 작업을 자동화된 워크플로우로 수행합니다.
  • Object Storage (데이터 레이크): 가공 전후의 모든 데이터를 안전하게 보관하며, NeMo Curator 및 Designer가 접근하는 공용 저장소 역할을 수행합니다.

2. AI 워크로드 실행

데이터 준비가 완료되면, 실제 모델 학습과 추론은 네이버 클라우드 플랫폼 상의 Managed Kubernetes 환경에서 구동됩니다.

  • Ncloud Kubernetes Service: NeMo Customizer, Evaluator, NIM, Guardrails 등 모든 마이크로서비스가 구동되는 컨테이너 환경입니다.
    • 보안: Private Subnet에 배포하여 외부 접근을 차단하고 네트워크 수준의 격리성을 확보합니다.
    • 확장성: 학습 및 추론 부하에 따라 GPU 노드를 유연하게 확장(Autoscaling)합니다.

3. 접근 제어 및 공유 스토리지

서비스의 안정적인 운영과 지속적인 모델 개선을 지원합니다.

  • Load Balancer: 외부 사용자의 추론 요청을 받아 내부 Private Subnet에 있는 NIM 서비스로 안전하게 라우팅합니다.
  • NAS (Shared Storage): NeMo Microservices 서비스가 공유하는 고성능 파일 스토리지입니다.

각 단계에 대해 세부적으로 살펴보도록 하겠습니다.

Data processing

1. Data Processing – Building a Robust Data Pipeline on NAVER Cloud Platform

성공적인 LLMOps의 시작은 서비스에서 발생하는 로그와 피드백 데이터를 누락 없이 수집하고, 학습 가능한 형태로 정제하는 것입니다.

NVIDIA NeMo Microservices는 테라바이트(TB) 급의 대규모 데이터 처리를 위해 NeMo Curator(데이터 정제)와 NeMo Designer(합성 데이터 생성)를 제공합니다. 하지만 본 프로젝트는 데이터 규모가 상대적으로 크지 않은 테스트 환경이었기에, 이러한 대규모 전용 도구들을 위한 인프라를 별도로 구축하고 최적화하는 것은 과도한 엔지니어링 비용이 발생할 수 있습니다.

따라서 이번 구축에서는 초기 구축 속도와 운영 편의성을 최우선으로 고려하여, 네이버 클라우드 플랫폼의 완전 관리형 서비스인 Data Stream과 Data Flow를 활용하는 실용적인 아키텍처를 채택했습니다. 이를 통해 인프라 관리 부담을 최소화하면서도 안정적인 데이터 파이프라인을 즉각적으로 확보할 수 있었습니다.

그림 3. 네이버 클라우드 플랫폼의 Data Stream과 Data Flow를 연동하여 실시간 피드백 로그를 수집하고, 이를 NeMo 학습용 데이터셋으로 변환하는 전체 파이프라인 아키텍처

2. Data Ingestion: Data Stream (Managed Kafka)

사용자의 질의(Prompt)와 모델의 응답(Completion), 그리고 피드백 데이터는 실시간으로 유입됩니다. 이를 안정적으로 수집하기 위해 Apache Kafka 기반의 Data Stream 상품을 사용합니다.

Data Stream은 별도의 Kafka 클러스터 구축 없이 토픽(Topic) 생성만으로 즉시 사용할 수 있으며, Object Storage와의 연동(Connector)을 통해 수집된 데이터를 자동으로 적재합니다.

아래는 애플리케이션 서버에서 사용자 로그를 Data Stream으로 전송하는 Python Producer 예시입니다.

from kafka import KafkaProducer
import json
import ssl

# NAVER Cloud Platform Data Stream ENV
brokers = ["{STREAM_ENDPOINT}:9092"]
context = ssl.create_default_context()
context.options &= ssl.OP_NO_TLSv1
context.options &= ssl.OP_NO_TLSv1_1

producer = KafkaProducer(
    bootstrap_servers=brokers,
    sasl_plain_username="NAVER Cloud Platform_iam_{ACCESS_KEY}",
    sasl_plain_password="NAVER Cloud Platform_iam_{SECRET_KEY}",
    security_protocol='SASL_SSL',
    ssl_context=context,
    sasl_mechanism='PLAIN',
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

# 데이터 전송 (User Feedback Sample)
msg = {
    "request_id": "req-12345",
    "user_input": "NeMo 프레임워크의 장점은?",
    "model_output": "NeMo는 엔드투엔드...",
    "feedback_score": 5,
    "timestamp": "2025-01-29 14:00:00"
}

producer.send(topic='llm-feedback-log', value=msg)
producer.flush()

3. ETL & Formatting: Data Flow (Managed Spark)

Object Storage에 적재된 Raw Data(JSONL)는 모델 학습에 바로 사용할 수 없습니다. Data Flow 서비스를 활용하면 별도의 인프라 관리 없이 Spark 기반의 배치 작업을 수행하여 데이터를 정제하고 포맷팅할 수 있습니다.

본 프로젝트에서는 Data Flow 상품의 Script Mode를 활용하여, 수집된 로그에서 불필요한 필드를 제거하고 NeMo Customizer가 요구하는 학습 포맷인 JSONL(JSON Lines) 형태로 변환하여 저장했습니다.

from pyspark.sql import SparkSession
from pyspark.sql.functions import col
from pyspark.sql.types import StructType, StringType, IntegerType

# Spark Session 초기화
spark = SparkSession.builder.enableHiveSupport().getOrCreate()

# 1. Object Storage에서 Raw Data 로드 (Data Stream으로 적재된 데이터)
df_raw = spark.read.json("s3a://raw-data-bucket/llm-feedback-log/")

# 2. 데이터 필터링 및 NeMo 포맷(input/output)으로 변환
# NeMo SFT 포맷: {"input": "User Prompt", "output": "Model Response", "mask": "User"}
df_processed = df_raw \
    .filter(col("feedback_score") >= 4) \
    .select(
        col("user_input").alias("input"),
        col("model_output").alias("output")
    )

# 3. NeMo Customizer 학습 데이터셋 포맷 : JSONL
df_processed.write.mode("append").json("s3a://processed-dataset/train-v1/")

# (참고) 만약 데이터 백업/분석용으로 고효율 포맷이 필요하다면 Parquet으로 별도 저장할 수 있습니다.
# df_processed.write.mode("append").parquet("s3a://archive-dataset/train-v1/")

이렇게 정제된 데이터는 Data store API를 통해서 datastore-shared-storage (NAS)에 저장되어 ,이후 단계인 NeMo Customizer에서 즉시 로드할 수 있게 됩니다.

Tuning – Customizer & Evaluator

NeMo Customizer는 준비된 데이터셋을 이용해 모델 파인튜닝을 실행하는 핵심 마이크로서비스입니다.

사전에 Data Store에 등록된 데이터셋과 베이스 모델을 워커 파드(Worker Pod)로 로드하여 튜닝 작업을 수행하고, 완료된 튜닝 모델은 다시 Datastore-shared-stroage(NAS)에 저장됩니다. 이렇게 생성된 모델은 이후 NIM을 통해 손쉽게 추론 서비스로 배포할 수 있어, 학습부터 서빙까지의 흐름이 하나의 파이프라인으로 연결됩니다.

NeMo Customizer는 파인튜닝 과정 전반을 마이크로서비스 단위로 캡슐화함으로써, 반복적인 실험과 모델 버전 관리를 효율적으로 수행할 수 있도록 설계되었습니다.

튜닝 작업의 연산 요구사항이 단일 워커 노드의 컴퓨팅 자원을 초과하는 경우, NeMo Microservices는 VOLCANO 기반의 분산 스케줄링을 활용해 멀티 호스트 환경에서 작업을 실행합니다. 이를 통해 대규모 모델이나 대용량 데이터셋에 대해서도 유연한 확장이 가능하며, GPU 자원을 효율적으로 활용할 수 있습니다.

이러한 구조는 파인튜닝 워크로드를 인프라 제약으로부터 분리하여, 모델 크기나 연산 복잡도에 관계없이 안정적인 학습 환경을 제공합니다.

curl -X POST "${CUSTOMIZER_URL}/jobs" \
-H "Content-Type: application/json" \
-d '{
"name": "hcx-sft-job-02-wandb", "namespace": "hcx",
"project": "hcx-custom-tuning", "config": "hcx-sft-config-gpu@v1.1", "dataset": "hcx/ko-catpaca-dataset",
"output_model": "hcx/hcx-sft-tuned-model@v1.1", "hyperparameters": {
"finetuning_type": "lora", "training_type": "sft", "learning_rate": "1e-5", "epochs": 3
},
"wandb": {
"secretName": "wandb-api-key-secret", "apiKeyKey": "apiKey",
"projectName": "hcx-custom-tuning"
}
}'

위 코드는 /jobs 엔드포인트에 POST 요청을 보내 SFT 튜닝 Job을 생성하는 Customizer API 호출 예시입니다.

Customizer 기반 파인튜닝은 /v1/customization/jobs 엔드포인트로 튜닝 요청을 보내는 것부터 시작됩니다. 사용자는 Job 생성 요청 시 학습에 사용할 데이터셋과 결과로 저장할 모델 정보를 함께 지정하며, 예를 들어 SFT(Supervised Fine-Tuning) 방식과 LoRA 기반 파인튜닝을 선택해 효율적으로 튜닝을 수행할 수 있습니다. 이때 학습 데이터는 hcx/ko-catpaca-dataset과 같이 데이터셋 메타스토어에 등록된 리소스를 참조하고, 튜닝 완료 후 생성되는 모델은 hcx/hcx-sft-tuned-model@v1.1 형태로 저장되도록 설정할 수 있습니다.

튜닝 요청이 접수되면 Customizer는 앞 단계에서 데이터스토어에 저장된 데이터셋 파일과 베이스 모델 파일을 읽어와 튜닝 작업을 실행할 Worker Pod를 생성합니다. 특히 단일 Pod 단위가 아니라 멀티 워커 노드 기반의 분산 튜닝이 가능하도록, Kubernetes 환경에서 LWS 컨트롤러와 Volcano 커스텀 스케줄러를 활용해 Pod Group 단위로 Job을 스케줄링하고 실행합니다. 이를 통해 대규모 학습이나 여러 GPU를 활용하는 튜닝 워크로드도 안정적으로 처리할 수 있습니다.

학습이 완료되면 파인튜닝된 결과 파일은 다시 데이터스토어에 저장되며, 이후 NIM에 등록되어 바로 추론(Inference) 엔드포인트로 연결됩니다. 즉, 데이터 준비부터 튜닝 실행, 결과 저장, 서빙까지가 하나의 흐름으로 이어지는 구조입니다. 또한 운영 환경에서는 W&B(Weights & Biases)를 함께 연동해 학습 과정을 관측할 수 있는데, Customizer Job 배포 시 W&B API Key를 Kubernetes Secret으로 주입하도록 구성하면 training loss, validation loss 등 주요 지표를 실시간으로 모니터링할 수 있습니다.

Job 명세 관점에서는 모델/데이터셋이 속한 네임스페이스를 지정하고, 튜닝 결과로 생성될 target 모델을 정의한 뒤, 학습 파라미터(learning_rate, epochs 등) 와 튜닝 설정을 포함하는 config 값을 선언하여 호출하면 됩니다.

이러한 방식으로 Customizer는 선언형 API 기반의 간단한 요청만으로도 엔터프라이즈 환경에서 재현 가능하고 확장 가능한 파인튜닝 파이프라인을 구성할 수 있게 해줍니다.

튜닝한 모델이 실제로 성능 향상을 달성했는지를 객관적으로 검증하는 것은 모델 품질을 보장하는 데 있어 매우 중요한 단계입니다.

NeMo Evaluator는 이러한 역할을 수행하는 평가 전용 마이크로서비스로, 파인튜닝 이후 모델의 성능을 체계적으로 검증할 수 있는 환경을 제공합니다. NeMo Evaluator를 활용한 모델 평가는 MMLU, HumanEval과 같은 표준 벤치마크뿐만 아니라, 파인튜닝에 사용된 데이터셋의 일부를 발췌하거나 사전에 정의된 골든셋을 평가 데이터로 활용할 수 있습니다. 이러한 평가 데이터셋은 Data Store에 등록되어 반복적이고 일관된 평가에 사용됩니다.

Ingress를 통해 NeMo Evaluator의 평가 API를 호출하면 전체 평가 워크플로우가 자동으로 시작됩니다. Evaluator는 Data Store와 Entity Store로부터 평가 데이터셋과 대상 모델 정보를 로드해 평가 환경을 구성합니다. 이후 NIM을 통해 배포된 모델에 질의를 전달하고, 모델의 응답을 수집해 정답과 비교함으로써 정확도, 통과율 등의 성능 지표를 산출합니다. 모든 평가 결과는 PostgreSQL에 저장되어 이력 관리가 가능하며, 최종적으로 시각화된 리포트 형태로 제공됩니다. 이러한 평가 결과는 모델 버전 간 성능 비교, 배포 여부 판단, 추가 파인튜닝 필요성 검토 등 의사결정 과정의 핵심 근거로 활용됩니다.

W&B(Weights & Biases) 모니터링 대시보드를 통해 파인튜닝 작업이 수행되는 동안 학습 상태를 실시간으로 확인할 수 있습니다. 학습이 진행되면서 생성되는 주요 지표들이 자동으로 시각화되며, 여러 번 수행한 실험 결과를 한 화면에서 비교할 수 있도록 구성되어 있습니다.

예를 들어 train/loss 그래프를 통해 모델이 학습을 통해 손실값을 안정적으로 줄여가며 수렴하는지 확인할 수 있고, train/learning_rate에서는 스케줄러 설정에 따라 학습률이 어떻게 변화하는지 확인할 수 있습니다. 또한 train/num_input_tokens_seen은 학습 과정에서 처리된 입력 토큰의 누적량을 보여주기 때문에, 학습 진행률과 데이터 소비량을 직관적으로 파악하는데 도움이 됩니다.

이 외에도 train/grad_norm을 통해 그래디언트 폭주나 불안정한 학습 징후를 조기에 탐지할 수 있으며, train/global_step과 train/epoch 지표를 통해 학습이 정상적으로 진행되고 있는지와 전체 학습 반복 횟수를 함께 확인할 수 있습니다. 결과적으로 W&B 대시보드는 파인튜닝 실험의 품질과 안정성을 검증하고, 하이퍼파라미터 변경에 따른 효과를 빠르게 비교·분석할 수 있는 핵심 관측 도구로 활용됩니다.

NIM 기반 LLM 엔드포인트에서 제공하는 Example Request–Response Pair 화면을 통해 사용자는 모델 호출 결과를 단순 텍스트가 아니라 관측 가능한 로그형태로 확인할 수 있습니다.

각 요청은 고유한 Cache Key로 식별되어 동일한 입력에 대한 재현성과 성능 최적화에 활용됩니다

Request 영역에는 실제 API 호출에 사용되는 프롬프트와 생성 파라미터(max_tokens, temperature, top_p 등)가 포함되고 Response 영역에는 모델이 생성 한 결과 텍스트뿐 아니라 요청 ID, 토큰 사용량, 처리 시간 같은 실행 메타데이터가 함께 반환됩니다.

Inference – NVIDIA Inference Microservice (NIM)

NIM은 파인튜닝과 검증을 마친 모델을 실제 서비스 환경에 배포할 때, 최적의 추론 엔진으로 구동하여 안정적이고 빠른 응답 속도를 제공하는 모델 서빙 솔루션입니다

기존의 모델 서빙 방식에서는 엔지니어가 모델 특성에 맞는 추론 엔진(vLLM, TensorRT-LLM 등)을 직접 선택하고, 성능 벤치마킹과 튜닝을 반복하며 운영 환경에 최적화해야 했습니다. 이는 높은 전문성을 요구할 뿐만 아니라, 모델이 변경될 때마다 재작업이 필요한 구조적 한계가 있었습니다.

반면, NIM은 이 복잡한 과정을 추상화하여 배포 속도와 운영 안정성을 획기적으로 향상시킵니다. 구체적으로 어떻게 동작하는지 살펴보겠습니다.

1. 단일 엔드포인트를 통한 멀티 모델 서빙 아키텍처

NIM은 표준화된 API 인터페이스를 제공하여 클라이언트와 모델 간의 복잡한 연결을 단순화합니다.

위 아키텍처와 같이 v1/chat/completions라는 단일 API 엔드포인트를 통해 여러 모델을 동시에 서빙할 수 있습니다.

Ingress 기반 라우팅 : Kubernetes Ingress 객체를 활용해서 단일 엔드포인트로 인입되는 모델에 대한 사용자 쿼리를 모델 별 Alias 네임 기반으로 요청을 분기 처리할 수 있습니다

POST http://<ALB >/v1/chat/completions
{
"model": "model-A",
"messages": [...]
}

객체를 활용해서 단일 엔드포인트로 인입되는 모델에 대한 사용자 쿼리를 모델 별 Alias 네임 기반으로 요청을 분기 처리할 수 있습니다.

2. Kubernetes CRD(Custom Resource Definition) 기반의 간편한 배포

Ncloud Kubernetes Service 환경에서 NIM을 배포하는 것은 매우 직관적입니다. 복잡한 도커 명령어나 스크립트 대신, 쿠버네티스 네이티브 방식인 YAML 명세를 통해 인프라를 정의합니다.

kind: NIMService metadata:
name: hcx-dash-002-nim-llm namespace: nemo-microservices
spec:
replicas: 1 # : root userID: 0
groupID: 0 # (CRD )
image:
repository: nvcr.io/nim/nvidia/llm-nim tag: "1.12.0"
pullSecrets: [nvcrimagepullsecret-250812] authSecret: ngc-api
env:
-	name: NIM_MODEL_NAME
value: "/model-store/HCX-DASH-002-text-org"
-	name: NIM_SERVED_MODEL_NAME
value: "hcx/HCX-DASH-002-text-org"
-	name: HF_HUB_OFFLINE value: "1"
-	name: HF_HOME
value: "/model-store/huggingface"
-	name: NIM_CACHE_DIR value: "/model-store"
-	name: NGC_API_KEY valueFrom:
secretKeyRef: name: ngc-api key: NGC_API_KEY
storage: pvc:
name: datastore-shared-storage resources:
limits: nvidia.com/gpu: 1
expose: service:
type: ClusterIP port: 8000

위 설정 파일(NIMService)에서 볼 수 있듯이, 개발자는 모델의 경로와 리소스만 정의하면 됩니다.

개발자가 위 설정 파일과 같이 kubernetes 상에서 NIMService 라는 CRD 객체를 선언하면, NIM Proxy 에서 객체 생성을 감지(Watch)하여 모델 엔드포인트를 등록, 노출하게 됩니다.

  • 모델 마운트: NIM_MODEL_NAME 환경 변수를 통해 로컬 스토리지나 PVC(Persistent Volume Claim)에 저장된 모델 가중치(Weights) 경로를 지정합니다.
  • 보안 및 인증: NGC_API_KEY와 같은 민감한 정보는 Kubernetes Secret으로 안전하게 주입됩니다.
  • 리소스 할당: nvidia.com/gpu: 1과 같이 필요한 GPU 리소스를 명시하면 Ncloud Kubernetes Service 스케줄러가 이를 할당합니다.

3. 최적 추론 엔진 자동 선택

NIM의 가장 강력한 기능 중 하나는 실행 시점에 하드웨어와 모델을 분석하여 최적의 추론 백엔드를 자동으로 선택한다는 점입니다.

INFO 2025-08-23 16:49:23.300 ngc_injector.py:149] Valid profile: e2f00b2cbfb168f907c8d6d4d40406f7261111fbab8b3417a485dcd19d10cc98 (vllm) on GPUs [0] INFO 2025-08-23 16:49:23.300 ngc_injector.py:149] Valid profile:
668b575f1701fa70a97cfeeae998b5d70b048a9b917682291bb82b67f308f80c (tensorrt_llm) on GPUs [0] INFO 2025-08-23 16:49:23.300 ngc_injector.py:149] Valid profile: 50e138f94d85b97117e484660d13b6b54234e60c20584b1de6ed55d109ca4f21 (slang) on GPUs [0]
INFO 2025-08-23 16:49:23.300 ngc_injector.py:302] Selected profile: e2f00b2cbfb168f907c8d6d4d40406f7261111fbab8b3417a485dcd19d10cc98 (vllm)
INFO 2025-08-23 16:49:23.300 ngc_injector.py:321] Profile metadata: llm_engine: vllm

위 로그 화면은 NIM 컨테이너가 시작될 때 수행하는 Profile Selection 과정을 보여줍니다.

  • 프로파일 스캔: 현재 환경에서 구동 가능한 엔진 후보군(vLLM, TensorRT-LLM, sglang)을 탐색합니다.
  • 자동 선택: 탐지된 GPU 하드웨어(A100, H100 등)와 모델 아키텍처에 가장 적합한 엔진(위 예시에서는 vLLM)을 자동으로 Selected profile로 지정하여 로드합니다.

이로 인해 엔지니어는 엔진별 벤치마킹에 시간을 쏟을 필요 없이, NIM에 모델만 넣으면 즉시 최적화된 성능을 얻을 수 있습니다.

4. 엔터프라이즈급 운영을 위한 스케일링 전략

실제 운영 환경(Production)에서는 트래픽 변화에 유연하게 대응하고 병목 현상을 제거하는 것이 필수적입니다. Ncloud Kubernetes Service 위에서 NIM은 다음과 같은 스케일링 전략을 갖습니다.

  • Autoscaling (HPA): nemo-nim-proxy와 각 모델 파드(Pod)에는 HPA(Horizontal Pod Autoscaler)가 적용되어 있어, 트래픽 급증 시 자동으로 파드 수를 늘려 처리량을 확보합니다.
  • 지능형 스케줄링 (Volcano & LWS): GPU와 같은 고가용성 자원은 일반적인 쿠버네티스 스케줄러보다 정교한 관리가 필요합니다. Volcano 커스텀 스케줄러와 LWS(LeaderWorkerSet) 컨트롤러를 통해 분산 배포된 NIM 파드들이 최적의 노드에 배치되도록 보장합니다.
  • Observability: 단순한 모니터링을 넘어, OpenTelemetry 기반의 통합 관측 환경을 구축하여 운영 안정성을 극대화합니다.
    • Metric & Scaling (Prometheus): 사용자 서비스에 최적화된 Latency 및 Throughput 메트릭(Metric)을 수집하고, 이를 HPA와 연동하여 트래픽 변화에 따라 리소스를 자동으로 확장합니다.
    • Tracing (Jaeger): 분산 트레이싱을 통해 복잡한 추론 파이프라인 내의 병목 구간을 실시간으로 시각화하고, 장애 원인을 즉각적으로 식별합니다.

Safety & Control – NeMo Guardrails

LLM을 엔터프라이즈 환경에 도입할 때 가장 큰 우려는 “모델이 부적절한 답변을 하거나, 비즈니스 범위를 벗어난 질문에 답하는 것”입니다. NeMo Guardrails 는 LLM의 입출력 앞단에 위치하여 정교한 전처리기 및 라우터 역할을 수행하는 프로그래머블 가드레일입니다.

단순히 금칙어를 기반으로 차단하는 수준을 넘어, 사용자의 의도(Intent)를 파악하고 정의된 보안 정책과 비즈니스 로직(Colang)에 따라 LLM 호출 여부를 결정하거나 외부 도구(Tool)를 실행합니다.

1. 프로그래머블 대화 제어 아키텍처

Guardrails는 Input Rails(입력 제어)와 Output Rails(출력 제어) 사이에서 사용자의 발화를 가로채 검증합니다.

위 흐름도(Flow Chart)와 같이 동작 원리는 다음과 같습니다.

  1. 사용자 의도 분석: LLM을 이용해 사용자의 입력이 ‘일반 대화’인지, ‘특정 작업(검색, 툴)’이 필요한지 분류합니다.
  2. 실행 경로 결정:
    • 차단: 민감한 주제(정치 등)인 경우 즉시 거부 메시지를 반환합니다.
    • RAG 검색: 내부 지식이 필요한 경우 벡터 DB를 검색합니다.
    • 액션 실행: 날씨 확인 등 외부 사용자 정의 함수 실행을 통해서 그 결과를 반환합니다.
    • 그 외 : 일반 LLM text 호출 및 응답을 반환합니다.
    • 출력 레일 : 출력 전 최종 키워드 레벨로 다시 한 번 출력 컨텐츠에 대한 검증을 진행합니다.

2. Colang을 이용한 정책 정의 (Code-based Policy)

NeMo Guardrails는 사람이 읽기 쉬운 스크립트 언어인 Colang을 사용하여 대화 흐름을 정의합니다. 다음 이미지는 실제 적용 가능한 주요 시나리오들의 코드 예시입니다.

define flow input_policy_politics
    $t = $user_message
    if "정치" in $t or "정치인" in $t or "정당" in $t or "선거" in $t or "후보" in $t or "여당" in $t or "야당" in $t or "국회" in $t or "의원" in $t or "대통령" in $t
        bot block_politics_input
        stop
define flow route_or_llm
    $q = $user_message
    if not $q
        $q = $last_user_message
    if not $q
        bot "죄송합니다. 질문을 이해하지 못했습니다."
        stop

    # 정규식/검색 함수 없이 단순 포함 매칭만 사용
    if "날씨" in $q or "weather" in $q
        $wx = execute GetWeatherAction(city="서울")
        bot $wx
        stop

define flow retrieve_relevant_chunks
    $q = $user_message
    if "kubernetes" in $q or "k8s" in $q or "deployment" in $q or "파드" in $q or "컨테이너" in $q
        # 내장 retrieve_relevant_chunks 액션 사용 (임베딩 기반)
        $chunks = execute retrieve_relevant_chunks
        if $chunks
            # 1. 먼저 완전한 응답 문자열을 별도의 변수에 저장합니다.
            $rag_response = "관련 문서에서 찾은 정보: " + $chunks
            # 2. 그 다음, 변수를 bot 액션에 전달합니다.
            bot $rag_response
            stop
        else
            bot "관련 문서를 찾을 수 없습니다."
            stop

위 설정 파일들의 내용을 상세히 살펴보면 다음과 같습니다.

시나리오 A: 민감 주제 차단 (Topic Blocking) 위 이미지의 첫 번째 코드(input_policy_politics)를 보면, 사용자의 발화($user_message)에 “정치”, “선거”, “대통령” 등의 키워드가 포함될 경우, block_politics_input 봇 메시지를 출력하고 대화를 stop 시킵니다. 이 과정에서 LLM은 호출되지 않으므 로 토큰 비용이 절약됩니다.

시나리오 B: 외부 도구 연동을 통한 LLM 우회 (Tool Execution) LLM은 실시간 정보(날씨, 주가 등)를 알지 못합니다. 두 번째 코드 예시처럼 “날씨”라는 단어가 감지되면 LLM 대신 GetWeatherAction을 실행하고 그 결과($wx)를 사용자에게 바로 반환합니다. 이는 할루시네이션(거짓 답변)을 방지하고 응답 속도를 높이는 효과가 있습니다.

시나리오 C: 문맥 인지형 RAG 통합 세 번째 예시와 같이 사용자가 “쿠버네티스”, “배포” 등 사내 기술 용어를 언급할 때만 선택적으로 벡터 DB를 검색(retrieve_relevant_chunks)하여 컨텍스트에 주입하는 RAG 파이프라인을 구성할 수 있습니다.

3. 병렬 실행(Parallel Execution)을 통한 지연 시간 최적화

여러 가드레일(보안 검사, 문법 검사, 팩트 체크 등)을 순차적으로 실행할 경우 응답 지연(Latency)이 발생할 수 있습니다.

이미지 하단의 설정(config.yml)과 같이 parallel: true 옵션을 추가하면, 입력된 프롬프트에 대해 ‘주제 차단 검사’와 ‘개인정보(PII) 탐지’ 등의 흐름이 동시에 실행됩니다. 이를 통해 보안성은 유지하면서도 사용자 경험(UX)을 저해하지 않는 빠른 응답 속도를 확보할 수 있습니다.

# rails
rails:
  input:
    # parallel: true 를 추가하면 아래 flows 목록이 동시에 실행됩니다.
    parallel: true
    flows:
      - self check input      # 1. 정치 주제 검사
      - check_for_pii         # 2. 개인정보(PII) 탐지
  output:
    flows:
      - output_policy_politics
## 로그
15:40:43.273 | Event StartInputRails | {'uid': '51a5...'}
'StartInputRail', 'flow_id': '$triggered_input_rail'}}, 'action_result_key':
15:40:43.275 | Event StartInputRail | {'uid': 'f087...', 'flow_id': 
'StartInputRail', 'flow_id': '$triggered_input_rail'}}, 'action_result_key':

결론

LLM의 성능 저하는 시간이 지남에 따라 필연적으로 발생하는 운영상의 과제입니다. 이를 극복하고 모델의 가치를 지속적으로 유지하기 위해서는 신규 데이터를 활용한 주기적인 재학습과 배포를 자동화하는 LLMOps 파이프라인 구축이 선택이 아닌 필수입니다. 수많은 오픈소스를 조합하는 방식은 기술적 복잡성과 운영 부담, 그리고 보안의 허점을 드러내는 반면, NVIDIA NeMo Microservices는 데이터 처리부터 추론, 안전성 제어까지 LLMOps의 전 과정을 위한 통합된 엔터프라이즈급 컴포넌트를 제공합니다. 이러한 NeMo의 강력한 기능이 네이버클라우드의 안정적이고 확장성 높은 매니지드 인프라(Ncloud Kubernetes Service, Object Storage, Data Stream 등)와 결합될 때, 그 시너지는 극대화됩니다. 이는 HyperCLOVA X와 같은 대규모 언어 모델을 비즈니스에 도입하여 안정적이고 효율적으로 운영하고자 하는 기업에게 가장 강력하고 현실적인 최적의 선택지가 될 것입니다.

Discuss (0)

Tags