Linux(CentOS7)系统中部署Django web框架

Posted 大天狗子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux(CentOS7)系统中部署Django web框架相关的知识,希望对你有一定的参考价值。

1. 概述

部署django和vue架在逻辑上可以分为web层与数据库层:web前端通过实现了WSGI协议的模块对python代码进行解析,而python代码中则通过特定于数据库的操作接口对数据库进行读取与写入。

Django自身内置了轻量级的web服务器与sqlite数据库,可以用于简单的代码测试,并支持Apache httpd与nginx作为web前端,以及PostgreSQL/mysql/Oracle等数据库作为后端存储,用于实际的生产环境。

本文分别以MySQL + Apache httpd + mod_wsgi与MySQL + Nginx + uwsgi为例,通过源码安装的方式,简单描述Django服务框架在Linux系统生产环境下的部署过程。

2. 说明

1. 示例中包含两台服务器,操作系统版本均为CentOS 7.6.1810,最小化全新安装,无其他项目运行。

  • django-web(192.168.9.129):web前端,安装的组件包括Python解析器,mysqlclient数据库操作接口,apache/nginx web服务器,WSGI协议处理模块。
  • django-db(192.168.9.130):后端存储,安装mysql。

2. 示例中所使用的源码包均位于/usr/local/src,列表如下:

   Python-3.7.2.tgz
   Django-2.1.5.tar.gz
   mysql-boost-5.7.24.tar.gz
   mysqlclient-1.3.14.tar.gz
   httpd-2.4.37.tar.gz
   apr-1.6.5.tar.gz
   apr-util-1.6.1.tar.gz
   mod_wsgi-4.6.5.tar.gz
   tengine-2.2.3.tar.gz
   uwsgi-2.0.17.1.tar.gz

3. 示例中对源码包进行安装时,均使用默认的目标安装路径。在实际的生产环境中,为避免覆盖,安装前务必确认目标安装路径与文件是否存在,若存在则应查看当前已安装版本,并选择其他目录。

3. 步骤

3.1 - 后端存储(MySQL)


[[email protected] ~]# ip addr show eth0 | sed -n '/inet /p' | awk '{print $2}'
192.168.9.130/24
[[email protected] ~]#

安装依赖包:


[[email protected] ~]# yum -y install gcc gcc-c++ make cmake ncurses-devel openssl-devel

下载集成boost库的mysql源码包:


[[email protected] ~]# wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-boost-5.7.24.tar.gz -P /usr/local/src/

解压源码包并进入源码目录,配置编译选项:

源码安装mysql的默认目标路径为/usr/local/mysql,可以通过cmake的-DCMAKE_INSTALL_PREFIX选项显式指定。


[[email protected] ~]# cd /usr/local/src/
[[email protected] src]#
[[email protected] src]# ll
total 47960
-rw-r--r-- 1 root root 49110448 Oct  4 04:02 mysql-boost-5.7.24.tar.gz
[[email protected] src]#
[[email protected] src]# tar axf mysql-boost-5.7.24.tar.gz
[[email protected] src]#
[[email protected] src]# ll
total 47964
drwxr-xr-x 36 7161 31415     4096 Oct  4 06:02 mysql-5.7.24
-rw-r--r--  1 root root  49110448 Oct  4 04:02 mysql-boost-5.7.24.tar.gz
[[email protected] src]#
[[email protected] src]# cd mysql-5.7.24/
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]# ll /usr/local/mysql
ls: cannot access /usr/local/mysql: No such file or directory
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]# cmake . > -DCMAKE_INSTALL_PREFIX=/usr/local/mysql > -DWITH_BOOST=boost > -DWITH_INNOBASE_STORAGE_ENGINE=1 > -DWITH_PARTITION_STORAGE_ENGINE=1 > -DWITH_FEDERATED_STORAGE_ENGINE=1 > -DWITH_BLACKHOLE_STORAGE_ENGINE=1 > -DWITH_MYISAM_STORAGE_ENGINE=1 > -DENABLED_LOCAL_INFILE=1 > -DENABLE_DTRACE=0 > -DDEFAULT_CHARSET=utf8mb4 > -DDEFAULT_COLLATION=utf8mb4_general_ci > -DWITH_SSL=yes > -DWITH_EMBEDDED_SERVER=1

编译并安装:

可选择为make命令指定-j选项,执行多任务并行编译;该步骤耗时较长,建议在screen/tmux等终端中运行,或以nohup的方式后台运行,避免因终端关闭而导致运行终止。


[[email protected] mysql-5.7.24]# awk '/^processor/{print $3}' /proc/cpuinfo | wc -l
2
[[email protected] mysql-5.7.24]# make -j2 && make install

 创建mysql用户和组:


[[email protected] mysql-5.7.24]# id mysql
id: mysql: no such user
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]# useradd mysql -s /sbin/nologin
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]# id mysql
uid=1000(mysql) gid=1000(mysql) groups=1000(mysql)
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]#

创建mysql数据目录与日志目录(本例中分别为/usr/local/mysql/data与/usr/local/mysql/logs/):


[[email protected] mysql-5.7.24]# ll /usr/local/mysql/
total 64
drwxr-xr-x  2 root root  4096 Jan 16 15:26 bin
-rw-r--r--  1 root root 17987 Oct  4 05:48 COPYING
-rw-r--r--  1 root root 17987 Oct  4 05:48 COPYING-test
drwxr-xr-x  2 root root    55 Jan 16 15:25 docs
drwxr-xr-x  3 root root  4096 Jan 16 15:25 include
drwxr-xr-x  4 root root   192 Jan 16 15:26 lib
drwxr-xr-x  4 root root    30 Jan 16 15:26 man
drwxr-xr-x 10 root root  4096 Jan 16 15:26 mysql-test
-rw-r--r--  1 root root  2478 Oct  4 05:48 README
-rw-r--r--  1 root root  2478 Oct  4 05:48 README-test
drwxr-xr-x 28 root root  4096 Jan 16 15:26 share
drwxr-xr-x  2 root root    90 Jan 16 15:26 support-files
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]# mkdir -p /usr/local/mysql/{data,logs}

创建mysql运行时pid文件,错误日志文件,慢查询日志文件(本例中分别为/usr/local/mysql/logs/mysqld.pid,/usr/local/mysql/logs/mysqld-err.log与/usr/local/mysql/logs/mysqld-slw.log):


[[email protected] mysql-5.7.24]# touch /usr/local/mysql/logs/{mysqld.pid,mysqld-err.log,mysqld-slw.log}
[[email protected] mysql-5.7.24]#

将mysql安装的目标路径(本例中为/usr/local/mysql/)所有者设置为mysql用户和组:


[[email protected] mysql-5.7.24]# chown -R mysql:mysql /usr/local/mysql/
[[email protected] mysql-5.7.24]#

将mysql可执行文件的路径(本例为/usr/local/mysql/bin)添加至系统的查找路径列表PATH中:


[[email protected] mysql-5.7.24]# which mysqld
/usr/bin/which: no mysqld in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]# sed -i '/^PATH/ s|$|:/usr/local/mysql/bin|' ~/.bash_profile
[[email protected] mysql-5.7.24]# source ~/.bash_profile
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]#
[[email protected] mysql-5.7.24]# which mysqld
/usr/local/mysql/bin/mysqld
[[email protected] mysql-5.7.24]# mysqld --version
mysqld  Ver 5.7.24 for Linux on x86_64 (Source distribution)

初始化数据库:


[[email protected] mysql-5.7.24]# mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data
2019-01-16T15:51:56.003707Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2019-01-16T15:51:56.308083Z 0 [Warning] InnoDB: New log files created, LSN=45790
2019-01-16T15:51:56.355390Z 0 [Warning] InnoDB: Creating foreign key constraint system tables.
2019-01-16T15:51:56.414615Z 0 [Warning] No existing UUID has been found, so we assume that this is the first time that this server has been started. Generating a new UUID: a6bc7921-19a6-11e9-a9ff-000c29000409.
2019-01-16T15:51:56.415817Z 0 [Warning] Gtid table is not ready to be used. Table 'mysql.gtid_executed' cannot be opened.
2019-01-16T15:51:56.725302Z 0 [Warning] CA certificate ca.pem is self signed.
2019-01-16T15:51:56.897014Z 1 [Warning] [email protected] is created with an empty password ! Please consider switching off the --initialize-insecure option.
[[email protected] mysql-5.7.24]# 

编辑mysql配置文件:

MySQL服务器的运行参数需要根据实际的应用场景与服务器的硬件配置而进行调整。


[[email protected] mysql-5.7.24]# cd
[[email protected] ~]#
[[email protected] ~]# ll /etc/my.cnf
-rw-r--r--. 1 root root 570 Aug 16 14:00 /etc/my.cnf
[[email protected] ~]#
[[email protected] ~]# cp -a /etc/my.cnf{,.ori}
[[email protected] ~]#
[[email protected] ~]# vi /etc/my.cnf
[mysqld]
user = mysql
port = 3306
server-id = 1
character-set-server = utf8mb4
socket = /tmp/mysql.sock
basedir = /usr/local/mysql
datadir = /usr/local/mysql/data
pid-file = /usr/local/mysql/logs/mysqld.pid
log_error = /usr/local/mysql/logs/mysqld-err.log
slow_query_log_file = /usr/local/mysql/logs/mysqld-slw.log
skip-name-resolve = 1
back_log = 300
max_connections = 1000
max_connect_errors = 6000
open_files_limit = 65535
table_open_cache = 128
max_allowed_packet = 4M
binlog_cache_size = 1M
max_heap_table_size = 8M
tmp_table_size = 16M
read_buffer_size = 2M
read_rnd_buffer_size = 8M
sort_buffer_size = 8M
join_buffer_size = 8M
key_buffer_size = 4M
thread_cache_size = 8
query_cache_type = 1
query_cache_size = 8M
query_cache_limit = 2M
ft_min_word_len = 4
log_bin = mysql-bin
binlog_format = mixed
expire_logs_days = 30
slow_query_log = 1
long_query_time = 1
performance_schema = 0
explicit_defaults_for_timestamp
#lower_case_table_names = 1
skip-external-locking
default_storage_engine = InnoDB
innodb_file_per_table = 1
innodb_open_files = 500
innodb_buffer_pool_size = 64M
innodb_write_io_threads = 4
innodb_read_io_threads = 4
innodb_thread_concurrency = 0
innodb_purge_threads = 1
innodb_flush_log_at_trx_commit = 2
innodb_log_buffer_size = 2M
innodb_log_file_size = 32M
innodb_log_files_in_group = 3
innodb_max_dirty_pages_pct = 90
innodb_lock_wait_timeout = 120
bulk_insert_buffer_size = 8M
myisam_sort_buffer_size = 8M
myisam_max_sort_file_size = 10G
myisam_repair_threads = 1
interactive_timeout = 28800
wait_timeout = 28800

[mysqldump]
quick
max_allowed_packet = 16M

[myisamchk]
key_buffer_size = 8M
sort_buffer_size = 8M
read_buffer = 4M
write_buffer = 4M

将mysql的共享库文件所在目录(本例中为/usr/local/mysql/lib)添加到系统共享库查找路径列表的配置文件中:


