GDC 에어 갭 적용에 관한 개방형 가중치 LLM 사용자 가이드

이 배포 가이드에서는 Google Distributed Cloud (GDC) 에어갭 환경에서 오픈소스 소프트웨어 (OSS)를 사용하여 최첨단 대규모 언어 모델 (LLM)을 실행하는 방법을 자세히 설명합니다. 특히 이 가이드에서는 Gemma, DeepSeek, Llama 모델의 최신 버전을 실행하는 데 필요한 모든 단계를 제시하는 동시에 공개적으로 제공되는 다른 LLM을 제공하는 일반적인 절차를 소개합니다. 다양한 기능, 다양한 수준의 구성, 간편한 배포를 지원하는 vLLM, Ollama와 같은 컨테이너화된 서빙 백엔드를 활용합니다.

콘텐츠 요약

이 가이드에서는 Google Distributed Cloud (GDC) 오프라인 환경에서 오픈소스 대규모 언어 모델 (LLM)의 배포 및 운영을 자세히 설명합니다.

핵심 목표 및 인프라

  • 목적: vLLM 및 Ollama와 같은 최적화된 백엔드를 사용하여 Gemma 3 (멀티모달), DeepSeek-R1, Llama (3.1/3.2)를 비롯한 최첨단 LLM을 실행하기 위한 자세한 안내입니다.
  • 최소 출시: GDC 에어 갭 소프트웨어 출시 1.15.x*.
  • 하드웨어 요구사항: 최소 NVIDIA A100 GPU 1개가 필요합니다. 모델 가중치 로드 시간을 30분에서 5분 미만으로 최적화하려면 고성능 스토리지 (500GiB 이상의 PVC)를 사용하는 것이 좋습니다.

배포 구성요소

구성요소 설명
LLM 백엔드 vLLM (PagedAttention을 사용한 고처리량 서빙) 및 Ollama (간소화된 로컬 관리 및 멀티모달 지원)
모델 Gemma, DeepSeek, Llama 제품군 등 공개적으로 제공되는 LLM
가속기 모델의 VRAM 사용량에 따라 전체 NVIDIA A100 GPU 또는 멀티 인스턴스 GPU (MIG) 슬라이스 지원
GDC 서비스 GDC 컨테이너 서비스, 부하 분산기 (ELB/ILB), Harbor Container Registry 사용

주요 운영 절차

  • 모델 관리: LLM 백엔드는 사용량에 따라 모델 로드 및 언로드를 관리합니다. vLLM은 일관된 짧은 지연 시간을 위해 모델을 메모리에 고정하는 반면 Ollama는 동적 모델 로드를 허용하지만 30분 동안 비활성 상태가 되면 모델을 자동으로 언로드합니다.
  • 맞춤설정: 사용자는 인터넷에서 원하는 LLM을 가져오고, 새 Docker 이미지를 빌드하고, 로컬 Harbor 레지스트리에 업로드하여 원하는 LLM을 가져올 수 있습니다.

기본 요건

출시

  • GDC 오프라인 버전 1.15.1 이상

구성요소

  • 충분한 리소스 (CPU, 메모리, GPU)로 생성된 사용자 클러스터
  • NVIDIA A100 GPU가 1개 이상 필요합니다.
  • Harbor 인스턴스를 사용할 수 있고 액세스할 수 있습니다.
  • 사용자 클러스터에 액세스하도록 구성된 kubectl 및 gdcloud CLI
  • Harbor에 푸시하도록 Docker 클라이언트를 설치하고 구성했습니다.
  • 필요한 IAM 권한이 부여됨 (예: 네임스페이스 관리자, 클러스터 개발자).
  • 제한된 모델을 사용하는 경우 Hugging Face 계정 및 인증이 구성되어 있어야 합니다.

필요한 용량

리소스 용량
CPU 8 vCPU
RAM 메모리 32GiB
임시 스토리지 16 Gi
GPU NVIDIA A100 GPU 1개
영구 볼륨 *500GiB 이상

이러한 하드웨어 리소스는 GPU 가속으로 LLM을 실행하는 백엔드를 제공하는 데 필요합니다. 예를 들어 a2-ultragpu-1g-gdc 머신 유형 하나면 충분한 리소스를 제공합니다.

대략적인 다이어그램

개방형 가중치 LLM 제공 다이어그램

이 다이어그램은 두 LLM 백엔드인 vLLM과 Ollama에 의해 노출된 API를 사용하여 Gemma, Llama, DeepSeek과 같은 오픈소스 모델에서 추론 응답을 가져오는 사용자를 보여줍니다. 매개변수 수십억 개로 측정되는 모델 크기에 따라 각 LLM을 실행하려면 전체 GPU 또는 GPU 슬라이스가 필요할 수 있습니다.

LLM 배포의 서비스 및 보안 경계 다이어그램

이 솔루션은 GDC 에어 갭 소프트웨어 스택에 이미 포함된 다음 서비스를 사용합니다.

  • GDC 컨테이너 서비스: 모든 프로세스를 실행하고 HPA를 사용하여 확장합니다.
  • GDC 부하 분산기: Kubernetes 서비스를 사용하여 이 솔루션의 모든 백엔드를 제공하고 고객 네트워크에서 LLM에 액세스할 수 있도록 합니다. 이를 ELB(External Load Balancer)라고 합니다. 반대로 고객은 사용자 클러스터 내부에서만 LLM에 도달할 수 있도록 선택할 수 있습니다. 이는 솔루션 내에서 추론을 요청하는 내부 에이전트만 있는 경우에 유용할 수 있습니다. 이를 ILB(Internal Load Balancer)라고 합니다.
  • GDC CI/CD: LLM 백엔드 및 모델 가중치를 저장하는 Harbor Container Registry
  • GDC Observability Stack: 백엔드 로그를 수집하고 검색하며 대시보드를 제공합니다.

보안 측면에서 LLM 배포에 대한 액세스는 Kubernetes 프로젝트 / 네임스페이스에서 포드로 실행되는 LLM 서빙 백엔드를 노출하는 부하 분산기 서비스에 액세스할 수 있는 사용자에 따라 달라집니다.

서비스 및 보안 경계 아키텍처 다이어그램

배포

기본 요건

GDC 에어갭 환경에 연결된 워크스테이션에 다음 도구를 설치합니다.

  1. docker
  2. kubectl
  3. gdcloud CLI

이 문서에는 LLM을 배포하는 데 필요한 모든 소스 코드와 구성 파일이 포함되어 있습니다.

Harbor에서 이미지를 가져오는 보안 비밀 만들기

GDC 에어 갭에서 컨테이너 워크로드의 이미지 가져오기 보안 비밀을 구성하려면 비공개 Harbor 프로젝트에 액세스할 수 있는 사용자 인증 정보가 포함된 Kubernetes docker-registry 보안 비밀을 만들어야 합니다. 그러면 이 보안 비밀이 배포 사양에서 참조됩니다.

비공개 Harbor 프로젝트의 이미지에 프로그래매틱 방식으로 액세스하려면 Harbor 로봇 계정을 사용해야 합니다.

