
Игорь Голиков
Ведущий разработчик
Всем привет! Я Игорь Голиков, ведущий разработчик ГК “Юзтех”. В данной статье хочу рассказать о метриках памяти в VMware vCenter, в том числе как получить скрытые метрики.
Статья может быть полезна SRE/DevOps и администраторам VMware vCenter, заинтересованным в получении «гостевых метрик» виртуальных машин, тем, кто хочет обосновать снижение выделенной виртуальным машинам памяти и сократить расходы без риска для производительности.
На одном из наших проектов возникла необходимость отслеживать использование памяти в гостевой ОС на виртуальных машинах под управлением VMware vCetner и формировать рекомендации по увеличению/уменьшению памяти выделенной виртуальной машине (rightsizing). Стандартные метрики памяти, доступные через vSphere Web Services API, не позволяют оценить объём памяти, используемой гостевой ОС.
Метрика (производительности) — это количественный показатель, который отражает состояние или поведение системы во времени (CPU, память, диск, сеть и т.д.).
Задача: найти метрику, показывающую объем памяти, потребляемой гостевой ОС и процессами в Linux системах с установленными Guest Tools.
Требования к метрике:
Точность - позволяет оценить объём памяти используемой гостевой ОС
Отзывчивость к изменениям - оперативно отображать изменение используемой гостевой ОС памяти
Доступность – не требует установки дополнительных агентов, доступна через vSphere Web Services API
Стенд и сценарий
В рамках данной статьи для анализа я буду использовать виртуальную машину с ОС Alpine Linux с установленными Guest Tools c 8 GB RAM под управлением VMware vCenter 8.0.
Проверять я буду простой сценарий - на ВМ после перезагрузки (момент времени t0) запускается(t1) процесс резервирующий 5ГБ, через некоторое время (~30минут) процесс прерывается(t2).


Запускаю сценарий и изучаю доступные метрики
Метрики VMware vCenter
VMware vCenter предоставляет нам две основные метрики памяти посредством публичного API (специфичные метрики, такие как Ballooning, Compressed и т.д., меня не интересуют):
Consumed (Consumed Host Memory) — это объём памяти, выделенной хостом под ВМ (с учётом TPS и overhead).
Этот объём не равен «used внутри гостя», часто можно наблюдать такую картину, что при росте объема используемой памяти в гостевой ОС растет и Consumed, но после того, как мы освобождаем всю память, используемую процессами гостевой ОС, Consumed остается прежним и не уменьшается, в последствии эта память может быть изъята с помощью механизмов ESXi, например, Ballooning.
TPS (Transparent Page Sharing) — это механизм ESXi, который на лету находит идентичные 4-KB страницы RAM у разных ВМ и хранит их в одном экземпляре.
Ballooning – способ, с помощью которого гипервизор забирает неиспользуемую память у ВМ, когда у самого гипервизора не хватает памяти. Делает это драйвер vmmemctl (в составе VMware Tools) внутри ВМ: он закрепляет страницы памяти в гостевой ОС, как будто «надувает шарик», и, тем самым вынуждает гостевую ОС вытеснить малоиспользуемые данные (в кэш или swap). Закреплённые страницы затем возвращаются гипервизору(хосту).
Overhead (memory overhead) — это дополнительная память хоста, которая нужна гипервизору, чтобы запустить и обслуживать ВМ. Она не видна внутри гостя, но учитывается на хосте и попадает в Consumed.
Active (Active Guest Memory) — память(страницы), к которой гостевая ОС реально обращалась за некий интервал времени.
Четкой формулировки, что именно показывает эта метрика, я не нашел, это некая оценка, которая показывает «сколько памяти ВМ задействует по-настоящему сейчас», а не сколько ей выделено.

