vSphere 6.5 vNUMA Deep Dive

NUMA چیست؟

در دهه‌ اخیر سرعت محاسبات CPUها به شکل قابل توجهی افزایش یافته است، در این شرایط دسترسی سریع و با پهنای باند بالا به حافظه بیش از پیش مورد نیاز خواهد بود. در معماری سنتی تعداد زیادی CPU باید برای دستیابی به حافظه بر سر پهنای باند BUS رقابت می­کردند؛ این معماری را اصطلاحاً (Symmetric Multiprocessing(SMP می‌گویند. یک راه حل برای مشکل این است که بتوانیم از طریق چندین BUS به حافظه دسترسی داشته باشیم؛ این معماری جایگزین را اصطلاحاً (NUMA(Non Uniform Memory Access می‌گویند. در معماری NUMA چند پردازنده به همراه حافظه مستقل از طریق ارتباطات High Performance با هم در ارتباط هستند؛ اصطلاحاً هر کدام از این واحد‌های مستقل CPU و  حافظه را NUMA Node می‌گویند.

در معماری NUMA با وجود حل مشکل پهنای باند، چالش جدیدی به وجود آمده است. واضح است که پردازش‌های روی یک Node، نیازمند اطلاعات داخل حافظه خواهند بود؛ این داده‌های مورد نیاز یا داخل حافظه محلی همان نود هستند، یا اینکه داخل حافظه سایر نودها. به عبارتی در معماری NUMA دسترسی به حافظه یا Local است یا Remote. مشکل اصلی این است که Remote Memory Access بر خلاف Local Memory Access با تاخیر همراه بوده و غیر بهینه است. یکی از وظایف اصلی سیستم عامل این است که پردازش‌ها را به گونه‌ای Schedule کند که تا حد امکان، دسترسی به حافظه Local باشد.

مجازی سازی و NUMA

VMware از سال 2002 و از نسخه ESXi 1.5 با معماری NUMA کاملاً سازگار است. برای همین منظور در ساختار VMkernel در کنار CPU Scheduler، ساختاری با نام NUMA Scheduler تعبیه شده است. اگر ESXi روی سخت افزاری مبتنی بر معماری NUMA اجر شود، ماژول NUMA Scheduler فعال خواهد شد. همچنین لازم است که قابلیت (Node Interleaving (Interleaved Memory در BIOS غیر فعال شده باشد تا ESXi سیستم را به عنوان NUMA تشخیص دهد. علاوه بر این، سرور باید حداقل 4 CPU Core و حداقل 2 Core per NUMA Node داشته باشد.

هدف اصلی NUMA Scheduler بهینه سازی تخصیص CPU و RAM به ماشین مجازی است. برای این منظور دو وظیفه اصلی بر عهده NUMA Scheduler گذاشته شده است:

1. Initial Placement of VMs across NUMA Nodes؛ هر ماشین مجازی که توسط NUMA Scheduler مدیریت می‌شود، به یک نود اختصاص داده خواهد شد که اصطلاحاً به آن (NHN (Home Node NUMA گفته می‌شود. زمانی که قرار است حافظه به ماشین مجازی تخصیص داده شود، ESXi تلاش خواهد کرد که حافظه را داخل Home Node اختصاص دهد و همچنین vCPUهای ماشین مجازی محدود به NUMA Home Node خواهند بود تا تضمین شود که دسترسی به حافظه Local و بهینه خواهد بود.

2. Dynamically Load Balancing VMs across NUMA Nodes؛ با توجه تغییراتی که در Load سیستم در طول زمان ایجاد می‌شود، امکان دارد NUMA Scheduler برای حل مشکلات عدم توازن بین نودها، NUMA Home Node یک ماشین مجازی را تغییر دهد. به صورت پیش فرض هر 2 ثانیه یکبار سیستم بار موجود روی تمام نودها را بررسی خواهد کرد تا در صورت نیاز، Home Node یک یا چند ماشین مجازی را تغییر دهد. اما از آنجا که اینکار ممکن است Remote Memory Access را افزایش دهد، NUMA Scheduler باید حافظه ماشین مجازی را به نود جدید منتقل کند (Page Migration).

برخی از ماشین‌های مجازی ممکن است توسط NUMA Scheduler مدیریت نشوند. به عنوان مثال ماشین‌هایی که CPU Affinity یا Memory Affinity برای آن‌ها تعریف شده است.

بهینه سازی‌هایی که NUMA Scheduler اعمال خواهد کرد، کاملاً از دیدگاه Guest OS، به صورت Transparent خواهد بود؛ در نتیجه حتی روی سیستم عامل‌های قدیمی مثل Windows NT 4.0 که از معماری NUMA پشتیبانی نمی‌کنند نیز اعمال خواهد شد؛ و این خود یکی از مزایای مهم مجازی سازی است.

توجه دارید که تخصیص Physical CPU به ماشین مجازی و زمانبندی آن، همچنان وظیفه CPU Scheduler است نه NUMA Scheduler.

قبل از هر چیز لازم است که اجزای مختلف درگیر در این معماری را شرح دهیم. این اجزا در 3 لایه زیر ایفای نقش خواهند کرد:

* لایه فیزیکی

* لایه VMkernel

* لایه ماشین مجازی (VM)

در رابطه با لایه فیزیکی منظور ما از CPU Package یک CPU است که روی Socket سوار می‌شود و در ساختار خودش Core، L1 Cache، L2 Cache و (Last Level Cache (LLC دارد. این CPU Package در کنار Local Memory متصل، تشکیل یک NUMA Node را خواهد داد.

در لایه VMkernel مفهومی با نام pCPU پیاده سازی شده است که یک Abstraction از لایه فیزیکی است. یک pCPU می‌تواند از یک (Hyper-Threading (HT استفاده کند که اصطلاحاً به آن Logical Processor گفته می‌شود، یا اینکه یک Core را در اختیار بگیرد.

در لایه ماشین مجازی نیز مفهومی با نام vCPU و Virtual Socket وجود دارد. یک vSocket می‌تواند به یک pCPU نگاشت (Map) شود یا اینکه ممکن است یک vSocket روی چند pCPU نگاشت شود (در حالت Multi Core per Socket). همانطور که pCPU یک مفهومی منطقی و انتزاعی ازPhysical CPU است، vCPU هم همین نقش را برای pCPU بازی می‌کند. به عبارت دقیقتر vCPU یک Logical Representation و Abstraction از pCPU می‌باشد.

اما در شکل فوق هنوز اشاره‌ای به ساختار NUMA نشده است. در ادامه به بررسی ساختار NUMA در معماری ESXi خواهیم پرداخت.

گفتیم که وظیفه اصلی NUMA Scheduler یکی Initial Placement و دیگری Load Balancing بین NUMA Nodeهاست. NUMA Scheduler برای این منظور دو ساختار منطقی با نام (NUMA Home Node (NHN و NUMA Client ایجاد کرده است.

(NUMA Home Node (NHN

NHN یک Logical Representation از CPU Package و Local Memory آن است. NUMA Scheduler، باید Physical Coreهای موجود در CPU Package را شمارش کند. سپس نتیجه را با vCPUهای ماشین مجازی مقایسه می‌کند. این مقایسه برای Initial Placement و Load Balancing بسیار مهم است. چنانچه تعداد vCPUهای یک ماشین مجازی بیشتر از تعداد Coreهای فیزیکی موجود در CPU Package باشد، لازم است که آن VM روی چند NUMA Node توزیع شود. واضح است که با کاهش منطقی vCPUها می‌توان از این توزیع شدن روی چند NUMA Node جلوگیری کرد؛ هرچند که گاهی اوقات اجتناب ناپذیر خواهد بود!

به صورت پیش فرض فقط Coreها شمارش خواهد شد و Hyper-Threading را وارد بازی نخواهیم کرد!

توجه داشته باشید که فقط مساله شمارش vCPU و Physical Core نیست! اگر RAM اختصاصی به یک ماشین مجازی بیشتر از RAM موجود در یک NUMA Node باشد، باز هم ناگزیر ماشین‌ مجازی روی چند NUMA Node توزیع خواهد شد هرچند که به صورت پیش فرض، NUMA Scheduler نسبت به این مساله ناآگاه است که در نهایت اگر مواظب نباشید باعث افت کارایی خواهد شد.

حال وظیفه NUMA Scheduler این است که تمام تلاش خود را انجام دهد تا به ماشین مجازی Local Memory تخصیص داده شود تا درگیر عملیات پر هزینه Remote Memory Access نشویم.

NUMA Client

یک NUMA Client، شامل vCPUها و حافظه‌های ماشین مجازی است که داخل یک NUMA Home Node جا (fit) می‌شوند. NUMA Client کوچکترین واحدی است که NUMA Scheduler از آن برای Initial Placement و Load Balancing استفاده می‌کند.

وقتی یک ماشین مجازی را روشن می‌کنید، تعداد vCPUها شمارش و با تعداد Coreهای فیزیکی مقایسه می‌شود؛ اگر vCPUها کمتر بودند، یک توپولوژی (Virtual UMA (Uniform Memory Access به همراه یک Uniform Memory Address Space در اختیار ماشین مجازی قرار خواهد گرفت؛ در غیر این صورت باید چند NUMA Client برای آن ماشین مجازی ایجاد شود. به عنوان مثال اگر یک ماشین مجازی با 12 vCPU بخواهد روی یک سرور با CPU Package-10 Core روشن شود، باید دو NUMA Client ساخته شود و vCPUها به صورت مساوی بین این دو NUMA Client پخش شود.

یک نکته بسیار مهم وجود دارد که توجه به آن حیاتی است! بر خلاف آنچه در شکل فوق مشاهده می‌کنید، هیچ گونه وابستگی بین pCPUها (vCPUها) و NUMA Client وجود ندارد. CPU Scheduler برای توازن و بهینه سازی، آزادانه می‌تواند تصمیم بگیرد که یک vCPU را روی هر کدام از pCPUهایی که CPU Package در اختیارش قرار داده، Schedule کند.

vNUMA Node

چنانچه برای یک ماشین مجازی بیش از یک NUMA client ایجاد شود، اصطلاحاً آن ماشین مجازی را Wide-VM می‌نامند. در این شرایط NUMA Scheduler با هدف بهینه سازی بیشتر، برای این ماشین مجازی یک توپولوژی اختصاصی با نام (Virtual NUMA (vNUMA خواهد ساخت. vNUMA مستقیماً در اختیار Guest OS قرار خواهد داد تا لازم نباشد ماشین مجازی و سیستم عامل درگیر توپولوژی NUMA سرور فیزیکی (PNUMA) شوند. این قابلیت از ESXi نسخه 5 و VM Version نسخه 8 به بعد معرفی شد.

به عنوان مثال در همان سناریو قبلی، وقتی یک ماشین مجازی با 12 vCPU می‌سازیم، vNUMA دو نود مجازی که هر کدام 6 vCPU دارد به سیستم عامل ارائه می‌کند؛ این موضوع باعث می‌شود اجازه دهیم خود Guest OS بتواند مستقلاً یک لایه NUMA Optimization را اعمال کند.

اگر قرار باشد به هر دلیلی بیش از یک vNUMA Client برای یک ماشین مجازی ایجاد شود، NUMA Scheduler مسئول Auto-Sizing برای vNUMA Clientها خواهد بود. به صورت پیش فرض باید vCPUها به صورت مساوی بین کمترین تعداد NUMA Clientها تقسیم شوند. عملیات Auto-Sizing در اولین باری که ماشین مجازی روشن شود، انجام خواهد گرفت. در زمان اولین بار بوت شدن ماشین مجازی دو خط زیر به Advanced Settings ماشین مجازی افزوده خواهد شد:

numa.autosize.vcpu.maxPerVirtualNode=X

numa.autosize.cookie = XXXXXX

این تنظیمات تا زمانی که تعداد vCPUهای ماشین مجازی تغییر نکند، ثابت باقی خواهد ماند. از طرفی عملیات Auto-Sizing با توجه به معماری Physical NUMA سروری انجام خواهد شد که ماشین مجازی برای اولین بار روی آن روشن شده است. در نتیجه اگر کلاستری داشته باشیم که معماری NUMA سرورهای آن با هم متفاوت باشد، در نتیجه عملیات vMotion ممکن است به شدت باعث افت کارایی ماشین‌های مجازی شود. در چنین شرایطی حتی Reboot کردن ماشین مجازی به امید Auto-Sizing مجدد با توجه به معماری جدید نیز بی‌فایده خواهد بود؛ مگر اینکه از دو تنظیم زیر در Advanced Settings ماشین مجازی استفاده کنیم:

numa.autosize.once = FALSE

numa.autosize = TRUE

اینکار باعث خواهد شد که بعد از هر Power-cycle و بعد از هر بار vMotion، واحد NUMA Scheduler مجبور باشد که NUMA Client Size را تغییر دهد. به شدت مواظب استفاده از این دو تنظیم باشید؛ چرا که لزوماً تمام Workloadها با تغییرات جدید NUMA Client Size به خوبی کنار نخواهند آمد!! این خود انگیزه بسیار قدرتمندی خواهد بود تا شما را مجاب سازد کلاسترهایی با معماری یکسان (Homogeneous) ایجاد کنید.

تنظیمات پیشرفته معماری vNUMA

ممکن است شرایطی پیش آید که بخواهیم بنا به دلائلی، اندازه پیش فرض NUMA Client را تغییر دهیم؛ برای این منظور می‌توان از پارامترهای زیر جهت تغییر رفتار پیش فرض استفاده کنیم (هر چند این کار توصیه شده نیست، مگر اینکه آگاهی کامل از نتیجه کار داشته باشید!).

1. numa.vcpu.min

به صورت پیش فرض vNUMA تنها برای ماشین‌هایی ایجاد خواهد شد که یکی از شرایط زیر را داشته باشند:

* ماشین‌هایی که تعداد vCPUهای آن‌ها 9 یا بیشتر باشد.

* زمانی که تعداد vCPUهای ماشین مجازی بیشتر از تعداد Coreهای یک CPU Package باشد. گفتیم که این شمارش فقط تعداد Coreهای فیزیکی را در نظر خواهد گرفت نه Logical Processorها را!

با استفاده از این تنظیم می‌توان این حداقل، یعنی عدد 9 را تغییر داد، تا برای ماشین‌هایی با vCPU کمتر نیز vNUMA ساخته شود.

2. numa.vcpu.maxPerMachineNode

ممکن است برنامه‌ای داشته باشیم که حساس به Memory Bandwidth باشد نه Memory Access Latency! در چنین شرایطی حالت بهینه، بر خلاف حالت پیش فرض این خواهد بود که ماشین مجازی روی NUMA Nodeهای بیشتری توزیع شود تا پهنای باند بیشتری برای دسترسی به Memory داشته باشد. این تنظیم به ما اجازه می‌دهد حداکثر تعداد vCPUهایی که داخل یک NUMA Client قرار خواهند گرفت را تغییر دهیم تا NUMA Clientهای بیشتری ساخته شود.

3. Count Threads Not Cores

با استفاده از تنظیم numa.vcpu.preferHT=TRUE می‌توان NUMA Scheduler را مجبور کرد که در زمان شمارش، به جای شمارش Coreهای فیزیکی، تعداد Threadها یا Logical Processorها را مد نظر قرار دهد. با استفاده از این تنظیم ممکن است بتوانیم شرایطی را ایجاد کنیم که یک Wide-VM بتواند روی یک NUMA Node، جا بگیرد. به عنوان مثال یک ماشین مجازی با 12vCPU روی یــــک سرور 2Socket–10Core، دو NUMA Client ایجاد خواهد کرد (6-6). اما با استفاده از numa.vcpu.preferHT=TRUE، واحد NUMA Scheduler می‌تواند یک NUMA Client بسازد تا تمام vCPUها را روی یک CPU Package قرار دهد.

مجدداً توجه به این نکته ضروری است که در این حالت هم CPU Scheduler لزوماً مجبور نخواهد بود یک vCPU را روی یک (Logical Processor (HT، اجرا کند. CPU Scheduler همچنان تلاش خواهد کرد که یک vCPU بتواند یک Physical Core را تصاحب کند. این موضوع بر عهده CPU Scheduler است نه NUMA Scheduler و وابسته به Ratio CPU Overcommitment در محیط است.

با وجود اینکه تکنولوژی Hyper-Threading در محیط‌ مجازی CPU Utilization را افزایش خواهد داد و Overall Performance را در حدود 10-15% بهبود خواهد بخشید؛ اما همانطور که می‌دانید، Logical Processorها منابع یک Physical Core را به اشتراک خواهد گذاشت؛ در نتیجه CPU Progression در مقایسه با حالتی که پردازشها‌ Physical Core را در اختیار دارند، کاهش خواهد یافت. از اینرو لازم است بررسی کنیم که آیا ماشین مجازی و برنامه‌هایی که قرار است روی آن اجرا شوند، Cache Intensive هستند یا CPU-cycle Intensive؟ استفاده از numa.vcpu.preferHT=TRUE به CPU Scheduler دستور می‌دهد تا Memory Access را نسبت به CPU Resource در اولویت قرار دهد. همانطور که گفته شد، برای فعال کردن این گزینه، کاملاً آگاهانه و با تست کامل اقدام کنید؛ در غیر این صورت مقدار پیش فرض (False) را تغییر ندهید.

واضح است که در این شرایط هم لازم است میزان حافظه تخصیص داده شده به ماشین مجازی، کمتر یا مساوی با حافظه NUMA Node باشد، در غیر این صورت Remote Access اتفاق خواهد افتاد و کارایی preferHT را کاهش خواهد داد!

تنظیم این گزینه Per-VM خواهد بود، هر چند که می‌توان در صورت نیاز این گزینه را در سطح Host فعال کرد (KB2003582).

اگر این تنظیم را برای یک ماشین که قبلاً یکبار روشن شده است، اعمال کنیم، بنا به دلائلی که پیشتر گفته شد، تاثیری نخواهد داشت و نیاز به استفاده از تنظیمات تکمیلی دیگری خواهد بود.

4. Cores per Socket

تنظیم (Core per Socket (GUI یا (cpuid.coresPerSocket (Advanced Settings به صورت پیش فرض روی عدد یک تنظیم شده است. تغییر مقدار پیش فرض و افزایش آن باعث خواهد شد که vNUMA مستقیماً ساخته شده و در اختیار VM قرار گیرد که لزوماً ممکن است بهینه نباشد. البته از نسخه 6.5 به بعد، این تنظیم دیگر تاثیری در رفتار vNUMA نخواهد داشت و vNUMA تحت هر شرایطی حالت بهینه را در اختیار Guest OS قرار خواهد داد.

چگونه به معماری vNUMA دسترسی داشته باشیم؟

در داخل فایل VMware.log هر ماشین مجازی اطلاعاتی در رابطه با معماری و ساختار vNUMA وجود دارد. اما به جای دانلود و بررسی خط به خط این فایل می‌توان از دستور زیر استفاده کرد:

vmdumper -1 | cut -d \/ -f 2-5 | while read path; do egrep -oi DICT.* (displayname.*|numa.*|cores.*|vcpu.*|memsize.*|affinity.*)=.*|Inuma:.*|numaHost:.*’ “/$path/vmware.log”; echo -e; done

توجه داشته باشیم که برای کسب اطلاعات کامل، لازم است که حداقل یکبار ماشین مجازی روشن شده باشد تا ساختار NUMA Client، بتواند Auto-Size شود.

سناریو 1: تصویر زیر خروجی اجرای دستور فوق برای یک ماشین مجازی با 10vCPU روی یک سرور (Dual E5-2630 v4 (10 Core per CPU Package و ESXi 6 را نمایش می‌دهد.

بر اساس اطلاعات خروجی می‌توان تشخیص داد که این ماشین مجازی 10 vCPU دارد (numvcpus) که به صورت One Core per Socket تنظیم شده‌اند (cpuid.coresPerSocket = 1). و خط آخر توپولوژی vNUMA را شرح می‌دهد. تمام 10 vCPU داخل یک Physical Domain تجمیع شده‌اند. و این یعنی CPU Scheduler، تمام vCPUهای ماشین مجازی را روی یک Physical CPU Package و یک NUMA Node، Schedule خواهند کرد.

جهت کسب اطلاعات بیشتر لازم است که ساختار vNUMA را از داخل Guest OS بررسی کنیم. برای این منظور می‌توان از دستور CoreInfo در ویندوز و numactl در لینوکس استفاده کرد. خروجی زیر مربوط به ساختار vNUMA همین ماشین مجازی است که از داخل سیستم عامل گزارش شده است.

سناریو 2: فرض کنید تعداد vCPU این ماشین فرضی را به 16 عدد افزایش می‌دهیم، که بیشتر از تعداد Coreهای موجود در NUMA Node است. خروجی دستور vmdumper و CoreInfo برای این ماشین مجازی به شکل زیر خواهد بود.

همانطور که مشاهده می‌کنید در سناریو اول، numa.autosize.vcpu.maxPerVirtualNode=10 در حالی که در سناریو دوم numa.autosize.vcpu.maxPerVirtualNode=8. در حالت دوم، VMkernel کاملاً مساوی 16 vCPU را مساوی بین دو vNUMA Node پخش کرده است. NUMA Scheduler تلاش کرده است که بیشترین تعداد vCPU را در کمترین Virtual NUMA Node جا دهد؛ و این ایده‌آل ترین شرایط است. در نتیجه 8vCPU داخل یک Virtual Node قرار گرفته است.

توجه دارید که در این حالت، مدل پیش فرض یعنی 1Core per Socket را تغییر نداده‌ایم. اگر بنا به دلائلی از جمله Licensing نیاز به تغییر حالت پیش فرض و افزایش Coreها بودیم. ایده‌آل ترین شرایط 2vSocket–8Core برای این ماشین مجازی فرضی بود تا دقیقاً به همان vNUMA ایده‌آل برسیم.

پیش از ادامه بحث لازم است نگاه دقیق‌تری به مفهوم NUMA Client داشته باشیم. VMkernel در راستای پیاده سازی مفهوم NUMA Client از دو ساختار Physical Proximity Domain (PPD) و Virtual Proximity Domain (VPD) استفاده می‌کند. VPD ساختاری است که در اختیار Guest OS قرار می‌گیرد و PPD ساختاری است که NUMA Scheduler از آن برای Initial Placement و Load Balancing استفاده می‌کند.

(Physical Proximity Domain(PPD

مفهوم PPD با این هدف شکل گرفته است که بتوانیم گروهی از vCPUها را در کنار هم تجمیع کنیم تا در نهایت فقط یک CPU Package را در اختیار آن‌ها قرار دهیم (پخش و پلا نشوند!!) و نهایتاً از آن برای Initial Placement و Load Balancing استفاده خواهد شد. PPD همیشه منطبق بر VPD است به شرطی که سایز آن از بزرگتر از یک NUMA Node نشود! اندازه یک PPD هرگز از اندازه یک CPU Package (NUMA Node) بزرگتر نخواهد شد.

مجدداً ذکر این نکته ضروری خواهد بود که NUMA Scheduler تنها فرآیندی خواهد بود از Proximity Domainها (VPD و PPD) استفاده می‌کند و از آن‌ها آگاه است. این دو ساختار از دید CPU Scheduler کاملاً مخفی خواهند بود. CPU Scheduler تنها مسئول نگاشت بین vCPU و pCPU است. NUMA Scheduler از PPD استفاده می‌کند تا مطمئن شود vCPUهایی که با هم در یک گروه قرار گرفته‌اند، مشترکاً از منابع CPU Package استفاده خواهند کرد.

(Virtual Proximity Domain(VPD

VPD ساختاری است که در اختیار Guest OS قرار می‌گیرد. تا ESXi 6، اندازه VDP، وابسته به تعداد vCPUها و Physical Coreها است، به شرطی که Core per Socket مساوی یک باشد؛ در غیر این صورت، این تنظیمات Core per Socket خواهد بود که اندازه VDP را تعیین می‌کند. اما از نسخه 6.5 به بعد اندازه VPD همیشه به صورت بهینه تعیین خواهد شد و Core per Socket تاثیری روی اندازه VPD نخواهد داشت.

شکل فوق ساختار VPD و PPD را برای ماشین مجازی سناریو 2 نمایش می‌دهد.

سناریو 3: این احتمال وجود دارد که یک VPD روی چند PPD قرار گیرد. یک ماشین مجازی با 40 vCPU (Core Per Socket = 20) را در نظر بگیرید که روی سروری با 4 CPU Package (10 Core) که ESXi نسخه 6 روی آن نصب شده است در حال اجرا است.

با توجه به اینکه حالت پیش فرض (One Core per Socket) را تغییر داده‌ایم، این تنظیم Core per Socket است که اندازه را VPD را دیکته می‌کند؛ بنابراین اولین باری که ماشین مجازی روشن می‌شود، 2 VPD ساخته می‌شود که هر کدام 20 vCPU دارد. این دو VDP روی چهار PPD قرار گیرد. این موضوع سبب ایجاد یک حالت غیر بهینه خواهد شد. موضوع قرار گرفتن یک VPD روی چند PPD، مساله‌ای است که همیشه باید از آن اجتناب کرد؛ این وضعیت سیستم عامل را به اشتباه خواهد انداخت و سبب خواهد شد، Remote Memoryها به اشتباه توسط Guest OS به عنوان Local در نظر گرفته شوند.

اگر به خاطر مساله Licensing نیاز به تغییر مدل پیش فرض 1Core per Socket در این سناریو بودیم، بهینه ترین حالت ساخت ماشین مجازی با  4Socket–10Core است تا منطبق در PPD و ساختار Physical NUMA باشد.

سناریو 4: همانطور که پیشتر گفته شد، NUMA Scheduler در ESXi 6.5 مستقل از تنظیم Core Per Socket عمل می‌کند. تا نسخه 6 اگر ماشینی با (16vCPU (2 Core per Socket می‌ساختیم، 8 PPD و 8 VPD با جزئیات زیر ایجاد می‌شد.

مشکل اصلی این معماری این است که توپولوژیvNUMA ، نماینده مناسبی برای توپولوژی  Physical NUMA نیست! در ادامه دلیل این موضع را شرح خواهیم داد.

سیستم عامل 16 CPU را در اختیار خواهد داشت که روی 8 Socket توزیع شده‌اند. هر جفت از CPUها (یک سوکت مجازی)، Cache و Local Memory خودش را خواهد داشت. در چنین شرایطی سیستم عامل فضای آدرس دهی مربوط به یک vSocket دیگر را به عنوان Remote فرض خواهد کرد. در واقع سیستم عامل مجبور است با تکه‌ها و فضای‌های کوچکی از حافظه تعامل داشته باشد (Memory Fragmentation). تصویر زیر بزرگ نمایی قسمتی از معماری این vNUMA را نمایش می‌دهد.

اما در واقعیت، این 16 vCPU و 8 VPD در نهایت روی 2 Physical Node باید Schedule شوند. در نهایت 8vCPU، یک LLC را به اشتراک خواهند گذاشت و به Memory Pool یکسانی دسترسی خواهند داشت.

خروجی CoreInfo برای این ماشین به شکل زیر خواهد بود.

به بخش آخر خروجی فوق (Cross-NUMA Node Access Cost) با دقت بیشتری نگاه کنید. سیستم عامل فقط دسترسی 00 به 00 را بهینه و Local می‌داند در حالی که در معماری فیزیکی، بر خلاف تصور سیستم عامل و معماری vNUMA، دسترسی 00 به 00، 01، 02 و 03 کاملاً Local و بهینه است؛ اعداد این جدول به خوبی بیانگر این حقیقت است.

برای حل این مشکل Fragmentation، در نسخه 6.5، سایز VPD، فقط و فقط تابعی از تعداد Coreها در CPU Package است. در این شرایط vNUMA به شکل دقیق‌تری Physical NUMA را در اختیار ماشین مجازی قرار خواهد داد.

خروجی دستور vmdumper برای ماشین فوق در صورتی که روی ESXi 6.5 اجرا ‌شود به شکل زیر خواهد بود:

عبارت جدید NUMA Config: Consolidation=1، به این معنی است که  NUMA Scheduler تلاش می‌کند تا حد امکان vCPUها را در Physical Proximity Domainهای کمتری تجمیع کند.

توجه دارید که در این شرایط جدید، تنظیم Core Per Socket تغییر نکرده است بلکه فقط در تعیین اندازه VPD و PPD دخالت ندارد. در نتیجه داخل یک VPD چند سوکت ایجاد شده  و هنوز فضای آدرس دهی مربوط به Cache، به صورت Fragmented در دسترس خواهد بود. اما هر VPD، تنها یک فضای آدرس دهی را ارائه خواهد کرد که با معماری فیزیکی نیز همخوانی دارد.

خروجی CoreInfo در شرایط جدید به شکل زیر خواهد بود:

با این مدل جدید، توپولوژی vNUMA تطبیق بیشتری با معماری فیزیکی NUMA خواهد داشت و در نتیجه به Guest OS اجازه خواهیم داد که بهینه سازی دقیق‌تری برای پردازش‌ها و دسترسی به حافظه داشته باشد.

Memory Size و NUMA

NUMA Scheduler در زمان ساخت NUMA Clientها به صورت پیش فرض توجهی به میزان حافظه تخصیص داده شده به ماشین مجازی ندارد. احتمال دارد اندازه vRAM یک ماشین مجازی بزرگتر از حافظه موجود در یک NUMA Node باشد در حالی که تعداد vCPUها کمتر از تعداد Coreهای یک NUMA Node باشد.

به عنوان مثال فرض کنید هر CPU Package، دارای 12 Core باشد و هر Node دارای 64GB حافظه. در چنین شرایطی ساخت یک ماشین مجازی با 8 vCPU و 96GB حافظه شرایطی خاصی را ایجاد خواهد کرد. وقتی این ماشین مجازی روشن شود یک VPD و یک PPD ایجاد خواهد شد؛ چون اندازه VPD و PPD تنها و تنها به واسطه Physical Coreهای تعیین خواهد شد و موضوع CPS و vRAM تاثیری در آن ندارد.

با توجه تعداد vCPUها، این ماشین مجازی روی یک NUMA Node، به خوبی fit خواهد شد. در حالی که به خاطر اندازه vRAM، این ماشین مجازی مجبور به استفاده از Remote Memory خواهد بود.

جهت حل این مشکل ایده خوبی خواهد بود که از تنظیم numa.consolidate = 0 استفاده کنیم. این تنظیم که در نسخه ESXi 6.5 افزوده شده است به صورت پیش فرض روی 1 تنظیم شده است، به این معنی که NUMA Scheduler تلاش می‌کند تا حد امکان vCPUها را در Physical Proximity Domainهای کمتری تجمیع کند. چنانچه از numa.consolidate = 0 استفاده کنیم، NUMA Scheduler سعی می‌کند vCPUها تا حد امکان روی NUMA Nodeهای بیشتری توزیع شوند. بنابراین حالت زیر ایجاد خواهد شد.

اما چند نکته بسیار مهم در رابطه با معماری فوق؛

* اولاً در این معماری اصطلاحاً یک Soft Affinity وجود دارد که باعث خواهد شد vCPUها آزادانه بتوانند هر Node دلخواه را انتخاب کنند. اما در چنین شرایطی اگر Applicationهای ما Singe Thread باشند، نهایتاً در وضعیتی گرفتار خواهیم شد که حجم زیادی از حافظه به صورت Remote در دسترس خواهد بود.

* ثانیاً گفتیم که قرار گرفتن یک VPD روی چند PPD، مساله‌ای است که همیشه باید از آن اجتناب کرد؛ این وضعیت سیستم عامل را به اشتباه خواهد انداخت و سبب خواهد شد، Remote Memoryها به اشتباه به عنوان Local در نظر گرفته شوند. در معماری فوق سیستم عامل دچار این اشتباه پر هزینه خواهد شد. متاسفانه این مشکلی است که آقای Denneman در کتاب VMware vSphere 6.5 Host Resources Deep Dive از آن غافل بوده است! لازم است که در شرایط فوق سیستم عامل را کمک کنیم تا آگاه باشد روی دو Node جداگانه در حال اجرا است. استفاده از تنظیم numa.autosize.vcpu.maxPerVirtualNode=4 در کنار numa.consolidate = 0 ایده بسیار خوبی خواهد بود. این کار باعث ایجاد دو VDP خواهد شد که روی دو PPD قرار گرفته و نهایتاً روی دو Node اجرا خواهند شد. البته این تنظیمات مربوط به نسخه 6.5 می‌شود. در نسخه‌های قبل از 6.5 این هدف با ساخت یک ماشین مجازی 2Socket*4Core امکان پذیر بود.

* ثالثاً اصلاً ساختار vNUMA برای این ماشین مجازی پیاده سازی نخواهد شد! چراکه تعداد vCPUهای آن کمتر از 9 تا می‌باشد!! بنابراین لازم است که از تنظیم numa.vcpu.min=8 استفاده کنیم.

CPU Hot-add و NUMA

توجه به این نکته ضروری است که با فعال سازی CPU Hot Add، قابلیت vNUMA نیز غیر فعال خواهد شد. غیر فعال سازی vNUMA باعث می‌شود که Guest OS اطلاعات صحیحی از ساختار و توپولوژی NUMA در سرور فیزیکی نداشته باشد و در نتیجه امکان دارد سیستم عامل تصمیمات و Scheduling غیر بهینه‌ای داشته باشد که نهایتاً منجر به افت کارایی برنامه‌هایی داخل ماشین مجازی خواهد شد. (تنها یه VPD ساخته خواهد شد و در اختیار Guest OS قرار می‌گیرد.)

اما یک سوال!‌ چنانچه در ماشین مجازی تعداد vCPU‌ها کمتر از تعداد Coreهای CPU Package باشد و قابلیت CPU Hot-add را فعال کنیم چه اتفاقی خواهد افتاد؟ آیا باعث افت کارایی خواهد شد؟ جواب خیر است… البته تا زمانی که داخل یک NUMA Node بتواند fit شود.

اما اگر ماشین مجازی ما یک Wide-VM باشد، با فعال سازی CPU Hot-add، با قطعیت نمی‌توان اندازه NUMA Client را تعیین کرد. به یاد دارید که اندازه NUMA Client در اولین بار روشن ماشین مجازی تعیین خواهد شد، در حالی که CPU Hot-add اجازه خواهد داد تعداد vCPUها در حالی که ماشین مجازی ما روشن است، تغییر کند. با فعال بودن این قابلیت، VUMA Scheduler نمی‌تواند تضمین دهد که Optimization را به درستی انجام دهد.

 Memory Hot Plugو NUMA

بر خلاف CPU Hot-add، Memory Hot Plug به خوبی حتی برای Wide-VM هم پشتیبانی می‌شود. زمانی که Memory Hot Plug فعال باشد و به یک ماشین مجازی روشن Memory اضافه کنیم، NUMA Scheduler حافظه جدید تخصیص داده شده را روی تمام NUMA Nodeها پخش خواهد کرد؛ در حالی که در نسخه‌های قبلی ESXi، حافظه فقط روی Node 0 تخصیص داده می‌شد.

(Transparent Page Sharing (TPS و NUMA

خبر خوب این است که TPS برای NUMA بهینه سازی شده است، به عبارتی Page Sharing فقط داخل یک NUMA Node انجام می‌شود.

NUMA Monitoring

در نهایتاً لازم است که اشراف کاملی بر وضعیت NUMA و بهینه‌ سازی‌های مرتبط با آن داشته باشیم. این کار باعث خواهد شد که بتوانیم مشکلات احتمالی را به سرعت تشخیص داده و بر طرف نماییم. برای این منظور از دستور ESXTOP و پارامترهای زیر استفاده می‌کنیم.

  • NHN: شماره NUMA Home Node را نمایش می‌دهد.
  • NMIG: تعداد مهاجرت بین NUMA Nodeها از زمان روشن شدن ماشین مجازی را نمایش می‌دهد. بالا بودن بیش از حد این عدد نشان می‌دهد که ممکن است VM Tuning احتیاج باشد!
  • (NRMEM (MB: میزان حافظه‌ Remote را نمایش می‌دهد.
  • (NLMEM (MB: میزان حافظه تخصیص داده شده Local را نمایش می‌دهد.
  • N%L: درصد حافظه Local ماشین مجازی را نمایش می‌دهد. این عدد نباید از 80% کمتر باشد در غیر این صورت باعث افت کارایی خواهد شد.
  • (GST_ND(X: میزان حافظه تخصیص داده شده به ماشین مجازی روی نود X.
  • (OVD_ND(X: میزان حافظه سربار مربوط به VMM ماشین مجازی که روی NUMA Node شماره X قرار گرفته است.

سخن آخر…

* Right Size Your VM! این شاه کلید حل تمام مشکلات در محیط مجازی است. برای اکثر Workloadها بهترین Performance زمانی به دست خواهد آمد که دسترسی به Memory به صورت Local باشد. اندازه vCPU و vRAM باید به درستی نیازهای واقعی ماشین مجازی را برآورده کند. در ایده‌آل ترین حالت بهتر است که ماشین مجازی روی یک NUMA Node قرار بگیرد و Wide-VM نداشته باشیم. سری رو که درد نمی‌کنه، دستمال نمی‌بندن! اگر نهایتاً باز هم مجبور شدید؛ قبل از هر چیز بررسی کنید که آیا استفاده از numa.vcpu.preferHT=TRUE می‌تواند باعث شود که ماشین شما روی یک NUMA Node، fit شود یا خیر؟ اگر امکان پذیر است این کار را انجام دهید!

* به صورت کلی ESXi وظایف خود در قبال NUMA را به خوبی و بهینه انجام خواهد داد. بنابراین سعی کنید تا زمانی که مجبور نشده‌اید، از تنظیمات پیشرفته استفاده نکرده و حالات پیش فرض را تغییر ندهید!

* همواره به مقدار رم تخصیص داده به ماشین‌های مجازی توجه داشته باشید، چنانچه از یک NUMA Node بیشتر شد، لازم است که به صورت دستی NUMA را بهینه سازی کنید.

* اگر از نسخه 6.5 به بعد استفاده می‌کنید تنها کافی است که نکته فوق را مد نظر قرار دهید (به مساله حافظه توجه داشته باشید)!

* اگر از نسخه 6 و قبل از آن استفاده می‌کند کافی است سعی کنید حالت پیش فرض 1Core per Socket را دستکاری نکنید. اما اگر بنا به دلائلی از جمله Licensing مجبور به تغییر حالت پیش فرض CPS شدید، به چند نکته کلیدی زیر توجه داشته باشید.

  • همیشه سعی کنید Socket را برابر 1 قرار دهید و Coreها را افزایش دهید؛ تا زمانی که تعداد vCPU از تعداد Physical Coreهای یک نود بیشتر شود یا اینکه میزان رم اختصاصی به ماشین مجازی از Memory یک نود بیشتر شود؛ در این حالت Socket را به 2 افزایش دهید. به عبارتی همواره سعی کنید به گونه‌ای رفتار کنید که کمترین تعداد NUMA Client ایجاد شود.
  • زمانی که با تغییر CPS و افزایش تعداد Socket باعث ایجاد چند NUMA Client می‌شوید، سعی کنید هرگز از vCPU تعداد فرد استفاده نکنید. این کار باعث ایجاد شرایط غیر بهینه (Sub-Optimal) خواهد شد.

* از سرورهایی با معماری NUMA یکسان جهت ایجاد کلاستر استفاده کنید.

* CPU Hot-add قابلیتی بسیار کاربردی است، اما تنها زمانی آن را فعال کنید که مطمئن هستید تعداد vCPUهای ماشین مجازی شما بزرگتر از یک NUMA Node نیست و نخواهد شد در غیر این صورت با افت Performance مواجه خواهید شد.

* از مانیتورینگ غافل نشوید!

0 پاسخ

دیدگاه خود را ثبت کنید

Want to join the discussion?
Feel free to contribute!

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *