github.com/kata-containers/tests@v0.0.0-20240307153542-772105b56064/metrics/lib/common.bash (about) 1 #!/bin/bash 2 # 3 # Copyright (c) 2017-2021 Intel Corporation 4 # 5 # SPDX-License-Identifier: Apache-2.0 6 7 THIS_FILE=$(readlink -f ${BASH_SOURCE[0]}) 8 LIB_DIR=${THIS_FILE%/*} 9 RESULT_DIR="${LIB_DIR}/../results" 10 11 source ${LIB_DIR}/../../lib/common.bash 12 source ${LIB_DIR}/json.bash 13 source /etc/os-release || source /usr/lib/os-release 14 15 # Set variables to reasonable defaults if unset or empty 16 CTR_EXE="${CTR_EXE:-ctr}" 17 DOCKER_EXE="${DOCKER_EXE:-docker}" 18 CTR_RUNTIME="${CTR_RUNTIME:-io.containerd.kata.v2}" 19 RUNTIME="${RUNTIME:-containerd-shim-kata-v2}" 20 KATA_HYPERVISOR="${KATA_HYPERVISOR:-qemu}" 21 TEST_REPO="${TEST_REPO:-github.com/kata-containers/tests}" 22 JSON_HOST="${JSON_HOST:-}" 23 24 KSM_BASE="/sys/kernel/mm/ksm" 25 KSM_ENABLE_FILE="${KSM_BASE}/run" 26 KSM_PAGES_FILE="${KSM_BASE}/pages_to_scan" 27 KSM_SLEEP_FILE="${KSM_BASE}/sleep_millisecs" 28 KSM_PAGES_SHARED="${KSM_BASE}/pages_shared" 29 30 http_proxy="${http_proxy:-}" 31 https_proxy="${https_proxy:-}" 32 33 # The settings we use for an 'aggresive' KSM setup 34 # Scan 1000 pages every 50ms - 20,000 pages/s 35 KSM_AGGRESIVE_PAGES=1000 36 KSM_AGGRESIVE_SLEEP=50 37 38 declare -A registries 39 registries[ubuntu]=\ 40 "docker.io/library 41 public.ecr.aws/lts 42 mirror.gcr.io/library 43 quay.io/libpod" 44 45 # This function checks existence of commands. 46 # They can be received standalone or as an array, e.g. 47 # 48 # cmds=(“cmd1” “cmd2”) 49 # check_cmds "${cmds[@]}" 50 check_cmds() 51 { 52 local cmd req_cmds=( "$@" ) 53 for cmd in "${req_cmds[@]}"; do 54 if ! command -v "$cmd" > /dev/null 2>&1; then 55 die "command $cmd not available" 56 fi 57 echo "command: $cmd: yes" 58 done 59 } 60 61 # This function performs a pull on the image names 62 # passed in (notionally as 'about to be used'), to ensure 63 # - that we have the most upto date images 64 # - that any pull/refresh time (for a first pull) does not 65 # happen during the test itself. 66 # 67 # The image list can be received standalone or as an array, e.g. 68 # 69 # images=(“img1” “img2”) 70 # check_imgs "${images[@]}" 71 check_images() 72 { 73 local img req_images=( "$@" ) 74 for img in "${req_images[@]}"; do 75 echo "ctr pull'ing: $img" 76 if ! sudo "${CTR_EXE}" image pull "$img"; then 77 die "Failed to pull image $img" 78 fi 79 echo "ctr pull'd: $img" 80 done 81 } 82 83 generate_build_dockerfile() { 84 local dockerfile="$1" 85 local image="$2" 86 local map_key="$3" 87 local text_to_replace="$4" 88 local regs=(${registries["${map_key}"]}) 89 for r in ${regs[@]}; do 90 sed 's|'${text_to_replace}'|'${r}'|g' \ 91 "${dockerfile}.in" > "${dockerfile}" 92 if sudo "${DOCKER_EXE}" build --build-arg http_proxy="${http_proxy}" --build-arg https_proxy="${https_proxy}" --label "$image" --tag "${image}" -f "$dockerfile" "$dockerfile_dir"; then 93 return 0 94 fi 95 done 96 return 1 97 } 98 99 # This function performs a build on the image names 100 # passed in, to ensure that we have the latest changes from 101 # the dockerfiles 102 build_dockerfile_image() 103 { 104 local image="$1" 105 local dockerfile_path="$2" 106 local dockerfile_dir=${2%/*} 107 108 if [ -f "$dockerfile_path" ]; then 109 echo "docker building $image" 110 if ! sudo "${DOCKER_EXE}" build --build-arg http_proxy="${http_proxy}" --build-arg https_proxy="${https_proxy}" --label "$image" --tag "${image}" -f "$dockerfile_path" "$dockerfile_dir"; then 111 die "Failed to docker build image $image" 112 fi 113 return 0 114 fi 115 116 generate_build_dockerfile "${dockerfile_path}" "${image}" "ubuntu" "@UBUNTU_REGISTRY@" \ 117 || die "Failed to docker build image $image" 118 } 119 120 # This function removes the ctr image, builds a new one using a dockerfile 121 # and imports the image from docker to ctr 122 check_ctr_images() 123 { 124 local ctr_image="$1" 125 local dockerfile_path="$2" 126 local docker_image="$(echo ${ctr_image} | cut -d/ -f3 | cut -d: -f1)" 127 128 if [ -z "$ctr_image" ] || [ -z "$dockerfile_path" ]; then 129 die "Missing image or dockerfile path variable" 130 fi 131 132 sudo "${CTR_EXE}" i rm "${ctr_image}" 133 build_dockerfile_image "${docker_image}" "${dockerfile_path}" 134 sudo "${DOCKER_EXE}" save -o "${docker_image}.tar" "${docker_image}" 135 sudo "${CTR_EXE}" i import "${docker_image}.tar" 136 rm -rf "${docker_image}".tar 137 } 138 139 # A one time (per uber test cycle) init that tries to get the 140 # system to a 'known state' as much as possible 141 metrics_onetime_init() 142 { 143 # The onetime init must be called once, and only once 144 if [ ! -z "$onetime_init_done" ]; then 145 die "onetime_init() called more than once" 146 fi 147 148 # Restart services 149 restart_docker_service 150 restart_containerd_service 151 152 # We want this to be seen in sub shells as well... 153 # otherwise init_env() cannot check us 154 export onetime_init_done=1 155 } 156 157 # Print a banner to the logs noting clearly which test 158 # we are about to run 159 test_banner() 160 { 161 echo -e "\n===== starting test [$1] =====" 162 } 163 164 # Initialization/verification environment. This function makes 165 # minimal steps for metrics/tests execution. 166 init_env() 167 { 168 test_banner "${TEST_NAME}" 169 170 cmd=("docker" "ctr") 171 172 # check dependencies 173 check_cmds "${cmd[@]}" 174 175 # Remove all stopped containers 176 clean_env 177 clean_env_ctr 178 179 # This clean up is more aggressive, this is in order to 180 # decrease the factors that could affect the metrics results. 181 kill_processes_before_start 182 } 183 184 # This function checks if there are containers or 185 # shim/proxy/hypervisor processes up, if found, they are 186 # killed to start test with clean environment. 187 kill_processes_before_start() { 188 DOCKER_PROCS=$(sudo "${DOCKER_EXE}" ps -q) 189 [[ -n "${DOCKER_PROCS}" ]] && clean_env 190 191 CTR_PROCS=$(sudo "${CTR_EXE}" t list -q) 192 [[ -n "${CTR_PROCS}" ]] && clean_env_ctr 193 194 check_processes 195 } 196 197 # Generate a random name - generally used when creating containers, but can 198 # be used for any other appropriate purpose 199 random_name() { 200 mktemp -u kata-XXXXXX 201 } 202 203 show_system_ctr_state() { 204 echo "Showing system state:" 205 echo " --Check containers--" 206 sudo "${CTR_EXE}" c list 207 echo " --Check tasks--" 208 sudo "${CTR_EXE}" task list 209 210 local processes="containerd-shim-kata-v2" 211 212 for p in ${processes}; do 213 echo " --pgrep ${p}--" 214 pgrep -a ${p} 215 done 216 } 217 218 common_init(){ 219 if [ "$CTR_RUNTIME" == "io.containerd.kata.v2" ] || [ "$RUNTIME" == "containerd-shim-kata-v2" ]; then 220 extract_kata_env 221 else 222 # We know we have nothing to do for runc or shimv2 223 if [ "$CTR_RUNTIME" != "io.containerd.runc.v2" ] || [ "$RUNTIME" != "runc" ]; then 224 warn "Unrecognised runtime" 225 fi 226 fi 227 } 228 229 230 # Save the current KSM settings so we can restore them later 231 save_ksm_settings(){ 232 echo "saving KSM settings" 233 ksm_stored_run=$(cat ${KSM_ENABLE_FILE}) 234 ksm_stored_pages=$(cat ${KSM_ENABLE_FILE}) 235 ksm_stored_sleep=$(cat ${KSM_ENABLE_FILE}) 236 } 237 238 set_ksm_aggressive(){ 239 echo "setting KSM to aggressive mode" 240 # Flip the run off/on to ensure a restart/rescan 241 sudo bash -c "echo 0 > ${KSM_ENABLE_FILE}" 242 sudo bash -c "echo ${KSM_AGGRESIVE_PAGES} > ${KSM_PAGES_FILE}" 243 sudo bash -c "echo ${KSM_AGGRESIVE_SLEEP} > ${KSM_SLEEP_FILE}" 244 sudo bash -c "echo 1 > ${KSM_ENABLE_FILE}" 245 246 if [ "${KATA_HYPERVISOR}" == "qemu" ]; then 247 # Disable virtio-fs and save whether it was enabled previously 248 set_virtio_out=$(sudo -E PATH="$PATH" "${LIB_DIR}/../../.ci/set_kata_config.sh" shared_fs virtio-9p) 249 echo "${set_virtio_out}" 250 grep -q "already" <<< "${set_virtio_out}" || was_virtio_fs=true; 251 fi 252 } 253 254 restore_virtio_fs(){ 255 # Re-enable virtio-fs if it was enabled previously 256 [ -n "${was_virtio_fs}" ] && sudo -E PATH="$PATH" "${LIB_DIR}/../../.ci/set_kata_config.sh" shared_fs virtio-fs || \ 257 info "Not restoring virtio-fs since it wasn't enabled previously" 258 } 259 260 restore_ksm_settings(){ 261 echo "restoring KSM settings" 262 # First turn off the run to ensure if we are then re-enabling 263 # that any changes take effect 264 sudo bash -c "echo 0 > ${KSM_ENABLE_FILE}" 265 sudo bash -c "echo ${ksm_stored_pages} > ${KSM_PAGES_FILE}" 266 sudo bash -c "echo ${ksm_stored_sleep} > ${KSM_SLEEP_FILE}" 267 sudo bash -c "echo ${ksm_stored_run} > ${KSM_ENABLE_FILE}" 268 [ "${KATA_HYPERVISOR}" == "qemu" ] && restore_virtio_fs 269 } 270 271 disable_ksm(){ 272 echo "disabling KSM" 273 sudo bash -c "echo 0 > ${KSM_ENABLE_FILE}" 274 [ "${KATA_HYPERVISOR}" == "qemu" ] && restore_virtio_fs 275 } 276 277 # See if KSM is enabled. 278 # If so, amend the test name to reflect that 279 check_for_ksm(){ 280 if [ ! -f ${KSM_ENABLE_FILE} ]; then 281 return 282 fi 283 284 ksm_on=$(< ${KSM_ENABLE_FILE}) 285 286 if [ $ksm_on == "1" ]; then 287 TEST_NAME="${TEST_NAME} ksm" 288 fi 289 } 290 291 # Wait for KSM to settle down, or timeout waiting 292 # The basic algorithm is to look at the pages_shared value 293 # at the end of every 'full scan', and if the value 294 # has changed very little, then we are done (because we presume 295 # a full scan has managed to do few new merges) 296 # 297 # arg1 - timeout in seconds 298 wait_ksm_settle(){ 299 [[ "$RUNTIME" == "runc" ]] || [[ "$CTR_RUNTIME" == "io.containerd.runc.v2" ]] && return 300 local t pcnt 301 local oldscan=-1 newscan 302 local oldpages=-1 newpages 303 304 oldscan=$(cat /sys/kernel/mm/ksm/full_scans) 305 306 # Wait some time for KSM to kick in to avoid early dismissal 307 for ((t=0; t<5; t++)); do 308 pages=$(cat "${KSM_PAGES_SHARED}") 309 [[ "$pages" -ne 0 ]] && echo "Discovered KSM activity" && break 310 sleep 1 311 done 312 313 # Go around the loop until either we see a small % change 314 # between two full_scans, or we timeout 315 for ((t=0; t<$1; t++)); do 316 317 newscan=$(cat /sys/kernel/mm/ksm/full_scans) 318 newpages=$(cat "${KSM_PAGES_SHARED}") 319 [[ "$newpages" -eq 0 ]] && echo "No need to wait for KSM to settle" && return 320 321 if (( newscan != oldscan )); then 322 echo -e "\nnew full_scan ($oldscan to $newscan)" 323 324 # Do we have a previous scan to compare with 325 echo "check pages $oldpages to $newpages" 326 327 if (( oldpages != -1 )); then 328 # avoid divide by zero problems 329 if (( $oldpages > 0 )); then 330 pcnt=$(( 100 - ((newpages * 100) / oldpages) )) 331 # abs() 332 pcnt=$(( $pcnt * -1 )) 333 334 echo "$oldpages to $newpages is ${pcnt}%" 335 336 if (( $pcnt <= 5 )); then 337 echo "KSM stabilised at ${t}s" 338 return 339 fi 340 else 341 echo "$oldpages KSM pages... waiting" 342 fi 343 fi 344 oldscan=$newscan 345 oldpages=$newpages 346 else 347 echo -n "." 348 fi 349 sleep 1 350 done 351 echo "Timed out after ${1}s waiting for KSM to settle" 352 } 353 354 function start_kubernetes() { 355 info "Start k8s" 356 pushd "${GOPATH}/src/${TEST_REPO}/integration/kubernetes" 357 bash ./init.sh 358 popd 359 } 360 361 function end_kubernetes() { 362 info "End k8s" 363 pushd "${GOPATH}/src/${TEST_REPO}/integration/kubernetes" 364 bash ./cleanup_env.sh 365 popd 366 } 367 368 common_init