모델 서빙 인프라

환경에서 모델 서빙 솔루션을 아직 제공하지 않는 경우 모델 엔드포인트를 배포하고 구성하는 방법을 알아봅니다.

IBM Bob을 대형 언어 모델(LLM)에 연결하기 전에 배포된 모델 엔드포인트에 액세스할 수 있어야 합니다. Bob은 모델 서빙 인프라를 프로비저닝, 호스팅 또는 관리하지 않습니다. 조직에서 이미 OpenShift AI, GPU 기반 추론 서버, 클라우드 AI 서비스 또는 기타 추론 플랫폼을 통해 모델 엔드포인트를 제공하고 있다면 Model Gateway 구성으로 바로 이동하세요.

배포 옵션

사용자 환경과 운영 요구 사항에 가장 적합한 옵션을 선택하세요.

옵션사용 시기
OpenShift AI (클러스터 내부, OCI/ModelCar)에어갭 또는 자체 호스팅 모델, 클러스터에서 RHOAI를 이미 사용할 수 있는 경우
퍼블릭 클라우드AWS Bedrock, Azure OpenAI 또는 Google Vertex AI를 통한 프론티어 모델
프라이빗 인프라별도의 GPU 서버 또는 전용 추론 클러스터에서 제공되는 모델

OpenShift AI (클러스터 내부, OCI/ModelCar)

Red Hat OpenShift AI(RHOAI)는 에어갭 배포를 포함하여 클러스터 내에서 모델을 제공하는 데 권장되는 플랫폼입니다. 모델 가중치를 로드하는 데 선호되는 접근 방식은 OCI/ModelCar 패턴입니다. 모델 파일은 /models/ 아래의 OCI 이미지에 포함되어 프라이빗 레지스트리로 푸시됩니다. InferenceService가 배포되면 KServe가 modelcar-init 초기화 컨테이너를 주입하여 이미지를 풀하고 가중치를 /mnt/models/의 공유 볼륨에 복사하며, 서빙 런타임(예: vLLM)은 이 위치에서 로드합니다. 모델 이미지는 첫 번째 풀 후 노드에 캐시되므로 동일한 노드에서 이후 재시작할 때는 다운로드를 완전히 건너뜁니다.

전제 조건:

  • 클러스터에 설치된 Red Hat OpenShift AI 오퍼레이터
  • KServe 활성화 및 구성 완료
  • NVIDIA GPU Operator(또는 이에 상응하는 것)가 구성된 GPU 지원 워커 노드
  • 대상 네임스페이스에 리소스를 생성할 수 있는 권한이 있고 대상 클러스터에 인증된 oc CLI
  • 클러스터에서 액세스할 수 있고 이미지를 푸시할 수 있는 자격 증명이 있는 프라이빗 컨테이너 레지스트리

Red Hat OpenShift AI 구성

DataScienceClusterInitialization 및 DataScienceCluster 리소스를 다음과 같이 구성합니다:

  • serviceMesh를 비활성화합니다.
  • managementState: Managed로 설정하여 KServe를 활성화합니다.
  • KServe가 RawDeployment 모드를 사용하도록 구성합니다.
  • 사용하지 않는 모든 Red Hat OpenShift AI 컴포넌트를 제거합니다.

예제 KServe 구성:

kserve:
  defaultDeploymentMode: RawDeployment
  nim:
    managementState: Managed
  rawDeploymentServiceConfig: Headed
  serving:
    ingressGateway:
      certificate:
        type: OpenshiftDefaultIngress
    managementState: Removed
    name: knative-serving
  managementState: Managed

ModelCar 지원 활성화

ModelCar 지원은 redhat-ods-applications 네임스페이스의 inferenceservice-config ConfigMap에 의해 제어됩니다. storageInitializer 키에 "enableModelcar": true가 포함되어 있어야 합니다.

현재 구성을 확인합니다:

oc get configmap inferenceservice-config \
  -n redhat-ods-applications \
  -o jsonpath='{.data.storageInitializer}'

출력에 다음이 포함되어야 합니다:

{
  "enableModelcar": true,
  "cpuModelcar": "10m",
  "memoryModelcar": "15Mi"
}

enableModelcar가 누락되었거나 false인 경우 ConfigMap을 업데이트합니다:

# 현재 값을 검색하고 플래그를 병합한 후 패치 적용
CURRENT=$(oc get configmap inferenceservice-config \
  -n redhat-ods-applications \
  -o jsonpath='{.data.storageInitializer}')
