Engineering/AI

vLLM 설치 w/AKS

망고v 2026. 1. 5. 13:37

PC가 아닌 Kuernetes에 Container 형태로 vLLM을 설치 및 서비스해보자. 마침 VM size(NC40ads H100 v5) 40vcpus, 320 GiB memory의 Node pool이 존재하여 해당 자원을 활용하기로 한다.

 

 

설치하기

설치된 AKS에서 진행하는데 기존에 Ollama로 LLM을 간단하게 서비스하고 있었던  점을 참고하자.

 

[참고] vllm docs

https://docs.vllm.ai/projects/production-stack/en/vllm-stack-0.1.5/deployment/cloud-deployment/azure.html

 

우선 GitHub Repository를 clone하여 helm chart를 확인하자.

mango@mac llm % git clone https://github.com/vllm-project/production-stack.git
Cloning into 'production-stack'...
remote: Enumerating objects: 5036, done.
remote: Counting objects: 100% (1278/1278), done.
remote: Compressing objects: 100% (489/489), done.
remote: Total 5036 (delta 949), reused 789 (delta 789), pack-reused 3758 (from 2)
Receiving objects: 100% (5036/5036), 6.65 MiB | 22.69 MiB/s, done.
Resolving deltas: 100% (3051/3051), done.

 

values.yaml 작성하기

https://github.com/vllm-project/production-stack/blob/main/helm/values.yaml

 

production-stack/helm/values.yaml at main · vllm-project/production-stack

vLLM’s reference system for K8S-native cluster-wide deployment with community-driven performance optimization - vllm-project/production-stack

github.com

values.yaml 파일을 복사(values-gpt-oss-120b.yaml)하여 아래와 같이 modelSpec을 작성하였다.

..생략..
  modelSpec:
    - name: "gpt-oss-120b"
      annotations:
        model: "gpt-oss-120b"
      podAnnotations:
        model: "gpt-oss-120b"
      repository: "lmcache/vllm-openai"
      tag: "latest"
      imagePullPolicy: "Always"
      modelURL: "openai/gpt-oss-120b"
      replicaCount: 1
      
      # Standard NC40ads H100 v5: 40 vCPUs, 320 GiB memory
      requestCPU: 38
      requestMemory: "300Gi"
      limitCPU: "38"
      limitMemory: "310Gi"
      requestGPU: 1
      
      # Storage for large model
      pvcStorage: "300Gi"
      pvcAccessMode:
        - ReadWriteOnce
      storageClass: "managed-csi-premium"        
      
      # Shared memory for large model
      shmSize: "64Gi"
      
      # vLLM configuration for 120B model
      vllmConfig:
        enableChunkedPrefill: true
        enablePrefixCaching: false
        maxModelLen: 16384
        dtype: "bfloat16"
        tensorParallelSize: 1
        maxNumSeqs: 16
        gpuMemoryUtilization: 0.95
        extraArgs: ["--disable-log-requests", "--trust-remote-code"]
      
      # Node selector for Standard NC40ads H100 v5
      # Node pool에 gpu:true 라벨이 설정되어 있음
      nodeSelectorTerms:
        - matchExpressions:
          - key: gpu
            operator: "In"
            values:
            - "true"
..생략..

 

Helm 설치

설치 명령어를 수행하자 아래와 같이 정상적으로 결과가 출력된다. 하지만, vllm-stack-deployment-router Pod가 자꾸 재기동되는 현상이 발견되어 values-gpt-oss-120b.yaml 을 추가로 수정했다.

mango@mac helm % helm install vllm-stack . -f values-gpt-oss-120b.yaml -n vllm --create-namespace
NAME: vllm-stack
LAST DEPLOYED: Tue Dec 30 15:34:26 2025
NAMESPACE: vllm
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None

 

변경사항: startupProbe의 initialDelaySeconds 증가.

..생략..
routerSpec:
..생략..
  # Startup probe configuration.
  # Router 이미지가 크고(5GB+) 시작 시간이 오래 걸리므로 여유있게 설정
  startupProbe:
    initialDelaySeconds: 60
    periodSeconds: 10
    failureThreshold: 12
    httpGet:
      path: /health
..생략..

 

Helm upgrade