다음 단계에 따라 이미지 가져오기 보안 비밀을 구성하세요.

Harbor 로봇 계정 만들기:

  • Harbor 인스턴스 UI로 이동합니다.
  • Harbor 프로젝트로 이동합니다.
  • '로봇 계정' 탭을 선택합니다.
  • '+ 새 로봇 계정'을 클릭합니다.
  • 이름을 지정하고 (예: oss-llm-puller) 만료 시간까지 필요한 권한 (최소 'pull' 액세스)을 부여합니다.
  • 로봇 계정 이름 (예: robot$oss-llm-puller)과 제공된 보안 토큰을 안전하게 저장합니다.

Docker를 Harbor에 인증:

Docker가 설치되어 있고 Harbor 레지스트리에 네트워크로 액세스할 수 있는 머신에서 로봇 계정 사용자 인증 정보를 사용하여 로그인합니다.

export INSTANCE_URL="your-harbor-instance-url"
# for example, harbor1-project1.org1.zone1.google.gdc.com

export ROBOT_NAME="your-robot-account-name"
# for example, robot\$oss-llm-puller (note how we escape the $ character)

export ROBOT_SECRET="your-robot-account-secret"

docker login ${INSTANCE_URL} --username ${ROBOT_NAME} --password ${ROBOT_SECRET}

# Sample output:

WARNING! Using --password via the CLI is insecure. Use --password-stdin.

WARNING! Your credentials are stored unencrypted in '$HOME/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded

샘플 출력에 표시된 것처럼 이 명령어는 인증 세부정보로 로컬 Docker 구성 파일 ($HOME/.docker/config.json)을 업데이트합니다. config.json 파일은 다음과 같습니다.

{
    "auths": {
        "harbor1-project1.org1.zone1.google.gdc.com": {
            "auth": "...omitted..."
        }
    }
}

이전에 Docker 클라이언트를 사용하여 다른 레지스트리에 로그인한 경우 'auths' 내에 항목이 더 많이 표시됩니다.

Kubernetes 이미지 가져오기 보안 비밀을 만듭니다.

kubectl를 사용하여 이전 단계에서 업데이트한 Docker 구성 파일을 사용하여 프로젝트 네임스페이스에 docker-registry 유형의 보안 비밀을 만듭니다.

# Log in to GDC air-gapped using the next commands

gdcloud auth login --login-config-cert {path to your web TLS certificate}

gdcloud clusters get-credentials {Your User Cluster}
kubectl config set-context --current --namespace=NAMESPACE

export SECRET_NAME="oss-llm-pull-secret" # You can choose another name
export NAMESPACE="your-project-namespace"
# Assuming default Docker config path. Adjust if necessary.
export DOCKER_CONFIG_PATH="$HOME/.docker/config.json"

kubectl create secret docker-registry ${SECRET_NAME} \
      --from-file=.dockerconfigjson=${DOCKER_CONFIG_PATH} \
      -n ${NAMESPACE}

imagePullSecrets 필드를 포함하여 나중에 LLM 백엔드 컨테이너 사양에서 이 보안 비밀을 참조합니다.

이 단계를 따르면 Kubernetes 클러스터가 제공된 사용자 인증 정보를 사용하여 GDC 오프라인 환경의 비공개 Harbor 레지스트리에서 컨테이너 이미지를 가져옵니다. 이 단계는 제품 문서에도 포함되어 있습니다.

LLM 배포 설정

LLM 백엔드에는 다음 두 가지 옵션이 있습니다.

  • vLLM
  • Ollama

둘 중 하나를 선택하거나 두 백엔드를 모두 배포할 수 있습니다. 각 백엔드는 하나 이상의 LLM을 실행할 수 있습니다. 예를 들면 다음과 같습니다.

  • Gemma 3는
  • DeepSeek
  • Llama
  • 기타 LLM

섹션 1에서는 vLLM을 배포하는 방법을 설명하고 섹션 2에서는 Ollama를 설치하는 방법을 보여줍니다.

1. vLLM을 LLM 백엔드로 배포

vLLM Docker 이미지 가져오기

일반적으로 vLLM은 HuggingFace에서 다운로드한 모델을 제공하는 데 사용됩니다. 이 LLM 배포에서 이를 보여드리겠습니다. 일부 모델에는 게이트가 적용되어 있습니다. 즉, 사용자가 Hugging Face Hub에서 파일과 콘텐츠에 대한 액세스를 명시적으로 요청하거나 동의해야 다운로드하거나 사용할 수 있습니다.

문서에 설명된 대로 Docker를 사용하여 Harbor 인스턴스에 로그인합니다. 인터넷 액세스 권한이 있는 머신에서 vLLM Docker 이미지를 가져온 다음 Harbor 프로젝트로 전송합니다.

# Pull and Tag vLLM (v0.13.0 recommended for stability)
docker pull vllm/vllm-openai:v0.13.0
docker tag vllm/vllm-openai:v0.13.0 HARBOR_URL/PROJECT/vllm-openai:v0.13.0
docker push HARBOR_URL/PROJECT/vllm-openai:v0.13.0

다음을 바꿉니다.

  • HARBOR_URL: Harbor 인스턴스 URL입니다.
  • PROJECT: Harbor 프로젝트 이름입니다.

이 배포 가이드에 사용된 vLLM 버전은 v0.13.0입니다.

PVC에 LLM 모델 가중치 저장

새 터미널을 열고 vLLM 배포 파일을 저장할 폴더를 만듭니다.

mkdir vllm-deployment
cd vllm-deployment

그런 다음 표준 Kubernetes 기본 요소를 사용하여 Persistent Volume Claim (PVC)을 선언하는 YAML 파일 (예: model-pvc.yaml)을 만듭니다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: model-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 500Gi
  storageClassName: standard-rwo
  volumeMode: Filesystem

GDC 에어 갭 클러스터에 연결하고 PVC를 적용합니다.

# Log in to GDC air-gapped using the next commands

gdcloud auth login --login-config-cert {path to your web TLS cert}

gdcloud clusters get-credentials {Your User Cluster}
kubectl config set-context --current --namespace=NAMESPACE

# Apply the PVC manifest
kubectl apply -f model-pvc.yaml

Hugging Face에서 모델 가중치를 다운로드합니다.

# Log in to Hugging Face
hf auth login

# Download weights for Gemma 3 (for example, 4B instruction-tuned)
hf download google/gemma-3-4b-it

샘플 출력:

Fetching 9 files: 100%| 9/9 [00:00<00:00, 19.34it/s]
Downloading (…)7f58a/.gitattributes: 100%| 1.62k/1.62k [00:00<00:00, 7.82MB/s]
Downloading (…)del-00002-of-00002.safetensors: 100%| 3.75G/3.75G [00:46<00:00, 81.3MB/s]
Downloading (…)del-00001-of-00002.safetensors: 100%| 4.90G/4.90G [00:54<00:00, 90.1MB/s]
Downloading (…)-3-4b-it/README.md: 100%| 23.3k/23.3k [00:00<00:00, 48.0MB/s]
Downloading (…)8a/model.safetensors.index.json: 100%| 26.3k/26.3k [00:00<00:00, 39.5MB/s]
Downloading (…)f58a/config.json: 100%| 908/908 [00:00<00:00, 4.41MB/s]
Downloading (…)generation_config.json: 100%| 210/210 [00:00<00:00, 936kB/s]
Downloading (…)tokenizer.json: 100%| 33.1M/33.1M [00:01<00:00, 17.6MB/s]
/Users/rashjab/.cache/huggingface/hub/models--google--gemma-3-4b-it/snapshots/952ec8ffca3adadfd00c6d71b40280ebdbb7f58a