[[email protected] ~]# echo "/usr/local/mysql/lib" >> /etc/ld.so.conf.d/mysql-v5.7.24.conf
[[email protected] ~]#
[[email protected] ~]# cat /etc/ld.so.conf.d/mysql-v5.7.24.conf
/usr/local/mysql/lib
[[email protected] ~]#
[[email protected] ~]# ldconfig
[[email protected] ~]#

添加mysql服务,并设置为随系统启动:


[[email protected] ~]# cp -a /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
[[email protected] ~]# chmod 755 /etc/init.d/mysqld
[[email protected] ~]# chkconfig --level 35 mysqld on
[[email protected] ~]#

启动mysql服务:


[[email protected] ~]# /etc/init.d/mysqld start
Starting MySQL.. SUCCESS!
[[email protected] ~]# 

设置mysql的root用户密码,并添加相应的授权(初始密码为空,本例中将root密码设置为django):


[[email protected] ~]# mysql -uroot -e "use mysql; set password for 'root'@'localhost' = password('django');"
[[email protected] ~]#  
[[email protected] ~]# mysql -uroot -pdjango -e "grant all privileges on *.* to [email protected]'192.168.9.%' identified by 'django' with grant option;"
mysql: [Warning] Using a password on the command line interface can be insecure.
[[email protected] ~]#

MySQL服务器安装与配置完成。

3.2 - Django环境


[[email protected] ~]# ip addr show eth0 | sed -n '/inet /p' | awk '{print $2}'
192.168.9.129/24
[[email protected] ~]#

3.2.1 - Python3

安装依赖包:


[[email protected] ~]# yum -y install gcc make libffi-devel readline-devel zlib-devel openssl-devel

下载python3源码包:


[[email protected] ~]# wget https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tgz -P /usr/local/src/

解压源码包并进入源码目录,执行安装:

源码安装python3的默认目标路径为/usr/local,可以通过configure命令的--prefix选项显式指定。该步骤耗时较长,建议在screen/tmux等终端中运行,或以nohup的方式后台运行,避免因终端关闭而导致运行终止。


[[email protected] ~]# cd /usr/local/src/
[[email protected] src]#
[[email protected] src]# ll
total 22364
-rw-r--r-- 1 root root 22897802 Dec 24 03:42 Python-3.7.2.tgz
[[email protected] src]#
[[email protected] src]# tar axf Python-3.7.2.tgz
[[email protected] src]#
[[email protected] src]# cd Python-3.7.2/
[[email protected] Python-3.7.2]#
[[email protected] Python-3.7.2]# ./configure --enable-shared --enable-optimizations
...
[[email protected] Python-3.7.2]# awk '/^processor/{print $3}' /proc/cpuinfo | wc -l
2
[[email protected] Python-3.7.2]# make -j2 && make install

将python3的共享库文件所在目录(本例中为/usr/local/lib)添加到系统共享库查找路径列表的配置文件中:


[[email protected] Python-3.7.2]# which python3
/usr/local/bin/python3
[[email protected] Python-3.7.2]#
[[email protected] Python-3.7.2]# python3 -V
python3: error while loading shared libraries: libpython3.7m.so.1.0: cannot open shared object file: No such file or directory
[[email protected] Python-3.7.2]#
[[email protected] Python-3.7.2]# ll /usr/local/lib
total 12072
lrwxrwxrwx  1 root root       20 Jan 16 17:05 libpython3.7m.so -> libpython3.7m.so.1.0
-r-xr-xr-x  1 root root 12337216 Jan 16 17:05 libpython3.7m.so.1.0
-r-xr-xr-x  1 root root     7656 Jan 16 17:05 libpython3.so
drwxr-xr-x  2 root root       67 Jan 16 17:06 pkgconfig
drwxr-xr-x 35 root root     8192 Jan 16 17:06 python3.7
[[email protected] Python-3.7.2]#
[[email protected] Python-3.7.2]#
[[email protected] Python-3.7.2]# echo '/usr/local/lib' >> /etc/ld.so.conf.d/python-v3.7.2.conf
[[email protected] Python-3.7.2]# ldconfig
[[email protected] Python-3.7.2]#
[[email protected] Python-3.7.2]#
[[email protected] Python-3.7.2]# python3 -V
Python 3.7.2
[[email protected] Python-3.7.2]#

Python3解析器安装完成。

3.2.2 - MySQL数据库操作接口(mysqlclient)

安装依赖包mysql-devel:

mysqlclient依赖于mysql服务端开发库,若本机中先前已安装mysql服务器,则无需此步骤。


[[email protected] ~]# yum -y install mysql-devel

下载mysqlclient源码包:


[[email protected] ~]# wget https://files.pythonhosted.org/packages/f7/a2/1230ebbb4b91f42ad6b646e59eb8855559817ad5505d81c1ca2b5a216040/mysqlclient-1.3.14.tar.gz -P /usr/local/src/

解压源码包并进入源码目录,执行安装:


[[email protected] ~]# cd /usr/local/src/
[[email protected] src]#
[[email protected] src]# ll
total 22460
-rw-r--r--  1 root root    91391 Dec  4 10:06 mysqlclient-1.3.14.tar.gz
drwxr-xr-x 19  501  501     4096 Jan 16 17:05 Python-3.7.2
-rw-r--r--  1 root root 22897802 Dec 24 03:42 Python-3.7.2.tgz
[[email protected] src]#
[[email protected] src]# tar axf mysqlclient-1.3.14.tar.gz
[[email protected] src]# cd mysqlclient-1.3.14/
[[email protected] mysqlclient-1.3.14]# python3 setup.py install

查看版本:


[[email protected] mysqlclient-1.3.14]# python3 -c "import MySQLdb; print(MySQLdb.version_info)"
(1, 3, 14, 'final', 0)
[[email protected] mysqlclient-1.3.14]#

mysqlclient安装完成。

3.2.3 - django

下载源码包:


[[email protected] ~]# wget wget https://www.djangoproject.com/m/releases/2.1/Django-2.1.5.tar.gz -P /usr/local/src/

解压源码包并进入源码目录,执行安装:


[[email protected] ~]# cd /usr/local/src/
[[email protected] src]#
[[email protected] src]# ll
total 30876
-rw-r--r--  1 root root  8612384 Jan  4 13:47 Django-2.1.5.tar.gz
drwxrwxr-x  9 1000 1000     4096 Jan 16 17:16 mysqlclient-1.3.14
-rw-r--r--  1 root root    91391 Dec  4 10:06 mysqlclient-1.3.14.tar.gz
drwxr-xr-x 19  501  501     4096 Jan 16 17:05 Python-3.7.2
-rw-r--r--  1 root root 22897802 Dec 24 03:42 Python-3.7.2.tgz
[[email protected] src]#
[[email protected] src]# tar axf Django-2.1.5.tar.gz
[[email protected] src]# cd Django-2.1.5/
[[email protected] Django-2.1.5]# python3 setup.py install

查看版本:


[[email protected] Django-2.1.5]# python3 -m django --version
2.1.5
[[email protected] Django-2.1.5]#

安装成功后,将生成用于django项目管理的命令行工具django-admin:


[[email protected] Django-2.1.5]# which django-admin
/usr/local/bin/django-admin
[[email protected] Django-2.1.5]#

运行django-admin startproject命令,在指定目录中(本例中为/opt/webs/t_django)创建django项目:

django-admin startproject命令的格式为:django-admin startproject 项目名 [目标目录]

项目名是必选参数,目标目录是可选参数,若忽略,则在当前目录下创建一个与项目名同名的目录。


[[email protected] Django-2.1.5]# cd
[[email protected] ~]#
[[email protected] ~]# mkdir -p /opt/webs/t_django
[[email protected] ~]# django-admin startproject t_app /opt/webs/t_django
[[email protected] ~]#
[[email protected] ~]# tree /opt/webs/t_django/
/opt/webs/t_django/
├── manage.py
└── t_app
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

1 directory, 5 files
[[email protected] ~]#

以上显示的目录层级结构中,/opt/webs/下的t_djang为项目顶级目录,该目录下包含一个用于管理当前项目的manage.py程序,与一个python包(目录);该项目包内的settings.py用于配置当前项目的各项参数,urls.py用于定义当前项目的url路径,wsgi.py为web访问的入口。

为当前django项目创建单独的数据库以及相应的用户(本例中,数据库名称为t_django_db,用户名为u_django,密码为p_django):

步骤3.1中已添加root用户在192.168.9.%网段的登录权限。

在非mysql服务器所在主机上,运行mysql命令行客户端需要安装mysql软件包:yum -y install mysql


[[email protected] ~]# mysql -h192.168.9.130 -P3306 -uroot -pdjango -e "create database t_django_db; grant all privileges on t_django_db.* to [email protected]'192.168.9.129' identified by 'p_django';"

编辑项目配置文件settings.py,进行以下修改:

  • 将ALLOWED_HOSTS = []修改为ALLOWED_HOSTS = [‘*‘],添加允许访问的主机:

[[email protected] ~]# cd /opt/webs/t_django/
[[email protected] t_django]# ll
total 4
-rwxr-xr-x 1 root root 537 Jan 16 17:33 manage.py
drwxr-xr-x 2 root root  74 Jan 16 17:33 t_app
[[email protected] t_django]#
[[email protected] t_django]#
[[email protected] t_django]#
[[email protected] t_django]# cp -a t_app/settings.py{,.ori}
[[email protected] t_django]#
[[email protected] t_django]# sed -n '/^ALLOWED_HOSTS/p' t_app/settings.py
ALLOWED_HOSTS = []
[[email protected] t_django]# sed -i '/^ALLOWED_HOSTS/ s|[]|['''*''']|' t_app/settings.py
[[email protected] t_django]#
[[email protected] t_django]# sed -n '/^ALLOWED_HOSTS/p' t_app/settings.py
ALLOWED_HOSTS = ['*']
[[email protected] t_django]#
  • 设置数据库信息,将数据库引擎由sqlite3修改为mysql,并设置数据库IP与端口,数据库名称,用户名与密码,以及时区信息(注意缩进格式保持一致):

[[email protected] t_django]# sed -i '/ENGINE/ s/sqlite3/mysql/' t_app/settings.py
[[email protected] t_django]# sed -i '/ENGINE/{n;d}' t_app/settings.py
[[email protected] t_django]# sed -i -e '/ENGINE/ a        '''HOST''': '''192.168.9.130''',
        '''PORT''': '''3306''',
        '''NAME''': '''t_django_db''',
        '''USER''': '''u_django''',
        '''PASSWORD''': '''p_django''',
        '''TIME_ZONE''': '''Asia/Shanghai''',
        '''USE_TZ''': False' t_app/settings.py

修改前:


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

修改后:


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '192.168.9.130',
        'PORT': '3306',
        'NAME': 't_django_db',
        'USER': 'u_django',
        'PASSWORD': 'p_django',
        'TIME_ZONE': 'Asia/Shanghai',
        'USE_TZ': False
    }
}
  • settings.py末尾添加一行STATIC_ROOT = os.path.join(BASE_DIR, ‘static‘),指定静态文件的根目录:

[[email protected] t_django]# sed -i '$aSTATIC_ROOT = os.path.join(BASE_DIR, '''static''')' t_app/settings.py
[[email protected] t_django]#
[[email protected] t_django]# sed -n '$p' t_app/settings.py
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

运行python3 manage.py collectstatic命令,收集静态文件:

