How to set my Django app's Management commands as CRON jobs in default Linux Azure App Service container

pravasi 6 Reputation points
2022-02-02T14:55:21.6+00:00

We have deployed a Django app to Azure App Service successfully. It is using the Azure App Service's default Linux container and built and deployed via Github actions. I believe it is using the default startup script. I am the developer of the app but my knowledge as a system administrator is limited. This is our first deployment to Azure App Service , so our sysadmins are not able to help much.
Now I have to set up django commands as cron jobs. I saw one post which says to put the command to install cron and 'service cron start' in custom startup script. Since we don't use a custom startup script what is the alternative?.

How can I set up cron jobs in default App service container?

Azure App Service
Azure App Service
Azure App Service is a service used to create and deploy scalable, mission-critical web apps.
6,877 questions
0 comments No comments
{count} vote

2 answers

Sort by: Most helpful
  1. ajkuma 22,401 Reputation points Microsoft Employee
    2022-02-03T21:06:15.96+00:00

    pravasi-6094, Thanks for the good question.

    I understand you do not wish to use start script. On App Service, typically, you need to create your own custom docker container, or reference a start script to install the service | for CRON service (needs to installed and started on the container).

    Here is an example for a sample cron job with a startup script:
    Azure App Service Linux - Custom Startup Script for Nodejs & Python - (azureossd.github.io)

    --Just to highlight further, App Service uses the Gunicorn web server by default, which must be able to find your app object or wsgi.py folder.
    App Service looks for a file named wsgi.py within your app code, and then runs Gunicorn using the following command:

    <module> is the name of the folder that contains wsgi.py
    gunicorn --bind=0.0.0.0 --timeout 600 <module>.wsgi

    --GitHub Action workflow calls AppService-Build action and uses Oryx build system.

    See- Configure a Linux Python app for Azure App Service | for customizing build automation as per your requirement.


  2. eagle_app_server 1 Reputation point
    2022-09-20T20:52:49.647+00:00

    I also had the issue where I required django management commands to run on schedule. Some pitfalls I had whilst trying to achieve this:

    • WebJobs seemed like the obvious approach to this, but alas still no support for this on Linux based App Service at the time of writing.
    • A crontab configured with crontab -e, whilst shelling into my app service instance, doesn't have access to the environment which the app runs in, so the management commands fail.
    • I could try to acheive this with an Azure Trigger Function, or switch my app to an Azure Container setup with the eventual hope that I could schedule this command, but both of these required too much setup for the simple objective of scheduling a command, and are made more difficult because I'm using local git to deploy, so sharing this among different services is impossible or requires pushing to several remotes.

    My solution was the old skool linux one.

    Within the portal navigate to the blade for your App Service instance, and under Settings -> Configuration, find Startup Command which should be set to something like:

    gunicorn --bind=0.0.0.0:8000 --timeout 600 app_name.wsgi:application

    Ultimately you'll need to change this to a custom startup script, which is on the persistant home directory of your App Service instance. You may wish to create the script first, before changing this setting, so when you do restart the app it comes up smoothly first time:

    /home/startup_script.sh

    I chose to approach this with two scripts, which you must create via SSH (App Service Blade -> Development Tools -> SSH)

    The first one runs the update in a loop. Call this /home/my_scheduled_task.sh:

       while true  
       do  
         /home/site/repository/manage.py api_sync_now # This is your django management command  
         sleep 600  
       done  
    

    This will be launched once on startup, and runs the Django management command itself in a loop, sleeping for 600 seconds (10 minutes) between executions.

    Now to make this launch, the actual startup scripts itself, which should be called /home/startup_script.sh:

       /bin/sh /home/my_scheduled_task.sh >> /home/my_scheduled_task.log 2>&1 &  
       disown $!  
       gunicorn --bind=0.0.0.0:8000 --timeout 600 app_name.wsgi:application  
    

    Line 1 here is a standard linux way of launching a script (my_scheduled_task.sh in this case) in the background. Output is directed to the .log file, and 2>&1 causes STDERR output to also go to that log file. The final & makes it run in the background. When run manually this command will give you a PID of the backgroud job.

    Line 2 here causes the PID returned by the above line to be disowned so it will not die once startup_script.sh has finished executing.

    Line 3 launches the gunicorn server - this is identical to what was in Startup Command within the portal.

    Once you've created these two scripts, go back and change the Startup Command value to /home/startup_script.sh and on save you will be prompted to restart the app.

    Once restarted, SSH into the instance again and confirm the script is running with ps aux

    You should see an entry for /bin/sh /home/my_scheduled_task.sh

    This method means the scheduled task will start again after an app reboot, which includes a code push (local git in my case, but most likely would work for Github deploy also). The only other thing to remember is the 600s gap is between the end and beginning of the next management command execution, so if your management command takes 5m to run, then the gap between the execution start times would actually be 15m.

    I'm also not sure how this would behave if the app is scaled OUT; it may loop once per instance.

    0 comments No comments