샘플 출력에서 볼 수 있듯이 디스크의 모델 크기는 8.64GB로, 이전에 정의한 500GiB PVC에 적합합니다. 영구 볼륨 (PV)에 로드할 모델의 경우 항상 이 점을 확인하세요. 예를 들어 15GB 모델의 경우 500GiB PVC를 사용하여 1,500 IOPS를 달성하면 가중치 로드 시간이 약 30분에서 5분 미만으로 크게 줄어듭니다.

모델 파일을 볼륨에 복사하여 PV를 채웁니다. 여기에는 다음이 포함되는 경우가 많습니다.

  • PV를 마운트하는 임시 '헬퍼' 포드를 만듭니다.
  • kubectl cp를 사용하여 워크스테이션에서 헬퍼 포드의 마운트된 볼륨으로 모델 파일을 복사합니다.
  • 도우미 광고 모음의 예:

helper-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: model-uploader
spec:
  containers:
  - name: uploader
    image: HARBOR_URL/PROJECT/busybox:latest
    command: ["sleep", "3600"]
    volumeMounts:
    - name: model-data
      mountPath: /data
  imagePullSecrets:
  - name: oss-llm-pull-secret
  volumes:
  - name: model-data
    persistentVolumeClaim:
      claimName: model-pvc

다음을 바꿉니다.

  • HARBOR_URL: Harbor 인스턴스 URL입니다.
  • PROJECT: Harbor 프로젝트 이름입니다.
  • oss-llm-pull-secret: 다른 이미지 가져오기 보안 비밀 이름을 선택한 경우
  • model-pvc: PVC에 지정한 이름으로 바꿉니다.

도우미 Docker 이미지 (busybox)를 푸시합니다.

docker pull busybox:latest
docker tag busybox HARBOR_URL/PROJECT/busybox:latest
docker push HARBOR_URL/PROJECT/busybox:latest

다음을 바꿉니다.

  • HARBOR_URL: Harbor 인스턴스 URL입니다.
  • PROJECT: Harbor 프로젝트 이름입니다.

helper-pod.yaml 파일을 적용하고 모델 가중치를 PV에 복사합니다.

kubectl apply -f helper-pod.yaml

# Wait for the pod to be Running
kubectl cp ~/.cache/huggingface/hub/ NAMESPACE/model-uploader:/data/

# After copying, delete the helper pod
kubectl delete pod model-uploader

vLLM 백엔드 배포

모델 서버를 실행할 vLLM의 배포 파일을 만듭니다. 다음 예시에서는 gemma-3-4b-it 모델을 배포합니다.

vllm-gemma-3-4b-it-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gemma-3-4b-it
  labels:
    app: gemma-3-4b-it
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gemma-3-4b-it
  template:
    metadata:
      labels:
        app: gemma-3-4b-it
    spec:
      volumes:
      - name: cache-volume
        persistentVolumeClaim:
          claimName: model-pvc # change with the name you gave initially to your PVC
      # vLLM needs to access the host's shared memory for tensor parallel inference.
      - name: shm
        emptyDir:
          medium: Memory
          sizeLimit: "16Gi"
      containers:
      - name: gemma-3-4b-it
        image: HARBOR_URL/PROJECT/vllm-openai:v0.13.0
        command: ["python3"]
        args: [
          "-m",
          "vllm.entrypoints.openai.api_server",
          "--model",
          "google/gemma-3-4b-it", # Use the repo ID
          "--max-model-len",
          "32768",
          "--enforce-eager"
        ]
        env:
        - name: HF_HUB_OFFLINE
          value: "1"
        - name: HF_HOME
          value: "/model" # Tells vLLM to look for models in /model/hub
        # --- PERFORMANCE & STABILITY OPTIMIZATIONS ---
        - name: NCCL_P2P_DISABLE
          value: "1" # Prevents initialization hangs on P2P checks
        - name: NCCL_IB_DISABLE
          value: "1" # Prevents initialization hangs on InfiniBand checks
        # --- FIPS BYPASS ---
        - name: BORINGSSL_FIPS
          value: "0"
        - name: OPENSSL_FIPS
          value: "0"
        - name: OPENSSL_CONF
          value: "/dev/null"
        - name: FIPS_SIG
          value: "off"
        ports:
        - containerPort: 8000
        securityContext:
          privileged: true
          runAsUser: 0
        resources:
          limits:
            nvidia.com/gpu-pod-NVIDIA_A100_80GB_PCIE: 1
            cpu: "8"       # Increased to handle model weight verification
            memory: "64Gi"  # Increased to ensure headroom for weight loading
          requests:
            nvidia.com/gpu-pod-NVIDIA_A100_80GB_PCIE: 1
            cpu: "8"
            memory: "32Gi"
        volumeMounts:
        - name: cache-volume
          mountPath: /model
        - name: shm
          mountPath: /dev/shm
      imagePullSecrets:
      - name: oss-llm-pull-secret

다음을 바꿉니다.

  • HARBOR_URL: Harbor 인스턴스 URL입니다.
  • PROJECT: Harbor 프로젝트 이름입니다.
  • oss-llm-pull-secret: 이미지 가져오기 보안 비밀을 만들 때 지정한 이름입니다.
  • claimName: 초기 PVC 생성에 지정한 이름입니다.

다음으로 vLLM 백엔드 API를 노출하는 Kubernetes 서비스 파일을 만듭니다.

vllm-gemma-3-4b-it-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: gemma-3-4b-it
  namespace: NAMESPACE
spec:
  ports:
  - name: http-gemma-3-4b-it
    port: 80
    protocol: TCP
    targetPort: 8000
  selector:
    app: gemma-3-4b-it
  sessionAffinity: None
  type: LoadBalancer

배포 및 서비스 구성을 적용합니다.

kubectl apply -f vllm-gemma-3-4b-it-deployment.yaml
kubectl apply -f vllm-gemma-3-4b-it-service.yaml

