Ollama LLM이 K3s에서 느리게 실행되는 이유와 GPU 패스스루 해결 방법
요약
본 기사는 사내 AI 인프라 구축 시 Ollama LLM이 K3s 환경에서 느리게 실행되는 근본적인 원인을 분석합니다. 컨테이너가 물리적 GPU 하드웨어를 인식하지 못해 CPU로 폴백(fallback)하는 것이 주원인이며, 이를 해결하기 위해 NVIDIA Container Toolkit과 Device Plugin을 활용하여 진정한 GPU Pass-Through를 구현하는 단계별 가이드를 제공합니다.
핵심 포인트
- K3s 환경에서 Ollama가 느린 이유는 컨테이너가 물리적 GPU 하드웨어를 인식하지 못해 CPU로 폴백되기 때문입니다.
- 성능 개선을 위해서는 NVIDIA Container Toolkit과 Device Plugin을 사용하여 3계층의 GPU 브릿지를 구축해야 합니다.
- 이 가이드는 K3s 환경에서 Ollama를 최적화하고, 로컬 LLM 추론 성능을 극대화하는 실질적인 방법을 제시합니다.
Ollama LLM이 K3s에서 느리게 실행되는 이유 (그리고 GPU 패스스루를 수정하는 방법)
다음 시나리오를 상상해 보세요. 데이터 프라이버시를 직접 관리하기로 결정하고 사내 AI 인프라를 구축했습니다. K3s를 사용하여 경량 Kubernetes 클러스터를 올리고, Ollama를 배포했으며, 이를 Open-WebUI 프런트엔드에 연결한 후 로컬 LLM에게 간단한 질문을 던졌습니다.
그러자... 기다립니다.
커서가 깜빡입니다. 두 초마다 단어 하나씩 화면에 나타납니다 ($1 \text{ token/s}$). CPU 팬은 이륙하는 제트 엔진 소리가 나고, 시스템 메모리는 $100%$까지 치솟으며, 사용자 경험은 완전히 망가집니다.
무엇이 잘못된 것일까요? 서버 하드웨어 안에 값비싼 NVIDIA GPU가 장착되어 있습니다! 왜 로컬 AI가 트레드밀 위 거북이처럼 실행되는 걸까요?
본 심층 기사에서는 Kubernetes에서 Ollama의 느린 성능 뒤에 숨겨진 근본적인 원인을 파헤치고, K3s가 하드웨어 리소스를 어떻게 처리하는지 탐구하며, NVIDIA Container Toolkit과 Device Plugin을 사용하여 진정한 NVIDIA GPU Pass-Through를 활성화하는 단계별 프로덕션 가이드를 안내할 것입니다.
근본적인 원인: 왜 Ollama가 CPU로 기본 설정되는가?
Ollama를 베어메탈 데스크톱 OS(예: CUDA 드라이버가 설치된 macOS 또는 Windows)에서 로컬로 실행하면, Ollama는 사용 가능한 GPU를 자동으로 감지하고 신경망 레이어를 VRAM으로 오프로드합니다.
하지만 Kubernetes는 격리되고 컨테이너화된 우주 공간에서 작동합니다.
기본적으로 Kubernetes 내부의 컨테이너 런타임(특히 K3s에 번들로 포함된 containerd)은 근본적인 호스트의 물리적 PCI 하드웨어로부터 의도적으로 샌드박스 처리됩니다.
백그라운드에서 무슨 일이 일어나는가:
-
하드웨어 인식 불가 (Hardware Blindness): 컨테이너화된 Ollama 프로세스가 호스트 장치 노드인
/dev/nvidia*를 '볼' 수 없습니다. -
CPU 폴백 (CPU Fallback): 파드 내부에서 CUDA 런타임이 노출되지 않기 때문에, Ollama는 CPU 행렬 곱셈(AVX/AVX-512 명령어 사용)으로 자연스럽게 전환됩니다.
-
리소스 고갈 (Resource Exhaustion): 7B 또는 13B 파라미터 모델에 대한 CPU 추론은 시스템 RAM과 CPU 캐시 간의 지속적이고 높은 지연 시간 메모리 전송을 요구합니다. 이는 스레드 경합(thread contention), 열스로틀링(thermal throttling)을 유발하며, GPU VRAM에서 초당 50~80 토큰 수준에 비해 성능이 떨어지는 결과를 가져옵니다.
이를 해결하기 위해, 우리는 Linux 커널 드라이버부터 K3s 파드 네임스페이스까지 **하드웨어 브릿지(hardware bridge)**를 구축해야 합니다.
아키텍처 개요: 3계층 GPU 브릿지 (Architectural Overview: The 3-Layer GPU Bridge)
K3s에서 제로 지연 시간 GPU 패스스루를 달성하려면, 조화롭게 작동하는 세 가지 별개의 계층이 필요합니다:
┌─────────────────────────────────────────────────────────┐
│ Ollama Pod (K3s Namespace) │
│ resources: limits: { nvidia.com/gpu: "1" } │
...
단계별로 구현해 보겠습니다.
1단계: 호스트 노드 준비 (Host Node Preparation) (NVIDIA 드라이버 및 컨테이너 툴킷)
Kubernetes나 K3s 매니페스트를 건드리기 전에, 사용자의 Linux 호스트 커널에는 작동하는 NVIDIA 드라이버와 NVIDIA Container Toolkit이 설치되어 있어야 합니다.
1. NVIDIA 드라이버 설치 확인 (Verify NVIDIA Driver Installation)
호스트 머신에 공식 NVIDIA 독점 드라이버가 로드되었는지 확인하십시오:
Bash
nvidia-smi
GPU 장치 이름(예: RTX 3090 / RTX 4090 / A100), 드라이버 버전(예: 535.x 또는 550.x), 그리고 CUDA 버전 기능을 보여주는 깨끗한 드라이버 상태 테이블이 나타나야 합니다.
2. NVIDIA Container Toolkit 설치 (Install NVIDIA Container Toolkit)
컨테이너 툴킷은 containerd가 호스트 GPU 장치를 OCI 컨테이너 사양으로 전달하도록 구성합니다.
Ubuntu / Debian 시스템의 경우:
Bash
2단계: K3s Containerd 엔진을 구성하여 NVIDIA Runtime 지원하기
기본적으로 K3s는 /var/lib/rancher/k3s/agent/etc/containerd/config.toml에 위치한 자체 임베디드 containerd 인스턴스를 사용합니다.
중요 참고 사항: 이 config.toml 파일을 직접 수정하지 마십시오! K3s는 서비스가 재시작될 때마다 이 파일을 덮어씁니다. 대신, 우리는 K3s의 **템플릿 확장 메커니즘(template extension mechanism)**을 사용해야 합니다.
1. Containerd 템플릿 파일 생성하기
/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl에 템플릿 구성 파일을 생성합니다:
Bash
sudo cp /var/lib/rancher/k3s/agent/etc/containerd/config.toml /var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl
2. nvidia-container-runtime 등록하기
/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl 내부에서 NVIDIA Container Runtime을 기본/사용 가능한 runtime handler로 구성합니다:
Ini, TOML
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
...
3. K3s 서비스 재시작하기
K3s를 재시작하여 변경 사항을 적용합니다:
Bash
sudo systemctl restart k3s
3단계: NVIDIA Kubernetes Device Plugin 배포하기
이제 containerd가 CUDA 지원 컨테이너를 실행하는 방법을 알게 되었으므로, 물리적 GPU 리소스를 Kubernetes Control Plane 스케줄러에 광고해야 합니다.
공식 NVIDIA Kubernetes Device Plugin을 배포하기 위해 Helm을 사용합니다:
Bash
# NVIDIA Helm repository 추가
helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
helm repo update
...
GPU 리소스 용량 확인
K3s 노드가 물리적 GPU를 스케줄링 가능한 용량 단위로 인식하는지 확인합니다:
Bash
kubectl get nodes -o jsonpath='{.items[*].status.allocatable}' | jq .
다음과 유사한 할당 가능(allocatable) 리소스 키가 표시되어야 합니다:
JSON
{
"cpu": "16",
"memory": "32Gi",
...
만약 목록에 [nvidia.com/gpu](https://nvidia.com/gpu)가 나타난다면, 축하합니다! K3s 클러스터가 공식적으로 GPU 인식(GPU-aware) 상태입니다.
4단계: GPU 리소스 요청을 포함하여 Ollama 배포하기
이제 진정한 순간이 왔습니다. 쿠버네티스 스케줄러가 CUDA 드라이버를 컨테이너에 마운트하도록 명시적인 GPU 리소스 제한을 사용하여 Ollama를 배포합니다.
ollama-gpu-deployment.yaml이라는 이름의 프로덕션 배포 파일을 생성합니다:
YAML
apiVersion: apps/v1
kind: Deployment
metadata:
...
매니페스트를 적용합니다:
Bash
kubectl create namespace ai-workloads
kubectl apply -f ollama-gpu-deployment.yaml
5단계: 검증 및 성능 벤치마크
Ollama가 CPU 렌더링 대신 GPU 하드웨어 가속을 활발하게 사용하고 있는지 확인해 봅시다.
1. Pod 내부로 진입하여 VRAM 사용량 모니터링하기
호스트에서 직접 또는 모델을 당기면서(pulling) 파드 내부로 nvidia-smi를 실행합니다:
Bash
# 실행 중인 Ollama pod 내부로 진입
kubectl exec -ti -n ai-workloads deployment/ollama -- ollama run llama3:8b
호스트 머신에서 별도의 터미널 창을 열고 다음 명령어를 실행합니다:
Bash
watch -n 1 nvidia-smi
2. 주요 지표 확인하기
다음 신호를 찾으십시오:
-
프로세스 이름: GPU 프로세스 테이블에
ollama또는ollama_llama_server가 등록되어 있어야 합니다. -
VRAM 할당량: VRAM 사용량이 $0 \text{ MiB}$에서 $4.5\text{--}5.5 \text{ GiB}$ (양자화된 8B 모델의 경우)로 급증해야 합니다.
-
생성 속도: 토큰이 터미널 화면에 **$60\text{--}90 \text{ tokens/s}$**로 즉시 스트리밍되어야 합니다!
일반적인 엣지 케이스 문제 해결
상세한 설정을 했음에도 불구하고, 분산 컨테이너 런타임의 특이점(quirks)이 발생할 수 있습니다. Ollama를 K3s에 배포할 때 엔지니어들이 직면하는 상위 세 가지 문제는 다음과 같습니다:
문제 1: Insufficient [nvidia.com/gpu](https://nvidia.com/gpu) 오류
-
증상: Pod가
Pending상태에서 멈춰 있습니다.kubectl describe pod를 실행하면0/1 nodes are available: 1 Insufficient [nvidia.com/gpu]가 표시됩니다. -
해결 방법: Device Plugin이 GPU를 등록하는 데 실패했습니다. 호스트에서
nvidia-smi가 작동하는지 확인하고,nvidia-device-plugindaemonset을 재시작하세요:Bash
kubectl rollout restart daemonset nvidia-device-plugin-daemonset -n kube-system
문제 2: CUDA 메모리 부족 (OOM)
-
증상: 모델 로딩 중 Ollama pod가
CUDA error: out of memory와 함께 충돌합니다. -
해결 방법: 모델 파라미터 크기가 GPU VRAM 용량을 초과했습니다. 더 작은 양자화된(quantized) 모델(예: FP16 대신
llama3:8b-instruct-q4_K_M)을 사용하거나, Ollama 환경 플래그에서 CPU 오프로딩(Offloading) 파라미터(OLLAMA_NUM_PARALLEL=1)를 활성화하세요.
문제 3: Linux 커널 업데이트 후 드라이버 불일치
-
증상: 어제까지는 GPU 패스스루가 작동했지만, 시스템 재부팅 후에 문제가 발생했습니다.
-
해결 방법: 자동화된(Unattended) Linux 커널 업그레이드는 독점적인 NVIDIA DKMS 커널 모듈을 손상시킵니다. 커널/NVIDIA 패키지에 대한 자동 업데이트를 비활성화하거나 드라이버 버전을 고정하세요:
Bash
sudo apt-mark hold nvidia-driver-550
결론 및 다음 단계
K3s 위에 **프라이빗 AI 클라우드(Private AI Cloud)**를 구축하는 것은 궁극적인 조합을 제공합니다. K3s의 가벼운 오케스트레이션과 로컬 GPU 하드웨어의 순수 컴퓨팅 파워가 결합된 형태입니다.
NVIDIA Container Toolkit을 설정하고 배포(deployments)에 runtimeClassName: nvidia를 강제 적용하면 하드웨어의 전체 처리량(throughput)을 활용할 수 있습니다. 이를 통해 LLM 응답 지연 시간(latency)을 30초 이상에서 밀리초 단위까지 대폭 단축시킬 수 있습니다.
완성도 높은 사설 AI 스택 구축 준비가 되셨나요?
Ollama의 GPU 패스스루는 퍼즐 조각 중 하나일 뿐입니다. 엔터프라이즈급 로컬 AI 생태계를 실행하려면 다음 요소들도 필요합니다:
-
문서 검색 증강 생성(RAG)을 위한 벡터 데이터베이스 (Qdrant / Milvus).
-
Telegram/Discord 알림을 사용하는 K8sGPT를 이용한 자동화된 클러스터 진단.
-
영구적인 벡터 인덱스를 위한 고가용성 스토리지 (Longhorn).
이러한 아키텍처들을 프로덕션 레디 매니페스트와 실제 사용 스크립트를 통해 단계별로 마스터하고 싶다면, 저의 전체 과정을 살펴보세요:
👉 마스터클래스: K3s HA를 이용한 제로에서 사설 AI 클라우드까지
GPU 패스스루, CNI 구성 또는 로컬 LLM 추론에 대해 궁금한 점이 있으신가요? 아래에 댓글을 남겨주시고 함께 문제 해결해 봅시다!
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기