diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dffb66f --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +PREFIX ?= / + +SRC_DIR := $(dir $(lastword $(MAKEFILE_LIST))) + +SERVICE := $(PREFIX)/lib/systemd/system/libvirt-autoballoon.service +BIN := $(PREFIX)/usr/bin/libvirt-autoballoon + +default: help + +$(BIN): $(SRC_DIR)/libvirt-autoballoon + install -Dm755 $< $@ + +$(SERVICE): $(SRC_DIR)/libvirt-autoballoon.service + install -Dm644 $< $@ + + +install: ## Install libvirt-autoballoon +install: $(BIN) $(SERVICE) + +uninstall: ## Delete libvirt-autoballoon +uninstall: + @rm -fv $(BIN) $(SERVICE) + +help: ## Show help + @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##/\t/' diff --git a/README.md b/README.md index ca14070..415895d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,15 @@ # libvirt-autoballoon libvirt-autoballoon: daemon to autoballoon guest memory by virsh on host with libvirt + +That script just detect all running libvirt-qemu guests on localhost +and try balloon unused memory from guests to host + +Proof of concept + +# Installation + +``` +~# make install +~# systemctl enable libvirt-autoballoon +~# systemctl start libvirt-autoballon +``` diff --git a/libvirt-autoballoon b/libvirt-autoballoon new file mode 100755 index 0000000..6833739 --- /dev/null +++ b/libvirt-autoballoon @@ -0,0 +1,104 @@ +#!/bin/bash + +RUN_DIR=/run/libvirt-autoballoon +mkdir -p "$RUN_DIR" + +PAGE_SIZE=$(getconf PAGESIZE) +PAGE_SIZE_KiB=$((PAGE_SIZE/1024)) + +ROUND_DOWN=$((PAGE_SIZE_KiB*1024)) + +vm_memstat(){ + VM="$1" + type="$2" + grep "$type" $RUN_DIR/"$VM".memstat | grep -o '[0-9]*' +} + +vm_update_memstat(){ + VM="$1" + virsh dommemstat --domain "$VM" --live > $RUN_DIR/"$VM".memstat +} + +vm_balloon(){ + VM="$1" + SHRINK_TO_KiB="$2" + ACTUAL_MEM_OLD="$3" + virsh setmem --domain "$VM" --size "$SHRINK_TO_KiB" 1> /dev/null + if [ ! -z "$ACTUAL_MEM_OLD" ]; then + if ((ACTUAL_MEM_OLD >= SHRINK_TO_KiB)); then + echo "Shrink $VM: $((ACTUAL_MEM_OLD/1024)) -> $((SHRINK_TO_KiB/1024)) MiB" + else + echo "Grow $VM: $((SHRINK_TO_KiB/1024)) <- $((ACTUAL_MEM_OLD/1024)) MiB" + fi + fi +} + +main(){ + virsh -c qemu:///system list --name | sort -u | \ + while read -r VM; do + [ -z "$VM" ] && continue + vm_update_memstat "$VM" + + TOTAL_RAM_KiB=$(vm_memstat "$VM" available) + ACTUAL_RAM_KiB=$(vm_memstat "$VM" actual) + UNUSED_RAM_KiB=$(vm_memstat "$VM" unused) + USED_RAM_KiB=$((ACTUAL_RAM_KiB-UNUSED_RAM_KiB)) + + KEEP_AVAIL_THRESHOLD=$((TOTAL_RAM_KiB/8)) + + PERC=$((UNUSED_RAM_KiB*100/KEEP_AVAIL_THRESHOLD)) + if ((PERC < 90)); then + if ((ACTUAL_RAM_KiB < TOTAL_RAM_KiB)); then + DIFF=$((TOTAL_RAM_KiB - ACTUAL_RAM_KiB)) + DIFF=$((DIFF/8)) + if ((DIFF > ROUND_DOWN)); then + vm_balloon "$VM" $((ACTUAL_RAM_KiB+DIFF)) $ACTUAL_RAM_KiB + else + vm_balloon "$VM" $((ACTUAL_RAM_KiB+PAGE_SIZE_KiB)) $ACTUAL_RAM_KiB + fi + fi + elif ((PERC > 150)); then + DIFF=$((ACTUAL_RAM_KiB - UNUSED_RAM_KiB)) + DIFF=$((DIFF/8)) + if ((DIFF > ROUND_DOWN)); then + vm_balloon "$VM" $((ACTUAL_RAM_KiB-DIFF)) $ACTUAL_RAM_KiB + else + vm_balloon "$VM" $((ACTUAL_RAM_KiB-PAGE_SIZE_KiB)) $ACTUAL_RAM_KiB + fi + fi + done +} + +status(){ + virsh -c qemu:///system list --name | sort -u | \ + while read -r VM; do + [ -z "$VM" ] && continue + vm_update_memstat "$VM" + + TOTAL_RAM_KiB=$(vm_memstat "$VM" available) + ACTUAL_RAM_KiB=$(vm_memstat "$VM" actual) + UNUSED_RAM_KiB=$(vm_memstat "$VM" unused) + USED_RAM_KiB=$((ACTUAL_RAM_KiB-UNUSED_RAM_KiB)) + + KEEP_AVAIL_THRESHOLD=$((TOTAL_RAM_KiB/8)) + + PERC=$((UNUSED_RAM_KiB*100/KEEP_AVAIL_THRESHOLD)) + { + echo -e "Total Actual Used Unused Threshold - Thr_Perc" + echo -e "$((TOTAL_RAM_KiB/1024)) $((ACTUAL_RAM_KiB/1024)) $((USED_RAM_KiB/1024)) $((UNUSED_RAM_KiB/1024)) $((KEEP_AVAIL_THRESHOLD/1024)) MiB ${PERC}%" + } | column -t + done +} + +case "$1" in + start) + while sleep 1s; do + echo "Wait starting of libvirtd" + systemctl is-active libvirtd && break + done + while sleep 1s; do + main + done + ;; + status) status ;; +esac diff --git a/libvirt-autoballoon.service b/libvirt-autoballoon.service new file mode 100644 index 0000000..ecabec3 --- /dev/null +++ b/libvirt-autoballoon.service @@ -0,0 +1,20 @@ +[Unit] +Description=libvirt-autoballoon +After=local-fs.target + +[Service] +Type=simple +ExecStart=/usr/bin/libvirt-autoballoon start +Nice=19 +SuccessExitStatus=143 +OOMScoreAdjust=-999 +Restart=always +CPUAccounting=true +MemoryHigh=16M +MemoryMax=64M +ProtectSystem=true +ProtectHome=true +PrivateTmp=yes + +[Install] +WantedBy=local-fs.target