vLLM 배포 확인 및 테스트

  1. 포드 상태를 확인합니다.

    kubectl get pods
    

    포드가 Running 상태가 될 때까지 기다립니다.

  2. 서비스를 확인합니다.

    kubectl get service
    

    샘플 출력:

    # NAME          TYPE         CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
    # gemma-3-4b-it LoadBalancer 10.201.136.62  136.125.37.198   80:30109/TCP   3d1h
    

    이전 출력에서 vLLM 서비스의 EXTERNAL-IP (예: 136.125.37.198)를 기록해 둡니다. 다음과 같이 인쇄할 수도 있습니다.

    kubectl get service gemma-3-4b-it \
    -o jsonpath='{.status.loadBalancer.ingress[*].ip}'
    
  3. 로그를 봅니다.

    kubectl logs YOUR_VLLM_POD
    

    서버가 시작되고 모델이 로드되었음을 나타내는 메시지를 찾습니다(예: INFO: Application startup complete.). Hugging Face 다운로드가 없으므로 영구 볼륨 용량이 큰 PVC에 파일이 있는 경우 빠르게 진행됩니다.

  4. curl로 추론을 테스트합니다.

    vLLM 포드가 실행 중이고 서비스의 외부 IP 주소가 있는지 확인한 후 curl를 사용하여 추론 요청을 보낼 수 있습니다. vLLM은 OpenAI 호환 API를 제공하므로 /v1/chat/completions 엔드포인트를 사용할 수 있습니다.

    curl http://EXTERNAL_IP/v1/chat/completions \
      -H "Content-Type: application/json" \
      -d '{
        "model": "google/gemma-3-4b-it",
        "messages": [
          {"role": "user", "content": "What is Google Distributed Cloud air-gapped?"}
        ],
        "max_tokens": 100
      }'
    

    EXTERNAL_IP를 vLLM 서비스의 외부 IP 주소로 바꿉니다.

2. Ollama를 LLM 백엔드로 배포

Ollama는 오픈소스 LLM을 로컬 및 컨테이너화된 환경에서 실행하기 위한 인기 있는 프레임워크입니다. 가중치 다운로드, 양자화, GPU 가속을 처리하여 모델 실행을 간소화합니다. 여기에는 자동 완성, 채팅 응답, 임베딩을 생성하기 위한 기본 제공 REST API가 포함되어 있습니다.

다른 서빙 엔진과 달리 Ollama는 모델 실행을 동적으로 관리합니다.

  • 동적 모델 로드: 특정 모델에 대한 요청이 수신되면 Ollama는 필요에 따라 가중치를 메모리 (GPU VRAM 또는 시스템 RAM)에 로드합니다.
  • 비활성 언로드: 30분 동안 모델에 대한 추론 요청이 수신되지 않으면 (OLLAMA_KEEP_ALIVE로 구성 가능) Ollama는 다른 워크로드의 하드웨어 리소스를 확보하기 위해 메모리에서 모델을 언로드합니다.
  • 인스턴스당 활성 모델 하나: Ollama는 서버 인스턴스당 한 번에 하나의 활성 모델에 대한 요청을 처리합니다. 여러 모델이 병렬로 호출되면 Ollama는 요청을 대기열에 추가하고 모델을 순차적으로 전환하므로 지연 시간이 발생할 수 있습니다. 교체 없이 여러 모델을 동시에 제공하려면 각 모델에 대해 별도의 Ollama 인스턴스를 배포해야 합니다.

모델 가중치 다운로드 및 Docker 이미지 만들기

영구 볼륨에서 직접 가중치를 로드할 수 있는 vLLM과 달리 Ollama는 일반적으로 모델이 내부 디렉터리 형식(~/.ollama/models)으로 저장되어야 합니다.

런타임 컨테이너가 공개 인터넷에서 모델을 다운로드할 수 없는 오프라인 환경에서는 빌드 단계에서 모델 가중치를 컨테이너 이미지 자체에 사전 패키징해야 합니다. 이 접근 방식을 사용하면 컨테이너가 완전히 독립적이며 외부 네트워크 액세스 없이 배포 시 즉시 제공될 수 있습니다.

Ollama 배포용 폴더를 만듭니다.

mkdir ollama-gemma3
cd ollama-gemma3

다음 콘텐츠로 Dockerfile을 만듭니다.

# Use a standard Linux base image
FROM ubuntu

