Here we will create a cloud based virtual server from scratch, following things need to be done to prepare develop and deployment env for django1.7+.
Personally I prefer to setup deployment env before developing any project, put difficulties ahead may be a good thing. As python2.6 is not officially supported and django1.6 also end of the support life when I wrote this document, I choose to use python2.7 + latest django1.8. As CentOS6 only has python2.6 shipped, other application like yum depend on python2.6 to work properly, of course, we can install python2.7 manually but it may broke other existing system applications, so I choose CentOS7(more specifically CentOS7.1) as the deployment OS, which has python2.7 shipped by default, anyway still something need you pay attention to:
- mysql-server package is not shipped by default in CentOS7
- service start method is totally different with CentOS6
Though there are many wsgi servers could be deployed for Django, and 3 combination way has been introduced in Django official site, I just choose the most popular one as our targets, the first combination is easy to deploy, and the second one could obtain much high performance and consume much less system resources(e,g: memory).
- PlanA: Apache + mod_wsgi
- PlanB: uWSGI + nginx
| # useradd test |
| # passwd test |
- Make normal user to suders
| |
| uncomment below line |
| %wheel ALL=(ALL) ALL |
- Add normal user to wheel group
- security harden on sshd service, change below on /etc/ssh/sshd_config
| port <specific_port> |
| PermitRootLogin no |
| MaxAuthTries 3 |
| X11Forwarding no |
- upgrade system to the latest version
| $ sudo yum install python-pip |
| $ sudo pip install |
| $ sudo rpm -ivh http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm |
| $ sudo yum install mysql-server |
| $ sudo systemctl enable mysqld |
| $ sudo systemctl start mysqld |
| $ sudo yum install MySQL-python |
- virtualenv related packages
| $ sudo pip install virtualenv virtualenvwrapper |
| $ mkdir ~/.virtualenv |
| |
| export PIP_REQUIRE_VIRTUALENV=true |
| export WORKON_HOME=$HOME/.virtualenvs |
| source /usr/bin/virtualenvwrapper.sh |
- Create test virtualenv for Django
| $ source ~/.bashrc |
| $ mkvirtualenv test |
| $ lsvirtualenv |
| $ pip install "django==1.8.15" |
Configuration
- Assume you want deploy django web env for www.example.com
- Create a dir names “example” under “/var/www/html” directory
- Create a static dir “static” under “/var/www/html/example/”, of course, it can be anywhere you like
- Create a configuration file e,g: example.conf file under /etc/httpd/conf.d as following:
| LoadModule wsgi_module modules/mod_wsgi.so |
| |
| WSGISocketPrefix /var/run/wsgi |
| <virtualhost *:80> |
| ServerName www.example.com |
| DocumentRoot /var/www/html/example |
| ErrorLog logs/www.example.com-error_log |
| CustomLog logs/www.example.com-access_log common |
| Alias /static/ /var/www/html/example/static/ |
| <directory /var/www/html/example/static> |
| Order deny,allow |
| Allow from all |
| </directory> |
| |
| WSGIDaemonProcess example python-path=/var/www/html/example processes=1 threads=10 display-name=%{GROUP} |
| WSGIProcessGroup example |
| WSGIScriptAlias / /var/www/html/example/wsgi.py |
| <directory /var/www/html/example> |
| <files wsgi.py> |
| Order deny,allow |
| Allow from all |
| </files> |
| </directory> |
| </virtualhost> |
Configuration
- Assume a virtualenv named ‘test’ already created, and django1.8 installed there
- Start project named ‘proj’ under current user’s home directory, e,g: /home/ryan/
- Create a vassal common default config file
| (test) [ryan@localhost ~]$ sudo vi /etc/uwsgi-vassals-default.ini |
| [uwsgi] |
| master = true |
| processes = 2 |
| threads = 2 |
| socket = /run/uwsgi/%(vassal_name).sock |
| |
| vacuum = true |
| plugins = python |
- Create a specific vassal config file under django project dir
| (test) [ryan@localhost ~]$ django-admin.py startproject proj |
| (test) [ryan@localhost ~]$ cd ~/proj |
| (test) [ryan@localhost ~]$ vi proj-uwsgi.ini |
| [uwsgi] |
| vassal_name = proj |
| home_dir = /home/ryan |
| |
| chdir = %(home_dir)/%(vassal_name) |
| home= %(home_dir)/.virtualenvs/test <---- pls use the actual virtualenv dir you created, this is PYTHON_HOME |
| module=%(vassal_name).wsgi:application <---- pls use the real module path of wsgi.py file |
| chmod-socket=660 |
3. Link the specific vassal config file in CentOS7.2 default /etc/uwsgi.d/ dir
| (test) [ryan@localhost ~]$ sudo ln -s proj-uwsgi.ini /etc/uwsgi.d/ |
4. Change user/group of proj-uwsgi.ini to uwsgi:nginx for “emperor-tyrant = true” is the default setting in /etc/uwsgi.ini
| (test) [ryan@localhost ~]$ sudo chown uwsgi:nginx proj-uwsgi.ini |
When ’emperor-tyrant = true’ is set, the vassal process’s uid/gid will be set to the same as vassal config file, here it means file itself, not the uid/gid value in file content.
5. Change default /etc/uwsgi.ini to make it looks like below
| (test) [ryan@localhost ~]$ sudo vi /etc/uwsgi.ini |
| [uwsgi] |
| uid = uwsgi |
| gid = uwsgi |
| pidfile2 = /run/uwsgi/uwsgi.pid |
| emperor = /etc/uwsgi.d |
| stats = /run/uwsgi/stats.sock |
| emperor-tyrant = true |
| cap = setgid,setuid |
| |
| vassals-include = /etc/uwsgi-vassals-default.ini |
| |
| vacuum = true |
- start uwsgi service and check it’s status
| (test) [ryan@localhost ~]$ sudo systemctl start uwsgi |
| (test) [ryan@localhost ~]$ sudo systemctl status uwsgi -l |
- Configure nginx to establish connection with local unix socket created by uwsgi
| (test) [ryan@localhost ~]$ vi /etc/nginx/conf.d/proj-nginx.conf |
| server { |
| listen 80; |
| server_name www.test.com; |
| location = favicon.ico { access_log off; log_not_found off; } |
| location /static/ { |
| alias /home/ryan/proj/staticfiles/; |
| } |
| location / { |
| include uwsgi_params; |
| uwsgi_pass unix:/var/run/uwsgi/proj.sock; |
| } |
| } |
| (test) [ryan@localhost ~]$ usermod -aG ryan nginx |
| (test) [ryan@localhost ~]$ namei -om /path/to/project |
make sure all parent dir of /path/to/project all have execute permission
| (test) [ryan@localhost ~]$ chmod 711 /home/ryan |
- Why we still get HTTP 502 error even follow above steps to configure nginx and uwsgi, detail error message recorded in nginx log file as below?
connect() to unix:/var/run/uwsgi/xxx.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.1.1, server: www.test.com, request: “GET / HTTP/1.1”, upstream: “uwsgi://unix:/var/run/uwsgi/xxx.sock:”, host: “www.test.com“, referrer: “http://www.test.com/“
The reason may be SELinux not having the policy for nginx to write to sockets. You may need completely disable selinux or add the enforcement policy for nginx