데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      KubeVirt로 구현하는 GPU VM (2) - Ceph 연동 및 VM 생성

      dragonpipe 25.11.27
      1,388 7 1
      DEVOTEE 요약
      Kubernetes 환경에서 Ceph를 연동하고 GPU VM을 생성 및 설정하는 방법을 다룹니다. Ceph를 RBD 기반으로 저장해 rootdisk와 데이터 디스크를 안정적으로 관리하며, CDI를 사용해 OS 이미지를 PVC로 업로드하여 영구 볼륨으로 활용합니다. 또한 Multus와 Bridge CNI를 활용해 Host Bridge 기반 네트워크를 구성하고, KubeVirt를 사용해 VM의 리소스를 예약 및 할당하며 최종적으로는 VM 내부에서 네트워크, SSH, 데스크탑 환경 및 NVIDIA 드라이버 설정을 완료하여 GPU 가속을 지원합니다.
      DEVOTEE 추천 블로그

      시작하는 글

      이번 글에서는 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의 정보 조회 (예시)

      Ceph 클러스터 연결에 필요한 Credential 정보(FSID, 모니터 주소, pool 등)를 확인합니다.

      userID: csi-user
      userKey: ABCDEFG12345==
      fsid: ab12ba70-3482-11f0-bbb1-6b3bdc10dcdb
      mon_ip: "111.22.33.113:6789"
      pool_name: rbd


      Ceph CSI Helm repo 추가 및 values 수정

      Ceph 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 생성

      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


      Helm으로 ceph-csi 설치

      설정한 values를 기반으로 Ceph RBD CSI 드라이버를 Helm으로 설치해 볼륨 프로비저닝 기능을 활성화합니다.

      #  helm 설치
      helm upgrade --install ceph-csi-rbd ceph-csi/ceph-csi-rbd \
        -n ceph \
        -f ceph-csi-rbd-values.yaml


      Storage Class 설치 (RBD)

      Ceph 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-rbd
      kubectl 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


      OpenSSL로 비밀번호 생성

      이제 VM을 생성하고 초기 세팅을 해보겠습니다. 우선 cloud-init 등에 사용할 암호를 안전한 해시 형태로 생성합니다.

      openssl passwd -6 'PassW0rd!'
      $6$Kz2/atgfajUE.lRG$t10VxmhhZaWulIh7Qr1Xrg6Ipk2Sj6231.QK4qS0Fky3CedwveFYSXZXKuWydEZaT6xLIN7wylh5kYDvGO3Qp0


      Rootdisk 생성

      CDI를 이용해 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


      Network bridge 생성

      호스트 브리지를 생성하고 물리 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 ens18f0


      Multus와 Bridge CNI 설치

      Multus와 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 -xz


      NetworkAttachmentDefinition CRD 생성

      KubeVirt 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 default


      VM 템플릿 생성

      KubeVirt 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로 생성한 비밀번호 (로그인할 때 사용)


      VMI 생성

      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          19m


      VM 초기 설정

      VM 내부에 접속해 네트워크·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 가속 렌더링을 결합해 한층 빠르고 부드러운 그래픽 환경을 구현하는 방법을 소개할 예정입니다.

      감사합니다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      dragonpipe 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기