Метрика Active начинает падать сразу после резервирования, несмотря на то, что память все еще удерживается процессом, так как никаких обращений к зарезервированным страницам не происходит.
Примечание: такое поведение процесса - синтетическое, в реальной жизни тяжеловесные процессы, резервирующие большие объемы памяти, активно используют какую-то часть этой памяти.
Метрика Consumed остается неизменной после высвобождения зарезервированных страниц процессом, так как гипервизор ничего не знает о процессах внутри ВМ.
Название метрики | Значение метрик, минимальное | ||
До(t0-t1) | В течение (t1-t2) | После(t2-) | |
Active(KB) | 83 884 | 83 884 | 83 884 |
Consumed (KB) | 720 896 | 5 957 632 | 5 957 632 |
В таблице представлены минимальные значения каждой метрики в периоде
до запуска процесса, резервирующего память;
за промежуток времени, в течение которого процесс был запущен;
после завершения процесса.
Вердикт: ни одна из метрик не подходит для моих целей, так как не отражает выделение памяти внутри гостевой ОС.
Метрики VMware Aria Operations (vRealize Operations)
Если отбросить вышеперечисленные метрики, то в VMware Aria мы найдем следующие метрики (описание из официальной документации):
Memory Guest Usage (KB) - Guest memory entitlement (This metric shows the amount of memory the VM uses)
Memory Usage (%) - Memory currently in use as a percentage of total available memory
Memory Workload (%) - Demand over usable capacity. where it is applicable, demand includes limit and contention
Guest Used Memory (KB) - Memory value reported by 'in use' counter in Windows and used counter in Linux. This contains pages that was used in the past but still considered used at present. It includes compressed pages in windows.
Guest Needed Memory(KB) - Amount of memory needed for the Guest OS to perform optimally. This memory is considered as a cache for the disk and is a little more than the actual used memory.
Memory Usage (%) – показывает соотношение Guest Needed Memory и общего объема памяти и рассчитывается как:



Название метрики | Значение метрик | ||
До(t0-t1) | В течение(t1-t2) | После(t2-) | |
Memory Guest Usage (KB) | 720 896 | 5 956 949 | 5 956 949 |
Memory Usage(%) | 13.16 | 75.82 | 13.01 |
Memory Workload (%) | 13.16 | 75.82 | 13.01 |
Guest Needed Memory (KB) | 1 103 759 | 6 360 070 | 1 091 150 |
Guest Used Memory (KB) | 204 225 | 5 460 513 | 191 428 |
В наблюдениях Memory Usage совпадает с Memory Workload и является производным от Guest Needed Memory, поэтому их можно исключить.
Memory Guest Usage не отреагировал на высвобождение памяти процессом, исключаем его тоже.
«Guest Needed Memory (KB)» - отражает потребность гостевой ОC в памяти, собирается через VMware Tools и показывает, сколько ОЗУ гостевая ОС «хотела бы» иметь для оптимальной работы (обычно включает page cache, поэтому часто выше, чем «фактически используется»).
Согласно документации VMware ‘Guest Needed Memory’ рассчитывается как:
Где physUsable берется из /proc/zoneinfo, как сумма страниц со статусом “present”, умноженная на размер страницы. Это близко к MemTotal из /proc/meminfo, но может включать в себя зарезервированные и неуправляемые аллокатором страницы. Если physUsable больше MemTotal, то его необходимо уменьшить на 5%
А MemAvailable значение из /proc/meminfo.
https://knowledge.broadcom.com/external/article/410736
Попробуем вручную рассчитать Guest Needed Memory:
user:~$ getconf PAGE_SIZE 4096 user:~$ grep MemTotal /proc/meminfo MemTotal: 8146520 kB user:~$ free -k total used free shared buff/cache available Mem: 8146520 230232 5819544 512 2096744 7644540 Swap: 8388604 12024 8376580 echo $(awk '/present/ {sum+=$2} END{printf "%.0f", sum*4096/1024}' /proc/zoneinfo) 8388088 echo $(awk '/MemAvailable:/ {print $2}' /proc/meminfo) 7635284
Получаем:
physUsable = 8388088 kB
MemAvailable = 7635284 kB
Guest Used Memory (KB) — значение памяти, сообщаемое счетчиком “In use” в Windows и счетчиком “used” в Linux (прямой перевод определения из официальной документации). Совпадает с колонкой Used в выводе команды free в Linux, а следовательно, рассчитывается из /proc/meminfo.
Вывод: Guest Needed Memory и Guest Used Memory позволяют оценить объем памяти используемой гостевой ОС и оперативно отражают его изменение.
Примечание 1: данные метрики доступны только при условии, что на ВМ установлены Guest Tools, иначе происходит откат к метрикам гипервизора (Consumed/Active)
https://vmwarelab.org/2022/02/18/vrealize-operations-vrops-memory-reporting/
Примечание 2: иногда Guest Used Memorу перестает собираться Aria, в тоже время Guest Needed Memory продолжается собираться. Никаких объяснений этому мне обнаружить не удалось.

