最近在学习运用python Django框架开发服务器的相关知识。为了能够让服务器在生产环境中运行,也顺便学习了Centos操作系统。总的感觉是,从无到有是最困难的,一个新的环境,新的框架,入了门,接下来就简单了。当然,从入门到深入,又是另一回事,暂且按下不表。现在就记录下如何在Centos下安装Django服务器。
用到的开发配置如下:
虚拟机:
操作系统:
Django开发环境:
数据库:
服务器环境:
三方服务
我们可以去CentOS官网来下载对应的ISO镜像,来安装CentOS系统。
当我们在虚拟机中安装完Centos系统后,会发现无法连接网络。我们需要修改其网络配置。
通过Mac终端查看VMware Fusion的vmnet8目录下nat.conf文件内容
cat /Library/Preferences/VMware\ Fusion/vmnet8/nat.conf
查看dhcpd.conf中的内容:
cat /Library/Preferences/VMware\ Fusion/vmnet8/dhcpd.conf
注意range 这个是虚拟机允许选择的静态ip地址范围,自定义的静态ip地址必须要在这个范围内(本文打算使用172.16.9.200为例介绍)
获取DNS(在mac系统偏好设置—>网络—>)
登录CentOS系统,进入虚拟机的network-scripts目录,配置网络:
cd /etc/sysconfig/network-scripts
查找CentOS虚拟机对应的网卡配置文件,找到ifcfg-en开头的文件,上图中我的是ifcfg-ens33
想用vim编辑该文件,却发现vim在CentOS下面默认没有安装(WTF???),只能用vi来编辑:
原始内容:
重新启动network服务:
systemctl restart network
在CentOS系统中,我们可以通过YUM(Yellow dog Update Modifie)来管理安装软件,但是系统默认的YUM源上的软件包有限,我们需要安装扩展的epel源。
安装eple源:
输入命令
yum -y install epel-release
针对CentOS,其最小化安装已经不会包含ifconfig和netstat相关的网络工具了。
使用yum进行安装:
yum install net-tools
yum install vim
开发时,我们需要gcc/g++编译器来编译文件,在CentOS默认最小安装下是没有的,我们可以用yum来安装:
yum install gcc
yum install gcc-c++
CentOS 7 中的python默认版本是2.7.5,我们需要将其升级至python3。
yum -y install python36
yum会安装python3到本机,同时会安装python的依赖库pip3 。
这时,在命令行中输入python3,就会使用python 3。
如果要默认的python命令替换为python 3执行的话,需要断开原有的python命令连接,使其指向python 3:
$ sudo rm /bin/python
$ sudo rm /bin/python3
$ cd /bin
$ ln -s python3.6 python # Choose the Python 3.x binary here
成功:
NOTE:在安装python3的时候,会默认安装依赖库pip3。但有时候pip3 不能够正常运行,这时候只要用yum将python3删除重装就好。
sudo yum install python-devel # for python2.x installs
sudo yum install python3-devel # for python3.x installs
这时因为,yum使用python2来写的,因此我们将默认的python指向python3,会出现语法错误。
这时候可以根据yum的错误提示,修改对应的错误文件的头不引用:
!/usr/bin/python
改成了
!/usr/bin/python2.7
参考这里
当输入‘y’后,仍会报错,这时候要依次修改python引用。
PostgreSQL数据库是一款最高效的面向关系型数据库。我们可以到他的官网来下载安装对应的版本。
我们这里使用12这个版本。
首先安装postgresql YUM源:
yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
安装postgresql及server
yum install postgresql12
yum install postgresql12-server
初始化数据库,并设置默认开机启动postgresql服务
/usr/pgsql-12/bin/postgresql-12-setup initdb
systemctl enable postgresql-12
systemctl start postgresql-12
安装postgresql开发包
yum install postgresql-devel
psycopg2是python的postgresql DB适配器,为了能够在python中开发postgresql数据库,需要安装该python库。
注意,这时候我们需要使用pip3来安装到python3,默认的pip会安装到python2中。
pip3 install psycopg2-binary==2.8.4
这里我们选择用安装包安装,而不是本地编译源代码安装。因为源代码安装会发生编译错误,这时因为psycopg2使用python2编写的,在python3环境下编译会出现错误。
Redis数据库是一款优秀的非关系型数据库,利用键值对的关系,可以达到内存中的高效存取。我们可以用它来做Server中一些缓存的操作。
yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
yum --enablerepo=remi install redis
启用Redis服务
systemctl enable --now redis
修改Redis配置
vim /etc/redis.conf
Bind 0.0.0.0 (71 line)
重启Redis
systemctl restart redis
查看Redis状态
systemctl status redis
测试Redis Server是否可用
[root@linuxhelp ~]# redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> exit
安装Django
pip3 install Django==3.0.1
安装Django restframework
pip3 install djangorestframework==3.11.0
安装Python 图像处理库Pillow
pip3 install pillow==6.2.1
安装Django JWT认证库
pip3 install django-rest-auth==0.9.5
pip3 install djangorestframework-jwt==1.11.0
安装Django的Redis库
pip3 install django-redis==4.11.0
注意到在安装django-redis时的时候,会默认安装依赖的redis 3.3.11版本。但是我们之前安装启动了redis 5这个版本,不知道会如何处理。
因为项目中使用了阿里云的短信服务,所以需要安装阿里云Python SDK:
pip3 install aliyun_python_sdk_core
默认情况下,Postgresql数据库是采用host认证的方式,就是其DB用户是和Linux用户绑定的,当用户登录了系统后,便可直接访问Postgresql数据库。为了支持密码认证,我们可以通过修改他的host-based authentication (HBA)配置:
vim /var/lib/pgsql/12/data/pg_hba.conf
在文件最后的位置,找到:
host all all 127.0.0.1/32 ident
host all all ::1/128 ident
修改为:
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
启动Postgresql服务
systemctl start postgresql-12
systemctl enable postgresql-12
在Postgresql中,使用‘role’的概念来控制数据库的访问权限。所谓的role,可以理解为角色,他是和CentOS系统中的account是对应的。当我们创建一个role时,默认会与CentOS中同名的account进行关联(即该account能够按照role中的规定,访问Postgresql数据库)。当我们在CentOS中安装Postgresql时,安装包会默认创建一个postgres的用户,该用户被分配到Postgresql default role。
postgres用户类似于CentOS中的root用户,拥有顶级的数据库权限。因此,我们可以用postgres用户创建其他用户及数据库。
登录到postgres用户
sudo -i -u postgres
进入psql shell
-bash-4.2$ psql
psql (12.1)
Type "help" for help.
postgres=#
退出psql
postgres=# \q
利用postgres用户,我们可以创建新的Postgresql role:
-bash-4.2$ createuser -dP John.tyler
Enter password for new role:
Enter it again:
-d : role用户拥有创建数据库权限
-P : role用户需要密码认证。(我们在前面已经启用了密码认证)
上一步我们创建了一个 John.tyler的role,它会默认匹配到到当前CentOS同名用户中。
如果没有同名用户,我们需要创建一个新的John.tyler用户
adduser John.tyler
为John.tyler设置CentOS登录密码
[root@localhost ~]# passwd John.tyler
让John.tyler拥有sudo权限:
# 首先切换为root用户
su root
# 修改sudoers文件
d
# 找到权限设置,如下
root ALL=(ALL) ALL
# 若要给John.tyler用户增加sudo权限,需要增加如下一行
root ALL=(ALL) ALL
John.tyler ALL=(ALL) ALL
# 保存退出后John.tyler用户则拥有了sudo权限
首先切换到John.tyler
su - John.tyler
创建数据库
createdb -E utf8 -U John.tyler testDB
测试,运行psql命令,会发现报错,提示John.tyler数据库不存在。
[John.tyler@localhost ~]$ psql
psql: error: could not connect to server: FATAL: database "John.tyler" does not exist
这是因为psql默认会去尝试连接与当前role同名的数据库,我们可以用-d选项,指定连接数据库名称为testDB:
[John.tyler@localhost ~]$ psql -d testDB
psql (12.1)
Type "help" for help.
testDB=>
输入\conninfo查看当前的DB连接信息
testDB=> \conninfo
You are connected to database "testDB" as user "John.tyler" via socket in "/var/run/postgresql" at port "5432".
OK,至此,我们创建了一个叫做testDB的数据库,用户名称为John.tyler,testDB数据库登录密码是我们在createuser中指定的,注意,不是我们CentOS中John.tyler的account 密码!
在实际的项目中,往往需要处理多种环境,其中至少涉及本地开发和产品环境,某些设置可以适用于全部环境,而其他一些则需要针对性的设置。下面就说明如何设置多种环境配置。
在项目的settings.py文件旁创建settings目录,将settings.py重命名为base.py,并将其移动到settings目录下。同时,在settings目录中生成如下附加文件:
settings/
__init__.py
base.py
local.py
pro.py
local.py用于本地环境配置,pro.py用于产品环境配置。他们都继承自base.py:
from .base import *
编辑settings/base.py,找到如下代码行:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
修改为:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(os.path.join(__file__, os.pardir))))
因为我们已经将配置文件移动到了低一级的目录,因此需要将BASE_DIR指向其父目录方可有效,可以使用os.pardir
编辑settings/local.py :
from .base import *
DEBUT = True
DATABASE = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
编辑settings/pro.py:
from .base import *
DEBUG = False
ADMINS = (
('John.tyler', '[email protected]'),
)
ALLOWED_HOSTS = ['*']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'testDB',
'USER': 'John.tyler',
'PASSWORD': '******',
}
}
ADMINS : 因为此时DEBUG = False,因此对于视图的异常情况,会以邮件的形式发送到ADMINS。
ALLOWED_HOSTS:Django仅会在指定的服务器上运行,这里未来可以设置线上服务器的域名或IP地址。*表示可以再任意主机上运行。
DATABASES:产品环境下,我们将数据库替换为更强大的Postgresql,注意这里的参与要与我们创建的数据库相匹配。
我们已经为本地和产品环境设置了两套配置文件,那么Django如何知道使用哪个配置文件呢?
我们需要设置一个DJANGO_SETTINGS_MODULE
环境变量来指定我们使用的setting 文件:
打开Shell并运行以下命令:
export DJANGO_SETTINGS_MODULE=yourprojectname.settings.pro
或者在每次运行管理命令时,添加–settings参数:
python manage.py migrate --settings=yourprojectname.settings.pro
如果不想每次都添加settings变量,我们可以配置Shell的全局环境变量(以bash shell为例):
/bin/bash
The bash executable
/etc/profile
The systemwide initialization file, executed for login shells
~/.bash_profile
The personal initialization file, executed for login shells
~/.bashrc
The individual per-interactive-shell startup file
~/.bash_logout
The individual login shell cleanup file, executed when a login shell exits
~/.inputrc
Individual readline initialization file|
vim ~/.bash_profile
export DJANGO_SETTINGS_MODULE=yourprojectname.settings.pro
重新加载Shell变量
$ source ~/.bashrc
在本地开发中,我们用Django自带的服务器做开发测试。但是,在真正的产品环境中,需要使用真实的Web服务器。
对于Python来说,使用uWSGI。
pip3 install uwsgi==2.0.18
安装成功后,就可以用uwsgi来运行我们的server了。
为了让uWSGI能够正确的运行我们的server,我们需要为uWSGI编写启动配置文件。
在项目目录下生成如下文件结构:
config/
uwsgi.ini
uwsgi.ini内容如下:
[uwsgi]
# variables
projectname = yourprojectname
base = absoult path of your project folder
# configuration
pythonpath = %(base)
chdir = %(base)
env = DJANGO_SETTINGS_MODULE=%(projectname).settings.pro
module = %(projectname).wsgi:application
socket = /tmp/%(projectname).sock
# maximum number of worker processes
processes = 10
# ... with appropriate permissions - may be needed
chmod-socket = 666
# clear environment on exit
vacuum = true
enable-threads = true
socket :这里指定与uWSGI通信是使用unxi socket文件(比直接指定ip:端口形式通信效率高),socket文件的路径为/tmp/%(projectname).sock。
当uwsgi运行时,会自动在tmp目录下创建sock文件,无需人工参与。当使用NGINX时,NGINX会通过该sock文件与uWSGI通信。
chmod-socket:sock文件的访问权限,这里用666表示所有用户皆可访问。这时为了预防NGINX因为sock文件权限,而无法与uWSGI通信的问题。
我们访问网络,获取的资源,可以整体上分为静态资源和动态资源。而在当前流行的服务器架构中,静态资源和动态资源的获取可以分为两种不同的方式,从而减轻后台服务器的压力。
通用的做法是,在真正的服务器之前,设置一个反向代理,通常使用Nginx。Nginx的功能是过滤客户端的资源请求类型,对于静态资源,会有Nginx直接返回,并作缓存以备下次使用。而对于真正需要动态返回的动态资源,Nginx才会去请求后台服务器(在我们这里也就是uWGSI),由uWSGI将动态结果返回给Nginx,Nginx再返回给客户端。对于动态资源,Nginx起到一个代理请求的作用。
这也就是反向代理名称的由来,正向代理是由客户端使用的,常见的如VPN。正向代理代理的是客户端。而反向代理,并不是在客户端使用,而是在服务器内部作代理,这种服务器自身使用的代理对客户端来说是透明的,比如我们访问www.baidu.com,我们并不知道我们请求下来的资源究竟是来自Nginx还是其后台真正的服务器。
在我们的应用中,Nginx和uWSGI还是在同一个服务器上,在更复杂的应用中,我们可以将Nginx和uWSGI部署到不同的服务器中,来减轻同一个服务器的压力。
根据CentOS 7 下 yum 安装和配置 Nginx这篇文章中的内容,我们可以如下安装Nginx:
添加Nginx for CentOS7 的YUM源:
$ sudo rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
YUM安装Nginx:
$ sudo yum install nginx
为了Nginx能够和我们的uWSGI服务器能够协同工作,我们需要编写Nginx的配置文件。同样在config目录,添加nginx.conf文件:
config/
nginx.conf
uwsgi.ini
内容如下:
# the upstream componet nginx needs to connect ot
upstream ai_mei_jia {
server unix:///tmp/ai_mei_jia.sock;
}
server {
listen 80;
# the domain name it will serve for
server_name 172.16.9.130; # substitute your machine's IP address or FQDN(服务器域名)
charset utf-8;
# Django media
location /media {
alias /home/ai_mei_jia_db_mgr/ai_mei_jia/media; # your Django project's media files - amend as required
}
location /static {
alias /home/ai_mei_jia_db_mgr/ai_mei_jia/static; # your Django project's static files - amend as required
}
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass ai_mei_jia;
include /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
}
}
uwsgi_pass : 用来指定Nginx和uWSGI通信的socket地址。通常情况下,可以写为:
uwsgi_pass unix:///tmp/ai_mei_jia.sock;
这里我们使用了upstream ai_mei_jia。这样写可以支持多服务器负载均衡,因为在upstream里面,我可以指定多个server,Nginx会自动在这些server间做负载均衡,如:
upstream uwsgicluster {
server unix:///tmp/uwsgi.sock;
server 192.168.1.235:3031;
server 10.0.0.17:3017;
}
uwsgi_pass uwsgicluster;
Nginx配置有很多,具体可以参考Nginx支持。我们不做深入研究。
写好配置文件后,我们就可以将我们整个的Django工程目录,scp到服务器的对应目录下了。
当我们用scp命令将Django文件拷贝到CentOS中后,还需要做一些最后的配置。
进入到项目根目录,运行
$ python manage.py migrate
如果一切正常,恭喜,已经成功了一大半了。
然后,我们来创建第一个super user:
$ python manage.py createsuperuser
接下来,我们通过uWSG来启动工程,而不是本地开发的runserver命令。
在前面的步骤中,为了能够使Nginx和uWSGI联合工作,我们需要将之前我们写的nginx.conf拷贝到Nginx的配置文件目录中:
cp /home/John.tyler/ai_mei_jia/config/nginx.conf /etc/nginx/conf.d/ai_mei_jia.conf
启动Nginx
systemctl start nginx
并设置nginx服务开机自启动
systemctl enable nginx
尝试连接服务器:
http://172.16.9.150/admin
这可能是因为CentOS的防火墙的原因,我们让防火墙开放http的80端口:
firewall-cmd --zone=public --permanent --add-service=http
sudo firewall-cmd --reload
这可能是因为CentOS中SELinux导致Nginx无法访问uWSGI的sock文件导致的。
SELinux(Security-Enhanced Linux)
是美国国家安全局(NSA)对于强制访问控制的实现,是Linux历史上最杰出的新安全子系统。
NSA是在Linux社区的帮助下开发了一种访问控制体系,在这种访问控制体系的限制下,进程只能访问那些在他的任务中所需要文件。
这有点像沙盒机制的概念。但是SELinux也是的Nginx无法正常的使用。正统的方式应该是在SELinux中配置相应策略,但是配置起来比较麻烦。我们就根据selinux 不限制nginx
永久关闭selinux,永久关闭需要重启才能生效
编辑vim /etc/selinux/config
这个文件的注释写的非常清楚,直接将selinux设置为disable后,重启,即可。
再次尝试访问Django服务器,这时候应该可以正常访问。如果仍然提示502错误,可以查看nginx的error log,
cat /var/log/nginx/error.log
如果error log如下所示:
2019/12/30 18:33:50 [crit] 1454#1454: *1 connect() to unix:///tmp/ai_mei_jia.sock failed (13: Permission denied) while connecting to upstream, client: 172.16.9.1, server: 172.16.9.150, request: "GET /admin HTTP/1.1", upstream: "uwsgi://unix:///tmp/ai_mei_jia.sock:", host: "172.16.9.150"
说明nginx没有权限访问ai_mei_jia.sock,这时候要再次检查一下uwsgi.ini的配置,
chmod-socket = 666
来确保nginx能够有权限访问sock文件。
至此,我们的Nginx服务并没有初始化需要其提供的静态和媒体数据。
我们到Django工程中,编辑settings/base.py文件:
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
然后运行
python manage.py collectstatic
对应输出结果类似下面:
160 static files copied to '/ai_mei_jia/static'.
注意上面的路径要和nginx.conf中的配置一致:
# Django media
location /media {
alias /home/ai_mei_jia_db_mgr/ai_mei_jia/media; # your Django project's media files - amend as required
}
location /static {
alias /home/ai_mei_jia_db_mgr/ai_mei_jia/static; # your Django project's static files - amend as required
}
重新加载nginx服务
systemctl reload nginx
这时候再加载页面,会发现nginx仍然没有加载静态文件(这里是格式文件css):
查看nginx的error log,会发现是nginx没有权限访问css文件:
2019/12/31 11:17:16 [error] 2747#2747: *4 open() "/home/John.tyler/ai_mei_jia/static/admin/css/responsive.css" failed (13: Permission denied), client: 172.16.9.1, server: 172.16.9.150, request: "GET /static/admin/css/responsive.css HTTP/1.1", host: "172.16.9.150", referrer: "http://172.16.9.150/admin/login/?next=/admin/"
再用ll命令查看static文件夹的权限,发现其他用户明明是有rx权限的,很是奇怪。
尝试让nginx以root用户运行,修改nginx.conf文件:
user nginx; # 修改nginx为root
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
将第一行的nginx user修改为root,重启nginx服务,再次加载页面:
这次css文件终于加载成功了。
查了无数资料,终于将本地的server代码整到CentOS服务器上运行了。回头看一下,服务器的环境配置确实很很繁琐,但是又重复性的劳动。尤其是Nginx和uWSGI之间的通信,涉及到的用户权限问题,自己也是模模糊糊的用最高权限搞定了,但细想一下,这里肯定会有很大的安全隐患在里面。
下一步会继续研究Docker,来简化服务器环境的设置。