在本地完成开发之后,我们希望把我们的网站发布出去。我们可以选择自己搭建服务器,也可以选择云服务器。在对比了几个腾讯云,阿里云,华为云之后,我选择阿里云作为我的云服务器。 直接在淘宝网站上搜索买了一个基础配置的云服务器ECS。CPU选用了2G,带宽1Mbps,之前还担心1Mbps会不会卡,后来知乎上查了一下,还好。 阿里云好像有定期的团购活动,价格会比较便宜,可以关注一下。
下面来将我的部署过程。 部署网站主要是使用到了如下技能:
编程语言:Python 3.6
系统框架:Django
数据库:MySQL
服务架构:Nginx + Uwsgi
版本管理: Git
服务器系统:Ubuntu 18.04
阿里云环境部署
在淘宝阿里云旗舰店购买了阿里云服务器之后,在提货券之后提交订单时候就可以填写你的服务器的配置,我这边直接选择了最新的LTS版的ubuntu 18.04。
配置完成后,第一次登陆系统会给你发送一个远程链接密码,这个只会弹出一次,一定要做好记录。你可以选择修改为你直接的密码方便记忆。 远程链接之后,进入ubuntu的命令行模式,提醒你登陆,默认用户名是root,输入你购买服务器时候设置的密码就可以了。
服务器配置完成后还需要设置一下我们的安全组规则,以满足我们后续的建站需要。不过,也可以先默认,待后续需要时候再去修改。
另外,我们还需要一个ftp工具,方便我们向我们的服务器存取文件。我这边选用的是FileZilla。 FileZilla中新建站点,协议选用SFTP-SSH File Tranfer Protocol。主机填写我们云服务器的外网IP 用户名和密码就填我们云服务器的用户名和密码。
云服务器安装必要软件
在系统中,python2和python3共存,我们这边开发用的是python3.6, 因此我们查看一下python版本,并把python 3.6作为我们的优先版本,设置一下优先级
$ sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 100 $ sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 150
安装pip3
$ sudo apt-get update $ sudo apt-get install pip3
安装uwsgi
pip3 install uwsgi
安装Nginx
sudo apt-get install nginx
安装了Nginx之后,我们可以直接在在浏览器输入我们云服务器的外网IP地址查看安装情况。如果安装成功,我们应能看到以下页面。
安装virturalenv:
$ pip3 install virtualenv
创建虚拟环境并运行:
$ cd /var/www/ #nginx创建的目录,存放我们的项目文件 $ virtualenv --no-site-packages zcwBlog ... $ source zcwBlog/bin/activate ... (zcwBlog) $
进入到虚拟环境之后,可以看到命令行最开头就有了我们虚拟环境的标记。 然后我们就可以在虚拟环境下安装我们的各种包了。
迁移Django
此时我们就可以把我们的项目迁移过来了,当然包括安装各种依赖包。 最简单直接的,我们可以通过git clone的方法将我们存放在github上的项目克隆过来。 克隆过来之后可以通过requirements.txt文件安装所有的依赖包:
(zcwBlog) $ pip3 install -r requirements.txt
当然,我们也可以手动安装这些包
安装Django
(zcwBlog) $ pip3 install django==2.2.3
我们后台使用的是xadmin,但是我们之前是用源码安装的方式,xadmin放在我们项目的extra_apps目录下,因此我们就不用安装xadmin了。
安装DjangoUeditor:
由于ueditor对python3的支持问题,我们无法直接通过pip安装DjangoUeditor,我们需要从github上下载源码,然后手动安装。 下载地址 https://github.com/twz915/DjangoUeditor3/
下载之后,通过FileZilla存放到我们的云服务上,解压出来。解压使用的是7z 我们需要首先安装这个解压工具
$ sudo apt-get install p7zip-full
然后使用7z x 命令解压我们的文件,然后使用python setup.py install安装
$ python setup.py install
安装future, httplib2, Pillow, django-crispy-forms,django-formtools, django-import-export, six, python-mysqldb, libmysqlclient-dev, mysqlclient等
这些安装完成之后,我们可以运行一下试一试
(zcwBlog) $ python manage.py runserver 0.0.0.0:8080
安装数据库并迁移数据库
$ sudo apt-get install mysql-server mysql-client
登陆MySQL:
> mysql -u root -p
设置mysql密码 由于mysql的密码策略,我这边先将密码策略改了,以便我能设置一些简单容易记住的密码 默认密码进入mysql
> set global validate_password_policy=0; > set global validate_password_length=4;
然后修改root用户密码
>update MySQL.user set authentication_string=password('root') where user='root'
退出,输入新密码,进入MySQL后,创建数据库:
> create database zcwblog
然后将我们windows下开发时候的数据库给导出来。 (注意不要使用PowerShell导出,使用powershell导出后,在我们云服务器上导入时候会报错。具体原因我也没有深究。)
使用MySQL workbench导出数据库之后,再导入我们云服务器上的mysql,我这边还是出了问题,查了一下,应该是版本兼容性问题。
我这边修改了如下信息后就ok了:
文本编辑器打开MySQL文件,
将其中的 COLLATE=utf8mb4_0900_ai_ci修改为utf8_general_ci
将utf8mb4修改为utf8
具体请看:https://www.jianshu.com/p/788dceb93eff
解决了上述问题之后,我们通过FileZilla将数据库文件传到我们的云服务器上,然后使用命令导入
$ sudo mysql -u root -p zcwblog < zcwblog.sql
配置mysql文件:
$ sudo vim/etc/mysql/mysql.conf.d/mysqld.cnf
注释掉如下这行代码
# band-address = localhost
此时如果我们云服务器的安全组规格中需要添加8080端口。
然后我们在浏览器中输入我们阿里云的外网IP地址加8080端口,我们就能访问到我们的网站了。如下图:
配置项目
配置uwsig
首先我们来配置一下我们的uwsgi 在我们项目目录下的与manage.py同级目录下,新建一个wsgi.ini文件,写入如下内容
[uwsig] #使用nginx链接时候使用 #socket=172.x.x.x:8080 # 阿里云私有IP #直接做web服务器时使用 http=172.x.x.x:8080 # 阿里云私有IP #项目目录 chdir=/var/www/zcwBlog/zcwBlog #wsgi在项目总的位置 module=zcwBlog/wsgi #项目中wsgi.py文件的目录,相对于项目目录 wsgi-file=zcwBlog/wsgi.py #启动的进程数目 processes=4 #进程中的现场数 threads=2 master=True #保存项目启动之后主进程的pid pidfile=uwsgi.pid #设置uwsgi后台运行时,用于保存日志信息的文件 daemonize=uwsgi.log #虚拟环境的路径 virtualenv=/var/www/zcwBlog
这一步要注意上面的IP地址不要用127.0.0.1,而要使用阿里云服务器的私网IP地址。
使用uwsgi --ini uwsgi.ini开启
(zcwBlog) root@zcw:/var/www/zcwBlog/zcwBlog# uwsgi --ini uwsgi.ini
然后使用ps aux 查看进程
(zcwBlog) root@zcw:/var/www/zcwBlog/zcwBlog# ps aux | grep uwsgi root 2141 19.0 2.7 153664 56780 ? S 15:04 0:00 uwsgi --ini uwsgi.ini root 2143 0.0 2.3 227396 47624 ? Sl 15:04 0:00 uwsgi --ini uwsgi.ini root 2144 0.0 2.3 227396 47624 ? Sl 15:04 0:00 uwsgi --ini uwsgi.ini root 2145 0.0 2.3 227396 47624 ? Sl 15:04 0:00 uwsgi --ini uwsgi.ini root 2146 0.0 2.3 227396 47624 ? Sl 15:04 0:00 uwsgi --ini uwsgi.ini root 2147 0.0 2.2 154220 45472 ? S 15:04 0:00 uwsgi --ini uwsgi.ini root 2153 0.0 0.0 14428 1104 pts/2 S+ 15:04 0:00 grep --color=auto uwsgi
可以发现我们的uwsgi已经运行起来了。 此时,我们可以尝试使用公网IP+端口号访问我们的网站了。
看起来和上一步表现一样,其实,上一步中还是manage.py文件启动的,而现在是使用uwsgi运行的。
配置Nginx
找到nginx.conf文件。我的是在目录/etc/nginx目录下的 编辑该文件,写入如下内容:
server{ listen 80; server_name 120.x.x.x; #阿里云公网IP或者域名 location / { # 包含uwsgi的请求参数 include uwsgi_params; # 转交请求给uwsgi uwsgi_pass 172.x.x.x:8080;#阿里云私网IP,对应于uwsgi.ini文件中的socket的IP地址和端口 } }
配置完成之后,我们还需要修改一下uwsgi.ini文件
[uwsig]#使用nginx链接时候使用 socket=172.x.x.x:8080 # 阿里云私有IP #直接做web服务器时使用 #http=172.x.x.x:8080 # 阿里云私有IP ... #其它内容不动,此处省略
把socket一行解除注释,把http一行注释掉。
然后重启uwsgi
(venv) $ uwsgi --stop uwsgi.pid #关闭 (venv) $ uwsgi --ini uwsgi.ini #重新启动
重启nginx
(venv) $ nginx -s reload
然后我们现在直接在外网使用默认的80端口去访问我们的网站。
配置Nginx静态文件托管
此时我们可以发现我们网站页面显示正常,但是某些页面,比如后台页面,无法加载静态css和js文件。是因为这些静态文件无法被找到。我们现在设置Nginx托管我们所有的静态文件。 首先我们在与项目同一级的zcwBlog文件夹下新建一个static文件夹用于存放我们的静态文件。
然后,我们去搜索所有的静态文件。 在收集静态文件时,我们修改一下项目中的settings.py文件,添加STATIC_ROOT
STATIC_ROOT = '/var/www/zcwBlog/static'
这样理论上就可以使用 python manage.pycollectstatic收集所有的静态文件了。但是我使用这个命令时候总是报错,提示找不到static文件夹。最终排查发现是STATICFILES_DIRS的问题。 原来的内容是
STATICFILES_DIRS = [ os.path.join(BASE_DIR, '/static/'), ]
后来发现这个目录并不对,os.path.join之后还是/static目录,而非项目下的static目录。于是就手动改了一下
STATICFILES_DIRS = [ # os.path.join(BASE_DIR, '/static/'), '/var/www/zcwBlog/zcwBlog/static/', ]
直接把项目中static目录下的完整路径写上了。 此时就能正常收集到我们所有的static文件了
python manage.py collectstatic
我们发现我们项目目录下的static目录下的静态文件,还有admin, xadmin和ueditor下的静态文件也被搜集到了目的文件夹下。
接下来,我们再去修改一下我们的nginx.conf文件,添加如下内容
location /static { # 指定静态文件存放目录 alias /var/www/zcwBlog/static; }
指定静态文件的路径。
最后,我们再重启一下nginx就可以了
(venv) $ nginx -s reload
此时再次访问我们的网站,所有的静态文件请求都被Nginx托管了,这类请求直接去我们存放静态文件的文件夹下去寻找了。 而且现在我们xadmin的静态文件也能找到了。我们后台页面的显示也恢复正常了。
需要注意的几点
部署时,我们的setting.py文件中的Debug应设置为False;
使用了ueditor。 在进入后台编辑我们的博文内容时会报错,是因为兼容性的问题。根据报错提示信息,我们可以直接打开django/forms/boundfield.py文件,然后注释掉class BoundField下的 as_widget方法中的最后一行render = self.form.renderer
return widget.render( name=self.html_initial_name if only_initial else self.html_name, value=self.value(), attrs=attrs, # renderer=self.form.renderer, )
转载请注明:禅思 » 网站部署:Django+Nginx+Uwsgi+阿里云?