该命令在当前django项目的顶级目录/opt/webs/t_django下创建静态文件目录/static/


[[email protected] t_django]# pwd
/opt/webs/t_django
[[email protected] t_django]# ll
total 4
-rwxr-xr-x 1 root root 537 Jan 16 17:33 manage.py
drwxr-xr-x 2 root root  97 Jan 16 17:57 t_app
[[email protected] t_django]#
[[email protected] t_django]# python3 manage.py collectstatic

119 static files copied to '/opt/webs/t_django/static'.
[[email protected] t_django]#
[[email protected] t_django]# ll
total 4
-rwxr-xr-x 1 root root 537 Jan 16 17:33 manage.py
drwxr-xr-x 3 root root  19 Jan 16 17:59 static
drwxr-xr-x 3 root root 116 Jan 16 17:59 t_app
[[email protected] t_django]#
[[email protected] t_django]# du -sh static/
1.7M    static/
[[email protected] t_django]#

连接数据库并创建相应的表:


[[email protected] t_django]# mysql -h192.168.9.130 -P3306 -uu_django -pp_django -Dt_django_db -e "show tables;"
[[email protected] t_django]#
[[email protected] t_django]# python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, 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 admin.0003_logentry_add_action_flag_choices... 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 auth.0009_alter_user_last_name_max_length... OK
  Applying sessions.0001_initial... OK
[[email protected] t_django]# mysql -h192.168.9.130 -P3306 -uu_django -pp_django -Dt_django_db -e "show tables;"
+----------------------------+
| Tables_in_t_django_db      |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+
[[email protected] t_django]#
[[email protected] t_django]# ss -atn | grep :3306
ESTAB      0      0      192.168.9.129:44518              192.168.9.130:3306
[[email protected] t_django]#

运行python3 manage.py runserver 0:8000命令,以tcp的8000端口启动django内置的web服务器:

内置的web服务器初始化需要几秒钟。


[[email protected] t_django]# python3 manage.py runserver 0:8000 &
[1] 30910
[[email protected] t_django]# Performing system checks...

System check identified no issues (0 silenced).
January 16, 2019 - 18:06:19
Django version 2.1.5, using settings 't_app.settings'
Starting development server at http://0:8000/
Quit the server with CONTROL-C.

[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
[[email protected] t_django]# ss -atn | grep :8000
LISTEN     0      10           *:8000                     *:*

待8000端口处于LISTEN状态后,浏览器分别访问http://192.168.9.129:8000/与http://192.168.9.129:8000/admin,若显示以下页面,则说明设置成功:

技术分享图片

技术分享图片

终端输出的访问日志:


[16/Jan/2019 18:08:26] "GET / HTTP/1.1" 200 16348
[16/Jan/2019 18:08:26] "GET /static/admin/css/fonts.css HTTP/1.1" 200 423
Not Found: /favicon.ico
[16/Jan/2019 18:08:26] "GET /favicon.ico HTTP/1.1" 404 1975
[16/Jan/2019 18:08:26] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 200 80304
[16/Jan/2019 18:08:26] "GET /static/admin/fonts/Roboto-Bold-webfont.woff HTTP/1.1" 200 82564
[16/Jan/2019 18:08:26] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 200 81348
[16/Jan/2019 18:08:31] "GET /admin HTTP/1.1" 301 0
[16/Jan/2019 18:08:31] "GET /admin/ HTTP/1.1" 302 0
[16/Jan/2019 18:08:31] "GET /admin/login/?next=/admin/ HTTP/1.1" 200 1819
[16/Jan/2019 18:08:32] "GET /static/admin/css/responsive.css HTTP/1.1" 200 17976
[16/Jan/2019 18:08:32] "GET /static/admin/css/base.css HTTP/1.1" 200 16225
[16/Jan/2019 18:08:32] "GET /static/admin/css/login.css HTTP/1.1" 200 1203

Django环境的安装与初始配置完成。

3.3 - web前端(Apache)


[[email protected] ~]# ip addr show eth0 | sed -n '/inet /p' | awk '{print $2}'
192.168.9.129/24
[[email protected] ~]#

3.3.1 - 安装与基本配置

安装依赖包:


[[email protected] ~]# yum -y install expat-devel pcre-devel

下载apr,apr-util,httpd,mod_wsgi源码包:


[[email protected] ~]# wget > http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-1.6.5.tar.gz > http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-util-1.6.1.tar.gz > http://mirrors.hust.edu.cn/apache//httpd/httpd-2.4.37.tar.gz > https://files.pythonhosted.org/packages/47/69/5139588686eb40053f8355eba1fe18a8bee94dc3efc4e36720c73e07471a/mod_wsgi-4.6.5.tar.gz > -P /usr/local/src/

解压apr源码包并进入源码目录,执行安装:

源码安装apr的默认目标路径为/usr/local,可以通过configure命令的--prefix选项显式指定。


[[email protected] ~]# cd /usr/local/src/
[[email protected] src]#
[[email protected] src]# ll
total 41920
-rw-r--r--  1 root root  1073556 Sep 14 04:07 apr-1.6.5.tar.gz
-rw-r--r--  1 root root   554301 Oct 22  2017 apr-util-1.6.1.tar.gz
drwxr-xr-x 12 1000 1000     4096 Jan 16 17:31 Django-2.1.5
-rw-r--r--  1 root root  8612384 Jan  4 13:47 Django-2.1.5.tar.gz
-rw-r--r--  1 root root  9177278 Oct 22 14:13 httpd-2.4.37.tar.gz
-rw-r--r--  1 root root   490018 Oct 22 04:03 mod_wsgi-4.6.5.tar.gz
drwxrwxr-x  9 1000 1000     4096 Jan 16 17:16 mysqlclient-1.3.14
-rw-r--r--  1 root root    91391 Dec  4 10:06 mysqlclient-1.3.14.tar.gz
drwxr-xr-x 19  501  501     4096 Jan 16 17:05 Python-3.7.2
-rw-r--r--  1 root root 22897802 Dec 24 03:42 Python-3.7.2.tgz
[[email protected] src]#
[[email protected] src]#
[[email protected] src]# tar axf apr-1.6.5.tar.gz
[[email protected] src]# cd apr-1.6.5/
[[email protected] apr-1.6.5]# ./configure && make && make install

解压apr-util源码包并进入源码目录,执行安装:

以configure命令的--with-apr选项指定已安装的apr所在的目录。


[[email protected] apr-1.6.5]# cd ..
[[email protected] src]# tar axf apr-util-1.6.1.tar.gz
[[email protected] src]# cd apr-util-1.6.1/
[[email protected] apr-util-1.6.1]# ./configure --with-apr=/usr/local/apr && make && make install

解压httpd源码包并进入源码目录,执行安装:

源码安装httpd的默认目标路径为/usr/local,可以通过configure命令的--prefix选项显式指定。


[[email protected] apr-util-1.6.1]# cd ..
[[email protected] src]# tar axf httpd-2.4.37.tar.gz
[[email protected] src]# cd httpd-2.4.37/
[[email protected] httpd-2.4.37]# ./configure > --with-apr=/usr/local/apr/ > --with-apr-util=/usr/local/apr/ > --enable-mpms-shared=all && make -j2 && make install

将apache可执行文件的路径(本例为/usr/local/apache2/bin)添加至系统的查找路径列表PATH中:


[[email protected] httpd-2.4.37]# ls /usr/local/apache2/
bin  build  cgi-bin  conf  error  htdocs  icons  include  logs  man  manual  modules
[[email protected] httpd-2.4.37]# ls /usr/local/apache2/bin/
ab         apxs      dbmmanage  envvars-std  htcacheclean  htdigest  httpd      logresolve
apachectl  checkgid  envvars    fcgistarter  htdbm         htpasswd  httxt2dbm  rotatelogs
[[email protected] httpd-2.4.37]#
[[email protected] httpd-2.4.37]# which apachectl
/usr/bin/which: no apachectl in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)
[[email protected] httpd-2.4.37]#
[[email protected] httpd-2.4.37]# sed -i '/^PATH/ s|$|:/usr/local/apache2/bin|' ~/.bash_profile
[[email protected] httpd-2.4.37]# source ~/.bash_profile
[[email protected] httpd-2.4.37]# which apachectl
/usr/local/apache2/bin/apachectl
[[email protected] httpd-2.4.37]#
[[email protected] httpd-2.4.37]# apachectl -v
Server version: Apache/2.4.37 (Unix)
Server built:   Jan 17 2019 22:35:36
[[email protected] httpd-2.4.37]#

解压mod_wsgi源码包并进入源码目录,执行安装:

configure命令的--with-apxs与--with-python选项分别指定apache的apxs与python可执行文件的路径。


[[email protected] httpd-2.4.37]# cd ..
[[email protected] src]# tar axf mod_wsgi-4.6.5.tar.gz
[[email protected] src]# cd mod_wsgi-4.6.5/
[[email protected] mod_wsgi-4.6.5]# ./configure > --with-python=/usr/local/bin/python3 > --with-apxs=/usr/local/apache2/bin/apxs && > make -j && make install

创建用于运行apache的web用户与组(本例中为www):


[[email protected] mod_wsgi-4.6.5]# cd
[[email protected] ~]# id www
id: www: no such user
[[email protected] ~]# useradd www -s /sbin/nologin
[[email protected] ~]# id www
uid=1000(www) gid=1000(www) groups=1000(www)
[[email protected] ~]#

编辑apache主配置文件(本例中为/usr/local/apache2/conf/httpd.conf),执行以下修改:

  • 将默认的运行时用户和组daemon修改为www

[[email protected] ~]# ll /usr/local/apache2/conf/httpd.conf
-rw-r--r-- 1 root root 18541 Jan 17 13:57 /usr/local/apache2/conf/httpd.conf
[[email protected] ~]#
[[email protected] ~]# cp -a /usr/local/apache2/conf/httpd.conf{,.ori}
[[email protected] ~]#
[[email protected] ~]# sed -n '/^User|^Group/p' /usr/local/apache2/conf/httpd.conf
User daemon
Group daemon
[[email protected] ~]# sed -i '/^User|^Group/ s/daemon/www/' /usr/local/apache2/conf/httpd.conf
[[email protected] ~]#
[[email protected] ~]# sed -n '/^User|^Group/p' /usr/local/apache2/conf/httpd.conf
User www
Group www
[[email protected] ~]#
  • 添加ServerName为localhost:80

[[email protected] ~]# sed -n '/ServerName/p' /usr/local/apache2/conf/httpd.conf
# ServerName gives the name and port that the server uses to identify itself.
#ServerName www.example.com:80
[[email protected] ~]#
[[email protected] ~]# sed -i '/^#ServerName/aServerName localhost:80' /usr/local/apache2/conf/httpd.conf
[[email protected] ~]#
[[email protected] ~]# sed -n '/ServerName/p' /usr/local/apache2/conf/httpd.conf
# ServerName gives the name and port that the server uses to identify itself.
#ServerName www.example.com:80
ServerName localhost:80
[[email protected] ~]#

默认的文档根目录为/usr/local/apache2/htdocs,首页为index.html


[[email protected] ~]# sed -n '/^DocumentRoot/p' /usr/local/apache2/conf/httpd.conf
DocumentRoot "/usr/local/apache2/htdocs"
[[email protected] ~]#
[[email protected] ~]# ll /usr/local/apache2/htdocs/
total 4
-rw-r--r-- 1 root 40 45 Jun 11  2007 index.html
[[email protected] ~]#