Метрики Zabbix
Метрика Linux: memory utilization рассчитывается из /proc/meminfo как:

Название метрики | Значение метрик | ||
До(t0-t1) | В течение(t1-t2) | После(t2-) | |
Linux: memory utilization(%) | 5.46% | 69.9% | 5.46% |
Linux: memory utilization(KB) | 8388608 KB x0.0545= 457 179 | 5 863 636 | 457 179 |
Вывод: метрика Linux: memory utilization (%) также позволяют оценить объем памяти используемой гостевой ОС и оперативно отражают его изменение.
Итоги: выбор метрики
В шорт лист попали следующие метрики:
Aria - Guest Needed Memory – интересная метрика оценивающая «сколько памяти необходимо гостевой ОС»
Aria - Guest Used Memory – в отличие от предыдущей метрики, оценивающей сколько гостевая ОС хочет, эта метрика показывает сколько реально занято, предположительно Used посчитанная из /proc/meminfo
Zabbix - Linux: memory utilization - базовая MemAvailable из /proc/meminfo
Валидность результатов
Хотя эксперимент проводился на конкретной сборке Linux, я считаю, что выводы масштабируются на дистрибутивы и версии Linux в целом: подсистема управления памятью ядра (учёт страниц, page cache, MemAvailable/MemTotal, поведение reclaim) работает по единым принципам, а интерфейсы /proc/meminfo и счётчики ядра сохраняют совместимость. Для Unix-подобных систем и для Windows - модель памяти и экспонируемые метрики различаются, поэтому данные системы требуют отдельной проверки.
Получение гостевых метрик без VMware Aria Operations
Предположение: Aria не получает никаких дополнительных метрик с виртуальных машин, а рассчитывает “Guest Needed Memory” только по метрикам, полученным с vCenter, значит vCenter раскрывает не все метрики в своем публичном API.
Метод QueryPerfCounter не возвращает ни одной метрики похожей на “Guest Needed Memory”.
С помощью пакета mitmproxy запускаю reverse-proxy, который будет проксировать и сохранять на диск все HTTP запросы на реальный vCenter. Добавляю в Aria новый Data Source – vCenter, используя IP адрес моего reverse-proxy, жду сбора данных и получаю дамп всех запрос от Aria к vCenter и ответов на них.
#!/usr/bin/env bash docker run -it --rm --name mitmproxy \ -p 0.0.0.0:443:443 -p 0.0.0.0:8081:8081 \ -v "$PWD/mitmproxy/.mitmproxy:/home/mitmproxy/.mitmproxy" \ -v "$PWD/mitmproxy/addons:/addons" \ -v "$PWD/mitmproxy/out:/out" \ mitmproxy/mitmproxy \ mitmweb --mode reverse:https://VCENTER_IP:443 \ --listen-host 0.0.0.0 --listen-port 443 \ --set block_global=false \ --set connection_strategy=lazy \ --set ssl_insecure=true \ -s /addons/dump_soap.py
Скрипт dump_soap.py для сохранения запросов и ответов
from mitmproxy import http, ctx from datetime import datetime from pathlib import Path from xml.dom import minidom import re OUT_DIR = Path("/out") OUT_DIR.mkdir(parents=True, exist_ok=True) SOAP_SIG = re.compile(rb"<\s*(soap:)?Envelope", re.I) def _pretty_xml(data: bytes) -> bytes: try: return minidom.parseString(data).toprettyxml(indent=" ", encoding="utf-8") except Exception: return data def _is_xml_or_soap(ct: str) -> bool: ct = (ct or "").lower() return ("xml" in ct) or ("soap" in ct) or ("application/soap+xml" in ct) or ("text/xml" in ct) def _looks_like_soap(body: bytes) -> bool: return bool(body) and bool(SOAP_SIG.search(body)) def _fname(prefix: str, flow: http.HTTPFlow) -> Path: ts = datetime.utcnow().strftime("%Y%m%dT%H%M%S.%fZ") hostpath = (flow.request.host + flow.request.path).replace("/", "_") if len(hostpath) > 160: hostpath = hostpath[:160] return OUT_DIR / f"{ts}_{prefix}_{hostpath or 'root'}.xml" def request(flow: http.HTTPFlow): ct = flow.request.headers.get("Content-Type", "") body = flow.request.content or b"" if (_is_xml_or_soap(ct) or _looks_like_soap(body)) and body: ctx.log.info(f"SOAP request → {flow.request.method} {flow.request.url}") with _fname("req", flow).open("wb") as f: f.write(_pretty_xml(body)) def response(flow: http.HTTPFlow): if not flow.response: return ct = flow.response.headers.get("Content-Type", "") body = flow.response.content or b"" if (_is_xml_or_soap(ct) or _looks_like_soap(body)) and body: ctx.log.info(f"SOAP response ← {flow.response.status_code} {flow.request.url}") with _fname("res", flow).open("wb") as f: f.write(_pretty_xml(body))
Подсмотрев примерное название метрики делаю поиск по дампу.

