在DigitalOcean云服务器上部署Django项目实践笔记

首先得有一份Django app项目代码,在本地调试模式下(python manage.py runserver)跑起来各种没问题。

配置云服务器

在DigitalOcean上申请Ubuntu 16.04LTS的Droplet,拿到公网IP和root密码,登陆后安装Django项目中用到的数据库软件,比如MySql:

1
2
3
4
5
sudo apt-get update
sudo apt-get -y upgrade
sudo apt-get install mysql-server
sudo apt isntall mysql-client
sudo apt install libmysqlclient-dev

执行命令sudo netstat -tap | grep mysql来检查MySql是否在运行,登录打开MySql提示符执行mysql> create database proj_db_name character set utf8;来预创建Django项目要用到的数据库,这里数据库名字为proj_db_name

执行sudo apt-get -y install nginx安装NGINX,用来服务支持静态文件(css,js, images),还可以在代理服务器下运行Django app。

安装Supervisor,用来启动和管理Django应用程序服务。

1
2
3
sudo apt-get -y install supervisor
sudo systemctl enable supervisor
sudo systemctl start supervisor

安装Python 3.x,我实践中是安装的3.6版本,那就需要去下载Python 3.6源码做编译安装。

执行sudo apt-get -y install python-virtualenv安装Python Virtualenv,使用virtualenv已然是Python项目的最佳实践之一,它可以方便的建立独立python依赖空间,避免项目之间可能的依赖冲突问题。一般实践中是virtualenv venv在项目根目录下生成保存独立python环境与依赖的venv目录。

添加项目专用的用户,执行adduser joe,这里joe就是用户名,然后gpasswd -a joe sudo加入到sudo用户列表。

执行su - joe切换到joe账号,当前目录应该是/home/joe,再执行virtualenv -p python3 .,也即在当前目录生成一份独立python环境,python版本和系统命令python3指向的python版本一致,如果不加-p python3则就和默认命令python指向的python版本一致,再继续执行source bin/activate启用这个独立虚拟python环境。

1
2
3
4
joe@ubuntu-s-1vcpu-1gb-sfo2:~$ source bin/activate
(joe) joe@ubuntu-s-1vcpu-1gb-sfo2:~$ ls
bin include lib logs pip-selfcheck.json run
(joe) joe@ubuntu-s-1vcpu-1gb-sfo2:~$

设置Django项目

git clone项目代码到/home/joe,执行git clone https://github.com/python012/guest.git,继续执行pip install -r guest/requirements.txt安装所有依赖。

注意这里guest是Django项目(as Django app)名字。

修改Django项目中的./guest/guest/settings.py,以下是修改后的一个diff:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-DEBUG = True
+DEBUG = False # 在生产环境中必须关闭DEBUG模式

-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = ['167.99.104.197',] # 云主机的公网IP


# Application definition
@@ -81,11 +81,11 @@ DATABASES = {
'ENGINE': 'django.db.backends.mysql',
- 'HOST': '---------',
+ 'HOST': '127.0.0.1',
'PORT': '3306',
- 'NAME': 'guest',
+ 'NAME': 'proj_db_name',
'USER': 'MySqlUsername',
- 'PASSWORD': '---------',
+ 'PASSWORD': 'MysqlPassword',
'OPTIONS': {
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
},
@@ -132,9 +132,10 @@ USE_TZ = False

STATIC_URL = '/static/'

+STATIC_ROOT = '/home/joe/www/static' # 静态文件目录

回到当前目录/home/joe,生成静态文件目录mkdir /home/joe/www/static,执行python manage.py migrate连接MySql初始化各个数据表,再执行python manage.py createsuperuser来生成管理员账户,最后执行python manage.py collectstatic

设置Gunicorn、Supervisor、NGINX

执行pip install gunicorn安装Gunicorn(可能是发音G unicorn),这是一个WSGI容器(WSGI HTTP Server)。

执行vim bin/gunicorn_start,文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash

NAME="guest"
DIR=/home/joe/guest
USER=joe
GROUP=joe
WORKERS=3
BIND=unix:/home/joe/run/gunicorn.sock
DJANGO_SETTINGS_MODULE=guest.settings
DJANGO_WSGI_MODULE=guest.wsgi
LOG_LEVEL=error

cd $DIR
source ../bin/activate

export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DIR:$PYTHONPATH

exec ../bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $WORKERS \
--user=$USER \
--group=$GROUP \
--bind=$BIND \
--log-level=$LOG_LEVEL \
--log-file=-

执行chmod u+x bin/gunicorn_start,给gunicorn_start文件添加执行权限。在用户目录/home/joe下生成run、logs两个目录。执行touch logs/gunicorn-error.log用来保存Django app错误日志。

执行sudo vim /etc/supervisor/conf.d/guest.conf生成一个Supervisor配置文件,文件内容如下:

1
2
3
4
5
6
7
[program:guest]
command=/home/joe/bin/gunicorn_start
user=joe
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/home/joe/logs/gunicorn-error.log

刷新Supervisor的配置文件信息,启动Django app。

1
2
sudo supervisorctl reread
sudo supervisorctl update

继续设置Nginx,执行sudo vim /etc/nginx/sites-available/guest生成Django app对应的配置文件,文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
upstream app_server {
server unix:/home/joe/run/gunicorn.sock fail_timeout=0;
}

server {
# 如果设置为80则可以外网用户可以通过直接访问云主机的公网IP来打开Django app
listen 8089;

# add here the ip address of your server
# or a domain pointing to that ip (like example.com or www.example.com)
server_name 167.99.104.197;

keepalive_timeout 5;
client_max_body_size 4G;

access_log /home/joe/logs/nginx-access.log;
error_log /home/joe/logs/nginx-error.log;

location /static/ {
alias /home/joe/www/static/;
}

# checks for static file, if not found proxy to app
location / {
try_files $uri @proxy_to_app;
}

location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
}

执行sudo ln -s /etc/nginx/sites-available/guest /etc/nginx/sites-enabled/guest创建符号链接,执行sudo rm /etc/nginx/sites-enabled/default删除NGINX默认的网站配置,最后执行sudo service nginx restart来重启nginx,这时候应该能够通过云服务器的公网IP访问Django app了,注意还需要加上配置的端口号8089,如果设置为80则可直接访问IP了。如果后期Django项目代码有更改,可以执行sudo supervisorctl restart guest来重启Djang app以应用最新项目代码。