测试配置文件语法,若无误则启动apache:


[[email protected] ~]# apachectl configtest
Syntax OK
[[email protected] ~]# apachectl
[[email protected] ~]#
[[email protected] ~]# ss -atn | grep :80
LISTEN     0      10           *:8000                     *:*
ESTAB      0      0      192.168.9.129:8000               192.168.9.1:58666
ESTAB      0      0      192.168.9.129:8000               192.168.9.1:58665
ESTAB      0      0      192.168.9.129:8000               192.168.9.1:58667
ESTAB      0      0      192.168.9.129:8000               192.168.9.1:58663
LISTEN     0      128         :::80                      :::*
[[email protected] ~]#

浏览器访问http://192.168.9.129/,若页面显示"It works!",则说明配置成功。

3.3.2 - mod_wsgi模式配置

apache作为wsgi前端时,mod_wsgi可以配置为两种工作模式(mode):

  • 内嵌(embedded)模式:mod_wsgi作为apache进程的一部分;apache主进程将django后端代码视为配置文件而在启动时加载,且在整个运行周期内一直保持,更新代码需要重新加载或重新启动apache主进程。
  • 守护进程(daemon)模式;mod_wsgi作为独立于apache的进程,负责解析django后端代码;对于每一个传入的wsgi请求,mod_wsgi将以apache的名义为之分配一个守护进程,因而代码的更新无需重新加载或重新启动apache主进程。

对于非Windows平台,Django官方文档中推荐使用守护进程模式。

3.3.2.1 - 内嵌模式

内嵌模式的基本配置包括:

  • 在server或virtual host上下文中,以LoadModule指令加载mod_wsgi模块。
  • 在server上下文中,以WSGIPythonPath指令指定项目包所在的本地路径名(非项目包自身的路径名)。
  • 在server或virtual host上下文中,以WSGIScriptAlias指定web url路径对应的本地文件名。

为django项目创建单独的文档根目录与虚拟主机配置文件(本例中分别为/opt/webs/documents与/usr/local/apache2/conf/extra/httpd-django.conf):

Django自身并不提供文件服务,而是交由前端的web服务器处理;对于url路径到本地路径的映射,Apache通过server/virtual host/directory上下文中的Alias指令指定,而Nginx则通过location块中的alias指令指定。


[[email protected] ~]# mkdir -p /opt/webs/documents
[[email protected] ~]# vi /usr/local/apache2/conf/extra/httpd-django.conf
WSGIPythonPath /opt/webs/t_django
<VirtualHost *:80>
    ServerName localhost:80
    DocumentRoot /opt/webs/documents
    LoadModule wsgi_module modules/mod_wsgi.so

    WSGIScriptAlias / /opt/webs/t_django/t_app/wsgi.py

    Alias /static/ /opt/webs/t_django/static/
    <Directory /opt/webs/t_django>
        Require all granted
    </Directory>
</VirtualHost>

在apache主配置文件中包含django虚拟主机配置文件:


[[email protected] ~]# sed -i '$aInclude conf/extra/httpd-django.conf' /usr/local/apache2/conf/httpd.conf
[[email protected] ~]# sed -n '$p' /usr/local/apache2/conf/httpd.conf
Include conf/extra/httpd-django.conf
[[email protected] ~]#

测试配置文件并重新加载:


[[email protected] ~]# apachectl configtest
Syntax OK
[[email protected] ~]#
[[email protected] ~]# apachectl graceful
[[email protected] ~]#
[[email protected] ~]# apachectl -M | grep wsgi
 wsgi_module (shared)
[[email protected] ~]#
[[email protected] ~]# ss -atn | grep :80
LISTEN     0      10           *:8000                     *:*
LISTEN     0      128         :::80                      :::*
[[email protected] ~]#

浏览器访问http://192.168.9.129/与http://192.168.9.129/admin,若页面显示与步骤3.2.3中相同,则说明内嵌模式配置成功。

查看访问日志:


[[email protected] ~]# tail -f /usr/local/apache2/logs/access_log
192.168.9.1 - - [17/Jan/2019:15:58:48 +0000] "GET /admin HTTP/1.1" 301 -
192.168.9.1 - - [17/Jan/2019:15:58:48 +0000] "GET /admin/ HTTP/1.1" 302 -
192.168.9.1 - - [17/Jan/2019:15:58:48 +0000] "GET /admin/login/?next=/admin/ HTTP/1.1" 200 1819
192.168.9.1 - - [17/Jan/2019:15:58:48 +0000] "GET /static/admin/css/base.css HTTP/1.1" 200 16225
192.168.9.1 - - [17/Jan/2019:15:58:48 +0000] "GET /static/admin/css/login.css HTTP/1.1" 200 1203
192.168.9.1 - - [17/Jan/2019:15:58:48 +0000] "GET /static/admin/css/responsive.css HTTP/1.1" 200 17976
192.168.9.1 - - [17/Jan/2019:15:58:48 +0000] "GET /static/admin/css/fonts.css HTTP/1.1" 200 423
192.168.9.1 - - [17/Jan/2019:15:58:48 +0000] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 200 81348
192.168.9.1 - - [17/Jan/2019:15:58:48 +0000] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 200 80304
192.168.9.1 - - [17/Jan/2019:15:58:48 +0000] "GET /favicon.ico HTTP/1.1" 404 1970
192.168.9.1 - - [17/Jan/2019:16:00:20 +0000] "GET / HTTP/1.1" 200 16348
192.168.9.1 - - [17/Jan/2019:16:00:20 +0000] "GET /static/admin/fonts/Roboto-Bold-webfont.woff HTTP/1.1" 200 82564

3.3.2.2 - 守护进程模式

守护进程模式的基本配置包括:

  • 在server或virtual host上下文中,以LoadModule指令加载mod_wsgi模块。
  • 在server或virtual host/directory上下文中,以WSGIProcessGroup指令指定wsgi应用所属的进程组名称。
  • 在server或virtual host上下文中,以WSGIDaemonProcess指令指定守护进程的名称,以及项目包所在的本地路径名(非项目包自身的路径名)。
  • 在server或virtual host上下文中,以WSGIScriptAlias指定web url路径对应的本地文件名。

<code class="language-apache">[[email protected] ~]# cp -a /usr/local/apache2/conf/extra/httpd-django.conf /usr/local/apache2/conf/extra/httpd-django-embedded.conf
[[email protected] ~]#
[[email protected] ~]# vi /usr/local/apache2/conf/extra/httpd-django.conf
&lt;VirtualHost *:80&gt;
    ServerName localhost:80
    DocumentRoot /opt/webs/documents
    LoadModule wsgi_module modules/mod_wsgi.so

    WSGIProcessGroup t_app
    WSGIDaemonProcess t_app python-path=/opt/webs/t_django
    WSGIScriptAlias / /opt/webs/t_django/t_app/wsgi.py process-group=t_app

    Alias /static/ /opt/webs/t_django/static/
    &lt;Directory /opt/webs/t_django&gt;
        Require all granted
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;

[[email protected] ~]#
[[email protected] ~]# apachectl configtest
Syntax OK
[[email protected] ~]# apachectl
[[email protected] ~]# ps aux | grep http | grep -v grep
root      60941  0.0  0.2  85860  2820 ?        Ss   01:02   0:00 /usr/local/apache2/bin/httpd
www       60942  0.2  0.7 464672  7008 ?        Sl   01:02   0:00 /usr/local/apache2/bin/httpd
www       60943  0.2  0.9 483100  9148 ?        Sl   01:02   0:00 /usr/local/apache2/bin/httpd
www       60944  0.2  0.9 483100  9148 ?        Sl   01:02   0:00 /usr/local/apache2/bin/httpd
www       60945  0.1  0.9 483100  9144 ?        Sl   01:02   0:00 /usr/local/apache2/bin/httpd
[[email protected] ~]# pstree -a 60941
httpd
  ├─httpd
  │   └─17*[{httpd}]
  ├─httpd
  │   └─26*[{httpd}]
  ├─httpd
  │   └─26*[{httpd}]
  └─httpd
      └─26*[{httpd}]

apache根据多进程处理模块(MPM, Multi-Processing Module)中指定的工作模式(prefork/worker/event)分配资源以处理请求,一般会预先分配若干进程或进程+线程,具体依赖于特定的模式以及相应的参数配置。根据tree命令与ps命令的输出可以看到,apache主进程60941以root启动后,预分配了4个以www运行的子进程(60942/60943/60944/60945),每一个子进程又各自包含若干线程。


[[email protected] ~]# apachectl -V
Server version: Apache/2.4.37 (Unix)
Server built:   Jan 17 2019 22:35:36
Server's Module Magic Number: 20120211:83
Server loaded:  APR 1.6.5, APR-UTIL 1.6.1
Compiled using: APR 1.6.5, APR-UTIL 1.6.1
Architecture:   64-bit
Server MPM:     event
  threaded:     yes (fixed thread count)
    forked:     yes (variable process count)
Server compiled with....
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_SYSVSEM_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=256
 -D HTTPD_ROOT="/usr/local/apache2"
 -D SUEXEC_BIN="/usr/local/apache2/bin/suexec"
 -D DEFAULT_PIDLOG="logs/httpd.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="conf/mime.types"
 -D SERVER_CONFIG_FILE="conf/httpd.conf"

从apachectl -V命令的输出可以看出,当前使用的event MPM,且为多进程+多线程方式;但此时无法区分mod_wsgi与apache的其他子进程;WSGIDaemonProcess指令的display-name选项为进程组指定名称,通过自定义字符串加以区分,若指定为display-name=%{GROUP},则mod_wsgi进程将显示为(wsgi:进程组名称)。

添加display-name:


[[email protected] ~]# vi /usr/local/apache2/conf/extra/httpd-django.conf
&lt;VirtualHost *:80&gt;
    ServerName localhost:80
    DocumentRoot /opt/webs/documents
    LoadModule wsgi_module modules/mod_wsgi.so

    WSGIProcessGroup t_app
    WSGIDaemonProcess t_app python-path=/opt/webs/t_django display-name=%{GROUP}
    WSGIScriptAlias / /opt/webs/t_django/t_app/wsgi.py process-group=t_app

    Alias /static/ /opt/webs/t_django/static/
    &lt;Directory /opt/webs/t_django&gt;
        Require all granted
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;
[[email protected] ~]#
[[email protected] ~]# apachectl configtest
Syntax OK
[[email protected] ~]# apachectl graceful
[[email protected] ~]#
[[email protected] ~]# ps aux | grep http | grep -v grep
root      60941  0.0  0.3  85860  3864 ?        Ss   01:02   0:00 /usr/local/apache2/bin/httpd
www       61089  0.0  0.9 483100  9148 ?        Sl   01:18   0:00 /usr/local/apache2/bin/httpd
www       61090  0.0  0.9 483100  9144 ?        Sl   01:18   0:00 /usr/local/apache2/bin/httpd
www       61091  0.0  0.9 483100  9148 ?        Sl   01:18   0:00 /usr/local/apache2/bin/httpd
[[email protected] ~]#
[[email protected] ~]# pstree -a 60941
httpd
  ├─httpd
  │   └─17*[{httpd}]
  ├─httpd
  │   └─26*[{httpd}]
  ├─httpd
  │   └─26*[{httpd}]
  └─httpd
      └─26*[{httpd}]
