Managing User Services for Python Applications

About venvRun.sh

It is important to ensure any automated tasks being run are aware of already-running instances of itself, so it can prevent creating additional instances which would otherwise begin stacking or fighting over finite resources such as a listening socket, etc.

Host Networks are providing venvRun.sh as an example script to demonstrate using just one of various methods for handling the task of running a headless script, meaning it runs without being tied to any remote logged in session.

Additionally it handles automatically re-starting the python script should it close, crash, or quit for any reason.

Feel free to change it to suit your preferences as desired.


Create venvRun.sh for the account

SSH in and run the following command to create the management script in the account's home directory:

fn="${HOME}/venvRun.sh";[ -f "${fn}" ]&&echo "${fn} exists already."||curl -L "https://faq.hostnetworks.com.au/static/src/venvRun.sh" -o "${fn}";chmod 755 "${fn}"

Alternative Copy-Paste method of installing script:

[ -f "${HOME}/venvRun.sh" ]&&echo "${HOME}/venvRun.sh already exists, not proceeding."||echo '#!/usr/bin/env bash
if [ ${#} -eq 0 ]; then
    echo "No virtualenv name provided, cannot continue without a valid virtualenv name."
else
    venvName="${1}"; shift
    pyScriptArgs=""
    if [ $# -gt 0 ]; then
        pyScriptArgs="${*}"
    fi
    scriptName="${BASH_SOURCE##*/}"
    PIDFILE="/tmp/${scriptName}.${venvName}.pid"
    myPID="${$}"
    venvPyDIR="${HOME}/virtualenv/${venvName}"
    pyVerStr="$(find "${venvPyDIR}" -maxdepth 1 -type d -iname '?.?')";pyVer="${pyVerStr##*/}"
    venvPyBIN="${pyVerStr}/bin/python${pyVer}"
    myPath="${PWD}"
    pyScript="${pyScriptArgs}"
    [ -f "${pyScript}" ]&&myPath="${pyScript%/*}" # test if pyScriptArgs exists as a file & cd to the full path (preceding the final '/') of our absolute filepath string
    if [[ -e "${PIDFILE}" ]]; then # check for, and create a PID FILE if it does not already exist:
        echo "PID FILE: \"${PIDFILE}\" exists, and contains the following: \"$(cat "${PIDFILE}")\""
    else
        echo "PID FILE: \"${PIDFILE}\" not found, creating new PID FILE containing the following PID: \"${myPID}\"..."
        echo "${myPID}" > "${PIDFILE}"
    fi
    chmod 660 "${PIDFILE}" # ensure pid file contents are not readable to everyone, just this user and group
    if [ "$(cat "${PIDFILE}")" == "${myPID}" ]; then
        echo "${PIDFILE} contains the following PID: \"$(cat "${PIDFILE}")\""
        echo "First-time run detected (our current PID matches PIDFILE) - Initiating start-up..."
        echo; echo "HELPFUL REMINDER/TIP: To run this as an automatically self-recovering service simply add the following to your crontab:"
        echo "venvName=\"${venvName}\"; pyScript=\"${pyScriptArgs}\"; ${PWD}/${scriptName} \${venvName} \${pyScript} >/dev/null 2>&1"|sed -E "s/\/home\/${USER}/\${HOME}/g"; echo
        cd "${myPath}";"${venvPyBIN}" ${pyScriptArgs}
        echo "Python script has now exited. Removing PID FILE and stopping."
        rm "${PIDFILE}"
    else
        echo "\"${PIDFILE}\" contains the following PID: \"$(cat "${PIDFILE}")\""
        echo "Current PID: \"${myPID}\" differs from the above; confirming that our python service is running:"
        pyPID="$(pgrep -f "${venvPyBIN}")"
        if [ "${pyPID}" == "" ]; then
            echo "No python processes found, cleaning up PIDFILE then exiting."
            rm "${PIDFILE}"
        else
            echo "Found the following python PID: \"${pyPID}\", no further action required, exiting."
        fi
    fi
fi' > "${HOME}/venvRun.sh";chmod 755 "${HOME}/venvRun.sh"


Before Using venvRun.sh

First create a new Python Web Application in cPanel:

Provide the desired name for the Application root, in this example we have chosen pyHttpd

cPanel - Python Web Applications

Click CREATE then move on to Using venvRun.sh below.


Using venvRun.sh

On the first-time run it is recommended to perform an interactive test run from SSH shell:

NB: The venvRun.sh script expects the name of the Python App created earlier, and either the path to the python script to be run, or a short string of python arguments to run directly.

For this example we will simply call a built-in python3 module to run:

~/venvRun.sh pyHttpd -m http.server 8000 --directory ${HOME}/pyshare

Output of ~/venvRun.sh command:
[demo@example.com ~]$ ~/venvRun.sh pyHttpd -m http.server 8000 --directory ${HOME}/pyshare
PID FILE: "/tmp/venvRun.sh.pyHttpd.pid" not found, creating new PID FILE containing the following PID: "3079860"...
/tmp/venvRun.sh.pyHttpd.pid contains the following PID: "3079860"
First-time run detected (our current PID matches PIDFILE) - Initiating start-up...

HELPFUL REMINDER/TIP: To run this as an automatically self-recovering service simply add the following to your crontab:
venvName="pyHttpd"; pyScript="-m http.server 8000 --directory ${HOME}/pyshare"; ${HOME}/venvRun.sh ${venvName} ${pyScript} >/dev/null 2>&1

Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Press Ctrl+C to exit this interactive test run:

^C
Keyboard interrupt received, exiting.
Python script has now exited. Removing PID FILE and stopping.
[demo@example.com ~]$ 

Complete the new Service with Cronjobs

Now to finalise deploying your service, and enable automatic starting, and self-recovery, simply add the HELPFUL REMINDER/TIP crontab output from the interactive test run performed earlier:

HELPFUL REMINDER/TIP: To run this as an automatically self-recovering service simply add the following to your crontab:

venvName="pyHttpd"; pyScript="-m http.server 8000 --directory ${HOME}/pyshare"; ${HOME}/venvRun.sh ${venvName} ${pyScript} >/dev/null 2>&1

After adding this command to run every minute in cPanel the Current Cron Jobs should show the following:

cPanel - Adding and Viewing Current Cron Jobs

Finished! That's it, you can safely run this venvRun.sh script without fear of creating duplicate processes, as it detects itself as already running and simply closes.


Further Resources

Still in need of help? For speedy assistance our friendly staff are always ready to help via our helpdesk, on Twitter, through our website, or directly via email to support.