아래 Chart의 revision을 보면 5까지 올라간 것을 확인할 수 있다. Persistent Volume Claim에서 Storage Class 선언 관련하여 수차례 삽질한 결과이다. (위 values에는 AKS에서 기본 제공하는 storageClass 수정한 것으로 최종본 반영되어있다.)

mango@mac helm % helm upgrade vllm-stack . -f values-gpt-oss-120b.yaml -n vllm
Release "vllm-stack" has been upgraded. Happy Helming!
NAME: vllm-stack
LAST DEPLOYED: Tue Dec 30 16:07:47 2025
NAMESPACE: vllm
STATUS: deployed
REVISION: 5
DESCRIPTION: Upgrade complete
TEST SUITE: None

 

vllm-stack-deployment-router 점검

일단 router Pod가 기동되었으니, 중간 점검을 진행해보자. 아직 Ingress를 정의하지 않았으므로 Service의 vllm-stack-router-service를 Port-Forward하자.

mango@mac llm % k port-forward svc/vllm-stack-router-service 8000:80 -n vllm
Forwarding from 127.0.0.1:8000 -> 8000
Forwarding from [::1]:8000 -> 8000

 

익숙한 Swagger UI가 출력되는 것이 확인된다. 다만, model이 로딩되지 않았는데 무엇이 문제인지 찾아보자.

 

 

vllm-stack-gpt-oss-120b-deployment-vllm Pod 기동하기

Deployment에는 별다른 Event가 없고, 한참 찾다가 ReplicaSet에 아래와 같은 Event가 있는 것을 발견했다ㅠ 

Error creating: pods "vllm-stack-gpt-oss-120b-deployment-vllm-84497cf857-" is forbidden: pod rejected: RuntimeClass "nvidia" not found

 

아래와 같이 Cluster에 해당하는 runtimeclass(nvidia)가 존재하지 않았다. 

mango@mac helm % k get runtimeclass
NAME                     HANDLER   AGE
kata-mshv-vm-isolation   kata      87d
kata-vm-isolation        kata      87d
runc                     runc      87d

 

values-gpt-oss-120b.yaml  파일의 내용을 아래와 같이 빈값으로 수정하니, vllm-stack-gpt-oss-120b-deployment-vllm Pod가 기동되기 시작한다. 참고로 nvidia-device-plugin-daemonset 은 별도 설치해두었다. 나중에 성능 이슈 발생시 확인해보자.

..생략..
servingEngineSpec:
..생략..
  runtimeClassName: ""
..생략..

 

역시나 이 Pod도 Model 로딩 시간이 오래 걸려서 그런가 'Container vllm failed startup probe, will be restarted' 이벤트를 출력하며 재기동된다. Startup probe를 수정하자.

..생략..
servingEngineSpec:
..생략..
  # -- Startup probe configuration
  # 120B 모델은 시작하는데 매우 오래 걸리므로 여유있게 설정
  # - 모델 다운로드 시간
  # - 모델을 GPU 메모리에 로드하는 시간
  # - vLLM 서버 초기화 시간
  startupProbe:
    # -- Number of seconds after the container has started before startup probe is initiated
    initialDelaySeconds: 300
    # -- How often (in seconds) to perform the startup probe
    periodSeconds: 30
    # -- Number of times after which if a probe fails in a row, Kubernetes considers that the overall check has failed: the container is not ready
    failureThreshold: 30
    # -- Timeout for each probe attempt
    timeoutSeconds: 10
    # -- Configuration of the Kubelet http request on the server
    httpGet:
      # -- Path to access on the HTTP server
      path: /health
      # -- Name or number of the port to access on the container, on which the server is listening
      port: 8000
..생략..

 

참고로 Pod의 로그를 살펴보면 아래와 같이 무지막지하게 시간이 오래 걸리는 것을 확인할 수 있다.

