Blame SOURCES/bz1718219-podman-4-use-exec-to-avoid-performance-issues.patch

a89620
From 6016283dfdcb45bf750f96715fc653a4c0904bca Mon Sep 17 00:00:00 2001
a89620
From: Damien Ciabrini <dciabrin@redhat.com>
a89620
Date: Fri, 28 Jun 2019 13:34:40 +0200
a89620
Subject: [PATCH] podman: only use exec to manage container's lifecycle
a89620
a89620
Under heavy IO load, podman may be impacted and take a long time
a89620
to execute some actions. If that takes more than the default
a89620
20s container monitoring timeout, containers will restart unexpectedly.
a89620
a89620
Replace all IO-sensitive podman calls (inspect, exists...) by
a89620
equivalent "podman exec" calls, because the latter command seems
a89620
less prone to performance degradation under IO load.
a89620
a89620
With this commit, the resource agent now requires podman 1.0.2+,
a89620
because it relies on of two different patches [1,2] that improve
a89620
IO performance and enable to distinguish "container stopped"
a89620
"container doesn't exist" error codes.
a89620
a89620
Tested on an OpenStack environment with podman 1.0.2, with the
a89620
following scenario:
a89620
  . regular start/stop/monitor operations
a89620
  . probe operations (pcs resource cleanup/refresh)
a89620
  . unmanage/manage operations
a89620
  . reboot
a89620
a89620
[1] https://github.com/containers/libpod/commit/90b835db69d589de559462d988cb3fae5cf1ef49
a89620
[2] https://github.com/containers/libpod/commit/a19975f96d2ee7efe186d9aa0be42285cfafa3f4
a89620
---
a89620
 heartbeat/podman | 75 ++++++++++++++++++++++++------------------------
a89620
 1 file changed, 37 insertions(+), 38 deletions(-)
a89620
a89620
diff --git a/heartbeat/podman b/heartbeat/podman
a89620
index 51f6ba883..8fc2c4695 100755
a89620
--- a/heartbeat/podman
a89620
+++ b/heartbeat/podman
a89620
@@ -129,9 +129,6 @@ the health of the container. This command must return 0 to indicate that
a89620
 the container is healthy. A non-zero return code will indicate that the
a89620
 container has failed and should be recovered.
a89620
 
a89620
-If 'podman exec' is supported, it is used to execute the command. If not,
a89620
-nsenter is used.
a89620
-
a89620
 Note: Using this method for monitoring processes inside a container
a89620
 is not recommended, as containerd tries to track processes running
a89620
 inside the container and does not deal well with many short-lived
a89620
@@ -192,17 +189,13 @@ monitor_cmd_exec()
a89620
 	local rc=$OCF_SUCCESS
a89620
 	local out
a89620
 
a89620
-	if [ -z "$OCF_RESKEY_monitor_cmd" ]; then
a89620
-		return $rc
a89620
-	fi
a89620
-
a89620
 	out=$(podman exec ${CONTAINER} $OCF_RESKEY_monitor_cmd 2>&1)
a89620
 	rc=$?
a89620
-	if [ $rc -eq 127 ]; then
a89620
-		ocf_log err "monitor cmd failed (rc=$rc), output: $out"
a89620
-		ocf_exit_reason "monitor_cmd, ${OCF_RESKEY_monitor_cmd} , not found within container."
a89620
-		# there is no recovering from this, exit immediately
a89620
-		exit $OCF_ERR_ARGS
a89620
+	# 125: no container with name or ID ${CONTAINER} found
a89620
+	# 126: container state improper (not running)
a89620
+	# 127: any other error
a89620
+	if [ $rc -eq 125 ] || [ $rc -eq 126 ]; then
a89620
+		rc=$OCF_NOT_RUNNING
a89620
 	elif [ $rc -ne 0 ]; then
a89620
 		ocf_exit_reason "monitor cmd failed (rc=$rc), output: $out"
a89620
 		rc=$OCF_ERR_GENERIC
