Red Hat Blog를 보다가 관심 가는 글이 보여서 AI번역+약간 교정해 보았습니다.
출처:
소개
OpenShift Virtualization은 동일 클러스터 내에서 여러 포드와 함께 VM을 실행할 수 있도록 하여 인프라 현대화를 위한 토대를 마련합니다. OpenShift Virtualization의 백엔드 스토리지로 하이퍼컨버지드 ODF를 사용하면 스토리지가 연결된 동일한 노드에 VM을 배치할 수 있으며, 이를 통해 하드웨어 리소스 활용도를 극대화하고 비용을 절감할 수 있습니다. 이 문서는 하이퍼컨버지드 ODF(OpenShift Data Foundation) 스토리지 시스템으로 지원되는 OCP-Virt(OpenShift Virtualization) VM을 설정하는 자세한 단계별 가이드를 제공합니다. 이 가이드의 마지막 부분에서는 한 줄짜리 bash 명령을 사용하여 수천 개의 VM을 생성하는 방법을 보여주고, Windows 및 RHEL 9 VM의 PVC 및 VolumeSnapshot 복제와 VM 부팅 시간을 비교한 확장 데이터도 제공합니다.
환경
3 + 108 노드 베어 메탈 클러스터가 있습니다. 마스터 3개와 워커 108개입니다. 총 108개의 OSD 포드가 있으며, 총 원시 스토리지 용량은 313TiB입니다. 데이터 복원력을 제공하기 위해 3방향 복제가 활성화되어 있습니다.
노드당 하드웨어:
- 112개 CPU: 2개 소켓 x 28개 코어 x 2개 스레드 Intel(R) Xeon(R) Gold 6330 CPU @ 2.00GHz
- 512GiB RAM: DIMM DDR4 동기식 레지스터드(버퍼링) 3200MHz(0.3ns)
- 2.9 TiB 디스크: NVMe P5600 MU(혼합 사용) U.2
- 100Gbps NIC: MT2892 제품군 [ConnectX-6 Dx] (OVN)
OCP 및 운영자 버전:
- OCP 4.15.15
- CNV 4.15.2
- ODF 4.15.2-rhodf ceph quincy
- Local Storage Operator 4.15.0-202405161507
오퍼레이터 설치
필수 조건: 실행 중인 OCP 베어 메탈 클러스터
이는 명령줄 예제를 사용하는 실험실 설정으로, 자동화 파이프라인에 CLI 절차를 통합하려는 경우 유용할 수 있습니다. 이 특정 설정에는 세 가지 연산자가 필요합니다.
- OCP에 추가된 OpenShift Virtualization을 사용하면 컨테이너 워크로드와 나란히 VM을 실행할 수 있습니다.
- Local StorageOperator는 로컬 디스크 검색 및 로컬 볼륨 프로비저닝 프로세스를 자동화합니다.
- OpenShift Data Foundation 내부 모드는 로컬 볼륨을 사용하는 영구 공유 스토리지 제공자입니다.
OpenShift 가상화
Namespace, OperatorGroup 및 Subscription 객체를 생성할 때, 설명서 에 설명된 대로 startingCSV를 호환되는 버전으로 바꿔야 합니다 .
$ oc create -f - <<'END'
apiVersion: v1
kind: Namespace
metadata:
name: openshift-cnv
---
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
name: kubevirt-hyperconverged-group
namespace: openshift-cnv
spec:
targetNamespaces:
- openshift-cnv
---
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: hco-operatorhub
namespace: openshift-cnv
spec:
source: redhat-operators
sourceNamespace: openshift-marketplace
name: kubevirt-hyperconverged
startingCSV: kubevirt-hyperconverged-operator.v4.15.4
channel: "stable"
END해당 객체 생성이 완료되면 실행 중인 오퍼레이터 포드 목록을 볼 수 있어야 합니다.
$ oc get pod -n openshift-cnv aaq-operator-75b8d6c995-6q4g5 1/1 Running 0 44s cdi-operator-5c9c664ff8-k7rf2 1/1 Running 0 46s cluster-network-addons-operator-7c49c56976-s4nc8 2/2 Running 0 48s hco-operator-6bcfd97954-9bhm6 1/1 Running 0 49s hco-webhook-74c65945b4-glshx 1/1 Running 0 49s hostpath-provisioner-operator-fbd4ddf86-6qbhv 1/1 Running 0 45s hyperconverged-cluster-cli-download-667976d696-qf2nx 1/1 Running 0 48s mtq-operator-799f785f9f-8bntj 1/1 Running 0 45s ssp-operator-545976874d-k756l 1/1 Running 0 47s virt-operator-7bfcb4c964-27gxx 1/1 Running 0 47s virt-operator-7bfcb4c964-hv87x 1/1 Running 0 47s
다음 단계는 하이퍼컨버지드 객체를 만드는 것입니다.
$ oc create -f - <<'END' apiVersion: hco.kubevirt.io/v1beta1 kind: hyperconverged metadata: name: kubevirt-hyperconverged namespace: openshift-cnv spec: END
하이퍼컨버지드 객체는 각 노드에 대해 Linux 브리지 플러그인 및 Virt 핸들러 포드 등과 같은 일부 데몬 포드의 실행을 트리거합니다. 다음 단계로 넘어가기 전에 포드가 작동하고 실행되는지 확인할 수 있습니다.
$ oc get pod -n openshift-cnv -o wide kube-cni-linux-bridge-plugin-5hbpx 1/1 Running 0 4m27s 10.129.14.13 e40-h30-000-r650.example.com <none> <none> kube-cni-linux-bridge-plugin-6hdpz 1/1 Running 0 4m27s 10.130.8.17 d24-h03-000-r650.example.com <none> <none> kube-cni-linux-bridge-plugin-86l9n 1/1 Running 0 4m27s 10.129.12.20 e40-h33-000-r650.example.com <none> <none> kube-cni-linux-bridge-plugin-947rq 1/1 Running 0 4m27s 10.131.10.24 d24-h15-000-r650.example.com <none> <none> kube-cni-linux-bridge-plugin-9bwcq 1/1 Running 0 4m27s 10.130.15.66 e40-h36-000-r650.example.com <none> <none> ...
클러스터 서비스 버전(CSV) 개체를 확인하여 OpenShift Virtualization 애드온이 성공적으로 배포되었는지 확인할 수 있습니다.
$ oc get csv -n openshift-cnv NAME DISPLAY VERSION REPLACES PHASE kubevirt-hyperconverged-operator.v4.15.4 OpenShift Virtualization 4.15.4 kubevirt-hyperconverged-operator.v4.15.3 Succeeded
로컬 스토리지
Namespace, OperatorGroup 및 Subscription 개체 생성: (GUI 설치 단계는 설명서 에 설명되어 있습니다 )
$ oc create -f - <<'END'
apiVersion: v1
kind: Namespace
metadata:
name: openshift-local-storage
---
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
name: local-operator-group
namespace: openshift-local-storage
spec:
targetNamespaces:
- openshift-local-storage
---
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: local-storage-operator
namespace: openshift-local-storage
spec:
channel: stable
installPlanApproval: Automatic
name: local-storage-operator
source: redhat-operators
sourceNamespace: openshift-marketplace
END오퍼레이터 및 디스크 관리자 포드가 실행 중인지 확인하세요.
$ oc get pod -n openshift-local-storage NAME READY STATUS RESTARTS AGE diskmaker-manager-w2vxk 2/2 Running 0 2m36s diskmaker-manager-zkgkr 2/2 Running 0 2m36s local-storage-operator-5d54c95c8b-nvn78 1/1 Running 0 2m40s
local storage operator가 성공적으로 배포되었는지 확인하세요.
$ oc get csv -n openshift-local-storage NAME DISPLAY VERSION REPLACES PHASE local-storage-operator.v4.15.0-202407120536 Local Storage 4.15.0-202407120536 Succeeded
OpenShift Data Foundation
openshift-storage 네임스페이스를 만듭니다. (GUI 설치 단계는 설명서 에 설명되어 있습니다 .)
$ oc apply -f - <<'END'
apiVersion: v1
kind: Namespace
metadata:
labels:
openshift.io/cluster-monitoring: "true"
name: openshift-storage
spec: {}
ENDOperatorGroup 생성
$ oc apply -f - <<'END' apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: name: openshift-storage-operatorgroup namespace: openshift-storage spec: targetNamespaces: - openshift-storage END
Subscription 객체를 생성하고, 채널 값을 올바른 값으로 바꿔야 합니다 .
$ oc apply -f - <<'END' apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: odf-operator namespace: openshift-storage spec: channel: "stable-4.15" installPlanApproval: Automatic name: odf-operator source: redhat-operators sourceNamespace: openshift-marketplace END
ODF 오퍼레이터가 성공적으로 설치되었는지 확인하세요.
$ oc get pod -n openshift-storage NAME READY STATUS RESTARTS AGE csi-addons-controller-manager-7db788474f-gkxw7 2/2 Running 0 2m43s noobaa-operator-57c499978c-kbb6c 1/1 Running 0 2m48s ocs-operator-ddfb8dfd8-4r2tk 1/1 Running 0 2m26s odf-console-75cc6644d-tnr54 1/1 Running 0 2m58s odf-operator-controller-manager-764dc97458-9kqdk 2/2 Running 0 2m58s rook-ceph-operator-ff85fd6cd-9lfk8 1/1 Running 0 2m17s ux-backend-server-b497fcf7c-f6d79 2/2 Running 0 2m26s
$ oc get csv -n openshift-storage NAME DISPLAY VERSION REPLACES PHASE mcg-operator.v4.15.5-rhodf NooBaa Operator 4.15.5-rhodf mcg-operator.v4.15.4-rhodf Succeeded ocs-operator.v4.15.5-rhodf OpenShift Container Storage 4.15.5-rhodf ocs-operator.v4.15.4-rhodf Succeeded odf-csi-addons-operator.v4.15.5-rhodf CSI Addons 4.15.5-rhodf odf-csi-addons-operator.v4.15.4-rhodf Succeeded odf-operator.v4.15.5-rhodf OpenShift Data Foundation 4.15.5-rhodf odf-operator.v4.15.4-rhodf Succeeded
영구 저장소 구성
ODF 스토리지 노드 라벨링
지금까지 필요한 모든 오퍼레이터를 설치했으므로 이제 저장 노드에 레이블을 지정하여 ODF의 스케줄링 대상이 되도록 할 수 있습니다.
$ oc label node <NodeName> cluster.ocs.openshift.io/openshift-storage=''
로컬 볼륨 검색
Local Storage Operator v4.6부터 새로운 기능인 LocalVolumeDiscovery를 통해 저장 장치를 검색할 수 있습니다. 다음 LVD 객체를 생성하면 openshift-storage 레이블이 있는 노드의 모든 저장 장치가 자동으로 검색됩니다.
$ oc apply -f - <<'END'
apiVersion: local.storage.openshift.io/v1alpha1
kind: LocalVolumeDiscovery
metadata:
name: auto-discover-devices
namespace: openshift-local-storage
spec:
nodeSelector:
nodeSelectorTerms:
- matchExpressions:
- key: cluster.ocs.openshift.io/openshift-storage
operator: In
values:
- ""
ENDLVD 객체를 생성한 후에는 모든 스토리지 노드에서 diskmaker-discovery 데몬 포드가 실행되는 것을 볼 수 있습니다.
$ oc get pod -n openshift-local-storage | head NAME READY STATUS RESTARTS AGE diskmaker-discovery-4d9gc 2/2 Running 0 10m diskmaker-discovery-54tk2 2/2 Running 0 10m diskmaker-discovery-5d765 2/2 Running 0 10m …
그리고 각 노드의 로컬 볼륨 검색 결과는 다음과 같이 검색할 수 있습니다.
$ oc get localvolumediscoveryresults -n openshift-local-storage | head NAME AGE discovery-result-d23-h29-000-r650.example.com 8m44s discovery-result-d23-h31-000-r650.example.com 8m43s discovery-result-d24-h01-000-r650.example.com 8m44s discovery-result-d24-h02-000-r650.example.com 8m44s …
로컬 볼륨 세트
로컬 볼륨 검색 포드는 노드의 모든 블록 장치를 검색하며, deviceInclusionSpec 에 지정된 특정 기준에 따라 노드의 대상 장치를 사용할 수 있습니다. 선택 기준의 예는 설명서 에서 확인할 수 있습니다 . 각 스토리지 노드에서 2.9TiB NVME 디스크를 사용할 예정인데, 이 디스크는 2TiB보다 큰 유일한 디스크이기도 합니다. 따라서 장치 포함 사양은 minSize: 2TiB로 간단히 지정할 수 있습니다.
$ oc apply -f - <<'END'
apiVersion: local.storage.openshift.io/v1alpha1
kind: LocalVolumeSet
metadata:
name: local-nvme-volume
namespace: openshift-local-storage
spec:
nodeSelector:
nodeSelectorTerms:
- matchExpressions:
- key: cluster.ocs.openshift.io/openshift-storage
operator: In
values:
- ""
storageClassName: local-nvme-volume
volumeMode: Block
maxDeviceCount: 1
deviceInclusionSpec:
deviceTypes:
- disk
deviceMechanicalProperties:
- NonRotational
minSize: 2Ti
END모든 것이 잘 진행되면 local-nvme-volume이라는 스토리지 클래스 이름으로 PV가 생성되는 것을 볼 수 있습니다.
$ oc get pv | grep local-nvme-volume | head local-pv-19d9dc8d 2980Gi RWO Delete Available local-nvme-volume 42s local-pv-225c120c 2980Gi RWO Delete Available local-nvme-volume 43s local-pv-29ba4d67 2980Gi RWO Delete Available local-nvme-volume 42s …
스토리지 클러스터
이제 OSD와 rbd 플러그인과 같은 데몬 포드가 실행될 StorageCluster를 생성할 수 있습니다. 스토리지 장치 클러스터가 상당히 큰 경우 시간이 다소 걸릴 수 있습니다. 총 108개의 디스크가 있으므로 storageDeviceSets 개수는 36으로 설정되어 있습니다. 디스크가 3개 추가될 때마다 개수는 1씩 증가합니다. 디스크 크기에 맞게 스토리지 요청 크기를 올바르게 설정해야 합니다 . 이 예시에서는 NVMe가 2980GiB입니다.
$ oc apply -f - <<'END'
apiVersion: ocs.openshift.io/v1
kind: StorageCluster
metadata:
name: ocs-storagecluster
namespace: openshift-storage
spec:
resources:
mds:
limits:
cpu: "3"
memory: "8Gi"
requests:
cpu: "3"
memory: "8Gi"
monDataDirHostPath: /var/lib/rook
managedResources:
cephBlockPools:
reconcileStrategy: manage
cephConfig: {}
cephFilesystems: {}
cephObjectStoreUsers: {}
cephObjectStores: {}
multiCloudGateway:
reconcileStrategy: manage
storageDeviceSets:
- count: 36
dataPVCTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: "2980Gi"
storageClassName: local-nvme-volume
volumeMode: Block
name: ocs-deviceset
placement: {}
portable: false
replica: 3
resources:
limits:
cpu: "2"
memory: "5Gi"
requests:
cpu: "2"
memory: "5Gi"
ENDODF 스토리지 클래스가 생성되는지 확인하세요.
$ oc get sc -A NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE local-nvme-volume kubernetes.io/no-provisioner Delete WaitForFirstConsumer false 88m ocs-storagecluster-ceph-rbd openshift-storage.rbd.csi.ceph.com Delete Immediate true 71m ocs-storagecluster-ceph-rbd-virtualization openshift-storage.rbd.csi.ceph.com Delete Immediate true 71m ocs-storagecluster-ceph-rgw openshift-storage.ceph.rook.io/bucket Delete Immediate false 84m ocs-storagecluster-cephfs openshift-storage.cephfs.csi.ceph.com Delete Immediate true 70m openshift-storage.noobaa.io openshift-storage.noobaa.io/obc Delete Immediate false 69m
OCP-Virt VM 생성
이제 모든 오퍼레이터가 준비되었고 ODF에서 제공하는 영구 저장소를 사용할 준비가 되었습니다. 다음 섹션에서는 VM .qcow 콘텐츠가 PVC에 채워지는 방법과 해당 PVC의 스냅샷을 생성하여 수백 개의 VM을 복제하는 방법을 설명합니다.
데이터 볼륨
DataVolume은 PVC 생성 프로세스와 가상 머신 qcow 이미지 콘텐츠를 PVC로 가져오는 프로세스를 자동화합니다.
$ oc apply -f - <<'END'
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: rhel9-placeholder
spec:
source:
http:
url: http://127.0.0.1:8000/rhel9_uefi.qcow2
pvc:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 22Gi
volumeMode: Block
storageClassName: ocs-storagecluster-ceph-rbd-virtualization
END위의 YAML은 PVC 생성을 트리거하고 로컬에 호스팅된 VM qcow 이미지를 다운로드하는 데이터 볼륨 객체를 만드는 예를 보여줍니다.
$ oc get dv NAME PHASE PROGRESS RESTARTS AGE rhel9-placeholder ImportInProgress 9.98% 1 46s
qcow 이미지 다운로드가 완료되면 PVC 상태가 보류에서 바인딩으로 변경됩니다.
$ oc get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE rhel9-placeholder Bound pvc-ff79c6af-4d35-47c5-bb17-cf09cdf9eca6 22Gi RWX ocs-storagecluster-ceph-rbd-virtualization 3m36s
볼륨 스냅샷
볼륨 스냅샷 기능을 사용하면 수백 개의 VM을 편리하게 복제할 수 있습니다. 전체 PVC 콘텐츠를 복제하는 대신, 해당 PVC의 스냅샷을 복제하면 많은 수의 VM을 더 빠르게 확장할 수 있습니다. 아래 YAML 예제는 PVC의 볼륨 스냅샷(rhel9-placeholder) 을 생성하며 , 나중에 이 스냅샷을 사용하여 수백 개의 복제본을 생성할 수 있습니다.
$ oc apply -f - <<'END'
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: rhel9-snap
namespace: default
spec:
volumeSnapshotClassName: ocs-storagecluster-rbdplugin-snapclass
source:
persistentVolumeClaimName: rhel9-placeholder
END볼륨 스냅샷을 사용할 준비가 되었는지 확인하세요.
$ oc get vs -n default NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT CREATIONTIME AGE rhel9-snap true rhel9-placeholder 22Gi ocs-storagecluster-rbdplugin-snapclass snapcontent-ea2341e1-e543-4489-9326-23ea5f44b0c5 6d 6d
가상 머신 템플릿
볼륨 스냅샷이 준비되면 다음 VM YAML 템플릿을 사용하여 VM을 생성할 수 있습니다. spec.dataVolumeTemplates.spec.source 를 살펴보면 이 VM이 기본 네임스페이스에서 rhel9-snap 이라는 스냅샷을 복제한다는 것을 알 수 있습니다 . 이 YAML 정의에는 두 개의 DataVolume이 있는데, 이는 두 개의 디스크가 VM에 연결됨을 의미합니다. 스냅샷에서 복제된 디스크는 루트 디스크가 되고, blank:{}가 있는 다른 디스크 는 VM 내에서 빈 블록 장치로 노출됩니다. 다음 YAML 템플릿을 vm-2-disk-template.yaml로 저장하면 다음 섹션에서는 여러 VM을 일괄 생성하는 방법을 보여줍니다.
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: rhel9-placeholder
spec:
dataVolumeTemplates:
- apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: root-placeholder
spec:
pvc:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 22Gi
volumeMode: Block
storageClassName: ocs-storagecluster-ceph-rbd-virtualization
source:
snapshot:
namespace: default
name: rhel9-snap
- apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: data-placeholder
spec:
pvc:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 50Gi
volumeMode: Block
storageClassName: ocs-storagecluster-ceph-rbd-virtualization
source:
blank: {}
running: false
template:
metadata:
labels:
kubevirt.io/domain: rhel9
spec:
domain:
ioThreadsPolicy: auto
cpu:
cores: 16
devices:
blockMultiQueue: true
disks:
- disk:
bus: virtio
name: vda
- disk:
bus: virtio
name: vdb
dedicatedIOThread: true
interfaces:
- masquerade: {}
model: virtio
name: default
networkInterfaceMultiqueue: true
rng: {}
features:
smm:
enabled: true
firmware:
bootloader:
efi:
secureBoot: false
resources:
requests:
memory: 128Gi
cpu: 16
evictionStrategy: LiveMigrate
networks:
- name: default
pod: {}
volumes:
- name: vda
dataVolume:
name: root-placeholder
- name: vdb
dataVolume:
name: data-placeholderVM 배치 생성
VM YAML 템플릿에서는 VM 배치 생성을 더 쉽게 하기 위해 의도적으로 문자열 자리 표시자를 사용했습니다. 예를 들어, rhel9-1부터 rhel9-1000까지 이름이 같은 VM 1000개를 생성하려면 다음 bash 명령어를 sed 명령과 함께 사용할 수 있습니다.
$ for i in {1..1000}; do sed "s/placeholder/$i/g" vm-2-disk-template.yaml | oc create -f - ; done1,000개의 VM을 모두 시작하려면:
$ for i in {1..1000}; do virtctl start rhel9-$i; done스케일 데이터
PVC와 스냅샷 복제 시간, 그리고 RHEL 9와 Windows 11 부팅 시간을 비교한 데이터를 제시합니다. 하이퍼컨버지드 ODF 환경에서 총 10,000개의 VM(RHEL 6,000개, Windows 4,000개)을 가동하고 실행할 수 있었습니다.
PVC 클로닝 대 스냅샷 클로닝


스냅샷을 복제하는 데 걸리는 평균 시간은 PVC 전체를 복제하는 것보다 10배 빠릅니다. 튜닝 가이드 에서는 스냅샷 복제를 권장합니다. VM을 더 빠르게 시작하고 실행할 수 있을 뿐만 아니라 저장 공간도 크게 절약할 수 있습니다.
RHEL 9 vs Windows 10 부팅


6,000개의 RHEL 9 및 Windows 11 VM을 모두 부팅했습니다(배치당 100개씩). 평균적으로 100개의 RHEL VM을 모두 부팅하는 데 약 32초, Windows 11 VM 100개를 부팅하는 데 65초가 걸렸습니다.
요약
이 블로그에서는 하이퍼컨버지드 ODF 기반 OpenShift Virtualization을 설정하는 자세한 단계별 가이드를 다루었습니다. 하이퍼컨버지드 ODF 환경에서 10,000개의 RHEL 및 Windows VM을 혼합하여 확장하는 기능을 시연했습니다.