
Игорь Голиков
Ведущий разработчик
Всем привет! Я Игорь Голиков, ведущий разработчик ГК “Юзтех”. В данной статье хочу рассказать о метриках памяти в 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/