a89620
@@ -215,7 +208,16 @@ monitor_cmd_exec()
a89620
 
a89620
 container_exists()
a89620
 {
a89620
-	podman inspect --format {{.State.Running}} $CONTAINER | egrep '(true|false)' >/dev/null 2>&1
a89620
+	local rc
a89620
+	local out
a89620
+
a89620
+	out=$(podman exec ${CONTAINER} $OCF_RESKEY_monitor_cmd 2>&1)
a89620
+	rc=$?
a89620
+	# 125: no container with name or ID ${CONTAINER} found
a89620
+	if [ $rc -ne 125 ]; then
a89620
+		return 0
a89620
+	fi
a89620
+	return 1
a89620
 }
a89620
 
a89620
 remove_container()
a89620
@@ -236,30 +238,30 @@ remove_container()
a89620
 
a89620
 podman_simple_status()
a89620
 {
a89620
-	local val
a89620
-
a89620
-	# retrieve the 'Running' attribute for the container
a89620
-	val=$(podman inspect --format {{.State.Running}} $CONTAINER 2>/dev/null)
a89620
-	if [ $? -ne 0 ]; then
a89620
-		#not running as a result of container not being found
a89620
-		return $OCF_NOT_RUNNING
a89620
-	fi
a89620
+	local rc
a89620
 
a89620
-	if ocf_is_true "$val"; then
a89620
-		# container exists and is running
a89620
-		return $OCF_SUCCESS
a89620
+	# simple status is implemented via podman exec
a89620
+	# everything besides success is considered "not running"
a89620
+	monitor_cmd_exec
a89620
+	rc=$?
a89620
+	if [ $rc -ne $OCF_SUCCESS ]; then
a89620
+		rc=$OCF_NOT_RUNNING;
a89620
 	fi
a89620
-
a89620
-	return $OCF_NOT_RUNNING
a89620
+	return $rc
a89620
 }
a89620
 
a89620
 podman_monitor()
a89620
 {
a89620
-	if [ -z "$OCF_RESKEY_monitor_cmd" ]; then
a89620
-		podman_simple_status
a89620
-		return $?
a89620
-	fi
a89620
+	# We rely on running podman exec to monitor the container
a89620
+	# state because that command seems to be less prone to
a89620
+	# performance issue under IO load.
a89620
+	#
a89620
+	# For probes to work, we expect cmd_exec to be able to report
a89620
+	# when a container is not running. Here, we're not interested
a89620
+	# in distinguishing whether it's stopped or non existing
a89620
+	# (there's function container_exists for that)
a89620
 	monitor_cmd_exec
a89620
+	return $?
a89620
 }
a89620
 
a89620
 podman_create_mounts() {
a89620
@@ -416,14 +418,6 @@ podman_validate()
a89620
 		exit $OCF_ERR_CONFIGURED
a89620
 	fi
a89620
 
a89620
-	if [ -n "$OCF_RESKEY_monitor_cmd" ]; then
a89620
-		podman exec --help >/dev/null 2>&1
a89620
-		if [ ! $? ]; then
a89620
-			ocf_log info "checking for nsenter, which is required when 'monitor_cmd' is specified"
a89620
-			check_binary nsenter
a89620
-		fi
a89620
-	fi
a89620
-
a89620
 	image_exists
a89620
 	if [ $? -ne 0 ]; then
a89620
 		ocf_exit_reason "base image, ${OCF_RESKEY_image}, could not be found."
a89620
@@ -457,6 +451,11 @@ fi
a89620
 
a89620
 CONTAINER=$OCF_RESKEY_name
a89620
 
a89620
+# Note: we currently monitor podman containers by with the "podman exec"
a89620
+# command, so make sure that invocation is always valid by enforcing the
a89620
+# exec command to be non-empty
a89620
+: ${OCF_RESKEY_monitor_cmd:=/bin/true}
a89620
+
a89620
 case $__OCF_ACTION in
a89620
 meta-data) meta_data
a89620
 		exit $OCF_SUCCESS;;