mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-04-27 19:35:32 +00:00
> Since you double-quoted this, it will not word split, and the loop will only run once. Signed-off-by: stevenhorsman <steven@uk.ibm.com>
408 lines
10 KiB
Bash
Executable File
408 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
|
# Copyright (c) 2017-2023 Intel Corporation
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
# Description of the test:
|
|
# This test launches a number of containers in idle mode,
|
|
# It will then sleep for a configurable period of time to allow
|
|
# any memory optimisations to 'settle, and then checks the
|
|
# amount of memory used by all the containers to come up with
|
|
# an average (using the PSS measurements)
|
|
# This test uses smem tool to get the memory used.
|
|
|
|
set -e
|
|
|
|
SCRIPT_PATH=$(dirname "$(readlink -f "$0")")
|
|
source "${SCRIPT_PATH}/../lib/common.bash"
|
|
|
|
# Busybox image: Choose a small workload image, this is
|
|
# in order to measure the runtime footprint, not the workload
|
|
# footprint.
|
|
IMAGE="quay.io/prometheus/busybox:latest"
|
|
|
|
CMD='tail -f /dev/null'
|
|
NUM_CONTAINERS="$1"
|
|
WAIT_TIME="$2"
|
|
AUTO_MODE="$3"
|
|
TEST_NAME="memory footprint"
|
|
SMEM_BIN="smem"
|
|
KSM_ENABLE_FILE="/sys/kernel/mm/ksm/run"
|
|
MEM_TMP_FILE=$(mktemp meminfo.XXXXXXXXXX)
|
|
PS_TMP_FILE=$(mktemp psinfo.XXXXXXXXXX)
|
|
SKIP_VIRTIO_FS=0
|
|
|
|
# Variables used to collect memory footprint
|
|
global_hypervisor_mem=0
|
|
global_virtiofsd_mem=0
|
|
global_shim_mem=0
|
|
|
|
function remove_tmp_file() {
|
|
rm -rf "${MEM_TMP_FILE}" "${PS_TMP_FILE}"
|
|
clean_env_ctr
|
|
}
|
|
|
|
trap remove_tmp_file EXIT
|
|
|
|
# Show help about this script
|
|
function help(){
|
|
cat << EOF
|
|
|
|
Usage: $0 <count> <wait_time> [auto]
|
|
|
|
Description:
|
|
<count> : Number of containers to run.
|
|
<wait_time> : Time in seconds to wait before taking
|
|
metrics.
|
|
[auto] : Optional 'auto KSM settle' mode
|
|
waits for ksm pages_shared to settle down
|
|
|
|
EOF
|
|
}
|
|
|
|
|
|
function get_runc_pss_memory(){
|
|
ctr_runc_shim_path="/usr/local/bin/containerd-shim-runc-v2"
|
|
get_pss_memory "${ctr_runc_shim_path}"
|
|
}
|
|
|
|
function get_runc_individual_memory() {
|
|
runc_process_result=$(cat "${MEM_TMP_FILE}" | tr "\n" " " | sed -e 's/\s$//g' | sed 's/ /, /g')
|
|
|
|
# Verify runc process result
|
|
if [ -z "${runc_process_result}" ];then
|
|
die "Runc process not found"
|
|
fi
|
|
|
|
read -r -a runc_values <<< "${runc_process_result}"
|
|
|
|
metrics_json_start_array
|
|
|
|
local json="$(cat << EOF
|
|
{
|
|
"runc individual results": [
|
|
$(for ((i=0;i<"${NUM_CONTAINERS[@]}";++i)); do
|
|
printf '%s\n\t\t\t' "${runc_values[i]}"
|
|
done)
|
|
]
|
|
}
|
|
EOF
|
|
)"
|
|
metrics_json_add_array_element "$json"
|
|
metrics_json_end_array "Raw results"
|
|
}
|
|
|
|
# This function measures the PSS average
|
|
# memory of a process.
|
|
function get_pss_memory(){
|
|
local ps="${1}"
|
|
local shim_result_on="${2:-0}"
|
|
local mem_amount=0
|
|
local count=0
|
|
local avg=0
|
|
|
|
[ -z "${ps}" ] && die "No argument to get_pss_memory()"
|
|
|
|
ps="$(readlink -f ${ps})"
|
|
|
|
# Save all the processes names
|
|
# This will be help us to retrieve raw information
|
|
echo "${ps}" >> "${PS_TMP_FILE}"
|
|
|
|
data="$(sudo "${SMEM_BIN}" --no-header -P "^${ps}" -c "pss" | sed 's/[[:space:]]//g' | tr '\n' ' ' | sed 's/[[:blank:]]*$//')"
|
|
|
|
# Save all the smem results
|
|
# This will help us to retrieve raw information
|
|
echo "${data}" >> "${MEM_TMP_FILE}"
|
|
|
|
arrData=(${data})
|
|
|
|
for i in "${arrData[@]}"; do
|
|
if [ "${i}" -gt 0 ]; then
|
|
let "mem_amount+=i"
|
|
let "count+=1"
|
|
[ "${count}" -eq "${NUM_CONTAINERS}" ] && break
|
|
fi
|
|
done
|
|
|
|
[ "${count}" -eq 0 ] && die "No pss memory was measured for PID: ${ps}"
|
|
|
|
avg=$(bc -l <<< "scale=2; ${mem_amount} / ${count}")
|
|
|
|
if [ "${shim_result_on}" -eq "1" ]; then
|
|
global_shim_mem="${avg}"
|
|
else
|
|
global_hypervisor_mem="${avg}"
|
|
fi
|
|
}
|
|
|
|
function ppid() {
|
|
local pid
|
|
pid=$(ps -p "${1:-nopid}" -o ppid=)
|
|
echo "${pid//[[:blank:]]/}"
|
|
}
|
|
|
|
# This function measures the PSS average
|
|
# memory of virtiofsd.
|
|
# It is a special case of get_pss_memory,
|
|
# virtiofsd forks itself so, smem sees the process
|
|
# two times, this function sum both pss values:
|
|
# pss_virtiofsd=pss_fork + pss_parent
|
|
function get_pss_memory_virtiofsd() {
|
|
mem_amount=0
|
|
count=0
|
|
avg=0
|
|
virtiofsd_path="${1:-}"
|
|
|
|
if [ $(ps aux | grep -c '[v]irtiofsd') -eq 0 ]; then
|
|
SKIP_VIRTIO_FS=1
|
|
return
|
|
fi
|
|
|
|
[ -z "${virtiofsd_path}" ] && die "virtiofs path not provided"
|
|
|
|
echo "${virtiofsd_path}" >> "${PS_TMP_FILE}"
|
|
|
|
virtiofsd_pids="$(ps aux | grep '[v]irtiofsd' | awk '{print $2}' | head -1)"
|
|
|
|
data="$(sudo smem --no-header -P "^${virtiofsd_path}" -c "pid pss")"
|
|
|
|
for p in ${virtiofsd_pids}; do
|
|
echo "get_pss_memory_virtiofsd: p=${p}"
|
|
parent_pid=$(ppid "${p}")
|
|
cmd="$(cat /proc/${p}/cmdline | tr -d '\0')"
|
|
cmd_parent="$(cat /proc/${parent_pid}/cmdline | tr -d '\0')"
|
|
if [ "${cmd}" != "${cmd_parent}" ]; then
|
|
pss_parent=$(printf "%s" "${data}" | grep "\s^${p}" | awk '{print $2}')
|
|
|
|
fork=$(pgrep -P "${p}")
|
|
|
|
pss_fork=$(printf "%s" "${data}" | grep "^\s*${fork}" | awk '{print $2}')
|
|
pss_process=$((pss_fork + pss_parent))
|
|
|
|
# Save all the smem results
|
|
# This will help us to retrieve raw information
|
|
echo "${pss_process}" >>"${MEM_TMP_FILE}"
|
|
|
|
if ((pss_process > 0)); then
|
|
mem_amount=$((pss_process + mem_amount))
|
|
let "count+=1"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
[ "${count}" -gt 0 ] && global_virtiofsd_mem=$(bc -l <<< "scale=2; ${mem_amount} / ${count}")
|
|
}
|
|
|
|
function get_individual_memory(){
|
|
# Getting all the individual container information
|
|
first_process_name=$(cat "${PS_TMP_FILE}" | awk 'NR==1' | awk -F "/" '{print $NF}' | sed 's/[[:space:]]//g')
|
|
first_process_result=$(cat "${MEM_TMP_FILE}" | awk 'NR==1' | sed 's/ /, /g')
|
|
|
|
second_process_name=$(cat "${PS_TMP_FILE}" | awk 'NR==2' | awk -F "/" '{print $NF}' | sed 's/[[:space:]]//g')
|
|
second_process_result=$(cat "${MEM_TMP_FILE}" | awk 'NR==2' | sed 's/ /, /g')
|
|
|
|
third_process_name=$(cat "${PS_TMP_FILE}" | awk 'NR==3' | awk -F "/" '{print $NF}' | sed 's/[[:space:]]//g')
|
|
third_process_result=$(cat "${MEM_TMP_FILE}" | awk 'NR==3' | sed 's/ /, /g')
|
|
|
|
read -r -a first_values <<< "${first_process_result}"
|
|
read -r -a second_values <<< "${second_process_result}"
|
|
read -r -a third_values <<< "${third_process_result}"
|
|
|
|
declare -a fv_array
|
|
declare -a sv_array
|
|
declare -a tv_array
|
|
|
|
# remove null values from arrays of results
|
|
for ((i=0;i<"${NUM_CONTAINERS}";++i)); do
|
|
[ -n "${first_values[i]}" ] && fv_array+=( "${first_values[i]}" )
|
|
[ -n "${second_values[i]}" ] && sv_array+=( "${second_values[i]}" )
|
|
[ -n "${third_values[i]}" ] && tv_array+=( "${third_values[i]}" )
|
|
done
|
|
|
|
# remove trailing commas
|
|
fv_array[-1]="$(sed -r 's/,*$//g' <<< "${fv_array[-1]}")"
|
|
sv_array[-1]="$(sed -r 's/,*$//g' <<< "${sv_array[-1]}")"
|
|
|
|
[ "${SKIP_VIRTIO_FS}" -eq 0 ] && tv_array[-1]="$(sed -r 's/,*$//g' <<< "${tv_array[-1]}")"
|
|
|
|
metrics_json_start_array
|
|
|
|
local json="$(cat << EOF
|
|
{
|
|
"${first_process_name} memory": [
|
|
$(for i in "${fv_array[@]}"; do
|
|
printf '\n\t\t\t%s' "${i}"
|
|
done)
|
|
],
|
|
"${second_process_name} memory": [
|
|
$(for i in "${sv_array[@]}"; do
|
|
printf '\n\t\t\t%s' "${i}"
|
|
done)
|
|
],
|
|
"${third_process_name} memory": [
|
|
$(for i in "${tv_array[@]}"; do
|
|
printf '\n\t\t\t%s' "${i}"
|
|
done)
|
|
]
|
|
}
|
|
EOF
|
|
)"
|
|
metrics_json_add_array_element "${json}"
|
|
metrics_json_end_array "Raw results"
|
|
}
|
|
|
|
# Try to work out the 'average memory footprint' of a container.
|
|
function get_memory_usage(){
|
|
hypervisor_mem=0
|
|
virtiofsd_mem=0
|
|
shim_mem=0
|
|
memory_usage=0
|
|
|
|
containers=()
|
|
|
|
info "Creating ${NUM_CONTAINERS} containers"
|
|
for ((i=1; i<="${NUM_CONTAINERS}"; i++)); do
|
|
containers+=($(random_name))
|
|
sudo "${CTR_EXE}" run --runtime "${CTR_RUNTIME}" -d "${IMAGE}" "${containers[-1]}" sh -c "${CMD}"
|
|
done
|
|
|
|
if [ "${AUTO_MODE}" == "auto" ]; then
|
|
if (( ksm_on != 1 )); then
|
|
die "KSM not enabled, cannot use auto mode"
|
|
fi
|
|
|
|
echo "Entering KSM settle auto detect mode..."
|
|
wait_ksm_settle "${WAIT_TIME}"
|
|
else
|
|
# If KSM is enabled, then you normally want to sleep long enough to
|
|
# let it do its work and for the numbers to 'settle'.
|
|
echo "napping ${WAIT_TIME} s"
|
|
sleep "${WAIT_TIME}"
|
|
fi
|
|
|
|
metrics_json_start_array
|
|
# Check the runtime in order in order to determine which process will
|
|
# be measured about PSS
|
|
if [ "${CTR_RUNTIME}" == "io.containerd.runc.v2" ]; then
|
|
runc_workload_mem="$(get_runc_pss_memory)"
|
|
memory_usage="${runc_workload_mem}"
|
|
|
|
local json="$(cat << EOF
|
|
{
|
|
"average": {
|
|
"Result": ${memory_usage},
|
|
"Units" : "KB"
|
|
},
|
|
"runc": {
|
|
"Result": ${runc_workload_mem},
|
|
"Units" : "KB"
|
|
}
|
|
}
|
|
EOF
|
|
)"
|
|
else [ "${CTR_RUNTIME}" == "io.containerd.kata.v2" ]
|
|
# Get PSS memory of VM runtime components.
|
|
# And check that the smem search has found the process - we get a "0"
|
|
# back if that procedure fails (such as if a process has changed its name
|
|
# or is not running when expected to be so)
|
|
# As an added bonus - this script must be run as root.
|
|
# Now if you do not have enough rights
|
|
# the smem failure to read the stats will also be trapped.
|
|
get_pss_memory "${HYPERVISOR_PATH}"
|
|
|
|
if [ "${global_hypervisor_mem}" == "0" ]; then
|
|
die "Failed to find PSS for ${HYPERVISOR_PATH}"
|
|
fi
|
|
|
|
get_pss_memory_virtiofsd "${VIRTIOFSD_PATH}"
|
|
|
|
if [ "${global_virtiofsd_mem}" == "0" ]; then
|
|
echo >&2 "WARNING: Failed to find PSS for ${VIRTIOFSD_PATH}"
|
|
fi
|
|
get_pss_memory "${SHIM_PATH}" 1
|
|
|
|
if [ "${global_shim_mem}" == "0" ]; then
|
|
die "Failed to find PSS for ${SHIM_PATH}"
|
|
fi
|
|
mem_usage="$(bc -l <<< "scale=2; ${global_hypervisor_mem} + ${global_virtiofsd_mem} + ${global_shim_mem}")"
|
|
|
|
local json="$(cat << EOF
|
|
{
|
|
"average": {
|
|
"Result": ${mem_usage},
|
|
"Units" : "KB"
|
|
},
|
|
"qemus": {
|
|
"Result": ${global_hypervisor_mem},
|
|
"Units" : "KB"
|
|
},
|
|
"virtiofsds": {
|
|
"Result": ${global_virtiofsd_mem},
|
|
"Units" : "KB"
|
|
},
|
|
"shims": {
|
|
"Result": ${global_shim_mem},
|
|
"Units" : "KB"
|
|
}
|
|
}
|
|
EOF
|
|
)"
|
|
fi
|
|
|
|
metrics_json_add_array_element "$json"
|
|
metrics_json_end_array "Results"
|
|
}
|
|
|
|
function save_config(){
|
|
metrics_json_start_array
|
|
|
|
local json="$(cat << EOF
|
|
{
|
|
"containers": "${NUM_CONTAINERS}",
|
|
"ksm": "${ksm_on}",
|
|
"auto": "${AUTO_MODE}",
|
|
"waittime": "${WAIT_TIME}",
|
|
"image": "${IMAGE}",
|
|
"command": "${CMD}"
|
|
}
|
|
EOF
|
|
|
|
)"
|
|
metrics_json_add_array_element "$json"
|
|
metrics_json_end_array "Config"
|
|
}
|
|
|
|
function main(){
|
|
# Verify enough arguments
|
|
if [ $# != 2 ] && [ $# != 3 ];then
|
|
help
|
|
die "Not enough arguments [$*]"
|
|
fi
|
|
|
|
if [ "${CTR_RUNTIME}" != "io.containerd.runc.v2" ] && [ "${CTR_RUNTIME}" != "io.containerd.kata.v2" ]; then
|
|
die "Unknown runtime: ${CTR_RUNTIME}."
|
|
fi
|
|
|
|
#Check for KSM before reporting test name, as it can modify it
|
|
check_for_ksm
|
|
check_cmds "${SMEM_BIN}" bc
|
|
init_env
|
|
check_images "${IMAGE}"
|
|
|
|
metrics_json_init
|
|
save_config
|
|
get_memory_usage
|
|
|
|
if [ "${CTR_RUNTIME}" == "io.containerd.kata.v2" ]; then
|
|
get_individual_memory
|
|
elif [ "${CTR_RUNTIME}" == "io.containerd.runc.v2" ]; then
|
|
get_runc_individual_memory
|
|
fi
|
|
|
|
info "memory usage test completed"
|
|
metrics_json_save
|
|
}
|
|
|
|
main "$@"
|