Overview
1 剥离项目源码 - N/A
2 首次部署
2.1 Django 项目与环境
2.1.1 获取项目源码
从你的 git 仓库下载 django 项目:
$ cd $HOME/tmp/
tmp/ $ git clone https://<path>/<to>/django-proj.git
tmp/ $ ls -F
django-proj/
tmp/ $ cd django-proj
django-proj/ $
我预期你的项目文件结构会像以下这样:
django-proj/ $ tree ./ -L 3
./
├── Docs/
│ └── n/a
├── source/
│ ├── source/
│ │ ├── __init__.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── functional_test/
│ │ ├── __init__.py
│ │ └── tests.py
│ ├── application/
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── forms.py
│ │ ├── __init__.py
│ │ ├── migrations/
│ │ ├── models.py
│ │ ├── static/
│ │ ├── templates/
│ │ ├── tests/
│ │ ├── urls.py
│ │ └── views.py
│ ├── manage.py
│ └── requirements.txt
├── README.md
├── static
│ └── favicon.ico
└── virtualenv
└── readme.virtualenv --python=python3 virtualenv.txt
* directories, * files
2.1.2 配置运行环境
django-proj/ $ ls -F
README.md virtualenv/ static/ source/ Doc/
django-proj/ $ virtualenv --python=python3 ./virtualenv/
...
django-proj/ $ cd source/
source/ $ ls -F
source/ application/ requirements.txt functional_test/ manage.py
source/ $ source ../virtualenv/bin/activate
(virtualenv) source/ $ pip install -r requirements.txt
...
(virtualenv) source/ $ deactivate
2.2 运行测试
2.2.1 可以使用 python manage.py runserver
运行看看
source/ $ source ../virtualenv/bin/activate
(virtualenv) source/ $ python manage.py runserver
...
Ctrl+C to quit
^C
2.2.2 运行 Test Case
(virtualenv) source/ $ ### if have test case #####
(virtualenv) source/ $ python manage.py test application
(virtualenv) source/ $ python manage.py test functional_test # 如果没有就不用运行
2.3 Django WSGI
2.3.1 使用绿色独角兽 gunicorn
(virtualenv) source/ $ pip install gunicorn # 可能已经包含在了 requirements.txt 中了。
(virtualenv) source/ $ gunicorn source.wsgi:application
...
注意:
上面运行的
source.wsgi.application
和项目中的application/
文件夹不同
项目中的application/
可能是其它任意名字,比如poll/
,blog/
,todolists/
等等。
而gunicorn
运行的application
就是application
是一个“命令”。关于
source.wsgi.application
这句命令你可以在django-proj/source/source/settings.py
中可以看到:WSGI_APPLICATION = 'source.wsgi.application'
如果是 follow 上述步骤,那么当前项目源码还缺少数据库和静态文件收集。
数据库:这个目前没什么影响,可以暂时先略过。项目静态文件问题,gunicorn 不会自动伺服静态文件,所以你看到样式会有问题。
但是这一步至少可以确认 django 项目的 server 是可以正常运行的。
浏览器打开 ‘http://localhost:8000’ 可以查看一下效果。
2.4 Django 项目周边 - 数据库和静态文件
2.4.1 数据库
这里仅说明 django 默认使用的 SQLite3 数据库在部署时的相关操作。
2.4.1.1 配置
我预期你的 django-proj/source/source/settings.py
文件中的数据库部分内容为如下样子:
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, '../database/db.sqlite3'),
}
}
如果不是则可以改成以上? 代码。
如果不存在 django-proj/database/
文件夹,则创建它!
2.4.1.2 migrate
运行以下命令迁移/创建一个空白的数据库文件:
(virtualenv) source/ $ python manage.py migrate --noinput
2.4.2 django 项目的静态文件
2.4.1 收集静态文件
当前项目目录结构预期如下:
$ tree ./ -L 1
./
├── database/
├── Docs/
├── source/
├── README.md
└── virtualenv/
创建 static 目录:
$ mkdir static
$ tree ./ -L 1
./
├── database/
├── Docs/
├── source/
├── README.md
├── static/
└── virtualenv/
执行 collectstatic 前
我的方式首先是要修改一下 settings.py 文件如下:
[...]
STATIC_URL = '/static/'
## uncomment this \/ when need runining `python manage.py collectstatic`
STATIC_ROOT = os.path.abspath(os.path.join(BASE_DIR, '../static'))
#STATICFILES_DIRS = [
# os.path.join(BASE_DIR, '../static'),
#]
注意上面的
../static
使用的是针对项目文件夹的相对路径。
最后STATIC_ROOT
变量绑定的值会是这个放置静态文件的static/
文件夹的绝对路径。
需要将该文件中STATICFILES_DIRS
的配置内容注释掉。
然后执行
(virtualenv) $ ls -F
database/ source/ README.md static/
Docs/ virtualenv/
(virtualenv) $ cd source/
$ python manage.py collectstatic
........output.......
.....................
相信你已经注意到了有的命令行有指示
(virutalenv) $
有的命令行没有(只有单独的$
)。
和执行 python 程序有关的命令都需要(virtualenv) $
。它代表了当前激活了虚拟环境并且是在该虚拟环境下运行的。
激活虚拟环境的方式上面写过很多次了,这里再单独提醒一下:django-proj/ $ source ./virtualenv/bin/activate # or source/ $ source ../virtualenv/bin/activate ## 这只是代表当前所在目录位置不同而已 django-proj/ $ deactivate ## 退出当前虚拟环境。
最后不要忘记改回 settings.py 文件的配置
(至少在我的情况下,是需要改回去的。)
[...]
STATIC_URL = '/static/'
## uncomment this \/ when need runining `python manage.py collectstatic`
# STATIC_ROOT = os.path.abspath(os.path.join(BASE_DIR, '../static'))
STATICFILES_DIRS = [
os.path.join(BASE_DIR, '../static'),
]
2.5 配置 nginx
回顾一下,我们现在完成了哪些工作。
首先 安装/下载了 django 项目代码 ⇒ 配置了 server 运行的环境
⇒ 运行了 server ⇒ 运行了测试
⇒ 安装了 gunicorn,通过 django 的 wsgi 运行 server
⇒ 完成了数据库和静态文件的环境设置
现在我们使用 nginx 做代理服务。
以监听 11100
端口为例,配置 nginx。
2.5.1 安装 nginx
$ sudo apt install nginx
如果已经安装 Apache,并且发生 nginx 安装出错的话,则可以修改一下 Apache 的默认监听端口
80
,然后重启 Apache,再次安装 nginx。
2.5.2 简单的 nginx 配置
server {
listen 11100;
server_name <your-domain>;
location / {
proxy_pass http://localhost:8000;
}
}
将上面配置代码保存为 <your-domain>
文件(例如 example.com
);放在 /etc/nginx/sites-available/
文件夹下(例如 /etc/nginx/sites-available/example.com
)。
然后运行如下命令:
$ sudo ln -s /etc/nginx/sites-available/<your-domain> \
/etc/nginx/sites-enabled/<your-domain>
$ sudo service nginx reload # 有 restart, stop, start 等命令可用。
$ cd <path>/<to>/django-proj/source
$ ../virtualenv/bin/python manage.py runserver
# or $ ../virtualenv/bin/gunicorn source.wsgi.application
现在你可以启动浏览器,访问 http://localhost:11100/
来访问该项目
或者它运行在你的域名对应的服务器上,访问 http://<your-domain>:11100/
来访问。
注意,如果使用上面 line 6 代替 line 5 运行,则目前静态文件加载会有问题,下一节进一步配置 nginx 会解决。
2.5.3 Nginx 的 Django 项目的静态文件配置
修改上面的配置文件,其它内容不变,增加以下 line 4~6 内容:
...
server_name <your-domain>
location /static {
alias /<path>/<to>/django-proj/static;
}
location / {
...
现在:
$ sudo service nginx reload
$ cd <path>/<to>/django-proj/source
$ ../virtualenv/bin/gunicorn source.wsgi:application
...
访问使用 gunicorn
启动的 server 能够正确地加载静态文件了。
2.5.4 使用 UNIX 套接字
再一次修改上面地配置文件:
...
location /static {
alias /<path>/<to>/django-proj/static;
}
location / {
proxy_set_header Host $host;
proxy_pass http://unix:/run/gunicorn-<your-domain>/gunicorn.socket;
}
}
然后重启 nginx(sudo service nginx reload
)。
现在 gunicorn 的启动方式需要换一换:
其中,应该先提前创建
sudo mkdir /run/gunicorn-<your-domain>/
文件夹一下再运行。
$ cd <path>/<to>/django-proj/source
$ ../virtualenv/bin/gunicorn \
--bind unix:/run/gunicorn-<your-domain>/gunicorn.socket \
source.wsgi:application
...
现在因为使用了 UNIX 套接字,现在已经和 django 默认的 8000 端口无关了。
即,从 ngnix 的配置文件可以看出来它不再代理 8000 端口了。
其中, gunicorn-<your-domain>.socket
中 gunicorn-<your-domain>.com
是为了一致性而命名的,实际上它可以起其它无含义的名称亦可,但是需要和下文的 systemd 的相关配置一致!
完整示例:
以 example.com
站点为例,一份完整的 /etc/nginx/sites-available/example.com
示例:
server {
listen 11100;
server_name example.com;
location /static {
alias /home/example_user/django-proj/static;
}
location / {
proxy_set_header Host $host;
proxy_pass http://unix:/run/gunicorn-example.com/gunicorn.socket;
}
}
然后开启 django 项目 server 的服务:
$ cd /home/example_user/django-proj/source
$ ../virtualenv/bin/gunicorn \
--bind unix:/run/gunicorn-example.com/gunicorn.socket \
source.wsgi:application
...
在“用户”网络使用浏览器访问 http://example.com/
测试(当然,真的访问 example.com
是得不到想要的结果的,example 的意思当然就是把它替换成真实的域名)。
2.5.5 总结
nginx 的配置先简单代理了 django 在本地运行的服务器(localhost:8000
)。
然后为 nginx 增加了伺服静态文件的配置,这样可以完整代理通过 gunicorn 运行的本地服务器。
最后进一步修改了 nginx 文件,代理的服务器不再直接通过 django 的默认 port,而是 UNIX 套接字。
2.6. systemd
现在已经有了 Nginx 的配置文件,可以运行 Nginx 代理服务器。
为了使项目能够在服务器上自动“开机”运行,我们预期托管给 systemd。
为此我们还需要三个配置文件:
tmpfiles.d
配置文件。- 两个 systemd 配置文件。
其中 tmpfiles.d
负责管理本机该项目运行的服务的临时文件夹。
该文件夹下用来放置如 .pid
这样在软件运行时产生的文件;
还有上面配置 nginx 提到过的 .socket
- 这个文件是 nginx <- 通信 -> wsgi(Gunicorn) 必须的,也是运行时产生。
systemd
的两个配置文件在系统启动时,systemd 进程会根据配置启动相关进程。
2.6.1 tmpfiles.d
d /run/gunicorn-<your-domain> 0755 nobody nobody -
将上面内容保存在 /etc/tmpfiles.d/gunicorn-<your-domain>.conf
文件中。
gunicorn-<your-domain>.conf
预期是不存在的,需要创建。
/run/gunicorn-<your-domain>
是指示由其管理的临时文件夹。当然,它的名称也是可以自己定义的。
但是需要和下面的 systemd 的配置文件中的内容保持一致。
gunicorn-<your-domain>.conf
文件名也是自己定义的,这里我仍然按照简单的显而易见的规则为其命名。
2.6.2 systemd
本段参考 Gunicorn 官方文档中 systemd 配置文件部分1
systemd
是个什么东东的参考:
The importance of Devuan
Linux:为什么那么多人讨厌systemd?
Systemd 入门教程:命令篇 (了解即可)
Node 应用的 Systemd 启动
2.6.2.1 gunicorn.socket
/etc/systemd/system/gunicorn-<your-domain>.socket
[Unit]
Description=<your-domain>'s gunicorn socket
[Socket]
ListenStream=/run/gunicorn-<your-domain>/gunicorn.socket
[Install]
WantedBy=sockets.target
2.6.2.2 gunicorn.service
/etc/systemd/system/gunicorn-<your-domain>.service
[Unit]
Description=gunicorn-<your-domain>'s gunicorn daemon
Requires=gunicorn-<your-domain>.socket
After=network.target
[Service]
PIDFil=/run/gunicorn-<your-domain>/pid
User=nobody
Group=nobody
RuntimeDirectory=gunicorn-<your-domain>
WorkingDirectory=/<USER>/django-proj/source/
ExecStart=/<USER>/django-proj/virtualenv/bin/gunicorn \
--pid /run/gunicorn-<your-domain>/pid \
--bind unix:/run/gunicorn-<your-domain>/gunicorn.socket \
source.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
其中:
- line 15 是基于 WorkingDirectory 的路径;即
source.wsgi:application
是相对路径,完整路径是/<USER>/django-proj/source/source.wsgi:application
- line 7,13,14 的
/run/gunicorn-<your-domain>/
文件夹基于上文的 tmpfiles.d 配置创建。 - line 11 中,
/<USER>/django-proj/
是整个站点服务文件夹,自动化部署时,如果没有,则创建;其下的source/
是 Django 项目根目录。关于
source/
这个根目录,它等效于当前处于/<USER>/django-proj/
目录下的时候,执行django-admin startproject source
这一命令的效果。所以说source/
是 Django 项目(源代码)的根目录。而它的往上一级还有称为django-proj/
的目录是为了放置整个项目的其它文件,比如Docs/
(还有显然后来创建的virtualenv/
文件夹)等等。
2.6.3 Systemd 下该 HTTP 服务的运行与测试
Enable
$ sudo systemctl enable /etc/systemd/system/gunicorn-<your-domain>.socket
Start
$ sudo systemctl start /etc/systemd/system/gunicorn-<your-domain>.socket
enable 只是在 *.socket 文件第一次存在在 ‘system/’ 文件夹下的时候运行。
之后重启整个 django web 服务器项目只需要使用:
sudo systemctl restart /etc/systemd/system/gunicorn-<your-domain>.socket
即可。
如果项目(软件)已经部署好了,则可以运行:
$ curl --unix-socket /run/gunicorn-<your-domain>/gunicorn.socket http
这样能够测试和启动 gunicorn 提供 http server 服务,配置运行正常的情况下,这里应该有 HTML 输出。
它可能是一个 HTTP 错误消息,这也是成功的表现。只要不是 curl 工具的错误提示!
用户环境(客户端)发起的请求,需要 Nginx 代理处理链接。
(逻辑是:nginx -交给-> unix socket -> gunicorn(wsgi) -> django url+view)
所以要从用户环境测试,则需要配置好 nginx,然后启动 nginx 就完成了(前面已经介绍过了)。
如果出现问题:
$ sudo systemctl list-socekts --all | grep gunicorn-<your-domain>.socket
$ sudo systemctl list-socekts | grep gunicorn-<your-domain>.socket
这个命令可以看出 systemctl enable
这一步有没有出问题。
而 systemctl start
这一步有问题的话会有提示。
3 自动化部署 - N/A
4 升级服务器上的 Django 项目 - n/a
这是在上面“首次部署”之后,已经部署在了服务器上之后。
现在要升级部署过的项目。
5 HTTPS on Nginx Proxy Django Server
这篇博文 ? 足够好,参考它完成使用 HTTPS。