[[email protected] ~]# ps aux | grep t_app
www       61088  0.0  0.7 464656  7020 ?        Sl   01:18   0:00 (wsgi:t_app)
root      61195  0.0  0.0 112708   976 pts/0    S+   01:19   0:00 grep --color=auto t_app
[[email protected] ~]#
[[email protected] ~]# ps -p 61088 -o pid,ppid,args
   PID   PPID COMMAND
 61088  60941 (wsgi:t_app)
[[email protected] ~]#

从以上输出可以看出,ps aux | grep http仅显示了4个进程,包括一个以root用户启动的主进程以及3个以www用户运行的子进程;另一个预先分配的子进程被wsgi进程占据,仍作为httpd的子进程,但显示为(wsgi:t_app)。

守护进程模式的wsgi通过unix域套接字的方式与apache主进程通信;unix域套接字为本地文件,对于Apache + mod_wsgi,该文件一般位于apache的logs目录下,可以在配置文件的server上下文中,通过WSGISocketPrefix指令指定为其他目录;mod_wsgi官方文档中建议该指令指定的目录应仅对root用户或apache的运行时用户可写。


[[email protected] ~]# ll /usr/local/apache2/logs/
total 12
-rw-r--r-- 1 root root  655 Jan 17 22:41 access_log
-rw-r--r-- 1 root root 2112 Jan 18 01:18 error_log
-rw-r--r-- 1 root root    6 Jan 18 01:18 httpd.pid
srwx------ 1 www  root    0 Jan 18 01:18 wsgi.60941.1.1.sock
[[email protected] ~]#
[[email protected] ~]# file /usr/local/apache2/logs/wsgi.60941.1.1.sock
/usr/local/apache2/logs/wsgi.60941.1.1.sock: socket

Apache + mod_wsgi基本配置完成,更多参数配置可查看官方文档。

3.4 - web前端(Nginx)

Nginx + uwsgi作为django前端时,Nginx起到的作用更多仍在于反向代理服务器,在逻辑架构上可以与具体的后端应用相分离,而uwsgi作为实现了wsgi协议的web服务器则以独立的进程运行。Nginx自0.8.40版本开始提供对uwsgi的原生支持,uwsgi自身可以指定通过本地unix域套接字或网络套接字与Nginx进行通信,且支持丰富的配置参数,整个服务框架相较于Apache + mod_wsgi的部署方式更加复杂与灵活。


[[email protected] ~]# ip addr show eth0 | sed -n '/inet /p' | awk '{print $2}'
192.168.9.129/24
[[email protected] ~]#

安装依赖包:


[[email protected] ~]# yum -y install gcc make openssl-devel pcre-devel

下载nginx(本例使用tengine)与uwsgi源码包:


[[email protected] ~]# wget &gt; https://projects.unbit.it/downloads/uwsgi-2.0.17.1.tar.gz &gt; http://tengine.taobao.org/download/tengine-2.2.3.tar.gz -P /usr/local/src/

解压nginx源码包并进入源码目录,执行安装:

源码安装nginx的默认目标路径为/usr/local,可以通过configure命令的--prefix选项显式指定;--with-http_uwsgi_module=shared选项指定将uwsgi模块以共享库的方式安装。


[[email protected] ~]# cd /usr/local/src/
[[email protected] src]# ll
total 44084
drwxr-xr-x 28 1001  1001     4096 Jan 17 13:51 apr-1.6.5
-rw-r--r--  1 root root   1073556 Sep 14 04:07 apr-1.6.5.tar.gz
drwxr-xr-x 21 1001  1001     4096 Jan 17 13:53 apr-util-1.6.1
-rw-r--r--  1 root root    554301 Oct 22  2017 apr-util-1.6.1.tar.gz
drwxr-xr-x 12 www  www       4096 Jan 16 17:31 Django-2.1.5
-rw-r--r--  1 root root   8612384 Jan  4 13:47 Django-2.1.5.tar.gz
drwxr-sr-x 12 root    40     4096 Jan 17 13:57 httpd-2.4.37
-rw-r--r--  1 root root   9177278 Oct 22 14:13 httpd-2.4.37.tar.gz
drwxr-xr-x  8  501 games      325 Jan 17 14:02 mod_wsgi-4.6.5
-rw-r--r--  1 root root    490018 Oct 22 04:03 mod_wsgi-4.6.5.tar.gz
drwxrwxr-x  9 www  www       4096 Jan 16 17:16 mysqlclient-1.3.14
-rw-r--r--  1 root root     91391 Dec  4 10:06 mysqlclient-1.3.14.tar.gz
drwxr-xr-x 19  501   501     4096 Jan 16 17:05 Python-3.7.2
-rw-r--r--  1 root root  22897802 Dec 24 03:42 Python-3.7.2.tgz
-rw-r--r--  1 root root   2203079 Jan  3 04:17 tengine-2.2.3.tar.gz
-rw-r--r--  1 root root    800156 Jul  8  2018 uwsgi-2.0.17.1.tar.gz
[[email protected] src]#
[[email protected] src]# tar axf tengine-2.2.3.tar.gz
[[email protected] src]# cd tengine-2.2.3/
[[email protected] tengine-2.2.3]#
[[email protected] tengine-2.2.3]# ./configure &gt; --with-http_uwsgi_module=shared &gt; --with-http_gzip_static_module &gt; --with-http_concat_module &gt; --with-http_stub_status_module &gt; --with-http_ssl_module &gt; --with-pcre &gt; --with-backtrace_module &gt; --with-http_upstream_check_module &gt; --with-http_sysguard_module &gt; --with-http_slice_module &gt; --with-http_upstream_ip_hash_module=shared &gt; --with-http_upstream_session_sticky_module=shared &gt; --with-http_upstream_least_conn_module=shared &amp;&amp; &gt; make -j &amp;&amp; make install

将nginx可执行文件的路径(本例为/usr/local/nginx/sbin)添加至系统的查找路径列表PATH中:


[[email protected] tengine-2.2.3]# ls /usr/local/nginx/
conf  html  include  logs  modules  sbin
[[email protected] tengine-2.2.3]# ls /usr/local/nginx/sbin/nginx
/usr/local/nginx/sbin/nginx
[[email protected] tengine-2.2.3]# which nginx
/usr/bin/which: no nginx in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin:/usr/local/apache2/bin)
[[email protected] tengine-2.2.3]#
[[email protected] tengine-2.2.3]# sed -i '/^PATH/ s|$|:/usr/local/nginx/sbin|' ~/.bash_profile
[[email protected] tengine-2.2.3]# source ~/.bash_profile
[[email protected] tengine-2.2.3]# which nginx
/usr/local/nginx/sbin/nginx
[[email protected] tengine-2.2.3]# nginx -v
Tengine version: Tengine/2.2.3 (nginx/1.8.1)
[[email protected] tengine-2.2.3]#

解压uwsgi源码包并进入源码目录,执行安装:


[[email protected] tengine-2.2.3]# cd ..
[[email protected] src]# tar axf uwsgi-2.0.17.1.tar.gz
[[email protected] src]# cd uwsgi-2.0.17.1/
[[email protected] uwsgi-2.0.17.1]# python3 setup.py install

安装成功后将生成uwsgi管理程序/usr/local/bin/uwsgi:


[[email protected] uwsgi-2.0.17.1]# which uwsgi
/usr/local/bin/uwsgi
[[email protected] uwsgi-2.0.17.1]#
[[email protected] uwsgi-2.0.17.1]# uwsgi --version
2.0.17.1
[[email protected] uwsgi-2.0.17.1]#

以tcp套接字的方式启动uwsgi:

--socket 127.0.0.1:8000 以tcp套接字的127.0.0.1:8000启动

--uid/--gid 指定运行时的用户与组

--master 守护进程方式运行

--pidfile 运行时的pid文件

--daemonize 守护进程方式的日志输出文件

--chdir 项目包所在的目录

--module 项目包:入口文件:方法


[[email protected] ~]# uwsgi &gt; --socket 127.0.0.1:8000 &gt; --uid=www --gid=www &gt; --master &gt; --pidfile=/opt/webs/t_django/t_app.pid &gt; --daemonize=/opt/webs/t_django/t_app.log &gt; --chdir=/opt/webs/t_django &gt; --module=t_app.wsgi:application &gt; --processes=5 &gt; --harakiri=20 &gt; --max-requests=500 &gt; --vacuum
[[email protected] ~]#
[[email protected] ~]# ss -atn | grep 8000
LISTEN     0      100    127.0.0.1:8000                     *:*
[[email protected] ~]#

查看日志:


[[email protected] ~]# tail -f /opt/webs/t_django/t_app.log
mapped 437520 bytes (427 KB) for 5 cores
*** Operational MODE: preforking ***
WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0xd8e280 pid: 65614 (default app)
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 65614)
spawned uWSGI worker 1 (pid: 65615, cores: 1)
spawned uWSGI worker 2 (pid: 65616, cores: 1)
spawned uWSGI worker 3 (pid: 65617, cores: 1)
spawned uWSGI worker 4 (pid: 65618, cores: 1)
spawned uWSGI worker 5 (pid: 65619, cores: 1)

设置nginx将wsgi请求转发至127.0.0.1:8000

  • 为django项目创建单独的配置文件:

Nginx转发wsgi协议需通过wsgi_pass指令指定后端服务,且在location块中包含定义了uwsgi相关参数的uwsgi_params文件。


[[email protected] ~]# mkdir -p /usr/local/nginx/conf/vhosts
[[email protected] ~]# vi /usr/local/nginx/conf/vhosts/upstream.conf
upstream t_django {
    server 127.0.0.1:8000;
}

[[email protected] ~]# vi /usr/local/nginx/conf/vhosts/t_django.conf
server {
    listen 80;
    server_name 192.168.9.129;
    charset utf-8;
    client_max_body_size 75M;

    location /static {
        alias /opt/webs/t_django/static;
    }

    location / {
        include /usr/local/nginx/conf/uwsgi_params;
        uwsgi_pass t_django;
        uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto;
    }

    access_log logs/t_django-access.log main;
}
  • 在nginx主配置文件(本例中为/usr/local/nginx/conf/nginx.conf)中设置全局参数:

转发wsgi需要加载ngx_http_uwsgi_module.so模块,且在http上下文中包含以上两个配置文件。


[[email protected] ~]# vi /usr/local/nginx/conf/nginx.conf
user www www;
worker_processes auto;
pid /usr/local/nginx/logs/nginx.pid;
error_log /usr/local/nginx/logs/error.log warn;

#Specifies the value for maximum file descriptors that can be opened by this process.
worker_rlimit_nofile 51200;

events {
    use epoll;
    worker_connections 4096;
}

dso {
    load ngx_http_uwsgi_module.so;
    load ngx_http_upstream_ip_hash_module.so;
    load ngx_http_upstream_least_conn_module.so;      
    load ngx_http_upstream_session_sticky_module.so;
}