(EngineCore_DP0 pid=213) INFO 12-30 08:15:15 [mxfp4.py:165] Using Triton backend
(EngineCore_DP0 pid=213) INFO 12-30 08:19:22 [weight_utils.py:487] Time spent downloading weights for openai/gpt-oss-120b: 246.795983 seconds
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:   0% Completed | 0/15 [00:00<?, ?it/s]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:   7% Completed | 1/15 [00:45<10:39, 45.67s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  13% Completed | 2/15 [01:23<08:50, 40.84s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  20% Completed | 3/15 [02:07<08:27, 42.30s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  27% Completed | 4/15 [02:46<07:30, 40.93s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  33% Completed | 5/15 [03:28<06:53, 41.36s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  40% Completed | 6/15 [04:08<06:09, 41.04s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  47% Completed | 7/15 [04:53<05:37, 42.20s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  53% Completed | 8/15 [05:31<04:46, 40.99s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  60% Completed | 9/15 [06:16<04:13, 42.31s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  67% Completed | 10/15 [06:56<03:27, 41.58s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  73% Completed | 11/15 [07:43<02:52, 43.03s/it]
(EngineCore_DP0 pid=213) Loading safetensors checkpoint shards:  80% Completed | 12/15 [08:23<02:06, 42.12s/it]

 

 

테스트

아래와 같이 기본적인 채팅에 대한 응답을 확인할 수 있었다.

curl --location 'http://localhost:8000/v1/chat/completions' \
--header 'Content-Type: application/json' \
--data '{
    "model": "openai/gpt-oss-120b",
    "messages": [
        {
            "role": "system",
            "content": "You are a helpful assistant."
        },
        {
            "role": "user",
            "content": "vLLM을 Docker로 기동하는 것과 Python으로 기동하는 것의 차이와 장단점은?"
        }
    ]
}'

 

응답결과는 아래와 같으며, 답변 및 추론 과정 그리고 Token 사용에 대한 정보도 확인 가능했다.

{
    "id": "chatcmpl-a85f97d3e5f135cf18778ee4bba13797",
    "object": "chat.completion",
    "created": 1767587108,
    "model": "openai/gpt-oss-120b",
    "choices": [
        {
            "index": 0,
            "message": {
                "role": "assistant",
                "content": "## vLLM 실행 방식 비교  \n| 구분 | Docker 로 실행 | Python 스크립트(직접 실행) |\n|------|----------------|---------------------------|\n| **배포·재현성** | - 이미지에 OS·라이브러리·CUDA·PyTorch·vLLM 모두 포함 → 어느 머신에서든 똑같이..", 
..생략..
                "reasoning": "The user asks in Korean: \"vLLM을 Docker로 기동하는 것과 Python으로 기동하는 것의 차이와 장단점은?\" They want differences and pros/cons of launching vLLM (a language model inference engine) via Docker vs via Python directly..",
                "reasoning_content": "The user asks in Korean: \"vLLM을 Docker로 기동하는 것과 Python으로 기동하는 것의 차이와 장단점은?\" They want differences and pros/cons of launching vLLM (a language model inference engine) via Docker vs via Python directly.."
            },
..생략..            
    "usage": {
        "prompt_tokens": 105,
        "total_tokens": 4580,
        "completion_tokens": 4475,
        "prompt_tokens_details": null
    },
..생략..
}

 

 

기타. 이슈

vllm-router OOME

이제 잘 되려나 하고 테스트하다보면 router가 간간히 재기동되는 경우가 있다. 로그는 Node에 아래와 같이 떨어졌다ㅠ  

Memory cgroup out of memory: Killed process 220258 (vllm-router) total-vm:5106464kB, anon-rss:500080kB, file-rss:65360kB, shmem-rss:0kB, UID:0 pgtables:2712kB oom_score_adj:938

 

resource가 타이트하게 잡혀있는 것 같아 values-gpt-oss-120b.yaml을 수정해서 memory로 인한 재기동을 해결했다.

..생략..
routerSpec:
..생략..
  # -- router resource requests and limits
  # Router가 OOMKilled 되지 않도록 메모리를 충분히 할당
  # 실제 사용량: anon-rss ~500MB + file-rss ~65MB + overhead
  resources:
    requests:
      cpu: 400m
      memory: 1Gi
    limits:
      cpu: 1000m
      memory: 2Gi

 

'Engineering > AI' 카테고리의 다른 글

Open WebUI 설치 w/AKS  (0) 2026.01.06
vLLM 설치 및 실행 w/macOS  (0) 2025.12.29
Docker LLM 설치하기 w/Ollama  (0) 2025.12.29
로컬(macOS) LLM 설치하기 w/Ollama  (0) 2025.12.24