OpenStack
OpenStack 환경에서 Compute(Nova) 서비스를 통해 NPU를 인스턴스에 할당할 수 있습니다.
PCI 패스스루를 사용한 NPU 할당
물리 NPU 장치의 PCI 주소를 확인한 뒤 Nova 설정에 등록하고, 플레이버를 통해 인스턴스에 할당합니다.
사전 요구 사항
- OpenStack이 설치 및 구성되어 있어야 합니다.
- NPU가 장착된 컴퓨트 노드에서 IOMMU가 활성화되어 있어야 합니다.
- 컴퓨트 노드의 커널에 vfio-pci 지원이 포함되어 있어야 하며, 모듈로 제공되는 경우 vfio-pci가 로드되어 있어야 합니다..
Note
이 문서는 OpenStack Bobcat 기준으로 작성되었습니다. 다른 OpenStack 릴리스에서는 일부 설정 이름이나 동작이 다를 수 있습니다.
1. 컴퓨트 노드에서 NPU PCI 장치 확인
각 컴퓨트 노드에서 NPU의 PCI 주소를 확인합니다.
| $ lspci -nn | grep accelerators
1b:00.0 Processing accelerators [1200]: Device [1eff:1220] (rev 03)
|
위 출력에서 vendor_id는 1eff, product_id는 1220이며, 이 값은 이후 설정에서 사용합니다.
2. IOMMU 활성화 및 권장 커널 파라미터 구성
PCI 패스스루용 IOMMU 설정 및 권장 커널 파라미터는 IOMMU 활성화 및 권장 커널 파라미터 구성을 참고하십시오.
3. 컴퓨트 노드 Nova 설정 (nova.conf)
각 컴퓨트 노드의 /etc/nova/nova.conf 파일에 PCI 패스스루 장치 스펙을 설정합니다.
| [pci]
device_spec = { "vendor_id":"1eff", "product_id": "1220", "resource_class": "ATOM_PF" }
|
설정 후 Nova Compute 서비스를 재시작합니다.
4. 컨트롤러 노드 Nova 설정 (nova.conf)
컨트롤러 노드의 /etc/nova/nova.conf 파일에 PCI 장치 별칭(alias)을 설정합니다. 이 별칭은 플레이버에서 NPU를 요청할 때 사용합니다.
| [pci]
alias = { "vendor_id":"1eff", "resource_class":"ATOM_PF", "device_type":"type-PF", "name":"atom-pci" }
|
Note
nova-scheduler가 실행되는 노드에도 동일한 [pci] alias 설정이 필요합니다.
nova-scheduler에서 PciPassthroughFilter를 활성화합니다.
| [filter_scheduler]
enabled_filters = ...,PciPassthroughFilter
available_filters = nova.scheduler.filters.all_filters
|
다음은 NPU PCI 패스스루와 성능 최적화 구성에서 다루는 NUMA 토폴로지 기반 최적화를 함께 적용하기 위해 권장하는 필터 구성입니다.
| [filter_scheduler]
available_filters = nova.scheduler.filters.all_filters
enabled_filters = ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,PciPassthroughFilter,AggregateInstanceExtraSpecsFilter,NUMATopologyFilter
|
설정 후 Nova API 및 Scheduler 서비스를 재시작합니다.
5. 이미지 및 NPU 플레이버 생성
NPU PCI 패스스루를 위해 q35 머신 타입과 UEFI 펌웨어를 지정하여 이미지를 등록합니다.
Note
NPU 패스스루는 PCIe 네이티브 토폴로지를 지원하는 q35 머신 타입과 UEFI 펌웨어 조합을 권장합니다. 레거시 i440fx/BIOS 환경에서는 PCIe 장치 열거가 제대로 이루어지지 않거나 64비트 BAR 매핑이 정상적으로 동작하지 않을 수 있습니다.
| $ openstack image create "Ubuntu-22.04-Cloud" \
--file jammy-server-cloudimg-amd64.img \
--disk-format qcow2 \
--container-format bare \
--public \
--property hw_machine_type=q35 \
--property hw_firmware_type=uefi
|
NPU가 포함된 인스턴스를 생성하기 위한 플레이버를 생성합니다.
| $ openstack flavor create --ram 131072 --disk 50 --vcpus 8 ca22.n1.c8.m128
|
플레이버에 NPU PCI 장치 요청을 추가합니다.
Note
hw:cpu_mode=host-passthrough는 호스트 CPU 모델과 모든 CPU 기능(가상화 확장, IOMMU 관련 명령 등)을 게스트에 그대로 노출합니다. PCI 패스스루 장치가 호스트와 동일한 CPU 동작을 기대하기 때문에, 이 모드에서 NPU 장치가 안정적으로 동작합니다.
| $ openstack flavor set ca22.n1.c8.m128 --property "pci_passthrough:alias"="atom-pci:1" --property hw:cpu_mode=host-passthrough
|
Note
atom-pci:1에서 1은 인스턴스에 할당할 NPU 장치 수입니다. 여러 개의 NPU를 할당하려면 숫자를 늘립니다(예: atom-pci:2).
6. NPU 인스턴스 생성
생성한 플레이버를 사용하여 인스턴스를 시작합니다.
| $ openstack server create --flavor ca22.n1.c8.m128 \
--image <image-name> \
--network <network-name> \
npu-instance
|
7. NPU 할당 확인
인스턴스가 생성된 후 인스턴스에 접속하여 NPU가 정상적으로 할당되었는지 확인합니다.
| $ openstack server ssh npu-instance -- lspci -nn | grep accelerators
05:00.0 Processing accelerators [1200]: Device [1eff:1220] (rev 03)
|
제품별 구성
이 섹션에서는 OpenStack PCI 패스스루에 필요한 제품별 구성 차이를 설명합니다.
RBLN-CA25 nova.conf 설정
RBLN-CA25는 슬롯당 4개의 PCI 장치가 하나의 NPU를 구성하므로 반드시 슬롯 단위(4의 배수)로 할당해야 합니다. device_spec에 개별 PCI 주소를 명시하고 슬롯별 resource_class로 그룹화하여, 동일 슬롯의 장치가 같은 인스턴스에 할당되도록 합니다.
| [pci]
alias = { "vendor_id":"1eff", "resource_class":"ATOM_PCI", "device_type":"type-PCI", "name":"atom-pci" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:05:00.0", "resource_class": "ATOM_PCI_CA25_SLOT1" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:06:00.0", "resource_class": "ATOM_PCI_CA25_SLOT1" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:07:00.0", "resource_class": "ATOM_PCI_CA25_SLOT1" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:08:00.0", "resource_class": "ATOM_PCI_CA25_SLOT1" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:0c:00.0", "resource_class": "ATOM_PCI_CA25_SLOT2" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:0d:00.0", "resource_class": "ATOM_PCI_CA25_SLOT2" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:0e:00.0", "resource_class": "ATOM_PCI_CA25_SLOT2" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:0f:00.0", "resource_class": "ATOM_PCI_CA25_SLOT2" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:46:00.0", "resource_class": "ATOM_PCI_CA25_SLOT3" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:47:00.0", "resource_class": "ATOM_PCI_CA25_SLOT3" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:48:00.0", "resource_class": "ATOM_PCI_CA25_SLOT3" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:49:00.0", "resource_class": "ATOM_PCI_CA25_SLOT3" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:4d:00.0", "resource_class": "ATOM_PCI_CA25_SLOT4" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:4e:00.0", "resource_class": "ATOM_PCI_CA25_SLOT4" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:4f:00.0", "resource_class": "ATOM_PCI_CA25_SLOT4" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:50:00.0", "resource_class": "ATOM_PCI_CA25_SLOT4" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:85:00.0", "resource_class": "ATOM_PCI_CA25_SLOT5" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:86:00.0", "resource_class": "ATOM_PCI_CA25_SLOT5" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:87:00.0", "resource_class": "ATOM_PCI_CA25_SLOT5" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:88:00.0", "resource_class": "ATOM_PCI_CA25_SLOT5" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:8c:00.0", "resource_class": "ATOM_PCI_CA25_SLOT6" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:8d:00.0", "resource_class": "ATOM_PCI_CA25_SLOT6" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:8e:00.0", "resource_class": "ATOM_PCI_CA25_SLOT6" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:8f:00.0", "resource_class": "ATOM_PCI_CA25_SLOT6" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:c5:00.0", "resource_class": "ATOM_PCI_CA25_SLOT7" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:c6:00.0", "resource_class": "ATOM_PCI_CA25_SLOT7" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:c7:00.0", "resource_class": "ATOM_PCI_CA25_SLOT7" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:c8:00.0", "resource_class": "ATOM_PCI_CA25_SLOT7" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:cc:00.0", "resource_class": "ATOM_PCI_CA25_SLOT8" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:cd:00.0", "resource_class": "ATOM_PCI_CA25_SLOT8" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:ce:00.0", "resource_class": "ATOM_PCI_CA25_SLOT8" }
device_spec = { "vendor_id":"1eff", "product_id": "1250", "address": "0000:cf:00.0", "resource_class": "ATOM_PCI_CA25_SLOT8" }
|
NPU 가상화 환경에서 최적의 성능을 확보하기 위해 NUMA 토폴로지, CPU 피닝, Hugepage를 구성합니다.
NUMA 토폴로지 최적화
NPU가 연결된 NUMA 노드와 동일한 노드에 인스턴스의 vCPU와 메모리를 배치하면(hw:numa_nodes=1) 다음과 같은 성능상의 이점이 있습니다.
- 메모리 접근 지연 감소: vCPU와 메모리가 NPU와 동일한 NUMA 노드에 위치하므로 원격 NUMA 노드를 경유하는 크로스 노드 메모리 접근(Remote Memory Access)이 발생하지 않아 메모리 지연이 크게 감소합니다.
- PCIe 트랜잭션 최적화: NPU와의 DMA 전송 시 동일 NUMA 노드의 로컬 메모리를 사용하므로 PCIe 대역폭을 최대로 활용할 수 있으며, QPI/UPI 인터커넥트를 경유하는 간접 경로가 제거됩니다.
플레이버에 NUMA 정책을 설정합니다.
| $ openstack flavor set ca22.n1.c8.m128 \
--property hw:numa_nodes=1
|
Note
호스트에 장착된 전체 NPU를 하나의 인스턴스에 할당하는 등 요청 리소스를 단일 NUMA 노드에 수용할 수 없고 NPU 슬롯이 2개의 NUMA 노드에 분산되어 있는 경우 hw:numa_nodes=2로 설정합니다. vCPU와 메모리가 NUMA 노드 수로 나누어떨어지면 Nova가 자동으로 균등 분배합니다.
균등 분배가 불가능하거나(예: vCPU 수가 NUMA 노드 수의 배수가 아닌 경우) 의도적으로 불균등 분배가 필요한 경우에는 hw:numa_cpus.N과 hw:numa_mem.N을 명시합니다.
| $ openstack flavor set ca22.n8.c64.m1024 \
--property hw:numa_nodes=2
|
CPU 피닝
vCPU를 물리 CPU 코어에 고정하면 컨텍스트 스위칭 오버헤드가 제거되고 L1/L2 캐시 히트율이 높아져 NPU와의 데이터 전송 시 안정적인 지연 시간을 보장합니다. 또한 다른 인스턴스와의 코어 경합(noisy neighbor)을 방지하여 p99 지연 시간의 일관성을 확보할 수 있습니다.
컴퓨트 노드의 /etc/nova/nova.conf에서 피닝 대상 CPU를 지정합니다.
| [compute]
cpu_dedicated_set = 0-43,48-91
cpu_shared_set = 44-47,92-95
|
Note
cpu_dedicated_set과 cpu_shared_set은 SMT 시블링 쌍이 분리되지 않도록 구성해야 합니다. lscpu -e 명령으로 NUMA 노드별 CPU와 시블링 매핑을 확인할 수 있습니다. hw:cpu_thread_policy=require는 vCPU를 시블링 쌍에 함께 배치하여 하이퍼스레딩을 활용하며, 코어 독점이 필요한 경우 isolate를 사용합니다(vCPU 1개당 논리 CPU 2개 소비).
플레이버에 CPU 피닝을 설정합니다.
| $ openstack flavor set ca22.n1.c8.m128 \
--property hw:cpu_policy=dedicated \
--property hw:cpu_thread_policy=require
|
Hugepage
PCI 패스스루 환경에서는 VFIO가 전체 게스트 메모리를 호스트에 고정(mlock)해야 합니다. 1GB Hugepage를 사용하면 메모리 핀 오버헤드가 제거되고 EPT 워크가 최소화되어 인스턴스 할당 안정성과 런타임 성능이 향상됩니다.
컴퓨트 노드의 /etc/default/grub에 Hugepage 커널 파라미터를 추가합니다.
| GRUB_CMDLINE_LINUX="... default_hugepagesz=1G hugepagesz=1G hugepages=960"
|
GRUB 업데이트 후 재부팅합니다.
플레이버에 Hugepage를 설정합니다.
| $ openstack flavor set ca22.n1.c8.m128 \
--property hw:mem_page_size=1GB
|
통합 플레이버 예시
| $ openstack flavor set ca22.n1.c8.m128 \
--property "pci_passthrough:alias"="atom-pci:1" \
--property hw:cpu_mode=host-passthrough \
--property hw:cpu_policy=dedicated \
--property hw:cpu_thread_policy=require \
--property hw:numa_nodes=1 \
--property hw:mem_page_size=1GB
|