totor
totor

Reputation: 155

Django / Azure : serving static files in production

I want to deploy a Django website hosted in an Azure Web App.

The static files are served perfectly in DEBUG mode (DEBUG=False), but I can't find the right settings to have the server take care of it in production. All my static files are collected in a "static" directory at the app root wwwroot/static/.

Here is the web.config, at the root of the application.

<?xml version="1.0" encoding="utf-8"?>    
<configuration>
<appSettings>
  <add key="WSGI_ALT_VIRTUALENV_HANDLER" value="MyApp.wsgi.application" />
  <add key="WSGI_ALT_VIRTUALENV_ACTIVATE_THIS" value="D:\home\site\wwwroot\env\Scripts\python.exe" />
  <add key="WSGI_HANDLER" value="ptvs_virtualenv_proxy.get_venv_handler()" />
  <add key="PYTHONPATH" value="D:\home\site\wwwroot" />
  <add key="DJANGO_SETTINGS_MODULE" value="MyApp.settings" />
  <add key="WSGI_LOG" value="D:\home\LogFiles\wfastcgit.log"/>
</appSettings>
<system.webServer>
  <handlers>
    <add name="PythonHandler" path="handler.fcgi" verb="*" modules="FastCgiModule" scriptProcessor="D:\Python34\python.exe|D:\Python34\Scripts\wfastcgi.py" resourceType="Unspecified" requireAccess="Script"/>
  </handlers>
  <rewrite>
    <rules>
      <rule name="Static Files" stopProcessing="true">
        <conditions>
          <add input="true" pattern="false" />
        </conditions>
      </rule>
      <rule name="Configure Python" stopProcessing="true">
        <match url="(.*)" ignoreCase="false" />
        <action type="Rewrite" url="handler.fcgi/{R:1}" appendQueryString="true" />
      </rule>
    </rules>
  </rewrite>
</system.webServer>

I also added the following web.config in the static directory:

<?xml version="1.0"?>  
<configuration>  
<system.webServer>
    <!-- this configuration overrides the FastCGI handler to let IIS serve the static files -->
    <handlers>
       <clear />
        <add 
            name="StaticFile"
            path="*" verb="*" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" 
            resourceType="Either" 
            requireAccess="Read" />
    </handlers>
</system.webServer>

I also defined the static directory as a "virtual directory" in the web app settings, with "\static" refering to "\site\wwwroot\static", and checking the "application" checkbox (i also tried to leave it unchecked but it does not change anything.

However, this is not working.

1) Is it possible to setup an Azure web app to serve the static files in production without using a CDN?

2) If yes, how to do it?

Upvotes: 1

Views: 6628

Answers (4)

Alvaro Rodriguez Scelza
Alvaro Rodriguez Scelza

Reputation: 4164

If after trying all the other proposed solutions you still find yourself at trouble, you may have to understand that depending on the server that's running you application is the way static files are server. Django has it's own server, which is run by using the command:

python manage.py runserver

But PAAS providers do not use this server in most of the cases. gunicorn is in most times the chosen one. Azure sometimes uses IIS's FastCGI handler but at current date it is intelligent enough to detect a django application and try to use django's default server.

So, the first step you have to take is to find out what server is azure using to run your app, and you may know that by reading azure's log:

https:// YOUR APP NAME.scm.azurewebsites.net/api/logstream

If you confirm that azure is using django's default server, you must bear in mind that django does not server static files automatically when in a production environment. You must configure the static folder's url. So in your config/urls.py set the static url, like this:

from django.conf import settings
from django.conf.urls.i18n import i18n_patterns
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path

urlpatterns = i18n_patterns(path('admin/', admin.site.urls), prefix_default_language=False) + \
    static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + \
    static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

As you can see, I enable admin, static and media urls. This is enough to let django's default server know where to find and serve static files (and media files too, as well as admin routes)

Upvotes: 0

totor
totor

Reputation: 155

I was able to solve the problem after contacting Azure support. This is actually not a trivial problem, and other may have experienced it. The solution below also significantly improves the performances of the app.

According to Microsoft Azure support team:

  • the default Python version I was using (3.4) creates some problems and it's better to install a new Python version using the web app extensions.
  • For Azure Web App in Python, it's better to use HTTP Platform Handler than fastcgi.
  • I was also experiencing very slow loading page. This was due to a high number of Python packages that were loaded when the application restart. for some reason, the default Python version tends to fail, so the application has to restart even if the "Always ON" settings is ON. It seems that Azure actually loads the Python environment from a different disk than the Python code, and this creates a significant slow down.
  • Don't use a virtual environment

Solution

Here is the procedure I followed to solve the problem(s):

  1. Install Python 3.5 as a web app extension. This can easily be done using the Azure portal for the web application : "Settings > Extensions"
  2. In the Azure portal for the web app, go to "Advanced Tools". Open the Kudu advanced tool, then the CMD console (from the "Debug console" menu at the top of the Kudu page). Navigate to the home directory and find where the new Python has been installed. In my case it was in "D:\home\python353x86"
  3. Now it's time to update the web.config using the new Python version. This path is explained in great details here: https://prashanthmadi.github.io/2016/11/15/django-app-with-httpplatformhandler-in-azure-app-services-windows-new.html. Be careful to replace the processPath="D:\home\Python27\python.exe" from the tutorial with your own Python path. Same thing for the deployment script
  4. Add a new web.config file in your static directory, as described in the same tutorial

  5. Install Python packages, by using the debug console and your "requirements.txt" file, running:

    D:\home\python353x86\python.exe -m pip install -r requirements.txt

Replace the python path with your actual path. The python packages are now installed along with your Python exe. Using a virtual environment will slow down the loading of your web page in case the application has to restart.

At this point, your web app should work properly

Additional note:

If you are using webjobs, you need to update the way you are running them. The solution is to create a "run.cmd" file in your webjob directory, containing :

D:\home\python353x86\python.exe start.py

Where start.py is your webjob script. Once again, replace the python path with your actual path.

Upvotes: 1

TerMax
TerMax

Reputation: 11

I've solved this problem by adding this 2 lines to the top of <handlers> section in root web.config file:

<remove name="Python27_via_FastCGI" />
<remove name="Python34_via_FastCGI" />

Everything else is the same as your's web.config and I didn't need separate config file in static/ directory

Upvotes: 1

Thiago Custodio
Thiago Custodio

Reputation: 18387

It's a good idea to move your static content to Azure Blob Storage and use a CDN for a better performance.

1-) You can define your web.config just like the following: https://github.com/prashanthmadi/azure-django-customdeployment/blob/master/web.config

2-) to configure the static file handler, add the following: https://stackoverflow.com/a/2066040/1384539

Upvotes: 1

Related Questions