欢迎来到咱们教程系列的最后一部分!在本教程中,咱们将把Django应用程序部署到生产服务器。咱们还将为咱们的服务器配置电子邮件服务和HTTPS证书。css
首先,我想到了一个使用虚拟专用服务器(VPS)的例子,它更通用,而后使用一个平台即服务,如Heroku。但它太详细了,因此我最终建立了关于VPS的本教程。html
咱们的项目现场直播!若是您想在查看文本以前在线查看,这是咱们要部署的应用程序:www.djangoboards.com。python
版本控制是软件开发中很是重要的主题。特别是在与团队合做并同时维护生产代码时,并行开发了多个功能。不管是一个开发人员项目仍是多个开发人员项目,每一个项目都应该使用版本控制。jquery
版本控制系统有几种选择。也许是由于GitHub的流行,Git 成为版本控制的事实标准。因此若是你不熟悉版本控制,Git是一个很好的起点。通常有不少教程,课程和资源,所以很容易找到帮助。linux
GitHub和Code School有一个关于Git的很棒的互动教程,几年前我开始从SVN转到Git。这是一个很是好的介绍。nginx
这是一个很是重要的主题,我可能应该从第一个教程开始提出它。但事实是我但愿本教程系列的重点放在Django上。若是这一切对您来讲都是新的,请不要担忧。一步一步是很重要的。你的第一个项目并不完美。重要的是要保持学习和慢慢发展你的技能,但要保持稳定。git
关于Git的一个很是好的事情是它不只仅是一个版本控制系统。围绕它创建了丰富的工具和服务生态系统。一些很好的例子是持续集成,部署,代码审查,代码质量和项目管理。github
使用Git来支持Django项目的部署过程很是有效。这是从源代码存储库中提取最新版本或在出现问题时回滚到特定版本的便捷方式。有许多服务与Git集成,以便自动化测试执行和部署。sql
若是您没有在本地计算机上安装Git,请从https://git-scm.com/downloads获取已安装的。数据库
首先,设置你的身份:
git config --global user.name "Vitor Freitas" git config --global user.email vitor@simpleisbetterthancomplex.com
在项目根目录(与manage.py相同的目录)中,初始化一个git存储库:
git init
Initialized empty Git repository in /Users/vitorfs/Development/myproject/.git/
检查存储库的状态:
git status
On branch master Initial commit Untracked files: (use "git add <file>..." to include in what will be committed) accounts/ boards/ manage.py myproject/ requirements.txt static/ templates/ nothing added to commit but untracked files present (use "git add" to track)
在继续添加源文件以前,请在项目根目录中建立名为.gitignore的新文件。这个特殊的文件将帮助咱们保持存储库的清洁,而不须要像缓存文件或日志这样的没必要要的文件。
您能够 从GitHub 获取Python项目的通用.gitignore文件。
确保将它从Python.gitignore重命名为.gitignore(点很重要!)。
您能够补充.gitignore文件,告诉它忽略SQLite数据库文件,例如:
的.gitignore
__pycache__/ *.py[cod] .env venv/ # SQLite database files *.sqlite3
如今将文件添加到存储库:
git add .
请注意这里的点。上面的命令告诉Git 在当前目录中添加全部未跟踪的文件。
如今进行第一次提交:
git commit -m "Initial commit"
老是写一个评论告诉这个提交是什么,简要描述你改变了什么。
如今让咱们将GitHub设置为远程存储库。首先,在GitHub上建立一个免费账户,而后确认您的电子邮件地址。以后,您将可以建立公共存储库。
如今,只需为存储库选择一个名称,不要使用自述文件初始化它,或者添加.gitignore或添加许可证到目前为止。确保将存储库空启动:
建立存储库后,您应该看到以下内容:
如今让咱们将其配置为咱们的远程存储库:
git remote add origin git@github.com:sibtc/django-boards.git
如今将代码推送到远程服务器,即GitHub存储库:
git push origin master Counting objects: 84, done. Delta compression using up to 4 threads. Compressing objects: 100% (81/81), done. Writing objects: 100% (84/84), 319.70 KiB | 0 bytes/s, done. Total 84 (delta 10), reused 0 (delta 0) remote: Resolving deltas: 100% (10/10), done. To git@github.com:sibtc/django-boards.git * [new branch] master -> master
我建立此存储库只是为了演示使用现有代码库建立远程存储库的过程。该项目的源代码正式托管在此存储库中:https: //github.com/sibtc/django-beginners-guide。
不管代码是存储在公共或私有远程存储库中,都不应提交敏感信息并将其推送到远程存储库。这包括密钥,密码,API密钥等。
此时,咱们必须在settings.py模块中处理两种特定类型的配置:
密码和密钥能够存储在环境变量中或使用本地文件(未提交到远程存储库):
# environment variables import os SECRET_KEY = os.environ['SECRET_KEY'] # or local files with open('/etc/secret_key.txt') as f: SECRET_KEY = f.read().strip()
为此, 我在我开发的每一个Django项目中都使用了一个名为Python Decouple的优秀实用程序库。它将搜索名为.env的本地文件以设置配置变量,并将回退到环境变量。它还提供了一个定义默认值的接口,在适用时将数据转换为int,bool和list。
这不是强制性的,但我真的以为它是一个很是有用的工具。它像Heroku这样的服务就像魅力同样。
首先,让咱们安装它:
pip install python-decouple
的myproject / settings.py
from decouple import config SECRET_KEY = config('SECRET_KEY')
如今,咱们能够将敏感信息放在一个名为.env的特殊文件中(注意前面的点),该文件位于manage.py文件所在的目录中:
myproject/ |-- myproject/ | |-- accounts/ | |-- boards/ | |-- myproject/ | |-- static/ | |-- templates/ | |-- .env <-- here! | |-- .gitignore | |-- db.sqlite3 | +-- manage.py +-- venv/
.ENV
SECRET_KEY=rqr_cjv4igscyu8&&(0ce(=sy=f2)p=f_wn&@0xsp7m$@!kp=d
该.ENV文件中忽略的.gitignore文件,因此每次咱们要部署应用程序或在不一样的机器上运行时,咱们将建立一个.ENV文件,并添加必要的配置。
如今让咱们安装另外一个库来帮助咱们在一行中编写数据库链接。这样,在不一样的环境中编写不一样的数据库链接字符串会更容易:
pip install dj-database-url
目前,咱们须要解耦的全部配置:
的myproject / settings.py
from decouple import config, Csv import dj_database_url SECRET_KEY = config('SECRET_KEY') DEBUG = config('DEBUG', default=False, cast=bool) ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv()) DATABASES = { 'default': dj_database_url.config( default=config('DATABASE_URL') ) }
本地计算机的.env文件示例:
SECRET_KEY=rqr_cjv4igscyu8&&(0ce(=sy=f2)p=f_wn&@0xsp7m$@!kp=d DEBUG=True ALLOWED_HOSTS=.localhost,127.0.0.1
请注意,在DEBUG
配置中咱们有一个默认值,所以在生产中咱们能够忽略此配置,由于它将被设置为False
自动设置,由于它应该是。
如今ALLOWED_HOSTS
将被转换成一个列表['.localhost', '127.0.0.1'. ]
。如今,这是在咱们的本地机器上,为了生产,咱们将其设置为相似于['.djangoboards.com', ]
您拥有的任何域。
此特定配置可确保您的应用程序仅提供给此域。
跟踪项目的依赖关系是一个很好的作法,所以更容易在另外一台机器上安装它。
咱们能够经过运行命令来检查当前安装的Python库:
pip freeze dj-database-url==0.4.2 Django==1.11.6 django-widget-tweaks==1.4.1 Markdown==2.6.9 python-decouple==3.1 pytz==2017.2
在项目根目录中建立名为requirements.txt的文件,并在其中添加依赖项:
requirements.txt
dj-database-url==0.4.2 Django==1.11.6 django-widget-tweaks==1.4.1 Markdown==2.6.9 python-decouple==3.1
我保留了pytz == 2017.2,由于它是由Django自动安装的。
您能够更新源代码存储库:
git add . git commit -m "Add requirements.txt file" git push origin master
若是咱们要正确部署Django应用程序,咱们将须要一个域名。拥有域名来为应用程序提供服务,配置电子邮件服务和配置https证书很是重要。
最近,我一直在使用Namecheap 。您能够以8.88美圆/年的价格得到.com域名,或者若是您只是尝试一下,您能够 以0.99美圆/年的价格注册.xyz域名。
不管如何,您能够自由使用任何注册商。为了演示部署过程,我注册了 www.DjangoBoards.com域名。
如下是咱们将在本教程中使用的部署策略的概述:
云是Digital Ocean提供的虚拟专用服务器。您可使用个人会员连接注册Digital Ocean以得到免费的10美圆信用额度(仅适用于新账户)。
咱们将提供NGINX,由食人魔说明。NGINX将收到对服务器的全部请求。但若是请求数据,它不会尝试作任何聪明的事情。它所要作的就是肯定所请求的信息是不是一个能够自行提供的静态资产,或者它是否更复杂。若是是这样,它会将请求传递给Gunicorn。
NGINX还将配置HTTPS证书。这意味着它只接受经过HTTPS的请求。若是客户端尝试经过HTTP请求,NGINX将首先将用户重定向到HTTPS,而后它才会决定如何处理请求。
咱们还将安装此certbot以自动续订Let的加密证书。
Gunicorn是一个应用服务器。根据服务器具备的处理器数量,它能够生成多个工做程序以并行处理多个请求。它管理工做负载并执行Python和Django代码。
Django是努力工做的人。它能够访问数据库(PostgreSQL)或文件系统。但在大多数状况下,工做是在视图内部完成,渲染模板,以及过去几周咱们编写过的全部内容。在Django处理请求以后,它会向Gunicorn返回一个响应,他将结果返回给NGINX,最终将响应传递给客户端。
咱们还将安装PostgreSQL,一个生产质量数据库系统。因为Django的ORM系统,很容易切换数据库。
最后一步是安装Supervisor。它是一个过程控制系统,它将密切关注Gunicorn和Django,以确保一切顺利进行。若是服务器从新启动,或者Gunicorn崩溃,它将自动重启。
您可使用您喜欢的任何其余VPS(虚拟专用服务器)。配置应该很是类似,毕竟咱们将使用Ubuntu 16.04做为咱们的服务器。
首先,让咱们建立一个新的服务器(在Digital Ocean上,他们称之为“Droplet”)。选择Ubuntu 16.04:
选择尺寸。最小的液滴足够了:
而后为您的Droplet选择一个主机名(在个人状况下为“django-boards”):
若是您有SSH密钥,则能够将其添加到您的账户。而后,您将可以使用它登陆服务器。不然,他们会经过电子邮件向您发送root密码。
如今选择服务器的IP地址:
在咱们登陆服务器以前,让咱们将域名指向此IP地址。这将节省一些时间,由于DNS设置一般须要几分钟才能传播。
因此这里咱们添加了两条A记录,一条指向裸域“djangoboards.com”,另外一条指向“www.djangoboards.com”。咱们将使用NGINX配置规范URL。
如今让咱们使用您的终端登陆服务器:
ssh root@45.55.144.54 root@45.55.144.54's password:
而后你应该看到如下消息:
You are required to change your password immediately (root enforced) Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-93-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage Get cloud support with Ubuntu Advantage Cloud Guest: http://www.ubuntu.com/business/services/cloud 0 packages can be updated. 0 updates are security updates. Last login: Sun Oct 15 18:39:21 2017 from 82.128.188.51 Changing password for root. (current) UNIX password:
设置新密码,而后开始配置服务器。
sudo apt-get update sudo apt-get -y upgrade
若是在升级过程当中收到任何提示,请选择“保持当前安装的本地版本”选项。
Python 3.6
sudo add-apt-repository ppa:deadsnakes/ppa sudo apt-get update sudo apt-get install python3.6
PostgreSQL的
sudo apt-get -y install postgresql postgresql-contrib
NGINX
sudo apt-get -y install nginx
监
sudo apt-get -y install supervisor sudo systemctl enable supervisor sudo systemctl start supervisor
VIRTUALENV
wget https://bootstrap.pypa.io/get-pip.py sudo python3.6 get-pip.py sudo pip3.6 install virtualenv
使用如下命令建立新用户:
adduser boards
一般,我只选择应用程序的名称。输入密码,并可选择在提示中添加一些额外信息。
如今将用户添加到sudoers列表:
gpasswd -a boards sudo
首先切换到postgres用户:
sudo su - postgres
建立数据库用户:
createuser u_boards
建立一个新数据库并将用户设置为全部者:
createdb django_boards --owner u_boards
为用户定义一个强密码:
psql -c "ALTER USER u_boards WITH PASSWORD 'BcAZoYWsJbvE7RMgBPzxOCexPRVAq'"
咱们如今能够退出postgres用户了:
exit
切换到应用程序用户:
sudo su - boards
首先,咱们能够检查咱们的位置:
pwd /home/boards
首先,让咱们用咱们的代码克隆存储库:
git clone https://github.com/sibtc/django-beginners-guide.git
启动虚拟环境:
virtualenv venv -p python3.6
初始化virtualenv:
source venv/bin/activate
安装要求:
pip install -r django-beginners-guide/requirements.txt
咱们将在这里添加两个额外的库,Gunicorn和PostgreSQL驱动程序:
pip install gunicorn pip install psycopg2
如今在/ home / boards / django-beginners-guide文件夹中,让咱们建立一个.env文件来存储数据库凭据,密钥和其余全部内容:
/home/boards/django-beginners-guide/.env
SECRET_KEY=rqr_cjv4igscyu8&&(0ce(=sy=f2)p=f_wn&@0xsp7m$@!kp=d ALLOWED_HOSTS=.djangoboards.com DATABASE_URL=postgres://u_boards:BcAZoYWsJbvE7RMgBPzxOCexPRVAq@localhost:5432/django_boards
如下是数据库URL的语法:postgres:// db_user
:db_password
@ db_host
:db_port
/ db_name
。
如今让咱们迁移数据库,收集静态文件并建立一个超级用户:
cd django-beginners-guide
python manage.py migrate Operations to perform: Apply all migrations: admin, auth, boards, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying boards.0001_initial... OK Applying boards.0002_auto_20170917_1618... OK Applying boards.0003_topic_views... OK Applying sessions.0001_initial... OK
如今静态文件:
python manage.py collectstatic Copying '/home/boards/django-beginners-guide/static/js/jquery-3.2.1.min.js' Copying '/home/boards/django-beginners-guide/static/js/popper.min.js' Copying '/home/boards/django-beginners-guide/static/js/bootstrap.min.js' Copying '/home/boards/django-beginners-guide/static/js/simplemde.min.js' Copying '/home/boards/django-beginners-guide/static/css/app.css' Copying '/home/boards/django-beginners-guide/static/css/bootstrap.min.css' Copying '/home/boards/django-beginners-guide/static/css/accounts.css' Copying '/home/boards/django-beginners-guide/static/css/simplemde.min.css' Copying '/home/boards/django-beginners-guide/static/img/avatar.svg' Copying '/home/boards/django-beginners-guide/static/img/shattered.png' ...
此命令将全部静态资产复制到外部目录,NGINX能够在该目录中为咱们提供文件。稍后会详细介绍。
如今为应用程序建立一个超级用户:
python manage.py createsuperuser
所以,Gunicorn是负责在代理服务器后面执行Django代码的人。
在/ home / boards中建立一个名为gunicorn_start的新文件:
#!/bin/bash NAME="django_boards" DIR=/home/boards/django-beginners-guide USER=boards GROUP=boards WORKERS=3 BIND=unix:/home/boards/run/gunicorn.sock DJANGO_SETTINGS_MODULE=myproject.settings DJANGO_WSGI_MODULE=myproject.wsgi LOG_LEVEL=error cd $DIR source ../venv/bin/activate export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE export PYTHONPATH=$DIR:$PYTHONPATH exec ../venv/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \ --name $NAME \ --workers $WORKERS \ --user=$USER \ --group=$GROUP \ --bind=$BIND \ --log-level=$LOG_LEVEL \ --log-file=-
此脚本将启动应用程序服务器。咱们提供了一些信息,例如Django项目的位置,用于运行服务器的应用程序用户等等。
如今让这个文件可执行:
chmod u+x gunicorn_start
建立两个空文件夹,一个用于套接字文件,另外一个用于存储日志:
mkdir run logs
如今/ home / boards里面的目录结构应该是这样的:
django-beginners-guide/ gunicorn_start logs/ run/ staticfiles/ venv/
该staticfiles文件夹由建立collectstatic命令。
首先,在/ home / boards / logs /文件夹中建立一个空的日志文件:
touch logs/gunicorn.log
如今建立一个新的supervisor文件:
sudo vim /etc/supervisor/conf.d/boards.conf
[program:boards] command=/home/boards/gunicorn_start user=boards autostart=true autorestart=true redirect_stderr=true stdout_logfile=/home/boards/logs/gunicorn.log
保存文件并运行如下命令:
sudo supervisorctl reread sudo supervisorctl update
如今检查状态:
sudo supervisorctl status boards
boards RUNNING pid 308, uptime 0:00:07
下一步是设置NGINX服务器以提供静态文件并将请求传递给Gunicorn:
在/ etc / nginx / sites-available /中添加一个名为boards的新配置文件:
upstream app_server { server unix:/home/boards/run/gunicorn.sock fail_timeout=0; } server { listen 80; server_name www.djangoboards.com; # here can also be the IP address of the server keepalive_timeout 5; client_max_body_size 4G; access_log /home/boards/logs/nginx-access.log; error_log /home/boards/logs/nginx-error.log; location /static/ { alias /home/boards/staticfiles/; } # 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/boards /etc/nginx/sites-enabled/boards
删除默认的NGINX网站:
sudo rm /etc/nginx/sites-enabled/default
重启NGINX服务:
sudo service nginx restart
此时,若是DNS已经传播,则该网站应在URL www.djangoboards.com上提供。
开始使用的最佳选择之一是Mailgun。它提供了一个很是可靠的免费计划,每个月包含12,000封电子邮件。
注册一个免费账户。而后按照步骤操做,这很是简单。您必须与您注册域名的服务一块儿工做。在个人例子中,它是Namecheap。
点击添加域以向您的账户添加新域。按照说明操做,确保使用“mg。”子域名:
如今抓住第一组DNS记录,它是两个TXT记录:
使用您的注册商提供的网络界面将其添加到您的域中:
对MX记录作一样的事情:
将它们添加到域中:
如今这一步不是强制性的,但因为咱们已经在这里,因此也要确认一下:
添加全部DNS记录后,单击“当即检查DNS记录”按钮:
如今咱们须要有一些耐心。有时须要一段时间来验证DNS。
同时,咱们能够配置应用程序以接收链接参数。
的myproject / settings.py
EMAIL_BACKEND = config('EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend') EMAIL_HOST = config('EMAIL_HOST', default='') EMAIL_PORT = config('EMAIL_PORT', default=587, cast=int) EMAIL_HOST_USER = config('EMAIL_HOST_USER', default='') EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default='') EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=True, cast=bool) DEFAULT_FROM_EMAIL = 'Django Boards <noreply@djangoboards.com>' EMAIL_SUBJECT_PREFIX = '[Django Boards] '
而后,个人本地计算机.env文件将以下所示:
SECRET_KEY=rqr_cjv4igscyu8&&(0ce(=sy=f2)p=f_wn&@0xsp7m$@!kp=d DEBUG=True ALLOWED_HOSTS=.localhost,127.0.0.1 DATABASE_URL=sqlite:///db.sqlite3 EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend
个人生产.env文件看起来像这样:
SECRET_KEY=rqr_cjv4igscyu8&&(0ce(=sy=f2)p=f_wn&@0xsp7m$@!kp=d ALLOWED_HOSTS=.djangoboards.com DATABASE_URL=postgres://u_boards:BcAZoYWsJbvE7RMgBPzxOCexPRVAq@localhost:5432/django_boards EMAIL_HOST=smtp.mailgun.org EMAIL_HOST_USER=postmaster@mg.djangoboards.com EMAIL_HOST_PASSWORD=ED2vmrnGTM1Rdwlhazyhxxcd0F
您能够在Mailgun 的“ 域信息”部分中找到您的凭据。
咱们能够在生产服务器中测试新设置。在本地计算机上的settings.py文件中进行更改,将更改提交到远程存储库。而后,在服务器中拉出新代码并从新启动Gunicorn进程:
git pull
使用电子邮件凭据编辑.env文件。
而后重启Gunicorn进程:
sudo supervisorctl restart boards
如今咱们能够尝试启动密码重置过程:
在Mailgun仪表板上,您能够得到有关电子邮件传递的一些统计信息:
如今让咱们使用Let's Encrypt提供的一个很好的HTTPS证书来保护咱们的应用程序。
设置HTTPS从未如此简单。更好的是,咱们如今能够免费得到它。他们提供了一个名为certbot的解决方案 ,负责为咱们安装和更新证书。这很是简单:
sudo apt-get update sudo apt-get install software-properties-common sudo add-apt-repository ppa:certbot/certbot sudo apt-get update sudo apt-get install python-certbot-nginx
如今安装证书:
sudo certbot --nginx
只需按照提示操做便可。当被问及:
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
选择2
将全部HTTP流量重定向到HTTPS。
有了这个,该网站已经经过HTTPS提供服务:
设置证书的自动续订。运行如下命令编辑crontab文件:
sudo crontab -e
将如下行添加到文件末尾:
0 4 * * * /usr/bin/certbot renew --quiet
该命令将天天凌晨4点运行。全部在30天内到期的证书将自动续订。