http {
    include mime.types;
    default_type application/octet-stream;
    server_names_hash_bucket_size 128;
    client_header_buffer_size 16k;
    large_client_header_buffers 4 32k;
    client_max_body_size 8m;
    access_log off;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 30;
    proxy_cache_methods POST GET HEAD;
    open_file_cache max=655350 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;

    gzip on;
    gzip_min_length  1k;
    gzip_buffers     8 8k;
    gzip_http_version 1.0;
    gzip_comp_level 4;
    gzip_types       text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php;
    gzip_vary on;
    server_tokens off;

    log_format main '$remote_addr	$upstream_addr	[$time_local]	$request	'
            '$status	$body_bytes_sent	$http_user_agent	$http_referer	'
                        '$http_x_forwarded_for	$request_time	$upstream_response_time	$remote_user	'
            '$request_body';

    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    include /usr/local/nginx/conf/vhosts/upstream.conf;
        include /usr/local/nginx/conf/vhosts/t_django.conf;
}

配置文件语法测试无误后,启动nginx:


[[email protected] ~]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[[email protected] ~]#
[[email protected] ~]# nginx
[[email protected] ~]#
[[email protected] ~]# ss -atn | grep 80
LISTEN     0      128          *:80                       *:*

浏览器访问http://192.168.9.129/与http://192.168.9.129/admin,若配置正确则应显示步骤3.2.3中的页面。

查看日志:


[[email protected] ~]# tail -f /usr/local/nginx/logs/t_django-access.log
192.168.9.1 127.0.0.1:8000  [18/Jan/2019:03:13:36 +0000]    GET / HTTP/1.1  200 4191    Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0  -   -0.005  0.005   -   -
192.168.9.1 -   [18/Jan/2019:03:13:36 +0000]    GET /static/admin/css/fonts.css HTTP/1.1    200 423 Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0http://192.168.9.129/ -   0.000   -   -   -
192.168.9.1 -   [18/Jan/2019:03:13:36 +0000]    GET /static/admin/fonts/Roboto-Bold-webfont.woff HTTP/1.1   200 82564   Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0  http://192.168.9.129/static/admin/css/fonts.css -   0.000   -   --
192.168.9.1 -   [18/Jan/2019:03:13:36 +0000]    GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1    200 80304   Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0  http://192.168.9.129/static/admin/css/fonts.css -   0.000   ---
192.168.9.1 -   [18/Jan/2019:03:13:36 +0000]    GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1  200 81348   Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0  http://192.168.9.129/static/admin/css/fonts.css -   0.000   ---
192.168.9.1 127.0.0.1:8000  [18/Jan/2019:03:13:40 +0000]    GET /admin/ HTTP/1.1    302 0Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0 -   -0.002  0.002   -   -
192.168.9.1 127.0.0.1:8000  [18/Jan/2019:03:13:40 +0000]    GET /admin/login/?next=/admin/ HTTP/1.1 200 781 Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0  -   -   0.010   0.010   -   -
192.168.9.1 -   [18/Jan/2019:03:13:40 +0000]    GET /static/admin/css/responsive.css HTTP/1.1   200 3580    Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0  http://192.168.9.129/admin/login/?next=/admin/  -   0.000   -   -   -
192.168.9.1 -   [18/Jan/2019:03:13:40 +0000]    GET /static/admin/css/base.css HTTP/1.1 200 3949    Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0http://192.168.9.129/admin/login/?next=/admin/    -   0.000   -   -   -
192.168.9.1 -   [18/Jan/2019:03:13:40 +0000]    GET /static/admin/css/login.css HTTP/1.1    200 502 Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0http://192.168.9.129/admin/login/?next=/admin/    -   0.000   -   -   -

停止绑定至tcp 8000端口的uwsgi,而以unix域套接字的方式启动:


[[email protected] ~]# uwsgi --stop /opt/webs/t_django/t_app.pid
[[email protected] ~]# uwsgi &gt; --socket /tmp/t_app.sock &gt; --uid=www --gid=www &gt; --master &gt; --pidfile=/opt/webs/t_django/t_app.pid &gt; --daemonize=/opt/webs/t_django/t_app.log &gt; --chdir=/opt/webs/t_django &gt; --module=t_app.wsgi:application &gt; --processes=5 &gt; --harakiri=20 &gt; --max-requests=500 &gt; --vacuum
[[email protected] ~]# tail -f /opt/webs/t_django/t_app.log
uwsgi socket 0 bound to UNIX address /tmp/t_app.sock fd 3
Python version: 3.7.2 (default, Jan 17 2019, 22:21:08)  [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
*** Python threads support is disabled. You can enable it with --enable-threads ***
Python main interpreter initialized at 0xccdda0
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 437520 bytes (427 KB) for 5 cores
*** Operational MODE: preforking ***
WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0xccdda0 pid: 69293 (default app)
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 69293)
spawned uWSGI worker 1 (pid: 69294, cores: 1)
spawned uWSGI worker 2 (pid: 69295, cores: 1)
spawned uWSGI worker 3 (pid: 69296, cores: 1)
spawned uWSGI worker 4 (pid: 69297, cores: 1)
spawned uWSGI worker 5 (pid: 69298, cores: 1)

本地unix域套接字已被创建:


[[email protected] ~]# ll /tmp/t_app.sock
srwxrwxrwx 1 www www 0 Jan 18 03:25 /tmp/t_app.sock
[[email protected] ~]#

将nginx的upstream由127.0.0.1:8000改为unix:/tmp/t_app.sock并重新加载nginx配置文件:


[[email protected] ~]# sed -n '/server/p' /usr/local/nginx/conf/vhosts/upstream.conf
    server 127.0.0.1:8000;
[[email protected] ~]#
[[email protected] ~]# sed -i '/server/ s|127.0.0.1:8000|unix:/tmp/t_app.sock|' /usr/local/nginx/conf/vhosts/upstream.conf
[[email protected] ~]#
[[email protected] ~]# sed -n '/server/p' /usr/local/nginx/conf/vhosts/upstream.conf
    server unix:/tmp/t_app.sock;
[[email protected] ~]#
[[email protected] ~]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[[email protected] ~]#
[[email protected] ~]# nginx -s reload
[[email protected] ~]#

浏览器再次访问http://192.168.9.129/与http://192.168.9.129/admin,并查看日志:


[[email protected] ~]# tail -f /usr/local/nginx/logs/t_django-access.log
192.168.9.1 unix:/tmp/t_app.sock    [18/Jan/2019:03:33:44 +0000]    GET / HTTP/1.1  200 4191    Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0  --0.038 0.038   -   -
192.168.9.1 -   [18/Jan/2019:03:33:44 +0000]    GET /static/admin/css/fonts.css HTTP/1.1    304 0   Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0http://192.168.9.129/ -   0.000   -   -   -
192.168.9.1 unix:/tmp/t_app.sock    [18/Jan/2019:03:33:49 +0000]    GET /admin/ HTTP/1.1    302 0   Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0- -   0.013   0.013   -   -
192.168.9.1 unix:/tmp/t_app.sock    [18/Jan/2019:03:33:49 +0000]    GET /admin/login/?next=/admin/ HTTP/1.1 200 781 Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0  -   -   0.080   0.080   -   -

Nginx + uwsgi基本配置完成,更多参数配置可查看官方文档。

4. 自动化安装脚本

以下脚本将上述步骤简单封装为脚本,分为两个文件:

  • django-environment.sh为函数定义,实现了python3/django/apache/nginx/mysql的安装,以及apache mod_wsgi的守护进程模式。
  • django-setup.sh以单实例运行的方式调用django-environment.sh文件中定义的函数。

用法:将两个文件放在同一目录下,根据需要实现的组件安装与配置,在django-setup.sh中添加相应的函数调用。建议以nohup后台的方式执行,并将输出重定向至指定的日志文件,可以避免因终端关闭导致的运行意外终止情况,且可以通过刷新日志文件查看运行过程与结果,例如:

nohup ./django-setup.sh &> /tmp/django-apache-daemon.log &

django-environment.sh脚本文件:


#! /bin/bash

# 函数列表
#install_common_dependencies
#install_python3_dependencies
#install_mysqlclient_dependencies
#install_apache_dependencies
#install_nginx_dependencies
#install_mysql_dependencies
#make_source
#path_not_exists
#install_python3
#install_mysqlclient
#install_apache
#install_nginx
#install_uwsgi
#install_django
#django_basic_setup
#apache_djange_daemon_mode
#install_setup_mysql

readonly SOURCE_DIR="/usr/local/src"
readonly USER_PROFILE="${HOME}/.bash_profile"
readonly DEFAULT_LIB_CONF="/etc/ld.so.conf"
readonly DJANGO_PROJECT_DIR="/opt/webs/t_django"
readonly DJANGO_APP="t_app"
readonly WEB_USER="www"
readonly WEB_GROUP="www"
readonly WEB_USER_SHELL="/sbin/nologin"

install_common_dependencies() {
    yum -y install gcc make
}

install_python3_dependencies() {
    yum -y install libffi-devel readline-devel zlib-devel openssl-devel
}

install_mysqlclient_dependencies() {
    yum -y install mysql-devel
}

install_apache_dependencies() {
    yum -y install expat-devel pcre-devel
}

install_nginx_dependencies() {
    yum -y install openssl-devel pcre-devel
}

install_mysql_dependencies() {
    yum -y install gcc-c++ cmake ncurses-devel openssl-devel
}

#多核编译
make_source() {
    local CPU_CORES=$(awk '/^processor/{print $3}' /proc/cpuinfo | wc -l)
    local OPTIONS="[email protected]"
    make -j ${CPU_CORES} ${OPTIONS}
    make install
}

# 检查第1个参数指定的路径是否位于系统环境变量PATH中,若不存在则返回0,反之则返回1
path_not_exists() {
    [[ ${1} != "" &amp;&amp; ${1} != " " ]] &amp;&amp; local PATH_ELEM=${1} || return 0
    local OLD_IFS="${IFS}"
    local IFS=":"
    local PATH_LIST=($PATH)
    IFS=${OLD_IFS}

    for i in ${PATH_LIST[@]}; do
        [[ ${i} == ${PATH_ELEM} ]] &amp;&amp; return 1
    done

    return 0
}

# 安装python3解析器
install_python3() {
    local PYTHON_BIN="/usr/local/bin/python3"
    [[ -x ${PYTHON_BIN} ]] &amp;&amp;         { echo "python3已存在,请检查当前版本"; return 0; }

    echo "开始安装python3解析器"
    local PYTHON_BIN_DIR="$(dirname ${PYTHON_BIN})"
    local PYTHON_LIB_CONF="/etc/ld.so.conf.d/python-v3.7.2.conf"
    local PYTHON_LINK="/usr/bin/python3"

    install_python3_dependencies

    # python3安装的目标路径为--prefix选项显式指定的目录,或默认的/usrl/local/bin目录
    # 若使用默认路径,则与之相关的共享库位于/usr/local/lib目录
    cd ${SOURCE_DIR}
    tar xaf Python-3.7.2.tgz
    cd Python-3.7.2/
    ./configure --enable-shared --enable-optimizations
    make_source -Wno-error=coverage-mismatch
    echo "python3解析器安装成功"

    # 将python3的共享库目录/usr/local/lib添加到系统共享库查找路径列表的配置文件中
    [[ ! -f ${PYTHON_LIB_CONF} ]] &amp;&amp;         echo '/usr/local/lib' &gt; ${PYTHON_LIB_CONF} ||         echo '/usr/local/lib' &gt;&gt; ${DEFAULT_LIB_CONF}
    ldconfig

    # 若python3可执行文件的路径不在PATH中,则添加
    path_not_exists "${PYTHON_BIN_DIR}" &amp;&amp;         sed -i "/^PATH/s|$|:${PYTHON_BIN_DIR}|" ${USER_PROFILE}; source ${USER_PROFILE}

    # 创建python3可执行文件的符号链接
    [[ ! -L ${PYTHON_LINK} ]] &amp;&amp;         ln -s ${PYTHON_BIN} ${PYTHON_LINK}
}

