Run Django + uwsgi + nginx + sqlite3 on Sakura VPS CentOS 7
Introduction
We are building an environment by installing each software version on the 1G plan of Sakura VPS.
name | version |
---|---|
CentOS | 7.8.2003 (Core) |
python | 3.6.8 |
django | 3.1 |
uwsgi | 2.0.19.1 |
nginx | 1.16.1 |
sqlite | 3.30.0 |
Set up Sakura VPS
As a prerequisite, it is assumed that the standard OS CentOS7 x86_64 is installed and the initial setting guide for the server in the startup guide is completed.
In addition, enable the Web in the server initial setting guide 6 “Let’s set the packet filter”.
Install Django
Check the Django site (https://www.djangoproject.com/) for instructions on how to install it.
You need Python to use Django, so first check the version of Python you have installed.
$ python --version
Python 2.7.5
According to the FAQ “Which version of Python can I use Django with?”, Django that can be used with Python 2.7.5 is 1.11. However, since 1.11 is no longer supported on April 1, 2020, Python I’ll use 3.
$ python3 --version
-bash: python3: command not found
Python 3 is not installed, so install it.
$ sudo yum -y install python3
Check the version of Python 3 installed.
$ python3 --version
Python 3.6.8
Since it’s Python 3.6.8, you can use up to the latest Django 3.1.
Now that Python is ready, install Django.
$ sudo pip3 install django
Check the version of Django you have installed.
$ django-admin --version
3.1
For the time being, let’s create a project and check if it works.
$ django-admin startproject mysite
$ cd mysite
$ python3 manger.py runserver
Somehow an error has occurred.
Watching for file changes with StatReloader
Exception in thread django-main-thread:
Traceback (most recent call last):
File "/usr/lib64/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/usr/lib64/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "/usr/local/lib64/python3.6/site-packages/django/utils/autoreload.py", line 53, in wrapper
fn(*args, **kwargs)
File "/usr/local/lib64/python3.6/site-packages/django/core/management/commands/runserver.py", line 110, in inner_run
autoreload.raise_last_exception()
File "/usr/local/lib64/python3.6/site-packages/django/utils/autoreload.py", line 76, in raise_last_exception
raise _exception[1]
File "/usr/local/lib64/python3.6/site-packages/django/core/management/__init__.py", line 357, in execute
autoreload.check_errors(django.setup)()
File "/usr/local/lib64/python3.6/site-packages/django/utils/autoreload.py", line 53, in wrapper
fn(*args, **kwargs)
File "/usr/local/lib64/python3.6/site-packages/django/__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "/usr/local/lib64/python3.6/site-packages/django/apps/registry.py", line 114, in populate
app_config.import_models()
File "/usr/local/lib64/python3.6/site-packages/django/apps/config.py", line 211, in import_models
self.models_module = import_module(models_module_name)
File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/usr/local/lib64/python3.6/site-packages/django/contrib/auth/models.py", line 2, in <module>
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
File "/usr/local/lib64/python3.6/site-packages/django/contrib/auth/base_user.py", line 48, in <module>
class AbstractBaseUser(models.Model):
File "/usr/local/lib64/python3.6/site-packages/django/db/models/base.py", line 122, in __new__
new_class.add_to_class('_meta', Options(meta, app_label))
File "/usr/local/lib64/python3.6/site-packages/django/db/models/base.py", line 326, in add_to_class
value.contribute_to_class(cls, name)
File "/usr/local/lib64/python3.6/site-packages/django/db/models/options.py", line 206, in contribute_to_class
self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
File "/usr/local/lib64/python3.6/site-packages/django/db/__init__.py", line 28, in __getattr__
return getattr(connections[DEFAULT_DB_ALIAS], item)
File "/usr/local/lib64/python3.6/site-packages/django/db/utils.py", line 214, in __getitem__
backend = load_backend(db['ENGINE'])
File "/usr/local/lib64/python3.6/site-packages/django/db/utils.py", line 111, in load_backend
return import_module('%s.base' % backend_name)
File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "/usr/local/lib64/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 70, in <module>
check_sqlite_version()
File "/usr/local/lib64/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 67, in check_sqlite_version
raise ImproperlyConfigured('SQLite 3.8.3 or later is required (found %s).' % Database.sqlite_version)
django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).
The error message says that I am requesting SQLite version 3.8.3 or later, but I found 3.7.17.
Let’s actually check the version of SQLite.
$ sqlite3 -version
3.7.17 2013-05-20 00:56:22 118a3b35693b134d56ebd780123b7fd6f1497668
Certainly the version is 3.7.17, which is old, so it is necessary to upgrade.
Upgrade SQLite
Download and install the source code from the SQLite site.
$ cd ~/
$ wget https://www.sqlite.org/2020/sqlite-autoconf-3330000.tar.gz
$ tar zxvf sqlite-autoconf-3330000.tar.gz
$ cd sqlite-autoconf-3330000
$ ./configure
$ make
$ sudo make install
Check the version of SQLite you have installed.
$ /usr/local/bin/sqlite3 -version
3.33.0 2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f
Since the version of SQLite has been upgraded, specify the library path in LD_LIBRARY_PATH and try again.
$ cd ~/mysite
$ LD_LIBRARY_PATH=/usr/local/lib python3 manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
August 20, 2020 - 18:26:21
Django version 3.1, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
This time it seems that it was able to be executed properly.
Let’s check if we can access the server.
$ curl -i http://127.0.0.1:8000
HTTP/1.1 200 OK
Date: Thu, 20 Aug 2020 18:35:41 GMT
Server: WSGIServer/0.2 CPython/3.6.8
Content-Type: text/html
X-Frame-Options: DENY
Content-Length: 16351
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
<!doctype html>
<html>
<head>
...
Since it is troublesome to specify LD_LIBRARY_PATH every time, register the shared library path with ldconfig.
First, check the current registration status.
$ ldconfig -p | grep sqlite
libsqlite3.so.0 (libc6,x86-64) => /lib64/libsqlite3.so.0
Add the configuration file under /etc/ld.so.conf.d.
/etc/ld.so.conf.d/sqlite3.33.0.conf
/usr/local/lib
Update your registration details.
$ sudo ldconfig
Check the registration status again.
$ ldconfig -p | grep sqlite
libsqlite3.so.0 (libc6,x86-64) => /usr/local/lib/libsqlite3.so.0
libsqlite3.so.0 (libc6,x86-64) => /lib64/libsqlite3.so.0
libsqlite3.so (libc6,x86-64) => /usr/local/lib/libsqlite3.so
Now you don’t have to specify LD_LIBRARY_PATH.
install uwsgi
Install uwsgi for Web Server Gateway Interface.
$ sudo pip3 install uwsgi
Somehow an error has occurred.
In file included from plugins/python/python_plugin.c:1:0:
plugins/python/uwsgi_python.h:2:20: fatal error: Python.h: No such file or directory
#include <Python.h>
^
compilation terminated.
In file included from plugins/python/pyutils.c:1:0:
plugins/python/uwsgi_python.h:2:20: fatal error: Python.h: No such file or directory
#include <Python.h>
^
compilation terminated.
----------------------------------------
Command "/usr/bin/python3 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-evpbzqre/uwsgi/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-9qkvdn3u-record/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-build-evpbzqre/uwsgi/
The cause is that python3-devel is not installed, so install it.
$ sudo yum -y install python3-devel
Install uwsgi again.
$ sudo pip3 install uwsgi
I installed it without any problem, so check the version of uwsgi.
$ uwsgi --version
2.0.19.1
Prepare a test script that just returns Hello World to check the operation.
test.py
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"]
Run the test script.
$ uwsgi --http :8000 --wsgi-file test.py
Access the local host on port number 8000 to see if it’s running.
$ curl -i localhost:8000
HTTP/1.1 200 OK
Content-Type: text/html
Hello World
It worked fine, so let’s run Django.
$ cd ~/mysite
$ uwsgi --http :8000 --module mysite.wsgi
Somehow an error has occurred.
*** Starting uWSGI 2.0.19.1 (64bit) on [Sun Aug 23 00:36:53 2020] ***
compiled with version: 4.8.5 20150623 (Red Hat 4.8.5-39) on 22 August 2020 13:27:06
os: Linux-3.10.0-1127.18.2.el7.x86_64 #1 SMP Sun Jul 26 15:27:06 UTC 2020
nodename:server name
machine: x86_64
clock source: unix
detected number of CPU cores: 2
current working directory: /home/username/mysite
detected binary path: /usr/local/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
*** WARNING: you are running uWSGI without its master process manager ***
your processes number limit is 3881
your memory page size is 4096 bytes
detected max file descriptor number: 1024
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
uWSGI http bound on :8000 fd 4
spawned uWSGI http 1 (pid: 11759)
uwsgi socket 0 bound to TCP address 127.0.0.1:40228 (port auto-assigned) fd 3
Python version: 3.6.8 (default, Apr 2 2020, 13:34:55) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
*** Python threads support is disabled. You can enable it with --enable-threads ***
Python main interpreter initialized at 0x165d4f0
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 72904 bytes (71 KB) for 1 cores
*** Operational MODE: single process ***
Traceback (most recent call last):
File "./mysite/wsgi.py", line 16, in <module>
application = get_wsgi_application()
File "/usr/local/lib64/python3.6/site-packages/django/core/wsgi.py", line 12, in get_wsgi_application
django.setup(set_prefix=False)
File "/usr/local/lib64/python3.6/site-packages/django/__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "/usr/local/lib64/python3.6/site-packages/django/apps/registry.py", line 114, in populate
app_config.import_models()
File "/usr/local/lib64/python3.6/site-packages/django/apps/config.py", line 211, in import_models
self.models_module = import_module(models_module_name)
File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "/usr/local/lib64/python3.6/site-packages/django/contrib/auth/models.py", line 2, in <module>
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
File "/usr/local/lib64/python3.6/site-packages/django/contrib/auth/base_user.py", line 48, in <module>
class AbstractBaseUser(models.Model):
File "/usr/local/lib64/python3.6/site-packages/django/db/models/base.py", line 122, in __new__
new_class.add_to_class('_meta', Options(meta, app_label))
File "/usr/local/lib64/python3.6/site-packages/django/db/models/base.py", line 326, in add_to_class
value.contribute_to_class(cls, name)
File "/usr/local/lib64/python3.6/site-packages/django/db/models/options.py", line 206, in contribute_to_class
self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
File "/usr/local/lib64/python3.6/site-packages/django/db/__init__.py", line 28, in __getattr__
return getattr(connections[DEFAULT_DB_ALIAS], item)
File "/usr/local/lib64/python3.6/site-packages/django/db/utils.py", line 214, in __getitem__
backend = load_backend(db['ENGINE'])
File "/usr/local/lib64/python3.6/site-packages/django/db/utils.py", line 111, in load_backend
return import_module('%s.base' % backend_name)
File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "/usr/local/lib64/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 70, in <module>
check_sqlite_version()
File "/usr/local/lib64/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 67, in check_sqlite_version
raise ImproperlyConfigured('SQLite 3.8.3 or later is required (found %s).' % Database.sqlite_version)
django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).
unable to load app 0 (mountpoint='') (callable not found or import error)
*** no app loaded. going in full dynamic mode ***
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI worker 1 (and the only) (pid: 11758, cores: 1)
The error message says that I am requesting SQLite version 3.8.3 or later, but I found 3.7.17.
For some reason, the same message is displayed even though the version should have been upgraded earlier.
The cause is that uwsgi has RPATH set.
$ objdump -p /usr/local/bin/uwsgi | grep RPATH
RPATH /usr/lib64
If RPATH is set, shared libraries will only be loaded from the specified directory.
Add / usr / local / lib to RPATH and recreate uwsgi.
$ sudo pip3 uninstall uwsgi
$ sudo LDFLAGS="-L/usr/local/lib -Wl,-rpath,/usr/local/lib" pip3 install uwsgi
Check what happened to RPATH.
$ objdump -p /usr/local/bin/uwsgi | grep RPATH
RPATH /usr/local/lib:/usr/lib64
Try running it again.
$ cd ~/mysite
$ uwsgi --http :8000 --module mysite.wsgi
I was able to execute it without any problems.
install nginx
Install the web server nginx.
$ sudo yum -y install nginx
Check the version of nginx you have installed.
$ nginx -v
nginx version: nginx/1.16.1
Prepare the configuration file.
Replace the server name with http: // servername / for the server name with your own username for the username.
nginx:/etc/nginx/conf.d/mysite.conf
upstream django {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name server name;
location / {
uwsgi_pass django;
include /home/username/mysite/uwsgi_params;
}
}
A parameter file for uwsgi is prepared, so copy it to the project.
$ cp /etc/nginx/uwsgi_params ~/mysite
Now that everything is ready, restart nginx.
$ sudo systemctl restart nginx
Try running Django.
$ cd ~/mysite
$ uwsgi --socket :8000 --module mysite.wsgi
It seems that it can be executed, so try accessing http: // server name /.
Somehow an error has occurred.
The cause is that it was executed without setting ALLOWED_HOSTS.
Set ALLOWED_HOSTS.
mysite/settings.py
ALLOWED_HOSTS = ['server name']
After setting, try again.
The page where the rocket is flying is displayed.
The rocket page is displayed because DEBUG is True, so let’s change it to False.
mysite/settings.py
DEBUG = False
After setting, try again.
This time nothing is displayed.
Since the top page is not prepared yet, I will display the management page instead.
http://サーバー名/admin/にアクセスします。
Since static files such as images are not prepared, only characters are displayed.
Prepare a static file.
First, prepare a directory to store static files.
$ sudo mkdir /usr/share/nginx/html/static
$sudo chown username/usr/share/nginx/html/static
Set Django to store static files.
mysite/settings.py
STATIC_URL = '/static/'
STATIC_ROOT = '/usr/share/nginx/html/static'
Collect static files in the storage location.
$ python3 manage.py collectstatic
Set the storage destination of the static file on the nginx side as well.
nginx:/etc/nginx/conf.d/mysite.conf
location /static {
alias /usr/share/nginx/html/static;
}
location / {
After changing the server settings, restart the server for application.
$ sudo systemctl restart nginx
Now that you’re ready, run it and try to access it.
It was displayed.
Finally, let’s change it to access with unix socket.
nginx:/etc/nginx/conf.d/mysite.conf
upstream django {
server unix:///home/username/mysite/mysite.sock;
# server 127.0.0.1:8000;
}
Restart nginx for the settings to apply.
$ sudo systemctl restart nginx
Run it and try to access it.
502 Bad Gateway has occurred.
Let’s look at the error log.
$ sudo cat /var/log/nginx/error.log
2020/08/31 03:41:54 [crit] 12700#0: *2 connect() to unix:///home/username/mysite/mysite.sock failed (13: Permission denied) while connecting to upstream, ...
You can see that Permission denied has occurred.
The cause is that nginx does not have permission to access the user’s directory.
Add nginx to your group of users so that nginx can access it.
$ sudo usermod -aG username nginx
$ chmod 750 /home/username
$ sudo systemctl restart nginx
Try running it again to access it.
This time it was displayed.
As mentioned above, I was able to run Django + uwsgi + nginx + sqlite on CentOS 7 of Sakura VPS.