23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
이번 글에서는 Kubernetes에 Ceph를 연동하고 이를 활용하여 GPU VM을 생성하고 설정하는 과정을 다룹니다.
VM을 생성할 때 Ceph를 사용해야 하는 이유는, VM의 rootdisk와 데이터 디스크를 분산 스토리지 기반으로 안정적으로 관리하기 위함입니다.
이 글에서는 Ceph는 이미 설치되었다고 가정하고 Ceph CSI를 설치하고,
StorageClass, PVC를 구성하여 VM이 사용할 Persistent Volume을 자동으로 프로비저닝하도록 설정하는 과정에 대해 설명드리겠습니다.
VM 생성 시 rootdisk를 단순히 Containerdisk로 만드는 대신 이미지를 포함한 PVC를 생성하는 이유는,
Containerdisk는 일시적인 설정 용으로 사용되며, 리부팅 시 초기화되기 때문입니다.
따라서 OS 이미지(.img)를 CDI를 통해 Ceph PVC에 업로드하여 rootdisk로 사용하는 방식이 가장 안정적입니다.
또한, VM의 네트워크는 기본적으로 Pod Network를 사용할 수도 있지만,
성능과 관리 효율성을 위해 Host Bridge 기반 네트워크를 구성했습니다.
Pod Network를 사용할 경우 외부 통신을 위해 MetalLB나 NodePort 설정이 필요하고,
서비스 포트를 매번 수정해야 하는 번거로움이 있습니다.
이에 따라 Multus + Bridge CNI를 이용해 별도의 Host Bridge를 구성하고 VM과 직접 연결했습니다.
CPU는 dedicatedCpuPlacement 설정을 통해 VM이 사용하는 vCPU를 물리 코어에 고정(Pinning)하도록 했습니다.
이를 위해 cpu 리소스의 requests와 limits를 정수 단위로 동일하게 지정해야 하며,
CPU Manager 정책이 static으로 설정되어 있어야 합니다.
GPU는 deviceName 필드를 통해 노드에 등록된 GPU를 지정하며,
여러 개를 지정하면 Multi-GPU 환경에서도 유연하게 사용할 수 있습니다.
그럼 Kubernetes에 Ceph 부터 연동해 보도록 하겠습니다.
Ceph 클러스터 연결에 필요한 Credential 정보(FSID, 모니터 주소, pool 등)를 확인합니다.
userID: csi-user
userKey: ABCDEFG12345==
fsid: ab12ba70-3482-11f0-bbb1-6b3bdc10dcdb
mon_ip: "111.22.33.113:6789"
pool_name: rbdCeph CSI Helm 차트를 클러스터에 배포하기 위해 레포를 추가하고 환경에 맞는 values 값을 설정합니다.
# helm 저장소 추가
helm repo add ceph-csi <https://ceph.github.io/csi-charts>
# namespace 생성
kubectl create ns ceph
# helm ceph-csi-rbd의 values 파일 다운로드
curl -o ceph-csi-rbd-values.yaml <https://raw.githubusercontent.com/ceph/ceph-csi/v3.14.2/charts/ceph-csi-rbd/values.yaml>
# 아래 부분 수정
...
csiConfig:
- clusterID: ab12ba70-3482-11f0-bbb1-6b3bdc10dcdb # Ceph 클러스터 FSID (ceph fsid 명령으로 확인)
- monitors:
- "111.22.33.113:6789" # Ceph의 mon_ip
provisioner:
name: provisioner
replicaCount: 1
...CSI가 Ceph 클러스터에 접근할 수 있도록 사용자 인증 정보(Secret)를 Kubernetes에 등록합니다.
apiVersion: v1
kind: Secret
metadata:
name: csi-ceph-secret
namespace: ceph
stringData:
userID: csi-user
userKey: ABCDEFG12345==kubectl apply -f ceph-csi-secret.yaml설정한 values를 기반으로 Ceph RBD CSI 드라이버를 Helm으로 설치해 볼륨 프로비저닝 기능을 활성화합니다.
# helm 설치
helm upgrade --install ceph-csi-rbd ceph-csi/ceph-csi-rbd \
-n ceph \
-f ceph-csi-rbd-values.yamlCeph RBD를 사용할 StorageClass를 정의하여 PVC 생성 시 자동 프로비저닝 동작을 지정합니다.
# ceph-rbd-storageclass.yaml
apiVersion: storage.k8s.io/v1 # Kubernetes Storage API 버전
kind: StorageClass # 리소스 종류: StorageClass (스토리지 유형 정의)
metadata:
name: ceph-rbd # StorageClass 이름 (PVC에서 참조할 때 사용)
annotations:
storageclass.kubesphere.io/supported-access-modes: '["ReadWriteOnce","ReadOnlyMany","ReadWriteMany"]'
provisioner: rbd.csi.ceph.com # Ceph RBD용 CSI 드라이버 지정
parameters:
clusterID: ab12ba70-3482-11f0-bbb1-6b3bdc10dcdb # Ceph 클러스터 FSID (ceph fsid 명령으로 확인)
pool: rbd # RBD 이미지를 저장할 Ceph Pool 이름
imageFeatures: layering # RBD 이미지 기능 (스냅샷/클론 기능 지원)
imageFormat: "2" # 이미지 포맷 버전 (2가 최신, 대부분 이걸 사용)
csi.storage.k8s.io/provisioner-secret-name: csi-ceph-secret # Ceph에 접속할 때 사용할 Secret 이름 (볼륨 생성용)
csi.storage.k8s.io/provisioner-secret-namespace: ceph # 위 Secret이 존재하는 namespace
csi.storage.k8s.io/node-stage-secret-name: csi-ceph-secret # Node에서 볼륨 마운트 시 사용할 Secret
csi.storage.k8s.io/node-stage-secret-namespace: ceph
csi.storage.k8s.io/controller-expand-secret-name: csi-ceph-secret # 볼륨 크기 확장 시 사용할 Secret
csi.storage.k8s.io/controller-expand-secret-namespace: ceph
csi.storage.k8s.io/fstype: ext4
reclaimPolicy: Delete # PVC 삭제 시 RBD 이미지도 삭제 (Retain이면 남겨둠)
allowVolumeExpansion: true # PVC 리사이징 허용
volumeBindingMode: Immediate # PVC 생성 시 PV 즉시 바인딩 (Pod 스케줄 전 바인딩kubectl apply -f ceph-rbd-storageclass.yaml테스트용 PVC를 생성해 바인딩 상태를 확인함으로써 Ceph CSI와의 연동이 정상인지 검증합니다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: rbd-test-pvc
namespace: ceph
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: ceph-rbdkubectl apply -f test-pvc-rbd.yaml
-> test-pvc-rbd가 잘 bound되면 ceph와 연동이 잘 되는 것을 확인할 수 있음특정 기능(예: CPU manager, KubeVirt 스케줄 가능 여부)을 위해 노드에 스케줄링/토폴로지 라벨을 부여합니다.
# 단일 노드 구성 중 affinity 이슈 대응
kubectl label node <node-name> cpumanager=true --overwrite
kubectl label node <node-name> kubevirt.io/schedulable=true --overwrite
kubectl label node <node-name> kubernetes.io/arch=amd64 --overwrite이제 VM을 생성하고 초기 세팅을 해보겠습니다. 우선 cloud-init 등에 사용할 암호를 안전한 해시 형태로 생성합니다.
openssl passwd -6 'PassW0rd!'
$6$Kz2/atgfajUE.lRG$t10VxmhhZaWulIh7Qr1Xrg6Ipk2Sj6231.QK4qS0Fky3CedwveFYSXZXKuWydEZaT6xLIN7wylh5kYDvGO3Qp0CDI를 이용해 OS 이미지를 Ceph RBD PVC로 업로드하여 VM의 루트 디스크로 사용할 영구 볼륨을 준비합니다.
# OS image 다운로드 (Ubuntu 22.04 이미지 다운로드)
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
# 서버로 image 파일 전송
scp ./jammy-server-cloudimg-amd64.img <username>@<server-ip>:/home/<user>/manifest/kubevirt
# cdi-uploadproxy의 cluster-ip 확인
kubectl get svc cdi-uploadproxy -n cdi
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cdi-uploadproxy ClusterIP <cluster-ip> <none> 443/TCP 3h31m
# ubuntu img 파일을 포함한 pvc 생성
virtctl image-upload pvc ubuntu-gpuvm1-pvc \
--image-path=./jammy-server-cloudimg-amd64.img \
--access-mode=ReadWriteOnce \
--size=500Gi \
--insecure \
--namespace=default \
--uploadproxy-url=https://<cdi-uploadproxy cluster-ip> \
--storage-class=ceph-rbd호스트 브리지를 생성하고 물리 NIC를 브리지에 연결하여 VM이 물리 네트워크를 직접 사용하도록 구성합니다.
# netplan yaml 아래와 같이 설정 수정
sudo nano /etc/netplan/01-network-manager-all.yaml
network:
version: 2
renderer: NetworkManager
ethernets:
ens18f0:
dhcp4: no
ens18f1: {}
ens18f2: {}
ens18f3: {}
bridges:
br111:
interfaces: [ens18f0]
addresses: [111.22.33.112/24]
routes:
- to: default
via: 111.22.33.1
nameservers:
addresses: [8.8.8.8, 8.8.4.4]
parameters:
stp: false
# Netplan 설정을 적용
sudo netplan apply
# 브리지와 NIC의 IP 주소를 확인하여 설정이 올바르게 적용되었는지 확인
ip addr show br111
ip addr show ens18f0Multus와 Bridge CNI를 설치해 멀티 네트워크 인터페이스를 지원하고 VM에 Host-bridge 네트워크를 연결할 수 있게 합니다.
# multus CNI 설치
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml
# Bridge CNI 설치
export VERSION="v1.5.0"
curl -L "https://github.com/containernetworking/plugins/releases/download/${VERSION}/cni-plugins-linux-amd64-${VERSION}.tgz" | tar -C /opt/cni/bin -xzKubeVirt VM이 사용할 Host-bridge 네트워크를 Multus용 NetworkAttachmentDefinition으로 선언합니다.
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
name: host-bridge-net
spec:
config: |
{
"cniVersion": "0.3.1",
"name": "host-bridge-net",
"type": "bridge",
"bridge": "br111", # 만든 bridge 명과 동일해야 함. 복사 붙여넣기로 만드실 경우 이 # 주석은 제거하셔야 에러가 발생하지 않습니다.
"ipam": {
"type": "host-local",
"subnet": "111.22.33.0/24"
},
"promiscMode": true
}# networkAttachmentDefinition CRD 생성
kubectl apply -f networkAttachmentDefinition.yaml
# 생성 확인
kubectl get network-attachment-definitions -n defaultKubeVirt VirtualMachine CRD를 작성해 nodeSelector·디바이스·디스크·네트워크 등 VM 템플릿을 정의합니다.
# vm-gpuvm1.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: gpuvm1
spec:
runStrategy: Halted # RunStrategy (Halted, Manual, RerunOnFailure, Always 중 선택)
template:
metadata:
labels:
kubevirt.io/domain: gpuvm1 # VM 이름
spec:
nodeSelector:
kubernetes.io/hostname: <hostname>
domain:
cpu:
dedicatedCpuPlacement: true # cpu pinning 설정
isolateEmulatorThread: true # 에뮬레이션 스레드에 전용 CPU 코어를 할당
devices:
disks:
- name: rootdisk
disk:
bus: virtio
- name: cloudinit
disk:
bus: virtio
interfaces:
- name: host-bridge-net
bridge: {}
gpus:
- deviceName: nvidia.com/AD102GL_L40S # kubectl describe node로 node의 capacity를 확인하면 그래픽카드의 이름을 알 수 있음
name: gpu1 # 2장 이상 multi-gpu를 사용하고 싶을 경우 이 device set을 하나 더 추가한 뒤 name만 다르게 추가한다 (예: gpu2)
rng: {}
resources:
limits:
cpu: 28 # 반드시 limits와 requests가 같아야 하며, 정수여야 함
memory: 80Gi
requests:
cpu: 28
memory: 80Gi
networks:
- name: host-bridge-net
multus:
default: true
networkName: default/host-bridge-net # <namespace명/NetworkAttachmentDefinition명>
terminationGracePeriodSeconds: 180
volumes:
- name: rootdisk
persistentVolumeCl
claimName: ubuntu-gpuvm1-pvc
- name: cloudinit
cloudInitNoCloud:
userData: |-
#cloud-config
hostname: <VM hostname>
users:
- name: <VM username>
groups: sudo
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
lock_passwd: false
passwd: $6$Kz2/atgfajUE.lRG$t10VxmhhZaWulIh7Qr1Xrg6Ipk2Sj6231.QK4qS0Fky3CedwveFYSXZXKuWydEZaT6xLIN7wylh5kYDvGO3Qp0 # openssl로 생성한 비밀번호 (로그인할 때 사용)VirtualMachineInstance(VMI)을 기동하여 실제로 virt-launcher가 생성되고 VM이 실행되는지를 확인합니다.
# vm 스펙정보 CRD 생성 (gpuvm1)
kubectl apply -f vm-gpuvm1.yaml
# 생성 확인
kubectl get vm
NAME AGE STATUS READY
gpuvm1 76s Stopped False
# vm 기동
virtctl start gpuvm1
# vmi 확인
kubectl get vmi
NAME AGE PHASE IP NODENAME READY
gpuvm1 18m Running <ip> <hostname> True
# vm pod 확인
kubectl get pod -n default | grep virt-launcher
virt-launcher-gpuvm1-cw2ns 2/2 Running 0 19mVM 내부에 접속해 네트워크·SSH·데스크탑·NVIDIA 드라이버 등을 설치·검증하는 마무리 설정 단계입니다.
# virtctl console(tty)로 접속
virtctl console gpuvm1
login: <username>
password: <password>
# 네트웍 확인
ip a # lo 외에 NIC가 하나더 추가되어있는지 확인. 아직 IP는 할당되지 않았을 것.
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether d6:8a:28:b9:b4:cf brd ff:ff:ff:ff:ff:ff
valid_lft forever preferred_lft forever
inet6 fe80::d48a:28ff:feb9:b4cf/64 scope link
valid_lft forever preferred_lft forever
ping 8.8.8.8 # 인터넷 접속 여부 확인. ping 연결 안되면 아래와 같이 진행
# netplan yaml 아래와 같이 수정
sudo nano /etc/netplan/50-cloud-init.yaml
network:
version: 2
ethernets:
enp1s0:
dhcp4: no
addresses: [111.22.33.115/16] # <VM 사용할 IP>/16 안에 gateway ip가 포함되어야 함
gateway4: 111.22.33.1 # <GatewayIP>
nameservers:
addresses: [8.8.8.8, 8.8.4.4] # 외부 URL 접속을 위한 DNS 서버 등록
# netplan 적용
sudo netplan apply
# 네트워크 테스트
ping 8.8.8.8 # 외부 인터넷 접속 여부 확인
ping google.com # dns 정상 여부 확인
# open-ssh 설치
sudo apt update
sudo apt install openssh-server
sudo systemctl enable ssh
sudo systemctl start ssh
# open-ssh password 방식으로 변경
sudo nano /etc/ssh/sshd_config.d/60-cloudimg-settings.conf
# PasswordAuthentication yes 로 변경
sudo systemctl restart ssh # ssh 재시작
ssh <user>@111.22.33.115 # SSH 접속 확인
# ubuntu-desktop 설치
sudo apt install ubuntu-desktop
# NVIDIA GPU 장치 인식 확인
lspci | grep -i nvidia
> 결과: 09:00.0 VGA compatible controller: NVIDIA Corporation Device 2c02 (rev a1)
# nvidia driver 추천
apt search nvidia-driver
또는
ubuntu-drivers devices
# 특정 버전 nvidia driver 설치
sudo apt install nvidia-driver-550
# 혹시 nvidia driver 설치 잘 안되면 secureBoot를 false 설정해서 다시 시도
...
spec:
domain:
firmware:
bootloader:
efi:
secureBoot: false
...
# 커널 모듈 확인
lsmod | grep nvidia
# 위에서 결과 아무것도 없으면 모듈 켜기
sudo modprobe nvidia
# nvidia-smi로 확인
nvidia-smi
Thu Jul 10 06:07:31 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.144.03 Driver Version: 550.144.03 CUDA Version: 12.4 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA L40S Off | 00000000:09:00.0 Off | 0 |
| N/A 34C P8 24W / 350W | 1MiB / 46068MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+이번 포스팅에서는 KubeVirt와 Ceph를 연동하여 GPU VM을 생성하는 전체 과정을 정리했습니다.
이 과정을 통해 단순한 VM 생성뿐만 아니라,
스토리지, 네트워크, GPU, CPU 자원까지 Kubernetes 네이티브 방식으로 통합 관리하는 방법을 확인할 수 있습니다.
Ceph를 이용한 RBD 기반 볼륨은 데이터 무결성과 확장성 면에서 매우 안정적이며,
Multus를 통한 Host Bridge 네트워킹은 VM을 물리 서버처럼 다루면서도 클러스터 내에서 일관된 관리가 가능합니다.
특히 dedicatedCpuPlacement와 GPU Passthrough 설정을 함께 적용하면,
컨테이너 환경에서도 고성능 GPU VM을 안정적으로 운용할 수 있습니다.
다음 글에서는 이렇게 구성된 GPU VM 환경 위에 HP Anyware의 PCoIP Graphics Agent를 설치하여,
GUI 기반 원격 접속에 GPU 가속 렌더링을 결합해 한층 빠르고 부드러운 그래픽 환경을 구현하는 방법을 소개할 예정입니다.
감사합니다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.