И нахожу, что строка «guest.mem.needed» есть в ответе на некий вызов QueryPerfCounterInt. В wsdl от vSphere Web Services API подобный вызов отсутствует, но есть QueryPerfCounter, возможно суффикс Int образован от Internal.
Метод возвращает список гостевых метрик(Perfomance counter в терминологии VMware, и необходимый мне counter Id для вызова QueryPerf).
Id Name –-|---------------------------- 541 guest.hugePage.size 542 guest.mem.physUsable 543 guest.mem.free 544 guest.mem.activeFileCache 545 guest.swap.spaceRemaining 546 guest.hugePage.total 547 guest.page.inRate 548 guest.page.outRate 549 guest.contextSwapRate 550 guest.page.size 551 guest.mem.needed 552 guest.mem.neededReservation 553 guest.mem.available 554 guest.mem.slabReclaim 555 guest.mem.buffers 556 guest.mem.cached 557 guest.mem.total 558 guest.cpu.runQueue 559 guest.disk.requestQueue 560 guest.disk.requestQueueAvg 801 guest.processor.queue 802 guest.mem.availableToMm 803 guest.mem.standby.normal 804 guest.mem.standby.reserve 805 guest.mem.standby.core 806 guest.mem.modifiedPages 807 guest.disk.queue 808 guest.disk.queueAvg
Добавим в wsdl новый метод по аналогии с QueryPerfCounter
<operation name="QueryPerfCounterInt"> <input message="vim25:QueryPerfCounterIntRequestMsg" /> <output message="vim25:QueryPerfCounterIntResponseMsg" /> <fault name="RuntimeFault" message="vim25:RuntimeFaultFaultMsg"/> </operation> <operation name="QueryPerfCounter"> <input message="vim25:QueryPerfCounterRequestMsg" /> <output message="vim25:QueryPerfCounterResponseMsg" /> <fault name="RuntimeFault" message="vim25:RuntimeFaultFaultMsg"/> </operation> <message name="QueryPerfCounterIntRequestMsg"> <part name="parameters" element="vim25:QueryPerfCounterInt" /> </message> <message name="QueryPerfCounterIntResponseMsg"> <part name="parameters" element="vim25:QueryPerfCounterIntResponse" /> </message> <operation name="QueryPerfCounterInt"> <soap:operation soapAction="urn:vim25/6.7" style="document" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> <fault name="RuntimeFault"> <soap:fault name="RuntimeFault" use="literal" /> </fault> </operation> <element name="QueryPerfCounterInt" type="vim25:QueryPerfCounterIntRequestType" /> <element name="QueryPerfCounterIntResponse"> <complexType> <sequence> <element name="returnval" type="vim25:PerfCounterInfoInt" minOccurs="0" maxOccurs="unbounded" /> </sequence> </complexType> </element>
Я не нашел, как в библиотеке pyVmomi вызвать нестандартный метод поэтому сделаем это с помощью Zeep и извлечем session cookie для последующего использования в pyVmomi
from requests import Session from zeep import Client, Settings from zeep.transports import Transport from pathlib import Path def enable_guest(addr: str, user: str, password: str): session = Session() session.verify = False transport = Transport(session=session) settings = Settings(strict=False, xml_huge_tree=True) # load WSDL from disk wsdl_path = Path(__file__).parent / "wsdl/vimService.wsdl" client = Client(wsdl=wsdl_path.as_uri(), transport=transport, settings=settings) service = client.bind("VimService", "VimPort") service._binding_options["address"] = f"https://{addr}/sdk" # log in mir = client.get_type("ns0:ManagedObjectReference") sm_ref = mir("SessionManager", type="SessionManager") service.Login(_this=sm_ref, userName=user, password=password) si_ref = mir("ServiceInstance", type="ServiceInstance") sc = service.RetrieveServiceContent(_this=si_ref) perf_mgr_ref = sc.perfManager print("Type:", perf_mgr_ref.type) print("Value:", perf_mgr_ref._value_1) # call QueryPerfCounterInt counters = service.QueryPerfCounterInt(_this=perf_mgr_ref) mappings = [] result = [] clm = client.get_type("ns0:PerformanceManagerCounterLevelMapping") for c in counters: print(c.key, f'{c.groupInfo.key}.{c.nameInfo.key}', c.rollupType) mappings.append(clm(counterId=c.key, aggregateLevel=1)) result.append((c.key, f'{c.groupInfo.key}.{c.nameInfo.key}', c.rollupType, c.unitInfo.label)) # Call updateCounterLevelMapping service.UpdateCounterLevelMapping(_this=perf_mgr_ref, counterLevelMap=mappings) session_cookie_value = client.transport.session.cookies.get('vmware_soap_session') return session_cookie_value, result
Для новых метрик может понадобиться вызвать UpdateCounterLevelMapping (судя по дампу, Aria делает это).
Запрашиваем значения для guest.mem.needed и получаем те же значения, что и в Aria Operations:

