오픈소스 OS와 클라우드 네이티브 AI의 결합
인공지능(AI) 워크로드가 기하급수적으로 증가함에 따라, 높은 처리량과 낮은 지연 시간을 보장하는 인프라의 중요성이 부각되고 있습니다. 디지털 인프라를 위해 설계된 오픈소스 운영체제인 openEuler는 고동시성 환경에 최적화된 커널 튜닝을 제공하여 AI 추론 서비스의 견고한 기반이 됩니다. 여기에 Kubernetes 생태계의 탄력성을 결합하면, 모델의 배포와 스케일링을 자동화하는 효율적인 AI 서비스 아키텍처를 구축할 수 있습니다. 본 가이드에서는 openEuler 환경에서 KServe를 사용하여 Qwen3 대규모 언어 모델(LLM)을 배포하고, 부하 테스트를 통해 성능을 분석 및 최적화하는 과정을 다룹니다.
핵심 기술 스택: Qwen3와 KServe
Qwen3 모델의 아키텍처와 특징
알리바바 클라우드 통이치엔원 팀에서 개발한 Qwen3는 혼합 전문가(MoE, Mixture of Experts) 아키텍처를 도입하여 추론 시 일부 파라미터만 동적으로 활성화합니다. 이를 통해 전체 파라미터 규모는 크더라도 실제 연산량은 대폭 줄어들어 하드웨어 비용을 절감하면서도 높은 성능을 유지합니다. 또한, 복잡한 추론을 위한 '사고 모드'와 빠른 응답을 위한 '비사고 모드'를 지원하여 서비스 요구사항에 따라 유연하게 대응할 수 있습니다. 다국어 및 멀티모달 처리 능력까지 갖춘 Qwen3는 현재 오픈소스 LLM 중 가장 뛰어난 성능을 보여주는 모델 중 하나입니다.
KServe의 서버리스 모델 서빙
KServe는 Kubernetes 위에서 머신러닝 모델의 배포와 관리를 표준화하는 클라우드 네이티브 플랫폼입니다. Knative Serving과 연동되어 요청량에 기반한 자동 스케일링 및 스케일 투 제로(Scale-to-Zero) 기능을 제공하여 유휴 리소스 낭비를 방지합니다. 또한, Istio 서비스 메시와 통합되어 카나리 배포, 트래픽 라우팅, 보안 네트워크 관리 등 엔터프라이즈급 기능을 기본적으로 지원합니다.
인프라 구축: openEuler와 Kubernetes 클러스터
1. openEuler OS 초기 설정
물리 서버 또는 가상 머신에 openEuler 22.03 LTS 버전을 설치합니다. 시스템 설치 후, 시간 동기화를 활성화하고 테스트 환경의 네트워크 복잡성을 줄이기 위해 방화벽 및 SELinux 정책을 조정합니다. 프로덕션 환경에서는 보안 정책에 따라 적절한 예외 규칙을 설정해야 합니다.
2. containerd와 Kubernetes 컴포넌트 설치
가벼운 컨테이너 런타임인 containerd를 설치하고, cgroup 드라이버를 systemd로 설정하여 kubelet과의 호환성을 맞춥니다. 그 후 Kubernetes 패키지 저장소를 등록하고 클러스터 초기화를 진행합니다.
# containerd 설치 및 설정
sudo dnf install -y containerd.io
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
sudo sed -i 's/ SystemdCgroup = false/ SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd && sudo systemctl enable containerd
# Kubernetes 저장소 추가 및 컴포넌트 설치
cat < /dev/null
[k8s-main]
name=Kubernetes Main Repository
baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
REPO_EOF
sudo dnf install -y kubelet kubeadm kubectl
sudo systemctl enable --now kubelet
# 클러스터 초기화 및 CNI(Flannel) 적용
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
3. KServe 및 종속성 배포
Helm을 사용하여 KServe 컨트롤러를 설치하고, 서버리스 기능과 네트워크 라우팅을 위해 Knative와 Istio를 구성합니다.
# KServe Helm 차트 설치
helm repo add kserve-charts https://kserve.github.io/website/helmcharts/
helm repo update
helm upgrade --install kserve-crd kserve-charts/kserve-crd -n kserve --create-namespace
helm upgrade --install kserve kserve-charts/kserve -n kserve --create-namespace
# Knative Serving 설치
export KNATIVE_VERSION="v1.12.0"
kubectl apply -f "https://github.com/knative/serving/releases/download/knative-${KNATIVE_VERSION}/serving-crds.yaml"
kubectl apply -f "https://github.com/knative/serving/releases/download/knative-${KNATIVE_VERSION}/serving-core.yaml"
# Istio 설치 및 네임스페이스 라벨링
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.20.3 sh -
cd istio-1.20.3 && export PATH=$PWD/bin:$PATH
istioctl install --set profile=default -y
kubectl label namespace default istio-injection=enabled
모델 서빙: Qwen3 InferenceService 배포
1. 모델 가중치 준비 및 스토리지 연결
ModelScope 또는 Hugging Face를 통해 Qwen3-8B 모델 가중치를 다운로드한 후, NFS 서버에 저장합니다. Kubernetes 클러스터에서 해당 NFS 경로를 마운트하기 위해 PersistentVolume(PV)과 PersistentVolumeClaim(PVC)을 정의합니다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: llm-weights-pv
labels:
type: nfs-storage
spec:
capacity:
storage: 50Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.1.100
path: /exports/llm-weights
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: llm-weights-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 50Gi
selector:
matchLabels:
type: nfs-storage
2. InferenceService 매니페스트 작성
KServe의 Custom Resource를 정의하여 GPU 리소스 할당, 모델 포맷, 스토리지 경로를 매핑합니다. 서버리스 모드를 활성화하여 트래픽에 따른 자동 스케일링을 준비합니다.
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: qwen3-8b-service
annotations:
serving.kserve.io/deploymentMode: Serverless
spec:
predictor:
minReplicas: 1
maxReplicas: 3
model:
modelFormat:
name: huggingface
storageUri: "pvc://llm-weights-pvc/qwen3-8b"
resources:
requests:
cpu: "4"
memory: 16Gi
nvidia.com/gpu: "1"
limits:
cpu: "8"
memory: 32Gi
nvidia.com/gpu: "1"
3. 엔드포인트 검증
Istio Ingress Gateway의 IP와 서비스 호스트네임을 추출하여 추론 API가 정상적으로 응답하는지 확인합니다.
INGRESS_HOST=$(kubectl get svc istio-ingressgateway -n istio-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SERVICE_HOSTNAME=$(kubectl get inferenceservice qwen3-8b-service -o jsonpath='{.status.url}' | cut -d "/" -f 3)
curl -v -H "Host: ${SERVICE_HOSTNAME}" -H "Content-Type: application/json" \
"http://${INGRESS_HOST}/v1/models/qwen3-8b-service:predict" \
-d '{"inputs": "안녕하세요, 당신의 주요 기능은 무엇인가요?"}'
부하 테스트 및 성능 병목 현상 분석
테스트 시나리오 및 도구
wrk와 locust를 활용하여 동시 접속자 수를 점진적으로 증가시키는 스트레스 테스트를 수행합니다. 초기 기능 검증을 거친 후, 동시 연결 수를 10에서 1000까지 단계적으로 늘리며 시스템의 한계점과 리소스 포화 상태를 관찰합니다.
주요 성능 지표 분석
- 처리량(RPS)과 동시 연결: 동시 연결 수가 150 내외일 때까지 RPS는 선형적으로 증가하지만, 임계점을 넘어서면 GPU 연산 큐가 포화되며 처리량이 정체되거나 감소하는 현상이 발생합니다.
- 지연 시간(Latency): 낮은 부하에서는 추론 지연 시간이 200ms 내외로 안정적이지만, 동시 요청이 300을 초과하면 큐잉 지연(Queuing Delay)이 발생하며 P99 지연 시간이 수 초대로 급증합니다.
- 리소스 포화도:
kubectl top및 DCGMExporter를 통한 모니터링 결과, GPU 사용률이 95% 이상으로 치솟는 반면 CPU와 메모리는 여유가 있어, 현재 아키텍처의 주요 병목 현상이 GPU 컴퓨팅 파워에 있음을 확인할 수 있습니다.
성능 튜닝 및 모범 사례
테스트 결과를 바탕으로 다음과 같은 최적화 전략을 적용하여 서비스의 처리량과 안정성을 향상시킬 수 있습니다.
- GPU 리소스 확장 및 병렬 처리: 텐서 병렬 처리(Tensor Parallelism)를 지원하도록 InferenceService 설정을 변경하여 여러 GPU에 모델을 분산 로드하면, 단일 GPU의 메모리 대역폭 병목을 해소하고 추론 속도를 높일 수 있습니다.
- 모델 양자화 및 추론 엔진 최적화: FP16 또는 INT8 양자화를 적용하고, 기본 Hugging Face 서빙 대신 vLLM이나 TensorRT-LLM과 같은 고성능 추론 엔진을 KServe의 커스텀 컨테이너로 연결하면 메모리 사용량을 줄이면서 RPS를 크게 향상시킬 수 있습니다.
- 동적 배치(Dynamic Batching) 활성화: KServe Predictor 설정에서
batcher구성을 활성화하여 짧은 시간 내 들어오는 여러 요청을 하나의 배치로 묶어 GPU에 전달하면, GPU 연산 효율성을 극대화할 수 있습니다. - HPA를 통한 수평적 확장: Knative의 자동 스케일링뿐만 아니라, Kubernetes HPA(Horizontal Pod Autoscaler)를 GPU 사용률 또는 사용자 정의 메트릭(예: 큐 길이)과 연동하여 트래픽 급증 시 Pod 인스턴스를 선제적으로 확장하도록 구성합니다.