PATCHED=$(echo "$CURRENT" | python3 -c "
import json, sys
d = json.load(sys.stdin)
d['enableModelcar'] = True
print(json.dumps(d))
")
oc patch configmap inferenceservice-config \
  -n redhat-ods-applications \
  --type merge \
  -p "{\"data\":{\"storageInitializer\":$(echo $PATCHED | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')}}"

KServe 컨트롤러를 재시작하여 변경 사항을 적용합니다:

oc rollout restart deployment kserve-controller-manager -n redhat-ods-applications
oc rollout status deployment kserve-controller-manager -n redhat-ods-applications

OCI 이미지로 모델 파일 패키징

Hugging Face에서 모델 가중치를 다운로드하여 OCI 이미지로 패키징합니다. 이미지에는 /models 디렉터리 아래에 모든 모델 파일이 있어야 합니다. 자세한 내용은 KServe 설명서를 참조하세요. vLLM 배포의 경우 safetensors 모델 샤드를 포함하고 .bin, .pt, original/*과 같은 레거시 체크포인트 파일은 제외합니다.

예제 Dockerfile:

FROM busybox:latest

# 모델 가중치 — safetensors 샤드 및 인덱스
COPY <model-name>/model-*.safetensors         /models/
COPY <model-name>/model.safetensors.index.json /models/

# 모델 구성
COPY <model-name>/config.json             /models/
COPY <model-name>/generation_config.json  /models/

# 토크나이저
COPY <model-name>/tokenizer.json           /models/
COPY <model-name>/tokenizer_config.json    /models/
COPY <model-name>/special_tokens_map.json  /models/

모델에 대화 템플릿(예: chat_template.jinja)이 포함되어 있는 경우 추가합니다. 또는 전체 디렉터리를 한 번의 명령으로 복사할 수도 있습니다(더 단순하지만 vLLM에 필요하지 않은 파일이 포함될 수 있음):

FROM busybox:latest
COPY <model-name>/ /models/

프라이빗 레지스트리에 이미지 푸시

클러스터에서 연결할 수 있는 프라이빗 컨테이너 레지스트리에 OCI 이미지를 푸시합니다:

<registry>/<project>/<model-name>:latest

다음을 포함하여 지원되는 모든 이미지 관리 도구를 사용할 수 있습니다:

  • Podman
  • Skopeo
  • CI/CD 파이프라인
  • 레지스트리 전용 툴링

대상 네임스페이스 및 이미지 풀 시크릿 생성

프로젝트 네임스페이스를 생성하고 모델 이미지를 가져오기 위한 자격 증명을 구성합니다:

oc new-project <namespace>

레지스트리 풀 시크릿을 생성합니다:

# KServe가 프라이빗 레지스트리에서 모델 이미지를 풀할 수 있도록 하는 풀 시크릿
oc create secret docker-registry model-registry-secret \
  --docker-server=<registry> \
  --docker-username=<username> \
  --docker-password=<password-or-token> \
  -n <namespace>

서비스 계정을 생성합니다:

# KServe 예측기에서 사용하는 서비스 계정
oc create sa model-puller-sa -n <namespace>

풀 시크릿을 서비스 계정에 연결합니다:

# 풀 시크릿 연결 — 두 명령 모두 필요합니다:
# oc secrets link는 일반 시크릿 사용을 처리합니다;
# imagePullSecrets는 KServe의 ModelCar init 컨테이너에 특별히 필요합니다
oc secrets link model-puller-sa model-registry-secret --for=pull -n <namespace>
oc patch serviceaccount model-puller-sa -n <namespace> \
  -p '{"imagePullSecrets": [{"name": "model-registry-secret"}]}'
참고:

두 명령이 모두 필요합니다. imagePullSecrets 항목은 모델 이미지 검색 중에 ModelCar 초기화 컨테이너에서 사용됩니다.

ServingRuntime 생성

대상 네임스페이스에 vLLM 기반 ServingRuntime을 배포합니다:

apiVersion: serving.kserve.io/v1alpha1
kind: ServingRuntime
metadata:
  name: vllm-runtime
  namespace: <namespace>
spec:
  multiModel: false
  supportedModelFormats:
    - name: pytorch
      autoSelect: true
  containers:
    - name: kserve-container
      image: vllm/vllm-openai:<version>
      ports:
        - containerPort: 3000
          protocol: TCP
      livenessProbe:
        httpGet:
          path: /health
          port: 3000
        periodSeconds: 30
        timeoutSeconds: 5
        failureThreshold: 3
      readinessProbe:
        httpGet:
          path: /health
          port: 3000
        periodSeconds: 10
        timeoutSeconds: 5
        failureThreshold: 3
      startupProbe:
        httpGet:
          path: /health
          port: 3000
        periodSeconds: 10
        timeoutSeconds: 5
        failureThreshold: 60

런타임 구성을 적용합니다:

oc apply -n <namespace> -f serving-runtime-vllm.yaml

InferenceService 배포

oci:// 스토리지 URI를 사용하여 OCI 모델 이미지를 참조하는 InferenceService를 생성합니다.

주요 구성 요구 사항:

  • 이전에 생성한 ServingRuntime을 참조합니다.
  • storageUri에 OCI 이미지 위치를 지정합니다.
  • 모델 요구 사항에 맞는 CPU, 메모리 및 GPU 리소스 한도를 구성합니다.
  • vLLM용 공유 메모리(/dev/shm)를 마운트합니다.
  • vLLM 런타임 인수 및 환경 변수를 구성합니다.

예제:

apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: <model-name>
  annotations:
    serving.kserve.io/autoscalerClass: external
    serving.kserve.io/deploymentMode: RawDeployment
spec:
  predictor:
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
            - matchExpressions:
                - key: kubernetes.io/arch
                  operator: In
                  values:
                    - amd64
    tolerations:
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
    volumes:
      - name: shm
        emptyDir:
          medium: Memory
          sizeLimit: 64Gi
    model:
      modelFormat:
        name: pytorch
      runtime: vllm-runtime
      storageUri: "oci://<registry>/<namespace-or-project>/<model-name>:latest"
      resources:
        requests:
          cpu: "<cpu-request>"          # 예: "8"
        limits:
          cpu: "<cpu-limit>"            # 예: "16"
          memory: <memory-limit>        # 예: 96Gi — 모델 VRAM 요구 사항에 맞춤
          nvidia.com/gpu: "<gpu-count>" # 예: "1"
      volumeMounts:
        - name: shm
          mountPath: /dev/shm
      args:
        - /mnt/models/
        - --served-model-name=<model-name>
        - --port=3000
        - --enable-auto-tool-choice
        - --tool-call-parser=openai
      env:
        - name: HOME
          value: /tmp
        - name: MAX_LOG_LEN
          value: "100"        # 로그 폭증 방지를 위해 vLLM 로그 라인 자르기
        - name: HF_HUB_CACHE
          value: /tmp
        - name: TRITON_CACHE_DIR
          value: /tmp
        - name: XDG_CACHE_HOME
          value: /tmp
        - name: HF_HOME
          value: /tmp/hf_home
        - name: NUM_GPUS
          value: "<gpu-count>" # 위의 nvidia.com/gpu 제한과 일치해야 함
        - name: CUDA_VISIBLE_DEVICES
          value: "<gpu-indices>" # 예: 단일 GPU의 경우 "0", 2개의 경우 "0,1"
        - name: VLLM_WORKER_MULTIPROC_METHOD
          value: spawn
        - name: LOGNAME
          value: vllm
        - name: USER
          value: vllm

InferenceService 매니페스트를 적용합니다:

oc apply -n <namespace> -f isvc-<model-name>.yaml

배포 확인

배포 상태, 파드 시작 및 런타임 로그를 모니터링합니다:

# InferenceService가 Ready 상태에 도달하는지 확인
oc get inferenceservice <model-name> -n <namespace> -w

# 예측기 파드 시작 감시
oc get pods -n <namespace> -w

# 예측기 로그 테일링 (Running 상태가 된 후 모델 로드에 몇 분 정도 걸릴 수 있음)
oc logs -f deployment/<model-name>-predictor -n <namespace>

InferenceService가 READY: True를 보고하면 엔드포인트를 검증합니다.

모델 등록을 확인하고 테스트 추론 요청을 전송합니다:

POD=$(oc get pods -n <namespace> \
  -l app=isvc.<model-name>-predictor \
  -o jsonpath='{.items[0].metadata.name}')

oc exec -n <namespace> "$POD" -c kserve-container -- \
  curl -fsS http://127.0.0.1:3000/v1/models

oc exec -n <namespace> "$POD" -c kserve-container -- \
  curl -fsS http://127.0.0.1:3000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "<model-name>",
    "messages": [{"role": "user", "content": "Hello"}],
    "max_tokens": 50
  }'

문제 해결

문제가능한 원인해결 방법
ErrImagePull레지스트리 자격 증명 누락 또는 유효하지 않음model-registry-secret이 존재하고 유효한 자격 증명이 있으며 oc secrets link 및 imagePullSecrets 패치를 모두 사용하여 model-puller-sa에 연결되었는지 확인합니다.
ImagePullBackOffModelCar용 이미지 풀 시크릿이 구성되지 않음서비스 계정에 imagePullSecrets 구성이 포함되어 있는지 확인합니다. oc patch serviceaccount model-puller-sa -n <namespace> -p '{"imagePullSecrets": [{"name": "model-registry-secret"}]}' 실행
예측기가 Init:0/1 상태로 유지됨모델 이미지 다운로드 진행 중oc describe pod 이벤트에서 Pulling/Pulled 진행 상황을 확인합니다. 풀 시간은 이미지 크기와 레지스트리 대역폭에 따라 달라집니다.
예측기가 무기한 Init:0/1 상태로 유지됨모델 파일을 찾을 수 없음OCI 이미지의 /models 아래에 모델 파일이 저장되어 있는지 확인합니다.
Engine core initialization failed초기 CUDA 컴파일 타임아웃캐시가 생성되는 동안 첫 번째 시작에 시간이 더 걸릴 수 있습니다. 재시작하고 다시 시도하세요.
모델 로드 실패지원되지 않는 모델 파일 형식Hugging Face safetensors 파일을 사용하고 레거시 체크포인트를 제외하세요.
OpenSSL FIPS 자체 테스트 오류컨테이너 이미지가 FIPS와 호환되지 않음FIPS 호환 vLLM 이미지를 사용하세요.
OutOfMemory / OOMKilledGPU 메모리 부족GPU 리소스를 늘리거나 컨텍스트 길이를 줄이거나 양자화된 모델을 사용하세요.
InferenceService가 Pending 상태로 유지됨클러스터 리소스 사용 불가GPU 가용성을 확인하고 사용하지 않는 워크로드에서 리소스를 해제하세요.

자세한 내용은 다음을 참조하세요:

퍼블릭 클라우드 모델 엔드포인트

IBM watsonx, AWS Bedrock, Azure OpenAI 또는 Google Vertex AI와 같은 클라우드 제공자가 모델을 호스팅할 때 이 옵션을 사용합니다.

전제 조건:

  • OpenShift 클러스터에서 클라우드 서비스 엔드포인트로의 아웃바운드 HTTPS(포트 443) 연결.
  • 유효한 API 키, IAM 자격 증명 또는 이에 상응하는 인증 자격 증명.

IBM Bob을 구성하기 전에

다음을 확인하세요:

  • 모델 배포가 프로비저닝되고 활성화되어 있습니다.
  • 인증 자격 증명이 생성되어 안전하게 저장되어 있습니다.
  • 제공자별 네트워킹 또는 액세스 요구 사항이 완료되었습니다.

엔드포인트를 사용할 수 있게 된 후 Model Gateway 구성으로 이동하세요.

프라이빗 인프라 모델 엔드포인트

다음과 같이 IBM Bob 클러스터 외부의 고객 관리 인프라에서 모델이 호스팅될 때 이 옵션을 사용합니다:

  • 전용 GPU 서버
  • 별도의 OpenShift 클러스터
  • 베어메탈 추론 서버
  • 엔터프라이즈 AI 플랫폼

전제 조건:

  • 모델 엔드포인트가 OpenAI 호환 API를 노출합니다.
  • IBM Bob 클러스터와 엔드포인트 간에 HTTPS 연결이 존재합니다.
  • 인증 자격 증명을 Kubernetes 시크릿으로 사용할 수 있습니다.

IBM Bob을 구성하기 전에

다음을 검증하세요:

  • 클러스터에서 엔드포인트로의 네트워크 연결.
  • 필요한 경우 TLS 또는 상호 TLS 구성.
  • 네트워크 정책 및 방화벽이 액세스를 허용하는지 여부.
  • 엔드포인트 인증이 올바르게 작동하는지 여부.

연결 및 인증이 확인된 후 Model Gateway 구성으로 이동하세요.

이 주제는 어떤가요?