KubeAI로 Hermes 3 등 오픈 LLM을 Kubernetes에서 서빙하기
KubeAI로 Hermes 3 등 오픈 LLM을 Kubernetes에서 서빙하기
Kubernetes 위에서 오픈 가중치 LLM을 서빙하고 싶을 때, KubeAI는 아주 편리한 선택지예요. KubeAI: Private Open AI on Kubernetes는 Nous Research의 Hermes 3(Llama 3.1 8B 기반 파인튜닝)와 NVIDIA의 Nemotron(Llama 3.1 70B 기반 파인튜닝) 같은 모델을 vLLM이나 Ollama 모델 서버로 띄워 주는 쿠버네티스 솔루션이에요.
웹 UI는 Open WebUI로, API는 OpenAI 호환 방식으로 제공돼요. 이 가이드에서는 8x H100 온디맨드 인스턴스에 K3s로 단일 노드 쿠버네티스 클러스터를 세우고, GPU Operator를 설치한 뒤 KubeAI로 Hermes 3와 Nemotron을 서빙하고, 마지막에 NVTOP으로 GPU 활용률까지 관찰해요. 한 번에 서빙과 관찰 흐름을 모두 잡을 수 있는 튜토리얼이에요.
출처: Using KubeAI to deploy Nous Research's Hermes 3 and other LLMs - Lambda Docs
단일 노드 쿠버네티스 클러스터 세우기
ssh ubuntu@<INSTANCE-IP-ADDRESS> -L 8080:localhost:8080
<INSTANCE-IP-ADDRESS>는 인스턴스 IP로 바꿔주세요.
-L 8080:localhost:8080옵션은 로컬 포트 포워딩을 활성화해요. KubeAI의 웹 UI를 내 컴퓨터에서 안전하게 접근하려면 로컬 포트 포워딩이 필요해요. SSH man page에서 더 자세히 볼 수 있어요.
- 이 튜토리얼 뒷부분의
kubectl port-forward명령에 필요한 socat을 설치해요.
sudo apt update && sudo apt -y install socat
- K3s(Kubernetes)를 설치해요.
curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE=644 sh -s - --default-runtime=nvidia
- 쿠버네티스 클러스터가 준비됐는지 확인해요.
kubectl get nodes
다음과 비슷한 출력이 보여요.
NAME STATUS ROLES AGE VERSION
192-222-54-148 Ready control-plane,master 1s v1.30.5+k3s1
kubectl탭 완성을 켜고 싶다면 이렇게 실행해요.echo "source <(kubectl completion bash)" >> ~/.bashrc && source ~/.bashrc
NVIDIA GPU Operator 설치
쿠버네티스 클러스터가 인스턴스의 GPU를 쓰도록 GPU Operator를 설치해요.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: gpu-operator
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: gpu-operator
namespace: gpu-operator
spec:
repo: https://helm.ngc.nvidia.com/nvidia
chart: gpu-operator
targetNamespace: gpu-operator
EOF
몇 분 뒤 클러스터가 GPU를 감지했는지 확인해요.
kubectl describe nodes | grep nvidia.com
다음과 비슷한 출력이 보여요. 핵심은 두 줄이에요. nvidia.com/gpu.count=8은 8개 GPU를 감지했다는 뜻이고, nvidia.com/gpu.product=NVIDIA-H100-80GB-HBM3은 감지된 GPU가 H100 80GB라는 뜻이에요.
nvidia.com/cuda.driver-version.full=535.129.03
nvidia.com/cuda.driver-version.major=535
nvidia.com/cuda.driver-version.minor=129
nvidia.com/cuda.driver-version.revision=03
nvidia.com/cuda.driver.major=535
nvidia.com/cuda.driver.minor=129
nvidia.com/cuda.driver.rev=03
nvidia.com/cuda.runtime-version.full=12.2
nvidia.com/cuda.runtime-version.major=12
nvidia.com/cuda.runtime-version.minor=2
nvidia.com/cuda.runtime.major=12
nvidia.com/cuda.runtime.minor=2
nvidia.com/gfd.timestamp=1729173628
nvidia.com/gpu-driver-upgrade-state=upgrade-done
nvidia.com/gpu.compute.major=9
nvidia.com/gpu.compute.minor=0
nvidia.com/gpu.count=8
nvidia.com/gpu.deploy.container-toolkit=true
nvidia.com/gpu.deploy.dcgm=true
nvidia.com/gpu.deploy.dcgm-exporter=true
nvidia.com/gpu.deploy.device-plugin=true
nvidia.com/gpu.deploy.driver=pre-installed
nvidia.com/gpu.deploy.gpu-feature-discovery=true
nvidia.com/gpu.deploy.mig-manager=true
nvidia.com/gpu.deploy.node-status-exporter=true
nvidia.com/gpu.deploy.operator-validator=true
nvidia.com/gpu.family=hopper
nvidia.com/gpu.machine=Standard-PC-Q35-ICH9-2009
nvidia.com/gpu.memory=81559
nvidia.com/gpu.mode=compute
nvidia.com/gpu.present=true
nvidia.com/gpu.product=NVIDIA-H100-80GB-HBM3
nvidia.com/gpu.replicas=1
nvidia.com/gpu.sharing-strategy=none
nvidia.com/mig.capable=true
nvidia.com/mig.config=all-disabled
nvidia.com/mig.config.state=success
nvidia.com/mig.strategy=single
nvidia.com/mps.capable=false
nvidia.com/vgpu.present=false
nvidia.com/gpu-driver-upgrade-enabled: true
KubeAI 설치
KubeAI를 쿠버네티스에 배포해요.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: kubeai
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: kubeai
namespace: kubeai
spec:
repo: https://www.kubeai.org
chart: kubeai
targetNamespace: kubeai
EOF
설치는 보통 2~3분 걸려요. 준비가 됐는지 확인하려면 파드를 지켜봐요.
kubectl get -n kubeai -w --field-selector=status.phase=Running pods
이 명령은 kubeai 네임스페이스의 실행 중인 파드를 계속 보여줘요. 아래처럼 나오면 KubeAI가 준비된 거예요.
kubeai-5f6cb9984b-nghpj 1/1 Running 0 10s
1/1은 요청한 파드 1개 중 1개가 준비됐다는 뜻이에요. 감시를 멈추려면 Ctrl + C를 눌러요.
Hermes 3와 Nemotron 모델 내려받고 서빙하기
두 모델을 vLLM으로 내려받고 서빙해요.
cat <<EOF | kubectl apply -f -
apiVersion: kubeai.org/v1
kind: Model
metadata:
name: hermes-3-llama-3.1-8b
namespace: kubeai
spec:
features: [TextGeneration]
owner: NousResearch
url: hf://NousResearch/Hermes-3-Llama-3.1-8B
engine: VLLM
resourceProfile: nvidia-gpu-h100:1
minReplicas: 1
---
apiVersion: kubeai.org/v1
kind: Model
metadata:
name: llama-3.1-nemotron-70b-instruct
namespace: kubeai
spec:
features: [TextGeneration]
owner: nvidia
url: hf://nvidia/Llama-3.1-Nemotron-70B-Instruct-HF
engine: VLLM
resourceProfile: nvidia-gpu-h100:4
minReplicas: 1
args:
- --tensor-parallel-size=4
EOF
Hermes 3 모델은 단일 GPU에 올릴 수 있어서
spec.resourceProfile을nvidia-gpu-h100:1로 두고 GPU 1개를 할당했어요. 반면 Nemotron 모델은 단일 GPU에 올릴 수 없어서spec.resourceProfile을nvidia-gpu-h100:4로 두고 GPU 4개를 할당하고, vLLM에--tensor-parallel-size=4인자를 넘겨 텐서 병렬 전략으로 모델을 4개 GPU에 나눠 담아요. 분산 추론·서빙은 vLLM 문서에서 더 자세히 볼 수 있어요.
모델이 내려받아지고 서빙되는 시점을 확인하려면 이렇게 파드를 지켜봐요.
kubectl get -n kubeai -w --field-selector=status.phase=Running pods | grep model-
아래처럼 나오면 두 모델이 내려받고 서빙 중이에요.
model-hermes-3-llama-3.1-8b-79cdb64947-cb7cd 1/1 Running 0 2m21s
model-llama-3.1-nemotron-70b-instruct-57cf757d9d-nc9l4 1/1 Running 0 5m5s
1/1은 각 모델에 요청한 파드 1개가 모두 실행 중이라는 뜻이에요. 감시를 멈추려면 Ctrl + C를 눌러요.
KubeAI 웹 UI로 모델 사용하기
KubeAI 웹 UI를 내 컴퓨터에서 접근할 수 있게 해요.
kubectl -n kubeai port-forward service/openwebui 8080:80 &> /dev/null &
브라우저에서 http://localhost:8080으로 가서, 페이지 왼쪽 위 Select a model을 누르고 다음 중 하나를 선택해요.
- hermes-3-llama-3.1-8b — Hermes 3 모델
- llama-3.1-nemotron-70b-instruct — Nemotron 모델
페이지 아래쪽 Send a Message 입력란에 테스트할 프롬프트를 넣어요.
In a small table, compare and contrast machine learning and deep learning.
KubeAI의 OpenAI 호환 API로 모델 사용하기
OpenAI 호환 API로도 모델을 쓸 수 있어요. 먼저 curl과 jq를 설치해요.
sudo apt update && sudo apt -y install curl jq
KubeAI의 OpenAI 호환 API에 접근을 활성화해요.
kubectl -n kubeai port-forward service/kubeai 8081:80 &> /dev/null &
API의 /models 엔드포인트로 KubeAI가 서빙하는 모델을 나열해요.
curl -sS http://localhost:8081/openai/v1/models | jq .
다음과 비슷한 출력이 보여요.
{
"object": "list",
"data": [
{
"id": "llama-3.1-nemotron-70b-instruct",
"created": 1729174478,
"object": "model",
"owned_by": "nvidia",
"features": [
"TextGeneration"
]
},
{
"id": "hermes-3-llama-3.1-8b",
"created": 1729174478,
"object": "model",
"owned_by": "NousResearch",
"features": [
"TextGeneration"
]
}
]
}
예제 프롬프트를 API에 보내볼게요.
curl -sS -d @- http://localhost:8081/openai/v1/completions -H "Content-Type: application/json" <<EOF | jq .
{
"model": "llama-3.1-nemotron-70b-instruct",
"prompt": "Machine learning engineers are ",
"temperature": 0
}
EOF
위 예시에서 Nemotron 모델이 "Machine learning engineers are"라는 프롬프트에 응답해요. 다음과 비슷한 출력이 나와요.
{
"id": "cmpl-04a8ee4abf7247b5859755e7684ade21",
"object": "text_completion",
"created": 1729179938,
"model": "llama-3.1-nemotron-70b-instruct",
"choices": [
{
"index": 0,
"text": "2023's most in-demand tech professionals\nAccording to a new report from Indeed",
"logprobs": null,
"finish_reason": "length",
"stop_reason": null,
"prompt_logprobs": null
}
],
"usage": {
"prompt_tokens": 6,
"total_tokens": 22,
"completion_tokens": 16
}
}
API 사용법은 vLLM 문서에서 더 자세히 볼 수 있어요.
NVTOP으로 GPU 활용률 관찰하기
인스턴스에서 NVTOP을 실행해요.
nvtop -d 2
출력을 보면 모델이 GPU1·GPU4·GPU5·GPU6·GPU7에 올라와 있고, GPU0·GPU2·GPU3은 할당되지 않아 다른 작업에 쓸 수 있음을 알 수 있어요. 메모리 사용량에 주목하세요. 할당된 GPU는 메모리를 쓰고 있고, 할당되지 않은 GPU는 메모리 사용이 없을 거예요.
브라우저에서 다시 http://localhost:8080으로 가서 llama-3.1-nemotron-70b-instruct 모델을 선택하고, 큰 프롬프트를 넣어봐요.
In 10,000 words or more, explain what large language models are.
nvtop에서 순간적으로 GPU4부터 GPU7까지가 Nemotron 추론에 쓰이는 모습이 보여요. 이번에는 hermes-3-llama-3.1-8b 모델을 선택하고 같은 프롬프트를 넣으면, GPU1 하나만 Hermes 3 추론에 쓰이는 걸 확인할 수 있어요. 이렇게 nvtop 하나만으로 어떤 모델이 몇 개 GPU를 점유하고 있는지 한눈에 파악할 수 있어요.
더 알아보기
- KubeAI — Kubernetes 위 오픈 LLM 서빙 솔루션
- Nous Research Hermes 3 — Hermes 3 모델 소개
- Open WebUI — LLM용 오픈소스 웹 인터페이스
- NVIDIA GPU Operator — 쿠버네티스에서 GPU를 다루는 공식 스택
- NVTOP — GPU/CPU 사용률 모니터링 도구