install_mysqlclient() {
    python3 -c "import MySQLdb; print(MySQLdb.version_info)" &amp;&gt; /dev/null &amp;&amp;         { echo "mysqlclient已存在,请检查当前版本"; return 0; }

    install_mysqlclient_dependencies
    
    cd ${SOURCE_DIR}
    tar xaf mysqlclient-1.3.14.tar.gz
    cd mysqlclient-1.3.14/
    python3 setup.py install
    echo "mysqlclient安装成功"
}

# 安装apache web环境,包括apr,apr-util,apache httpd,mod_wsgi模块
install_apache() {
    local APACHE_BIN="/usr/local/apache2/bin/httpd"

    # apr,apache安装的目标路径为--prefix选项显式指定的目录,或默认的/usrl/local/apr与/usrl/local/apache2目录
    # 对于apache,若使用默认目标路径,则可执行文件的目录为/usrl/local/apache2/bin
    if [[ ! -x ${APACHE_BIN} ]]; then 
        install_apache_dependencies

        local APR_BIN="/usr/local/apr/bin/apr-1-config"
        if [[ ! -x ${APR_BIN} ]]; then
            echo "开始安装apr"
            cd ${SOURCE_DIR}
            tar xaf apr-1.6.5.tar.gz
            cd apr-1.6.5/
            ./configure
            make_source
            echo "apr安装成功"
        else
            echo "apr已存在,请检查当前版本"
        fi

        local APU_BIN="/usr/local/apr/bin/apu-1-config"
        if [[ ! -x ${APU_BIN} ]]; then
            echo "开始安装apr-util"
            cd ${SOURCE_DIR}
            tar xaf apr-util-1.6.1.tar.gz
            cd apr-util-1.6.1/
            ./configure --with-apr=/usr/local/apr
            make_source
            echo "apr-util安装成功"
        else
            echo "apr-util已存在,请检查当前版本"
        fi

        echo "开始安装apache"
        cd ${SOURCE_DIR}
        tar xaf httpd-2.4.37.tar.gz
        cd httpd-2.4.37/

        ./configure             --with-apr=/usr/local/apr             --with-apr-util=/usr/local/apr             --enable-mpms-shared=all
        make_source
        echo "apache安装成功"

        local APACHE_BIN_DIR="$(dirname ${APACHE_BIN})"

        # 若apache可执行文件的目录不在PATH中,则添加
        path_not_exists "${APACHE_BIN_DIR}" &amp;&amp;             sed -i "/^PATH/s|$|:${APACHE_BIN_DIR}|" ${USER_PROFILE}; source ${USER_PROFILE}
    else
        echo "apache已存在,请检查当前版本"
    fi

    local APACHE_WSGI="/usr/local/apache2/modules/mod_wsgi.so"
    if [[ ! -e ${APACHE_WSGI} ]]; then
        echo "开始安装mod_wsgi模块"
        cd ${SOURCE_DIR}
        tar xaf mod_wsgi-4.6.5.tar.gz
        cd mod_wsgi-4.6.5/
        ./configure         --with-apxs=/usr/local/apache2/bin/apxs         --with-python=/usr/local/bin/python3
        make_source
        echo "mod_wsgi模块安装成功"
    else
        echo "mod_wsgi模块已存在,请检查当前版本"
    fi
}

install_nginx() {
    local NGINX_BIN="/usr/local/nginx/sbin/nginx"

    [[ -x ${NGINX_BIN} ]] &amp;&amp;         { echo "nginx已存在,请检查当前版本"; return 0; }
    
    local NGINX_CONFIG_OPTS="--with-http_uwsgi_module=shared                              --with-http_gzip_static_module                              --with-http_concat_module                              --with-http_stub_status_module                              --with-http_ssl_module                              --with-pcre                              --with-backtrace_module                              --with-http_upstream_check_module                              --with-http_sysguard_module                              --with-http_slice_module                              --with-http_upstream_ip_hash_module=shared                              --with-http_upstream_session_sticky_module=shared                              --with-http_upstream_least_conn_module=shared"
    
    install_nginx_dependencies

    local NGINX_BIN_DIR="$(dirname ${NGINX_BIN})"

    # nginx安装的目标路径为--prefix选项显式指定的目录,或默认的/usrl/local/nginx目录
    # 若使用默认目标路径,则可执行文件的目录为/usrl/local/nginx/sbin
    echo "开始安装nginx"
    cd ${SOURCE_DIR}
    tar xaf tengine-2.2.3.tar.gz
    cd tengine-2.2.3/
    ./configure ${NGINX_CONFIG_OPTS}
    make_source
    echo "nginx安装成功"

    # 若nginx可执行文件的目录不在系统的PATH中,则添加
    path_not_exists "${NGINX_BIN_DIR}" &amp;&amp;         sed -i "/^PATH/s|$|:${NGINX_BIN_DIR}|" ${USER_PROFILE}; source ${USER_PROFILE}
}

install_uwsgi() {
    local UWSGI_BIN="/usr/local/bin/uwsgi"
    [[ -e ${UWSGI_BIN} ]] &amp;&amp;         { echo "uwsgi已存在,请检查当前版本"; return 0; }

    local UWSGI_BIN_DIR="$(dirname ${UWSGI_BIN})"
    echo "开始安装uwsgi"
    cd ${SOURCE_DIR}
    tar xaf uwsgi-2.0.17.1.tar.gz
    cd uwsgi-2.0.17.1/
    python3 setup.py install
    echo "uwsgi安装成功"

    # 若uwsgi可执行文件的目录不在系统PATH中,则添加
    path_not_exists "${UWSGI_BIN_DIR}" &amp;&amp;         sed -i "/^PATH/s|$|:${UWSGI_BIN_DIR}|" ${USER_PROFILE}; source ${USER_PROFILE}
}

install_django() {
    python3 -m django --version &amp;&gt; /dev/null &amp;&amp;         { echo "django已存在,请检查当前版本"; return 0; }

    echo "开始安装django"
    cd ${SOURCE_DIR}
    tar xaf Django-2.1.5.tar.gz
    cd Django-2.1.5/
    python3 setup.py install
    echo "django安装成功"
}

