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.
pravasi-6094, Thanks for the follow-up. Glad you found the information helpful. You may checkout some generic examples here. Much appreciated.
I see that startup script is a Unix script. Since I need to run a django command in the script, I will need the python virtualenv to be active when the startup script runs. Will the virtualenv be active when the startup script runs?
The runtime during the app startup will already have the virtual environment activated and users can run commands (e.g. like migrate) without the need to activate the environment. Please see.
To highlight further on container start-up process - When your site starts, we run your Docker image and create a Docker container. Your app runs inside of that container, and the file system contains only what is in the Docker image. If your Docker image doesn't install a particular component, it won't be there when your container starts. You can take charge of this by building your own custom image that will install this without manual intervention. Ref.
Sign in to comment