An extension to django-multisite and djangocms-multisite that brings:
- Packaging as an Addon for Divio Cloud and configuration of django-multisite and djangocms-multisite
- Tools to help handling moving database dumps between environments (live/stage/test/local) and still maintaining sensible domain names.
- single-process and multi-process deployment options
Install the Addon.
To be able to use the multisite setup locally, define the DJANGO_MULTISITE_PLUS_REWRITE_DOMAIN_FORMAT
environment variable (and/or set a value in the configuration form as a default).
DJANGO_MULTISITE_PLUS_REWRITE_DOMAIN_FORMAT='{}.192.168.99.100.xip.io'
This controls how you'll access the various sites. {}
will be replaced with the site slug. In the example above
we're using xip.io
a service that returns the ip in the name for any domain following that format.
Alternatively you could create an entry in /etc/hosts
for every domain you want to support and point
it to your local ip. Or have a local proxy that handles name resolution.
The DJANGO_MULTISITE_PLUS_REWRITE_DOMAINS
environment variable controls whether the domain names
are re-written based on DJANGO_MULTISITE_PLUS_REWRITE_DOMAIN_FORMAT
on every app startup. Set it to
True
for local development and test servers. On the live server, when the real_domain
should be used,
set DJANGO_MULTISITE_PLUS_USE_REAL_DOMAIN
to True
.
Now either create multiple Site entries (in admin under "Multisite+") and set the slug and real domain
accordingly (real domain can be blank).
Or set the DJANGO_MULTISITE_PLUS_SITES
in settings.py
like this and make sure DJANGO_MULTISITE_PLUS_AUTO_POPULATE_SITES
is True
:
DJANGO_MULTISITE_PLUS_SITES = {
'portal': {
'id': 1,
'real_domain': 'www.example.com',
'name': 'Example Portal',
},
'site1': {
'id': 2,
'real_domain': 'www.site1.com',
'name': 'Site 1',
},
'site2': {
'id': 3,
'real_domain': 'www.site2.com',
'name': 'Site 2',
},
}
id
is the django.contrib.sites.Site.id
and is optional. If left out the slug (the key in the dict) will be
used to create or update the existing database entries. If present the site with the given id will be updated or
created.
DJANGO_MULTISITE_PLUS_MODE
can be either single-process
or multi-process
.
single-process
means that only one python process will be run, which will serve all domains. This makes requests
slower (~400ms) because of some reloading that needs to be done, but is much easier to deploy and does not use much
resources (ram). To accomplish this it uses the dynamic SITE_ID
and monkeypatches that come with
django-multisite
.
multi-process
means that there will be a process per SITE_ID
. The setup uses a combination of
uwsgi emperor mode (by reading domains directly from the
postgres database from django.contrib.sites.Site
) and
uwsgi fastrouter to route incoming requests to the
correct process. With the multi-process
approach requests can be served faster, but it uses much more ram
(multiplied by the amount of sites).
The multi-process
mode requires the emperor_pg
uwsgi plugin. It is cumbersome to compile a custom version of
uwsgi with this plugin, so we build the default uwsgi wheel on the divio cloud with support already in there.
At the time of writing alpine is not supported yet. If this stops working, ask a Divio Cloud SRE to update
https://wheels.aldryn.net/admin/wheelsproxy/package/59573/change/ . This is the setup command we run on the wheels
proxy before building the uwsgi wheel:
echo '[uwsgi]\nmain_plugin=python,gevent,emperor_pg\ninherit = base' >/tmp/profile.ini && export UWSGI_PROFILE=/tmp/profile.ini
uwsgi will sometimes restart workers and start new workers on-the-fly. The first request to a django process is really
slow though, because django loads the bulk of its code at that time. This can produce slow requests at random times.
To avoid this we can load the bulk of the django app already at wsgi init time. Replace wsgi.py
with the following:
import os
from aldryn_django import startup
application = startup.wsgi(path=os.path.dirname(__file__))
# Django loads most of its code when the first request comes in. But that
# means that the first request of a new worker will always be really
# slow. So we simulate a request here to warm the process up.
try:
from django.test.client import Client
client = Client()
response = client.get('/', follow=True)
print('Process warmed up with initial request.')
except Exception as exc:
print('Failed to warm up process with initial request.')
Now the model can carry additional configurations to be used on vassal/socket INI file. For example, one change the number of workers for a given by simply:
from django_multisite_plus.models import Site
site = Site.objects.get(id=XXXXX)
site.extra_uwsgi_ini = '''
workers = 5
'''
site.save()
Please note that those change will take effect only after a server restart.
- Some of the features of django-multisite-plus may be worth merging into django-multisite.
- The configuration of djangocms-multisite could be moved to a separate Addon so this package does not need to depend on django-cms.