Полный код скрипта https://github.com/igolikov/vmware-perf-counters/tree/main
В списке полученных метрик кроме guest.mem.needed есть еще 2 интересные метрики: guest.mem.free и guest.mem.available, построим для них нашу таблицу и пересчитаем утилизацию памяти (как обратную величину). Полная емкость равна 8146520 KB (метрика guest.mem.total).
Название метрики | Значение метрик | ||
До(t0-t1) | В течение(t1-t2) | После(t2-) | |
guest.mem.free (KB) | 7646536 (6.1%) | 2390064 (70.6%) | 7641128 (6.2%) |
guest.mem.available | 7693720 (5.5%) | 2440092 (70%) | 7692460 (5.5%) |
guest.mem.needed | 1099304(13.4%) | 6364228(78%) | 1100248(13.5%) |
Значения guest.mem.free и guest.mem.available совпадают со значениями столбцов free и available вывода команды free.
Метрики, соответствующей Guest Used Memorу в этом списке, обнаружить не удалось, думаю, она рассчитывается так же как и в результатах команды ”free”:
В терминах VMware vCenter:
Название метрики | Значение метрик | ||
До(t0-t1) | В течение(t1-t2) | После(t2-) | |
guest.mem.free (KB) | 7646536 | 2390064 | 7641128 |
guest.mem.buffers (KB) | 177155 | 177365 | 177521 |
guest.mem.cached (KB) | 118204 | 118278 | 135843 |
Расчетное guest.used (KB) | 204 625 | 5 460 813 | 192 028 |
Aria Guest Used Memory (KB) | 204 225 | 5 460 513 | 191 428 |
Выводы
Стандартные метрики памяти VMware vCenter (Memory Consumed и Memory Active) не отражают реальное потребление памяти в гостевой ОС, поскольку они учитывают лиш�� данные гипервизора, а не внутренние ресурсы виртуальной машины;
Гостевые метрики VMware vCenter (метрики с префиксом guest.mem), дают более точную картину, показывают фактическое потребление памяти внутри виртуальной машины, а не то, как гипервизор выделяет или использует память;
VMware Tools или Guest Tools должны быть установлены на виртуальной машине для сбора гостевых метрик;
Aria Operations не является обязательной для получения гостевых метрик. Даже если Aria не используется в инфраструктуре, метрики памяти можно получить через внутренние вызовы API vCenter (QueryPerfCounterInt) .
Полезные ссылки
https://knowledge.broadcom.com/external/article/410736
https://vmwarelab.org/2022/02/18/vrealize-operations-vrops-memory-reporting/
https://www.vmware.com/docs/perf-vsphere-memory_management
https://garyflynn.com/post/vrops-memory-metric-collection-changes-in-6-7-7-0/