# Install necessary dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    ca-certificates \
    zstd \
    && rm -rf /var/lib/apt/lists/*

# Install Ollama
# This uses Ollama's official installation script, which adds Ollama to /usr/local/bin
RUN curl -fsSL https://ollama.com/install.sh -o install.sh && \
    chmod +x install.sh && \
    ./install.sh && \
    rm install.sh

# Set environment variables for Ollama (optional, but a good practice)
# ENV OLLAMA_HOST="0.0.0.0"

# If you want to customize the model storage path within the container, set OLLAMA_MODELS, for example:
# ENV OLLAMA_MODELS="/usr/local/ollama/models"
# The default OLLAMA_MODELS is /root/.ollama
# Then, ensure you create and populate that directory.

# --- Download Gemma 3 model weights ---
# This step starts Ollama server in the background, pulls the model,
# and then kills the server to allow the Docker build to continue.
# This approach works around the Docker RUN command limitations for services.

RUN ollama serve & \
    # Give the Ollama server a moment to start up
    # Use --retry and --retry-connrefused to handle startup delays
    curl --retry 10 --retry-connrefused -s http://localhost:11434 || true && \
    # Pull the Gemma 3 model weights
    ollama pull gemma3:latest && \
    # Stop the background Ollama server process cleanly
    pkill ollama || true

# Expose Ollama's default port
EXPOSE 11434

# Command to run Ollama server when the container starts
CMD ["ollama", "serve"]

Llama 3.2 또는 DeepSeek-R1 계열의 모델과 같은 다른 오픈소스 LLM을 사용하려면 이전 Dockerfile에서 이 Ollama 명령어를 수정하세요.

ollama pull gemma3:latest

예를 들어 파라미터가 30억 개인 Llama 3.2를 가져오려면 다음 명령어를 사용합니다.

ollama pull llama3.2:3b

또는 80억 개의 파라미터가 있는 DeepSeek-R1을 사용하려면 다음 명령어를 사용하세요.

ollama pull deepseek-r1:8b

둘 이상의 LLM을 다운로드하고 모델 가중치를 컨테이너화할 수도 있습니다. 그러면 다양한 사전 로드된 모델로 추론을 요청할 때 Ollama가 모델 간에 전환할 수 있습니다. 예를 들어 Docker 이미지에 Gemma 모델과 Llama 모델을 모두 추가할 수 있습니다. 에어갭 환경에서는 Ollama 백엔드가 언제든지 모델을 다운로드할 수 있는 Ollama 라이브러리에 액세스할 수 없습니다.

애플리케이션에서 모델을 실행하기 전에 각 모델 라이선스 및 이용약관을 확인하고 동의해야 합니다. Ollama 라이브러리에는 다양한 LLM이 있습니다. LLM 백엔드 Docker 이미지를 빌드하는 동안 모델 가중치를 다운로드할 때만 인터넷 연결이 필요합니다.

Docker 이미지 빌드 및 Harbor에 푸시

문서에 설명된 대로 Docker를 사용하여 Harbor 인스턴스에 로그인합니다. Gemma 3 Docker 이미지를 빌드하고 Harbor 저장소에 업로드합니다.

docker build -t ollama-gemma3 .
docker tag ollama-gemma3 HARBOR_URL/PROJECT/ollama-gemma3:latest
docker push HARBOR_URL/PROJECT/ollama-gemma3:latest

다음을 바꿉니다.

  • HARBOR_URL: Harbor 인스턴스 URL입니다.
  • PROJECT: Harbor 프로젝트 이름입니다.

이 배포 가이드에 사용된 Ollama 버전: 0.14.1

Kubernetes 배포 및 서비스 준비

ollama-gemma3.yaml이라는 파일을 만들어 Ollama 백엔드 배포와 서비스로 노출하기 위한 부하 분산기 구성을 정의합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ollama-gemma3
  namespace: osd-dev
  labels:
    app: ollama-gemma3
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ollama-gemma3
  template:
    metadata:
      labels:
        app: ollama-gemma3
    spec:
      containers:
      - name: ollama-gemma3
        image: rashjab-mhs-rashjab-test2.gdc1.us-west6-a.staging.gpcdemolabs.com/rashjab-repo/ollama-gemma3:latest
        env:
        # This is the crucial fix: Tell Ollama to listen on all network interfaces
        - name: OLLAMA_HOST
          value: "0.0.0.0"
        imagePullPolicy: Always
        ports:
        - containerPort: 11434
        securityContext:
          privileged: true
          runAsUser: 0
        resources:
          limits:
            nvidia.com/gpu-pod-NVIDIA_A100_80GB_PCIE: 1
          requests:
            nvidia.com/gpu-pod-NVIDIA_A100_80GB_PCIE: 1
      imagePullSecrets:
      - name: oss-llm-pull-secret
---
apiVersion: v1
kind: Service
metadata:
  name: ollama-gemma3
  namespace: osd-dev
spec:
  type: LoadBalancer
  selector:
    app: ollama-gemma3
  ports:
  - name: ollama-gemma3-port
    port: 11434
    protocol: TCP
    targetPort: 11434

다음을 바꿉니다.

  • HARBOR_URL: Harbor 인스턴스 URL입니다.
  • PROJECT: Harbor 프로젝트 이름입니다.
  • oss-llm-pull-secret: 다른 이미지 가져오기 보안 비밀 이름을 선택한 경우

GPU 할당 확인

GDC 에어 갭은 NVIDIA 멀티 인스턴스 GPU(MIG)를 지원하며 다양한 MIG 프로필은 공개 문서에서 확인할 수 있습니다. 소규모 LLM을 배포하는 경우 메모리에서 크기를 확인하고 그에 따라 GPU를 파티셔닝하는 것이 좋습니다. 노드 풀의 파티셔닝 스킴은 클러스터 커스텀 리소스에 정의되어 있습니다. GPU 파티셔닝 스키마를 적용하는 방법에 관한 자세한 내용은 노드 풀 추가를 참고하세요. 애플리케이션 운영자 (AO)인 경우 프로젝트 / 클러스터에 설치되어 사용 가능한 액셀러레이터에 관해 플랫폼 관리자 (PA)에게 문의하세요. GPU 리소스를 사용하도록 컨테이너를 구성하는 방법과 GPU 리소스 할당을 확인하는 방법에 대한 자세한 내용을 확인하세요.

Gemma 3 4B 모델의 기본 정밀도는 16비트입니다. Google에서 게시한 이 표에 따르면 이 LLM의 GPU 메모리 요구사항은 BF16 (16비트) 정밀도를 사용하여 6.4GB입니다.

이전 ollama-gemma3.yaml에서는 10GB GPU 슬라이스 (NVIDIA A100)를 컨테이너에 연결했습니다. 이 정도의 VRAM 메모리면 Gemma 3 4B 모델을 로드하고 실행하기에 충분하기 때문입니다.

ollama-gemma3.yaml

... omitted ...

        resources:
          limits:
            nvidia.com/mig-1g.10gb-NVIDIA_A100_80GB_PCIE: 1
          requests:
            nvidia.com/mig-1g.10gb-NVIDIA_A100_80GB_PCIE: 1

... omitted ...

Ollama 백엔드 배포 및 서비스로 노출

클러스터에 연결하고 컨텍스트를 프로젝트 네임스페이스로 설정합니다.

# Log in to GDC air-gapped using the next commands

gdcloud auth login --login-config-cert {path to your web TLS certificate}

gdcloud clusters get-credentials {Your User Cluster}
kubectl config set-context --current --namespace=NAMESPACE

Ollama 매니페스트를 적용합니다.

kubectl apply -f ollama-gemma3.yaml

Ollama 포드가 실행 중이고 연결된 서비스가 준비되어 있는지 확인합니다.

kubectl get pods

샘플 출력:

# NAME                             READY   STATUS    RESTARTS   AGE
# ollama-gemma3-6fc6fff74b-9qtpf   1/1     Running   0          1h
kubectl get service

샘플 출력:

# NAME             TYPE          CLUSTER-IP    EXTERNAL-IP    PORT(S)          AGE
# ollama-gemma3   LoadBalancer  172.0.0.1     10.0.0.1       11434:31822/TCP  1d

이전 출력에서 Ollama 서비스의 EXTERNAL-IP (예: 10.0.0.1)를 기록해 둡니다. 다음과 같이 인쇄할 수도 있습니다.

kubectl get service ollama-gemma3 \
-o jsonpath='{.status.loadBalancer.ingress[*].ip}'

Ollama Gemma 3가 작동하는지 확인하기 전에 다음과 같은 네트워크 정책을 적용해야 합니다.

apiVersion: networking.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
  name: allow-ollama-ingress
  namespace: osd-dev
spec:
  subject:
    subjectType: UserWorkload
  policyType: Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 0.0.0.0/0 # Allows traffic from any IP. Restrict this for production.
    ports:
    - protocol: TCP
      port: 11434

네트워크 정책을 적용합니다.

kubectl apply -f ollama-netpol.yaml

Ollama 배포를 확인하고 테스트합니다.

  1. Ollama가 실행 중인지 확인합니다.

    curl http://EXTERNAL_IP:11434
    

    예상 응답: 'Ollama가 실행 중입니다.'

  2. 완성 요청을 전송합니다.

    curl -X POST http://EXTERNAL_IP:11434/v1/completions \
      -H "Content-Type: application/json" \
      -d '{
        "model": "gemma3:latest",
        "prompt": "Google Distributed Cloud air-gapped is a",
        "max_tokens": 128,
        "temperature": 0.90
      }'
    

추론 요청

이제 LLM 백엔드 (vLLM 및 Ollama)가 배포되었으며 각 LoadBalancer 서비스를 통해 연결할 수 있으므로 추론에 사용할 수 있습니다.

사용 사례에 따라 다음과 같은 여러 옵션이 있습니다.

  • 대화형 웹 UI: 테스트, 프로토타입 제작 또는 사용자에게 ChatGPT와 유사한 환경을 제공하려면 Open WebUI 또는 유사한 프런트엔드를 배포하면 됩니다.
  • 명령줄 인터페이스 (CLI): 빠른 확인, 스크립팅 또는 자동화의 경우 curl 또는 맞춤 스크립트를 사용할 수 있습니다.
  • 애플리케이션 통합: 내부 애플리케이션, AI 에이전트 또는 IDE 플러그인 (예: VS Code/JetBrains용 Continue)을 vLLM (/v1) 또는 Ollama (/v1)에서 제공하는 OpenAI 호환 엔드포인트에 직접 연결합니다.

1. UI를 사용하여 LLM과 상호작용

모델과 상호작용할 수 있는 ChatGPT와 유사한 인터페이스를 제공하려면 클러스터에 Open WebUI를 배포하세요.

Open WebUI 배포 만들기

open-webui-deployment.yaml 만들기:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: open-webui
  namespace: osd-dev
  labels:
    app: open-webui
spec:
  replicas: 1
  selector:
    matchLabels:
      app: open-webui
  template:
    metadata:
      labels:
        app: open-webui
    spec:
      containers:
      - name: open-webui
        image: HARBOR_URL/PROJECT/open-webui:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        env:
        - name: OPENAI_API_BASE_URL
          value: "http://gemma-3-4b-it/v1" # Point to your vLLM service
        - name: OPENAI_API_KEY
          value: "EMPTY"
        - name: WEBUI_AUTH
          value: "False"
      imagePullSecrets:
      - name: oss-llm-pull-secret
---
apiVersion: v1
kind: Service
metadata:
  name: open-webui
  namespace: osd-dev
spec:
  type: LoadBalancer
  selector:
    app: open-webui
  ports:
  - port: 80
    targetPort: 8080

네트워크 정책 적용

머신이 포트 80에서 Open WebUI 서비스에 도달할 수 있도록 허용해야 합니다.

open-webui 서비스를 포함하도록 PNP 프로젝트 네트워크 정책을 업데이트하거나 광범위한 정책을 적용합니다.

apiVersion: networking.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
  name: allow-webui-ingress
  namespace: NAMESPACE
spec:
  subject:
    subjectType: UserWorkload
  policyType: Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 0.0.0.0/0 # Allows traffic from any IP then restrict this for production
    ports:
    - protocol: TCP
      port: 8080

확인

  1. WebUI IP를 가져옵니다.

    kubectl get service open-webui -n NAMESPACE
    
  2. UI 액세스: 브라우저를 열고 http://WEB_UI_EXTERNAL_IP로 이동합니다.

  3. 구성: 처음 로그인할 때 관리자 계정을 만듭니다. 설정에서 OpenAI가 http://gemma-3-4b-it/v1를 가리키는지 확인합니다.

  4. 모델 선택: 채팅 인터페이스의 모델 드롭다운에서 google/gemma-3-4b-it을 선택합니다.

    WebUI 모델 선택 드롭다운을 엽니다.

2. CLI를 사용하여 HTTP 요청하기

  • curl:
curl -X POST http://OLLAMA_LOAD_BALANCER_IP:11434/v1/completions \
-H "Content-Type: application/json" \
-d '{
  "model": "gemma3:latest",
  "prompt": "Google Distributed Cloud air-gapped is a",
  "max_tokens": 128,
  "temperature": 0.90
}'

검증 및 확인

모든 컨테이너와 서비스가 실행 중인지 확인합니다.

# Log in to GDC air-gapped using the next commands

gdcloud auth login --login-config-cert {path to your web TLS cert}
gdcloud clusters get-credentials {Your User Cluster}
kubectl config set-context --current --namespace=NAMESPACE

# Pods

kubectl get pods # This command should return results like below

# NAME                                   READY   STATUS    RESTARTS   AGE
# vllm-6fc6fff74b-9qtpf                 1/1     Running   0          1h
# ollama-798fbf6ff6-9m7ln               1/1     Running   0          1h


# Services

kubectl get services # This command should return results like below

# NAME                      TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)           AGE
# vllm-service              LoadBalancer   172.0.0.1        10.0.0.1       11434:31822/TCP   1h
# ollama-service        LoadBalancer   172.0.0.2        10.0.0.2       11434:30728/TCP   1h

삭제 단계

  1. 모든 Kubernetes 리소스를 삭제합니다.

    kubectl delete -f vllm-gemma-3-4b-it-deployment.yaml
    kubectl delete -f vllm-gemma-3-4b-it-service.yaml
    
  2. Harbor 저장소에서 사용하지 않는 컨테이너를 수동으로 삭제합니다.

작업

vLLM 백엔드

vLLM 기본사항

vLLM은 고성능 단일 모델 서빙 엔진으로 설계되었습니다. 단일 서버 프로세스 내에서 여러 모델을 제공하거나 단일 시작 명령어로 여러 모델을 정의하는 것은 지원하지 않습니다. 두 개 이상의 모델 (예: 채팅 모델과 별도의 임베딩 모델)을 제공하려면 각 모델이 자체 포드 또는 컨테이너에서 별도의 vLLM 인스턴스를 실행해야 합니다. 필요에 따라 다른 GPU 또는 GPU 슬라이스에 할당할 수 있습니다. 모델을 동적으로 교체할 수 있는 다른 백엔드와 달리 vLLM 인스턴스는 초기화 시 모델과 연결된 KV 캐시 메모리를 고정하여 일관된 높은 처리량과 짧은 지연 시간을 보장합니다.

다음 명령어를 사용하여 vLLM 포드에서 터미널을 열 수 있습니다.

kubectl exec -it gemma-3-4b-random-string -n NAMESPACE -- /bin/bash

이렇게 하면 로드된 모델, 인수, PID가 표시됩니다.

ps aux | grep vllm

예상되는 유사한 출력:

root           1  0.1  0.8 9436960 1550504 ?     Ssl  Feb04   9:18 python3 -m vllm.entrypoints.openai.api_server --model google/gemma-3-4b-it --max-model-len 32768 --enforce-eager
root         504  0.0  0.0   3476  1596 pts/0    S+   02:48   0:00 grep --color=auto vllm

grep 명령어 자체와 시스템 PID의 혼란 없이 더 깔끔하고 'Ollama와 유사한' 뷰를 원하는 경우 다음 한 줄을 사용하면 됩니다.

ps aux | grep [v]llm.entrypoints | awk -F'--model ' '{print $2}' | awk '{print $1}'

샘플 응답 :

google/gemma-3-4b-it

표준 kubectl 명령어를 사용하고 내부 API 엔드포인트를 쿼리하여 vLLM 배포 상태를 모니터링할 수 있습니다.

포드 로그 확인

터미널을 열어 초기화 및 제공 로그를 확인합니다.

kubectl logs -f gemma-3-4b-it-random_string -n NAMESPACE

서버가 준비되었다는 확인 메시지를 찾습니다.

(APIServer pid=1) INFO:     Started server process [1]
(APIServer pid=1) INFO:     Waiting for application startup.
(APIServer pid=1) INFO:     Application startup complete.

Query Internal Status

vLLM은 OpenAI 호환 API를 제공하므로 클러스터 내부의 터미널에서 직접 로드된 모델과 시스템 상태를 확인할 수 있습니다 (예: debug-sh 포드 사용).

먼저 디버그 포드를 실행해야 합니다. 다음은 안내입니다.

디버그 포드 실행

다음 명령어를 실행하여 프로젝트 네임스페이스에서 임시 셸을 시작합니다. 이 구성은 busybox 이미지 (이전에 Harbor 레지스트리에 푸시됨)를 사용하고 --overrides 플래그를 사용하여 필요한 사용자 인증 정보를 삽입합니다.

kubectl run --rm -it debug-sh \
  --image=HARBOR_URL/PROJECT/busybox:latest \
  --restart=Never -n osd-dev --overrides='{
    "spec": {
      "imagePullSecrets": [{"name": "oss-llm-pull-secret"}]
    }
  }' -- sh

주요 파라미터:

  • --rm: 종료 시 포드를 자동으로 삭제하여 클러스터 리소스를 보존합니다.
  • --overrides: 오프라인 환경에서 포드가 비공개 Harbor 레지스트리로 인증할 수 있도록 하는 데 필요합니다.

모델 서버 상태 쿼리

포드 내에서 wget 유틸리티를 사용하여 vLLM API를 쿼리합니다. vLLM 서비스는 내부 포트 8000을 포트 80에 매핑하므로 서비스의 DNS 이름을 사용할 수 있습니다.

  • 로드된 모델 나열: vLLM 엔진이 성공적으로 초기화되고 Gemma 3 가중치가 로드되었는지 확인합니다.

    wget -qO- http://gemma-3-4b-it/v1/models
    

    성공 응답:

    {"object":"list","data":[{"id":"google/gemma-3-4b-it"}]}
    
  • 애플리케이션 상태 확인: API 서버가 요청을 처리할 준비가 되었는지 확인합니다.

    wget -qO- http://gemma-3-4b-it/health
    

    성공 응답:

    OK
    

Ollama 기본사항

Ollama는 여러 모델을 동시에 메모리에 로드하는 것을 지원하지 않습니다. 또한 일정 시간 (기본적으로 보통 30분) 동안 LLM을 사용하지 않으면 Ollama가 현재 모델을 메모리에서 언로드합니다.

앞서 설명한 동작으로 인해 솔루션은 필요와 사용 사례에 따라 두 개 이상의 Ollama 백엔드를 구현하므로 많은 개발자나 사용자가 하나의 Ollama 인스턴스만 모델을 자주 로드하고 언로드하여 불필요한 지연 시간을 생성하지 않고도 병렬로 사용할 수 있습니다.

다음 명령어를 사용하여 Ollama 포드에서 터미널을 열 수 있습니다.

kubectl exec -it ollama-chat-random_string -- sh
kubectl exec -it ollama-autocomplete-random_string -- sh

포드 내에서 Ollama CLI를 사용하여 사용 가능한 모델을 탐색하고 로드된 모델을 확인할 수 있습니다.

  • ollama-chat 포드에는 다음 모델이 있습니다.

    # ollama list
    NAME                     ID              SIZE      MODIFIED
    codegemma:7b-instruct    0c96700aaada    5.0 GB    2 hours ago
    
    # ollama ps
    NAME                     ID              SIZE     PROCESSOR    UNTIL
    codegemma:7b-instruct    0c96700aaada    10 GB    100% GPU     4 minutes from now
    
  • ollama-autocomplete 포드에는 다음 두 모델이 있습니다.

    # ollama list
    NAME                     ID              SIZE      MODIFIED
    codegemma:2b             926331004170    1.6 GB    2 hours ago
    codegemma:7b-code        aee9a63c13b9    5.0 GB    2 hours ago
    
    # ollama ps
    NAME                 ID              SIZE      PROCESSOR    UNTIL
    codegemma:7b-code    aee9a63c13b9    7.1 GB    100% GPU     4 minutes from now
    
  • Ollama가 현재 모델을 언로드하면 출력이 비어 있습니다.

    # ollama ps
    NAME    ID    SIZE    PROCESSOR    UNTIL
    

다음 코드 지원 요청을 보내면 Ollama가 수동 개입 없이 모델을 동적으로 다시 로드합니다.

GPU 용량의 중요성

10GB GPU 슬라이스를 프로비저닝했지만 모델 메모리 사용 공간이 이보다 (약간) 큰 경우 다음과 같이 LLM의 일부가 CPU에 로드됩니다.

# ollama ps
NAME                     ID              SIZE     PROCESSOR         UNTIL
codegemma:7b-instruct    0c96700aaada    10 GB    6%/94% CPU/GPU    29 minutes from now

이는 최적이 아니며 응답 지연 시간이 길어지므로 이 가이드에 제안된 GPU 크기를 제공해야 합니다.

LLM

vLLM으로 다른 LLM 또는 선호하는 LLM 가져오기

GDC 에어갭에서 vLLM을 사용하여 다양한 크기의 Gemma 또는 기타 오픈소스 대규모 언어 모델(LLM)(예: DeepSeek-R1 또는 Llama 3.1)을 실행하려면 새 모델 가중치를 환경에 인제스트하고 새 애셋을 가리키도록 배포 매니페스트를 업데이트해야 합니다.

이 솔루션에 설정된 운영 절차에 따라 다음 단계에 따라 다른 모델을 로드하세요.

  1. 새 모델 가중치 다운로드:

    인터넷에 액세스할 수 있는 워크스테이션에서 Hugging Face CLI를 사용하여 원하는 모델의 가중치를 다운로드합니다.

    • Gemma 3 (3-12b-it)의 경우:
    hf download google/gemma-3-12b-it
    
    • DeepSeek-R1 (7B Distill)의 경우:
    hf download deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
    
    • Llama 3.1 (8B)의 경우:
    hf download meta-llama/Meta-Llama-3.1-8B-Instruct
    

    참고: 다운로드하기 전에 Hugging Face에서 모델의 라이선스 약관을 수락해야 합니다.

  2. 영구 볼륨 (PVC)을 채웁니다.

    기존 model-pvc을 업데이트하거나 새 모델에 충분한 용량을 갖춘 새 을 만듭니다. 헬퍼 포드 메서드를 사용하여 워크스테이션에서 가중치를 전송합니다. 폼 팩터가 클수록 디스크 공간이 훨씬 더 많이 필요합니다. 새 가중치를 저장할 수 있는 충분한 용량이 영구 볼륨 클레임 (PVC)에 있는지 확인합니다.

    • Gemma 3 4B: ~15.2GB
    • Gemma 3 27B: ~54GB 이상
    • 권장사항: 모델 크기와 관계없이 빠른 로드를 위해 높은 IOPS (1,500)를 보장하려면 500GiB PVC 권장사항을 유지하세요.
    # Copy the new model's hub directory to the helper pod
    kubectl cp ~/.cache/huggingface/hub/ osd-dev/model-uploader:/data/
    
  3. vLLM 배포 매니페스트를 업데이트합니다.

    새 모델 ID를 참조하도록 vLLM 배포 YAML을 수정합니다. 모델을 동적으로 전환할 수 있는 Ollama와 달리 vLLM 인스턴스는 단일 모델 전용이며 모델을 변경하려면 업데이트하거나 재배포해야 합니다.

    사용 중인 모델에 따라 배포의 args 섹션을 업데이트합니다. 여기서는 deepseek를 샘플로 사용합니다.

    args: [
      "-m", "vllm.entrypoints.openai.api_server",
      "--model", "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", # Update to new Repo ID
      "--max-model-len", "32768",
      "--enforce-eager" # Recommended for GDC stability
    ]
    
  4. GPU 및 메모리 리소스 조정:

    모델이 클수록 가중치와 KV 캐시에 더 많은 VRAM이 필요합니다. 4B 모델에서 8B 또는 14B 모델로 이동하는 경우 '메모리 부족' 비정상 종료를 방지하기에 충분한 리소스 한도가 있는지 확인하세요.

    • VRAM 사용 공간: 16비트 정밀도에서 8B 모델에는 가중치에만 약 16GB가 필요하며 vLLM PagedAttention 캐시를 위한 추가 메모리가 필요합니다.
    • 권장사항: 70억 개 이상의 매개변수가 있는 모델의 경우 전체 A100 GPU(nvidia.com/gpu-pod-NVIDIA_A100_80GB_PCIE: 1)를 사용하여 최적의 성능과 KV 캐시의 헤드룸을 확보하세요.
  5. 새 모델 확인:

    배포가 조정되면 내부 API를 사용하여 새 모델이 활성 상태인지 확인합니다.

    # Inside a debug-sh pod
    wget -qO- http://gemma-3-4b-it/v1/models
    

    성공 응답:

    {"object":"list","data":[{"id":"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"}]}
    

Ollama로 다른 LLM 가져오기

Llama 3.2 또는 DeepSeek-R1 계열의 모델과 같은 다른 오픈소스 LLM을 사용해 보려면 이전 Dockerfile에서 이 Ollama 명령어를 수정하세요.

ollama pull gemma3:latest

예를 들어 파라미터가 30억 개인 Llama 3.2를 가져오려면 다음 명령어를 사용합니다.

ollama pull llama3.2:3b

또는 80억 개의 파라미터가 있는 DeepSeek-R1을 사용하려면 다음 명령어를 사용하세요.

ollama pull deepseek-r1:8b

둘 이상의 LLM을 다운로드하고 모델 가중치를 컨테이너화할 수도 있습니다. 그러면 다양한 사전 로드된 모델로 추론을 요청할 때 Ollama가 모델 간에 전환할 수 있습니다. 예를 들어 Docker 이미지에 Gemma 모델과 Llama 모델을 모두 추가할 수 있습니다. 에어갭 환경에서는 Ollama 백엔드가 언제든지 모델을 다운로드할 수 있는 Ollama 라이브러리에 액세스할 수 없습니다.

애플리케이션에서 모델을 실행하기 전에 각 모델 라이선스 및 이용약관을 확인하고 동의해야 합니다. Ollama 라이브러리에는 다양한 LLM이 있습니다. LLM 백엔드 Docker 이미지를 빌드하는 동안 모델 가중치를 다운로드할 때만 인터넷 연결이 필요합니다.

Open WebUI

Open WebUI 포드가 실행 중 상태인지 확인합니다.

kubectl get pods -n NAMESPACE

예상 출력 :

NAME                             READY   STATUS    RESTARTS   AGE
open-webui-69df4664d-przjl       1/1     Running   0          6d3h

서비스 IP를 확인합니다.

kubectl get service -n NAMESPACE

예상 출력:

NAME          TYPE            CLUSTER-IP       EXTERNAL-IP      PORT(S)           AGE
open-webui     LoadBalancer   10.201.137.164   136.125.37.224   80:30492/TCP      6d3h

다음 섹션에서는 기본 UI 기능을 사용하는 방법을 보여줍니다.

WebUI 사용자 인터페이스 다이어그램을 엽니다.

Ollama 백엔드로 솔루션 확장

세로

  • 전체 GPU를 사용할 때까지 병목 현상이 된 LLM에 더 큰 GPU 슬라이스를 할당합니다.
  • 더 나은 응답 정확도를 위해 더 큰 LLM(예: 7B 대신 405B 파라미터 LLM)을 사용하려면 원활하게 운영하기 위해 GPU가 두 개 이상 필요할 수 있습니다.
  • 두 개 이상의 GPU에 적합한 모델은 GPU 간 통신과 관련된 지연 시간이 발생합니다.

가로

  • 특정 LLM에서 목표 처리량을 달성하는 데 필요한 만큼 Ollama 포드를 배포합니다.
    • 이를 위해 해당 Ollama 배포 YAML 파일에서 replica 수를 늘립니다.
  • LoadBalancer 유형인 Kubernetes 서비스는 엔드포인트 (즉, 포드) 간에 코드 지원 요청을 분산하고 노출된 외부 IP를 통해 각 응답을 반환합니다.
  • Continue 플러그인은 기능당 하나의 IP 주소만 가리킵니다.

개방형 가중치 LLM 확장 아키텍처 다이어그램

GDC 에어갭 환경 내에서 vLLM 백엔드를 확장하려면 Ollama에 사용된 것과 유사한 전략을 따라 하드웨어 리소스 할당과 포드 복제를 모두 고려하면 됩니다.

vLLM 백엔드로 솔루션 확장

세로

  • GPU 할당 업그레이드: 추론 처리량 (토큰/초)이 병목 현상이 되면 NVIDIA A100 GPU를 완전히 사용할 때까지 더 큰 GPU 슬라이스를 할당합니다.
  • 멀티 GPU 구성: 단일 80GB A100의 메모리에 맞지 않는 대규모 모델 (예: 700억~4,050억 개의 파라미터)의 경우 텐서 동시 로드를 사용하여 여러 GPU로 확장해야 합니다.
  • 지연 시간 고려사항: GPU가 두 개 이상인 모델은 GPU 간 통신 (예: NCCL 동기화)과 관련된 약간의 오버헤드가 발생할 수 있습니다.

가로

  • 복제본을 통한 처리량 증가: 동일한 모델에 대한 동시 사용자 요청 볼륨을 늘리려면 vLLM 배포 YAML에서 replicas 수를 늘리세요.
  • 전용 모델 인스턴스: vLLM은 단일 모델 서비스 엔진으로 설계되어 초기화 시 KV 캐시 메모리를 고정하므로 호스팅하려는 각기 다른 LLM에 대해 별도의 포드 집합을 배포해야 합니다.
  • 부하 분산: GDC Kubernetes 서비스 (유형 LoadBalancer)는 해당 서비스와 연결된 모든 정상 vLLM 포드 엔드포인트 간에 수신되는 추론 요청을 자동으로 분산합니다.

문제 해결

오류 완화
FIPS 자체 테스트 실패 BoringSSL과 같은 라이브러리에 무결성 서명이 없는 경우 발생합니다. BORINGSSL_FIPS=0을 설정하고 공식 vLLM 이미지를 사용하여 수정
정체된 체중 로드 작은 PVC에서 IOPS 제한이 있는지 확인합니다. 성능 모델 초기화에는 500GiB 볼륨이 필요합니다.
연결 거부됨 PNP가 targetPort (8000/8080)를 명시적으로 허용하는지 확인합니다. GDC 방화벽은 부하 분산기 VIP의 백엔드 포트에 대한 액세스 권한을 자동으로 부여하지 않습니다.