External Monitor Information and Templates
This article is written by, and published on behalf of, DevCentral MVP Leonardo Souza.
---
Introduction
External monitors have been in use on F5 devices for a long time, and you can find a lot of code and articles about them here on DevCentral. However, most of those articles and code samples are very old and outdated. So, I think is a good time to update that with new information and new templates.
In this article, I will provide some information about external monitors and templates you can use to build your own external monitor script and how you setup an external monitor.
External Monitor
The external monitor is a script that the F5 device will run to determine the status of a pool member (note: you can’t assign an external monitor to a node). The script runs the code and indicates if it was successful or not.
The script indicates success outputting something to the standard output, and failure if nothing was outputted.
The external monitor is your last resource in relation to monitoring, only to be used if there is no built-in monitoring that could be used to perform the monitoring. Built-in monitors are generally faster and easier to support as they do not require programming skills.
External monitors run on Linux, while some built-in monitors can run on Linux or TMM. Linux is the kernel that CentOS uses, and CenOS is the base system that F5 uses, also known as the host system. TMM is the Kernel that F5 uses on TMOS, which is the F5 system. Running a built-in monitor in TMM is faster than in Linux. TMM monitors were introduced in version 13.1.0, for more information read this solution: https://support.f5.com/csp/article/K11323537
Configuration Steps
Note: Instructions are for version 15.1.0, but should be similar in other versions, and you only need to setup this configuration in one device if you have an HA pair, as the configuration is shared during the config sync.
Let’s create a simple external monitor to ping a pool member. You can do via tmsh, but it is simpler via GUI.
Download the following file: https://raw.githubusercontent.com/leonardobdes/External_Monitor_Templates/master/bash_external_monitor_template.sh
In the GUI go to System > File Management > External Monitor Program File List > Click Import. Select the file and give a name “simple_script” to the script, and import. That will import the file to the system, and make it available in the GUI.
Now create a new monitor that points to the script file. In the GUI go to Local Traffic > Monitor > Click Create.
Give the name “simple_monitor”, and select type as External. In the External Program select simple_script, click Finished.
Now apply the new monitor to a pool member or pool.
Here is the full configuration created:
sys file external-monitor simple_script { checksum SHA1:817:01e135449c710e21dd090fdd688d940e319f97f5 create-time 2020-04-23:17:45:25 created-by admin last-update-time 2020-04-23:17:45:25 mode 33261 revision 1 size 817 source-path none updated-by admin } ltm monitor external simple_monitor { defaults-from external interval 5 run /Common/simple_script time-until-up 0 timeout 16 } ltm pool pool_asm { members { LABServerPHP:http { address 172.16.0.14 session monitor-enabled state up } test_server:http { address 172.16.0.19 session monitor-enabled state down } } monitor simple_monitor }
The first part is the script imported to the system. Next, the monitor that was created. Lastly, a pool using that monitor, also showing that one pool member is up and another one is down.
Templates
You can write the script to be used in the external monitor in any language that is supported by the F5 devices.
I created a template for each of the following languages:
- Bash
- Perl
- Python
- Tcl
For the templates go to this codeshare link: https://devcentral.f5.com/s/articles/External-Monitor-Templates
On my speed tests, I got these times with the templates:
- Bash - 0.014 seconds
- Perl - 0.020 seconds
- Tcl - 0.021 seconds
- Python - 0.084 seconds
As you can see, Bash is definitely the fastest. Perl and Tcl are very close, and Python is the slowest. But, you may need to use something that works very well in Python, so it might be your only option.
Bash Template
I will explain the Bash template, but the logic is the same for other programming languages. Each script has 2 versions, one with debug and one without, as indicated in their filename. I will explain the one with debug.
This tells the system what to use to run the script.
#!/bin/bash
The script will be called with 2 arguments, first is the IP, and second the port. The IP is passed in IPv6 format, example “::ffff:172.16.0.14”, so you need to remove “::ffff:” Old scripts, and also the sample_monitor script that comes in the system, use “sed 's/::ffff://'`” that are very slow compared with the above.
ip=${1:7} port=$2
This will create a log line in the file /var/log/ltm.
logger -p local0.notice "$MON_TMPL_NAME - PID: $$ Name: $NODE_NAME IP: $ip Port: $port"
This is where you add your code to test the pool member. This example is just a ping against the IP, and the “-c1” means a single packet. The script then saves the result to know if it failed or not. Next, it will log another line to /var/log/ltm.
ping -c1 $ip &> /dev/null result=$? logger -p local0.notice "$MON_TMPL_NAME - PID: $$ Result: $result"
Lastly, the scripts check if the result was successful. If successful, it will send up to the standard output.
[ $result -eq 0 ] && echo "up"
The log lines will be similar to these ones.
Apr 24 13:59:15 LABBIGIP1.lab.local notice logger[5178]: /Common/simple_monitor - PID: 5177 Name: /Common/test_server IP: 172.16.0.19 Port: 80 Apr 24 13:59:18 LABBIGIP1.lab.local notice logger[5201]: /Common/simple_monitor - PID: 5200 Name: /Common/LABServerPHP IP: 172.16.0.14 Port: 80 Apr 24 13:59:18 LABBIGIP1.lab.local notice logger[5203]: /Common/simple_monitor - PID: 5200 Result: 0 Apr 24 13:59:18 LABBIGIP1.lab.local notice logger[5209]: /Common/simple_monitor - PID: 5177 Result: 1
Note that the monitoring tests can run in parallel, so the lines will not be sequential. Use the PID number (Process ID), to match the lines.
Old scripts, and also the sample_monitor script that comes in the system, have multiple lines of code to check if the same monitor is still running. If running, the new process kills the old one, before performing the monitoring.
I tested this behavior in version 11.3.0 and 15.1.0, and this is not necessary. Before starting the new process for the script, the system will kill the old one, so any code in the script to control that is unnecessary as it will not be used. I assume this was necessary for old versions, as I never tested, but is not necessary for currently supported versions.
Arguments and Variables
The external monitor has multiple settings, including the ones you will find in any other monitor, like interval and timeout. However, it has 2 extra settings, those are arguments and variables.
Arguments are passed when calling the script, for example:
bash simple_script.sh IP port argument3
Variables are set as environment variables. You can then access that variable in the script. Be aware that the system will add the variables from the monitor first, and then add some extra variables.
The environment variables for version 11.3.0 are, with values as an example:
MON_TMPL_NAME=/Common/bash_external_monitor_template ARGS_I=2 3 abc X=b PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/contrib/bin:/usr/local/bin:/usr/contrib/sbin:/usr/local/sbin:/usr/libexec NODE_PORT=80 PWD=/var/run SHLVL=1 NODE_IP=::ffff:172.16.0.19 NODE_NAME=/Common/test_server RUN_I=/Common/bash_external_monitor_template _=/bin/env
ARGS_I will include the arguments you set in the monitor configuration, in this case, I have “2 3 abc”.
X is the variable I added in the monitor configuration, with value “b”.
That means you can use those variables in your script. Also, if you set the variable $PATH in your script as an example, it will get reset by the system, as the system variables are set after the monitor configuration variables.
Let’s expand that with examples.
Here is a monitor configuration with one argument and one variable.
ltm monitor external simple_monitor { args 2 defaults-from external interval 5 run /Common/simple_script time-until-up 0 timeout 16 user-defined debug 1 }
Here is the part of the script that changed:
ping -c${3} $ip &> /dev/null result=$? [[ $debug -eq 1 ]] && logger -p local0.notice "$MON_TMPL_NAME - PID: $$ Result: $result Ping: ${3}"
Now the script is using the argument 3 for the ping command, and the variable debug to define if log should occur.
Let’s see the logs created:
Apr 24 12:18:04 LABBIGIP1.lab.local notice logger[8179]: /Common/simple_monitor - PID: 8178 Name: /Common/test_server IP: 172.16.0.19 Port: 80 Apr 24 12:18:06 LABBIGIP1.lab.local notice logger[8196]: /Common/simple_monitor - PID: 8195 Name: /Common/LABServerPHP IP: 172.16.0.14 Port: 80 Apr 24 12:18:07 LABBIGIP1.lab.local notice logger[8206]: /Common/simple_monitor - PID: 8178 Result: 1 Ping: 2 Apr 24 12:18:07 LABBIGIP1.lab.local notice logger[8208]: /Common/simple_monitor - PID: 8195 Result: 0 Ping: 2
Ping is done with 2 packets, and the second log line still logged.
Testing the Script
External scripts are saved in this location:
/config/filestore/files_d/Common_d/external_monitor_d
That is for the Common partition, replace Common to the name of the partition if you import the script in another partition.
The file will have an ID number (100479), and also a version number that changes when you update the file (19):
:Common:simple_script_100479_19
That means you can go to that directory and run the script from there, and troubleshoot the errors. Don’t forget that when testing this way, you will not have the environment variables the system setup, you will have the environment variables from the shell where you run the command.
Let’s test the simple simple_monitor discussed above.
First, create the environment variables:
export debug=1 MON_TMPL_NAME='/Common/simple_monitor' NODE_NAME='/Common/test_server'
Run the script:
./\:Common\:simple_script_100479_22 '::ffff:172.16.0.14' 80 2
You will get the following log lines:
Apr 24 16:03:48 LABBIGIP1.lab.local notice root[12206]: /Common/simple_monitor - PID: 12205 Name: /Common/test_server IP: 172.16.0.14 Port: 80 Apr 24 16:03:49 LABBIGIP1.lab.local notice root[12224]: /Common/simple_monitor - PID: 12205 Result: 0 Ping: 2
You need to escape the “:” with \.
After you do the tests, delete the environment variables created:
unset debug MON_TMPL_NAME NODE_NAME
Conclusion
I hope this article provides you all the information you need to create your own external monitor. After you understand how it works, it is very simple to use. Just don’t forget, no “echo down”, only output something when the monitor was successful, and make sure you suppress any command output.