[NUMA] Scheduler
ESXi Deep Dive 문서를 기반으로 확인한 내용을 정리합니다.
NUMA Scheduler는 NUMA Node간에 Workload를 Migration 해서 CPU load balance와 Memory Locality를 향상시키고자 함
이를 위해서 NUMA Scheduler는 가능한 동일 Node에 CPU와 Memory를 위치
동시에 NUMA Scheduler는 다른 VM의 CPU demand 균형을 맞춰서 fairness를 보장
Initial Placement는 가장 적합한(load가 가장 적은) NUMA Node에 Workload를 배치하여 ESXi Host의 Resource 활용률의 균형을 맞추고 향상
Load Balancing(Workload Migration)은 vCPU와 Memory를 Migration
VM의 demand와 NUMA Node의 자원 활용율을 계속해서 모니터링
vCPU와 Memory를 Node간 분산하여, Memory Locality를 최적화
NUMA Scheduler Initial Placement
VM이 Power On 될 때, NUMA Scheduler는 Initial Placement Algorithm을 적용
이 때는 신규 VM World가 생성되는 시점이기 때문에, 어떤 Workload도 생성되지 않고 있는 상태
따라서, NUMA Scheduler가 획득하고 분석할 수 있는 Workload 관련 정보가 없음
이에 앞으로 사용될 Workload Demand Pattern을 예측하기는 굉장히 어려움
NUMA Scheduler는 Advanced Workload Algorithm을 이용하여, NUMA Node의 전반적인 load pattern의 long-term trend를 정의 --> 결과적으로 가장 적합한 NUMA Node 선택하는 방법
Initial Placement의 가장 큰 목표는 Host의 load가 불균형하거나 VM Resource 경합이 있을 경우에 발생하는 Workload Migration의 비용을 최소화 하는 것
Determining Fit
첫 번째로, NUMA Scheduler는 VM의 vCPU 갯수가 단일 NUMA Node의 core 수를 초과하는지 확인
그 다음으로, preferred NUMA Node에 충분한 가용 Memory가 있는지 확인
Affinity and Node Masks
단일 PPD 내의 vCPU 만이 Home Node에서 Scheduling 가능
vCPU의 수가 단일 CPU package의 core 수를 초과하는 경우, 여러 PPD가 생성되며 PPD는 vCPU 그룹의 affinity 처럼 동작
Memory의 경우에도 일단은 Home Node를 통해서 할당 시도 ## Home Node의 Memory가 충분하지 않을 수 있으므로 이러한 경우에는 Remtoe Node로 부터 할당
Wide-VM의 경우에 NUMA Scheduler가 VM에 Node Mask를 적용
예들어, NUMA Node가 총 4개가 있고, VM의 PPD가 NUMA Node 2와 3에서 Running 중인 상황
vCPU가 NUMA Node2에서 Scheduling되어 Running 중인 경우 Memory는 NUMA Node2와 3에서 Access가 가능하지 NUMA Node0과 1은 node mask list에 없기 때문에 사용하지 않음
하지만 Node Mask는 Soft Affinity Rule을 사용하기 때문에 실환경에서 Memory가 부족한 경우에는 다른 NUMA Node의 Memory도 Access 가능
이러한 Affinity와 Node Mask는 Initial Placement 시점에 정의
VM Placement
NUMA Scheduler는 PPD를 NUMA Node에 위치시키는 결정을 할 때 처음에는 Round-Robin 방식을 이용
예를 들어, VM1이 NUMA Node2와 3에 위치한다면 다음 VM은 NUMA Node0를 사용
Active 상태의 VM의 수도 계속해서 달라지고, Workload Pattern도 동적이기 때문 Round-Robin 방식만 계속해서 사용하는 것은 너무 단순하기 때문에,
Placement를 위해서 자원 사용과 Internode Latency와 같은 추가 Metric을 같이 이용
Internode Latency
ESXi는 Boot 시점에 NUMA Node 간 latency를 확인하며, 이를 Internode Latency라 함
Wide-VM을 위치시킬 때, NUMA Scheduler는 어느 NUMA Node가 가까운지를 결정
즉, Memory를 제공하기 위해 가용한 Memory를 가지고 있는 NUMA Node 중 어느 Node가 가장 가까운지를 파악
Resource Usage
NUMA system은 Normalized Load Average를 구하는데 이를 위해서 NUMA Node의 Exponentially Weighted Moving Average(EWMA)를 이용
앞으로의 load balance의 예측을 하기는 매우 어렵기 때문에, EWMA와 같은 통계 방법을 이용하여 단기간의 변동폭을 줄이고, 장기적 관점에서 Trend를 정의
이러한 방법을 통해 Workload 변동의 위험성을 감소
NUMA Scheduler는 NUMA Node들의 Normalized Load Average를 이용하여, 어느 Node가 가장 Load가 적은지 결정하고 이에 따라 선호 Node를 선택
ESXi Host에서 sched-stats -t numa-pnode 명령어를 통해서 Normalized Load Average 정보 제공 ## loadAvgPct 값
# sched-stats -t numa-pnode nodeID used idle entitled owed loadAvgPct nVcpu freeMem totalMem 0 95 31905 300 0 0 2 118910736 133821428 1 141 31859 0 0 0 8 128794912 134217728 |
sched-stats 정보는 VSI Node에서도 확인 가능
# vsish -e cat /sched/numaStats/numaNodes/0 NUMA node statistics { used:133 msec idle:31867 msec entitled:100 msec owed:0 msec normalized load average:0 pct number of vcpus:2 free memory:118910760 KB total memory:133821428 KB } # vsish -e cat /sched/numaStats/numaNodes/1 NUMA node statistics { used:71 msec idle:31929 msec entitled:0 msec owed:0 msec normalized load average:0 pct number of vcpus:8 free memory:128765028 KB total memory:134217728 KB } |
Testing Initial Placement
SQL Workload를 실행하는 VM 3개, Front-end Workload를 실행하는 VM 1개가 10 Core CPU 2개가 장착된 환경에서 생성되는 것을 가정
첫 번째 VM 최초 배치
VM SQL00는 NUMA Node 0에 위치하고, 약 30% 정도의 load 생성 ## sched-stats -t numa-pnode
두 번째 VM 최초 배치
VM SQL01은 이전 sched-stats 결과 값 중 NUMA Node0의 loadAvgPct 값이 26% 이므로 NUMA Scheduler가 NUMA Node1에 배치
세 번째 VM 최초 배치
두 개의 SQL Workload를 실행하는 VM이 배치된 후 load 현황
NUMA Scheduler는 단순히 round-robin 알고리즘을 이용하여 세 번째 VM이 배치될 곳으로 NUMA Node0를 선택할 수 있지만,
현재 NUMA Node0와 Node1의 loadAvgPct 차이를 기반으로 VM SQL02를 NUMA Node1로 배치
esxtop 결과로도 동일한 내용 확인 가능
네 번째 VM 최초 배치
현재까지 하나의 VM이 NUMA Node0에 위치하고, 두 개의 VM이 NUMA Node1에 위치한 상태
vCPU 기준으로는 NUMA Node0에 vCPU 4개, NUMA Node1에 vCPU 8개 위치
NUMA Scheduler는 Workload의 유동성에 대한 Risk를 처리하기 위해서 Historical Data를 사용
하지만, Historical Data가 있다해도 실제로 앞으로의 Workload 사용량을 예측하기는 매우 어려움
결과적으로는 NUMA Scheduler는 vCPU 개수가 아닌, loadAvgPct 값을 기반으로 VM을 load balance하기 때문에 다음과 같이 NUMA Node1에 3개의 VM이 위치할 수 있음
따라서, NUMA Node의 상태를 정확하게 확인하기 위해서는 아래 2가지 명령어를 사용해야 함
## sched-stats -t numa-pnode
## sched-stats -t numa-clients
Wide-VM Placement
Wide-VM을 Power On 할 때, NUMA Scheduler는 Initial Placement를 위해 여러 NUMA Node에 PPD를 분산시켜야 함
PPD를 분산시키기 위한 NUMA Node를 결정하기 위해서 Internode latency를 고려 ## 가장 작은 Internode latency를 가진 NUMA Node를 활용 + 가장 작은 loadAvgPct도 확인
아래와 같은 사용 환경에서 vCPU가 12개 할당된 SQL03이 배치된다고 가정 ## 단일 CPU Package의 Core 수가 10개이므로 vCPU 12개이면 Wide-VM
vCPU가 12개이므로, 2개의 PPD가 생성되고 VPD마다 6개의 vCPU가 위치
PPD를 2개 가진 SQL03 VM이 배치된 후, NUMA Node0의 vCPU가 4 → 10으로 증가, NUMA Node1의 vCPU가 12 → 18로 증가
이 상황에서 다음 SQL Workload를 실행하는 vCPU 2개를 가진 VM SQL04가 Power On 되면, 최초에는 NUMA Node0와 Node1의 loadAvgPct 차이 때문에 NUMA Node1에 위치 ## NUMA Node1의 vCPU가 18 → 20으로 증가
PPD Internals
여러 vCPU는 여러 NUMA Node의 PPD에 Linear 하게 분산
PPD가 변경은 가능하지만, 수동으로 할 수 있는 것은 아니고 NUMA load balancer가 담당
Initial Placement Load Threshold
Initial Placement Focus
기본적으로 NUMA Scheduler는 Initial Placement 수행 시, loadAvgPct와 Internode latency를 고려한 Round-Robin 알고리즘을 사용
NUMA Scheduler Load Balancing
동일 Host에 서로 다른 VM에서 Workload가 실행 중이어도, load correlation과 load synchronicity가 NUMA node간 자원 사용에 영향을 줄 수 있음
Load Correlation
서로 다른 VM에서 실행 중인 Workload 간의 관계
Front-end와 Back-end Application이 실행 중인 VM 간의 관계로 이해
Load Synchronicity
log-on 이 다량으로 발생하는 시점에 서로 다른 VM의 Workload가 높아지는 현상
또는, 반대로 서로 다른 VM의 Workload가 둘 다 낮아지는 현상
NUMA Scheduler Objectives
NUMA Scheduler의 목표는 VM들의 Memory Locality를 유지하면서, NUMA Node에서 CPU Contention 회피하는 것
Memory Locality를 지키고, CPU Contention을 피하는 것 두 개를 모두 달성하는 것은 매우 어려운 Task
전통적으로 생각해보면, NUMA Scheduler가 NUMA Node 불균형을 감지하고 나면, 특정 VM을 CPU Contention이 낮은 NUMA Node로 Migration
이는 long-term CPU 불균형 관점에서는 맞을 수 있겠지만, short-term 하게 발생하는 불균형의 경우에는 이런 식으로 문제를 해소하기가 어려움
최신 CPU Microarchitecture에서 가장 중요한 부분은 shared Last Level Cache
그래서, 동일 NUMA Node에 여러 VM이 Scheduling 되는 것이 Cache로 부터 동일 데이터를 얻을 수 있어 성능 상 더 큰 이점을 얻는 경우도 있음
Determining CPU Imbalance
NUMA Scheduler는 2초마다 NUMA Node의 상태 평가 → 이를 NUMA balancing epoch라고 부름
NUMA Scheduler는 NUMA Node의 load를 확인하고 rebalance 여부를 결정
NUMA Scheduler는 fairness를 기록하는데, 이를 위해서 owed 라고 불리는 metric를 계산 ## owed는 실제 CPU 사용량과 entitlement 간의 차이
NUMA Scheduler는 개별 NUMA Node의 owed 값을 확인한 후, 두 NUMA Node의 owed 값을 비교
만약 적어도 10% 정도의 load 불균형이 있는 경우에, NUMA Scheduler는 load balancing 작업을 고려
NUMA Scheduler는 load balancing이 필요한 경우에, short-term migration / long-term migration / locality migration 중 하나의 load balancing 방법을 결정
Short-Term Migration
One-Way Move
NUMA Scheduler는 owed metric을 분석해서 어느 VM을 옮겨야 load 불균형을 줄일 수 있을지 파악
결정이 되면, PPD를 한 NUMA Node에서 다른 Node(Contention이 낮은)로 Migrate
vCPU가 Migration 되기 때문에 해당 VM의 Memory도 따라서 이관
하지만, Memory는 순식간에 이동될 수 없어서 일정 기간 동안 기존 NUMA Node에 Memory가 남아 있을 수 있음
문제는 단순한 알고리즘을 사용하는 관계로 연속해서 동일한 VM이 NUMA Node 간에 이동하는 Thrashing이 발생할 수 있음
이를 회피하기 위해서 Long-Term Fairness Threshold를 적용
SWAP
만약, 여러 One-Way Move를 했음에도 load 불균형이 충분히 해소되지 않는 경우,
NUMA Scheduler는 마지막으로 두 개의 NUMA Client(PPDs)를 교체 시도
Long-Term Fairness Migration
NUMA Scheduler의 어려운 점 중 하나는 성능과 load 불균형 간에 무엇을 우선 시 할 것인지를 결정해야 하는 것
3대의 VM과 2개의 NUMA Node를 예로 설명
3대의 VM은 Physical CPU의 용량을 초과하는 유사한 Workload를 실행 중
VM1과 VM2는 NUMA Node0에 위치하고, VM3은 NUMA Node1에 위치
VM1과 VM2는 자신의 CPU entitlement 만큼을 사용할 수 없어, NUMA Node0의 owed metric이 NUMA Node1 보다 높은 상태
NUMA Node0의 owed metric 값을 줄이기 위해서 NUMA Node0에 위치한 VM 한 대를 NUMA Node1로 이관 → 하지만, 이로 인하여 반대로 NUMA Node1의 owed metric 값이 높아지는 부작용 발생 → 이로 인해 위에서 설명한 Thrashing 발생 가능성
NUMA Scheduler는 Thrashing을 방지하기 위해서, 5번의 epoch 동안에 short-term fairness를 tracking 하여 long-term owed 값이 높은 경우에 VM을 다른 NUMA Node로 이관
# vsish -e get /config/Numa/intOpts/LTermFairnessInterval Vmkernel Config Option { Default value:5 Min value:0 Max value:1000 Current value:5 hidden config option:0 Description:duration of long term fairness interval in terms of NUMA rebalance period, 0 indicates that long term fairness is disabled } |
VM이 다른 NUMA Node로 이관되는 것은 Remote Memory 사용이 발생한다는 것을 의미하기 때문에, 만약 Ping pong 현상이 발생하면 이로 인하여 Interconnect bandwidth가 낭비될 수 있음
long-term fairness를 비활성화하여, memory locality가 안정적이도록 할 수 있지만 실제로 비활성화하가 필요한지는 sched-stats -t numa-migration 명령어로 모니터링 필요
# sched-stats -t numa-migration groupName groupID clientID balanceMig loadMig localityMig longTermMig monitorMig loadSwap localitySwap pageMigRate vm.2101536 26406 0 0 0 26 0 9 0 2 0 vm.2101537 26415 0 0 0 3 0 1 0 1 0 vm.2111855 65772 0 0 0 186 0 19 0 4 0 vm.2145499 213912 0 0 0 3 0 1 0 1 0 vm.2148046 226863 0 0 0 36 0 5 0 2 10 vm.2148863 232056 0 0 0 166 0 29 0 2 223 vm.2149367 235134 0 0 0 4 0 1 0 0 0 |
Locality Migrations
CPU Load Balancing이 필요하지 않을 때, NUMA Scheduler는 Locality를 향상시킬 수 있는지 확인
NUMA Scheduler는 Memory Locality를 통한 향상이 20% 이상인 경우 Node Swap만을 적용
Node Swap이 완료되고 나서, Memory Load Balancing을 통해 Data를 새로운 NUMA Node로 이관
Action-Affinity라고 CPU Cache 공유를 최적화하기 위한 기술이 있는데, Action-Affinity 때문에 여러 VM들(Data를 공유하고, IO Context를 공유하고 서로 Communication이 많은)이 같은 NUMA Node에 위치하게 되어 NUMA Node 관점에서 불균형이 발생할 수 있음
아래 예를 보면, 두 대의 VM이 Initial Placement를 통해 최초에는 서로 다른 NUMA Node에 할당
하지만, 두 VM의 Communication이 많은 관계로(Front-end 와 Back-end 관계) NUMA Scheduler는 이를 감지하고, 한 VM을 NUMA Node1에서 NUMA Node0로 Migration
결과적으로, 두 VM이 동일 NUMA Node에 위치하게 되고, NUMA 관점에서 불균형 발생
하지만, 반드시 VM을 여러 NUMA Node로 분산시키는 것만이 정답은 아니다.
예를 들어, VM들 간에 서로 Communication 하는 양이 매우 많은 경우에는 같은 NUMA Node에 위치하여, Cache를 공유함으로써 이점을 얻을 수도 있음
하지만 반대로, 다른 VM들은 Cache Contention을 피하기 위해서 더 많은 Cache를 이용함으로써 이점을 얻을 수도 있음
Numa.LocalityWeightActionAffinity 값을 0로 설정하여, 이 기능을 비활성화 할 수 있음
관련 케이스
https://confluence.eng.vmware.com/pages/viewpage.action?pageId=1478144173
Memory Load Balancing
NUMA Scheduler는 Locality를 최대화하려고 하지만, VM이 필요로 하는 Memory 요청을 만족시키기 위해서 Remote Memory를 할당하는 경우도 발생
일단 Remote Memory가 할당되는 경우, NUMA Scheduler는 가능한 Remote Memory를 Local Memory로 Migration 하려고 노력
Migration 과정에서 Virtual Memory가 새로운 Physical Memory에 Mapping 되지만, Guest OS 측면에서 Impact은 없음
VM이 local NUMA Node의 Memory보다 큰 Memory를 필요로 하는 경우를 생각해보자.
# sched-stats -t numa-global 명령어로 시스템의 전체 NUMA 통계치 확인 --> remoteMemory 값 확인
# sched-stats -t numa-migration 명령어로 개별 VM의 NUMA migration 확인 --> pageMigRate 값 확인