# django项目基本配置
django_basic_setup() {
    local DJANGO_DB_NAME="t_django_db"
    local DJANGO_DB_USER="u_django"
    local DJANGO_DB_PASS="p_django"
    local DJANGO_DB_HOST="192.168.9.130"
    local DJANGO_DB_PORT="3306"

    [[ ! -d ${DJANGO_PROJECT_DIR} ]] &amp;&amp; mkdir -p ${DJANGO_PROJECT_DIR}

    [[ ! -d ${DJANGO_PROJECT_DIR}/${DJANGO_APP} ]] &amp;&amp;         django-admin startproject ${DJANGO_APP} ${DJANGO_PROJECT_DIR}

    cd ${DJANGO_PROJECT_DIR}
    cp -a ${DJANGO_APP}/settings.py{,.ori}
    sed -i '/^ALLOWED_HOSTS/ s|[]|['''*''']|' ${DJANGO_APP}/settings.py
    sed -i '/ENGINE/{n;d}' ${DJANGO_APP}/settings.py
    sed -i -e '/ENGINE/ s/sqlite3/mysql/' -e '/ENGINE/a        '''HOST''': ''''${DJANGO_DB_HOST}'''',
        '''PORT''': ''''${DJANGO_DB_PORT}'''',
        '''NAME''': ''''${DJANGO_DB_NAME}'''',
        '''USER''': ''''${DJANGO_DB_USER}'''',
        '''PASSWORD''': ''''${DJANGO_DB_PASS}'''',
        '''TIME_ZONE''': '''Asia/Shanghai''',
        '''USE_TZ''': False' ${DJANGO_APP}/settings.py
    sed -i '$aSTATIC_ROOT = os.path.join(BASE_DIR, '''static''')' ${DJANGO_APP}/settings.py
    python3 manage.py collectstatic
    python3 manage.py migrate
#   python3 manage.py runserver 0:8000 &amp;
}

# mod_wsgi守护进程模式
apache_djange_daemon_mode() {
    local APACHE_BASE_DIR="/usr/local/apache2"
    local APACHE_DOC_ROOT_DIR="/opt/webs/documents"
    local APACHE_BASE_CONF="conf/httpd.conf"
    local APACHE_DJANGO_CONF="conf/extra/httpd-django.conf"
    local TAB="$(echo -ne '	')"

    id -u ${WEB_USER} &amp;&gt; /dev/null ||         useradd ${WEB_USER} -N -s ${WEB_USER_SHELL}
    
    getent group ${WEB_GROUP} &amp;&gt; /dev/null ||         groupadd ${WEB_GROUP}
    
    id -gr ${WEB_USER} &amp;&gt; /dev/null ||         usermod -g ${WEB_GROUP} ${WEB_USER}

    [[ ! -d ${APACHE_DOC_ROOT_DIR} ]] &amp;&amp;         mkdir -p ${APACHE_DOC_ROOT_DIR}
    chmod +x ${APACHE_DOC_ROOT_DIR}

    cd ${APACHE_BASE_DIR}
    cp -a ${APACHE_BASE_CONF}{,.ori}
    sed -i 's/User daemon/User '${WEB_USER}'/' ${APACHE_BASE_CONF}
    sed -i 's/Group daemon/Group '${WEB_GROUP}'/' ${APACHE_BASE_CONF}
    sed -i '/^DocumentRoot/,/&lt;/Directory&gt;/ s/^/#/' ${APACHE_BASE_CONF}
    sed -i '$a\nServerName localhost:80' ${APACHE_BASE_CONF}
    sed -i '$aInclude '${APACHE_DJANGO_CONF}'' ${APACHE_BASE_CONF}

    [[ -f ${APACHE_DJANGO_CONF} ]] &amp;&amp;         cp -a ${APACHE_DJANGO_CONF}{,.ori}

    cat &gt; ${APACHE_DJANGO_CONF} &lt;&lt;-EOF
        WSGISocketPrefix ${DJANGO_PROJECT_DIR}/${DJANGO_APP}
        &lt;VirtualHost *:80&gt;
        ${TAB}ServerName localhost:80
        ${TAB}DocumentRoot ${APACHE_DOC_ROOT_DIR}
        ${TAB}LoadModule wsgi_module modules/mod_wsgi.so

        ${TAB}WSGIDaemonProcess ${DJANGO_APP} python-path=${DJANGO_PROJECT_DIR} display-name=%{GROUP}
        ${TAB}WSGIProcessGroup ${DJANGO_APP}
        ${TAB}WSGIScriptAlias / ${DJANGO_PROJECT_DIR}/${DJANGO_APP}/wsgi.py process-group=${DJANGO_APP}

        Alias /static/ ${DJANGO_PROJECT_DIR}/static/
        ${TAB}&lt;Directory ${DJANGO_PROJECT_DIR}&gt;
        ${TAB}${TAB}Require all granted
        ${TAB}&lt;/Directory&gt;
        &lt;/VirtualHost&gt;
    EOF
}

# mysql安装与基本配置
install_setup_mysql() {
    local MYSQL_BIN="/usr/local/mysql/bin/mysqld"

    [[ -x ${MYSQL_BIN} ]] &amp;&amp;         { echo "mysql已存在,请检查当前版本"; return 0; }

    local MYSQL_BASE_DIR="/usr/local/mysql"
    local MYSQL_BIN_DIR="${MYSQL_BASE_DIR}/bin"
    local MYSQL_DATA_DIR="${MYSQL_BASE_DIR}/data"
    local MYSQL_LOGS_DIR="${MYSQL_BASE_DIR}/logs"
    local MYSQL_ERR_LOG="${MYSQL_LOGS_DIR}/mysqld-err.log"
    local MYSQL_SLW_LOG="${MYSQL_LOGS_DIR}/mysqld-slw.log"
    local MYSQL_PID_FILE="${MYSQL_LOGS_DIR}/mysqld.pid"
    local MYSQL_LIB_DIR="${MYSQL_BASE_DIR}/lib"
    local MYSQL_LIB_CONF="/etc/ld.so.conf.d/mysql-v5.7.24.conf"
    local MYSQL_USER="mysql"
    local MYSQL_GROUP=${MYSQL_USER}
    local MYSQL_USER_SHELL="/sbin/nologin"
    local MYSQL_CMAKE_OPTS="-DCMAKE_INSTALL_PREFIX=${MYSQL_BASE_DIR}                             -DWITH_BOOST=boost                             -DWITH_INNOBASE_STORAGE_ENGINE=1                             -DWITH_PARTITION_STORAGE_ENGINE=1                             -DWITH_FEDERATED_STORAGE_ENGINE=1                             -DWITH_BLACKHOLE_STORAGE_ENGINE=1                             -DWITH_MYISAM_STORAGE_ENGINE=1                             -DENABLED_LOCAL_INFILE=1                             -DENABLE_DTRACE=0                             -DDEFAULT_CHARSET=utf8mb4                             -DDEFAULT_COLLATION=utf8mb4_general_ci                             -DWITH_SSL=yes                             -DWITH_EMBEDDED_SERVER=1"

    local DJANGO_DB_NAME="t_django_db"
    local DJANGO_DB_USER="u_django"
    local DJANGO_DB_PASS="p_django"
    local DB_ROOT_PASS="django"
    local DJANGO_CLIENT="192.168.9.%"

    install_mysql_dependencies

    # 若mysql用户不存在,则创建
    id -u ${MYSQL_USER} &amp;&gt; /dev/null ||         useradd ${MYSQL_USER} -N -s ${MYSQL_USER_SHELL}
    
    # 若mysql组不存在,则创建
    getent group ${MYSQL_GROUP} &amp;&gt; /dev/null ||         groupadd ${MYSQL_GROUP}
    
    # 若mysql用户不是mysql组的成员,则加入
    id -gr ${MYSQL_USER} &amp;&gt; /dev/null ||         usermod -g ${MYSQL_GROUP} ${MYSQL_USER}

    # mysql安装的目标路径为-DCMAKE_INSTALL_PREFIX选项显式指定的目录,或默认的/usrl/local/mysql目录
    # 若使用默认目标路径,则与之相关的共享库位于/usr/local/mysql/lib
    cd ${SOURCE_DIR}
    tar xaf mysql-boost-5.7.24.tar.gz
    cd mysql-5.7.24/
    cmake . ${MYSQL_CMAKE_OPTS}
    make_source

    # 创建mysql数据文件,错误日志文件,慢查询日志文件,pid文件,并设置所属的用户和组
    [[ ! -d ${MYSQL_DATA_DIR} ]] &amp;&amp; mkdir -p ${MYSQL_DATA_DIR}
    [[ ! -d ${MYSQL_LOGS_DIR} ]] &amp;&amp; mkdir -p ${MYSQL_LOGS_DIR}
    [[ ! -f ${MYSQL_ERR_LOG} ]] &amp;&amp; touch ${MYSQL_ERR_LOG}
    [[ ! -f ${MYSQL_SLW_LOG} ]] &amp;&amp; touch ${MYSQL_SLW_LOG}
    [[ ! -f ${MYSQL_PID_FILE} ]] &amp;&amp; touch ${MYSQL_PID_FILE}
    chown -R ${MYSQL_USER}:${MYSQL_GROUP} ${MYSQL_BASE_DIR}

    # 若mysql可执行文件的路径不在PATH中,则添加
    path_not_exists "${MYSQL_BIN_DIR}" &amp;&amp;         sed -i "/^PATH/s|$|:${MYSQL_BIN_DIR}|" ${USER_PROFILE}; source ${USER_PROFILE}

    mysqld --initialize-insecure --user=${MYSQL_USER} --basedir=${MYSQL_BASE_DIR} --datadir=${MYSQL_DATA_DIR}

    mv /etc/my.cnf{,.ori}
    cat &gt; /etc/my.cnf &lt;&lt;-EOF
        [mysqld]
        user = mysql
        port = 3306
        server-id = 1
        character-set-server = utf8mb4
        socket = /tmp/mysql.sock
        basedir = ${MYSQL_BASE_DIR}
        datadir = ${MYSQL_DATA_DIR}
        pid-file = ${MYSQL_ERR_LOG}
        log_error = ${MYSQL_ERR_LOG}
        slow_query_log_file = ${MYSQL_SLW_LOG}
        skip-name-resolve = 1
        back_log = 300
        max_connections = 1000
        max_connect_errors = 6000
        open_files_limit = 65535
        table_open_cache = 128
        max_allowed_packet = 4M
        binlog_cache_size = 1M
        max_heap_table_size = 8M
        tmp_table_size = 16M
        read_buffer_size = 2M
        read_rnd_buffer_size = 8M
        sort_buffer_size = 8M
        join_buffer_size = 8M
        key_buffer_size = 4M
        thread_cache_size = 8
        query_cache_type = 1
        query_cache_size = 8M
        query_cache_limit = 2M
        ft_min_word_len = 4
        log_bin = mysql-bin
        binlog_format = mixed
        expire_logs_days = 30
        slow_query_log = 1
        long_query_time = 1
        performance_schema = 0
        explicit_defaults_for_timestamp
        #lower_case_table_names = 1
        skip-external-locking
        default_storage_engine = InnoDB
        innodb_file_per_table = 1
        innodb_open_files = 500
        innodb_buffer_pool_size = 64M
        innodb_write_io_threads = 4
        innodb_read_io_threads = 4
        innodb_thread_concurrency = 0
        innodb_purge_threads = 1
        innodb_flush_log_at_trx_commit = 2
        innodb_log_buffer_size = 2M
        innodb_log_file_size = 32M
        innodb_log_files_in_group = 3
        innodb_max_dirty_pages_pct = 90
        innodb_lock_wait_timeout = 120
        bulk_insert_buffer_size = 8M
        myisam_sort_buffer_size = 8M
        myisam_max_sort_file_size = 10G
        myisam_repair_threads = 1
        interactive_timeout = 28800
        wait_timeout = 28800

        [mysqldump]
        quick
        max_allowed_packet = 16M

        [myisamchk]
        key_buffer_size = 8M
        sort_buffer_size = 8M
        read_buffer = 4M
        write_buffer = 4M
    EOF

    # 将mysql的共享库目录添加到系统共享库查找路径列表的配置文件中
    [[ ! -f ${MYSQL_LIB_CONF} ]] &amp;&amp;         echo ${MYSQL_LIB_DIR} &gt; ${MYSQL_LIB_CONF} ||         echo ${MYSQL_LIB_DIR} &gt;&gt; ${DEFAULT_LIB_CONF}
    ldconfig

    cp -a ${MYSQL_BASE_DIR}/support-files/mysql.server /etc/init.d/mysqld
    chmod 755 /etc/init.d/mysqld
    /etc/init.d/mysqld start
    chkconfig --level 35 mysqld on
    mysql -uroot -e "use mysql; set password for 'root'@'localhost' = password('${DB_ROOT_PASS}');"
    mysql -uroot -pdjango -e "create database ${DJANGO_DB_NAME}; grant all privileges on ${DJANGO_DB_NAME}.* to ${DJANGO_DB_USER}@'${DJANGO_CLIENT}' identified by '${DJANGO_DB_PASS}';"
    
    echo "mysql安装成功"
}

django-setup.sh脚本文件:


#! /bin/bash

# usage: nohup ./django-setup.sh &amp;&gt; /tmp/django-apache-daemon.log &amp;

set -o errexit
source /etc/profile

readonly LOCK_FD=$[$(ls -l /proc/${BASHPID}/fd | sed -n '$p' | awk '{print $9}') + 1]
readonly LOCK_FILE="/tmp/$(basename $0).pid"
readonly FUNC_FILE="./django-environment.sh"

# 对指定的文件设置非阻塞模式的独占锁,并将正在运行的进程pid写入到加锁文件
# 程序运行前首先必须获取文件锁,并在运行期间始终持有;若获取锁操作失败,则立即以失败返回而无法运行
# 确保程序仅运行单个实例,避免出现竞争状态
eval "exec ${LOCK_FD}&gt;&gt;${LOCK_FILE}"
flock -n ${LOCK_FD} &amp;&amp;     { echo "${BASHPID} - 运行开始"; echo ${BASHPID}&gt;&gt;${LOCK_FILE}; } ||     { echo "${BASHPID} - 运行失败,($(cat ${LOCK_FILE}))正在运行"; exit 1; }

# 若程序成功执行完毕,则在退出前显式解锁,备份并清空锁文件
success() {
    echo "${BASHPID} - 运行完毕"
    flock -u ${LOCK_FD}
    eval "exec ${LOCK_FD}&gt;&amp;-"
    cp -a ${LOCK_FILE}{,.$(date +%Y-%m-%d-%H-%M-%S)}
    :&gt;${LOCK_FILE}
}

trap success EXIT

# 加载包含函数定义的文件
source ${FUNC_FILE}

setup_apache_django_daemon_mode() {
    install_common_dependencies
    install_python3
    install_mysqlclient
    install_django
    django_basic_setup
    install_apache
    apache_djange_daemon_mode
}

setup_mysql_for_django() {
    install_setup_mysql
}

setup_apache_django_daemon_mode
#setup_mysql_for_django

5. 参考

uwsgi官方文档:https://uwsgi-docs.readthedocs.io/en/latest/index.html

mod_wsgi官方文档:https://modwsgi.readthedocs.io/en/develop/index.html

Django官方文档:https://docs.djangoproject.com/en/2.1/intro/

Nginx官方文档:https://docs.nginx.com/nginx/admin-guide/

github项目地址:https://github.com/superwujc

尊重原创,欢迎转载,注明出处:https://my.oschina.net/superwjc/blog/3003027










以上是关于Linux(CentOS7)系统中部署Django web框架的主要内容,如果未能解决你的问题,请参考以下文章

Django项目发布 环境部署(中)

Linux学习8-CentOS部署自己本地的django项目

centos 6.6怎么部署vnc

[linux]centos7.4部署django+Uwsgi+Nginx

python3.8.0 Django 开发后端接口api 部署到 Linux Centos7上

白手起家Django项目发布中篇_Centos下Python2和3并存环境部署