您现在的位置是:首页 > 文章详情

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

日期:2019-01-17点击:452

1. 概述

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

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

Django服务框架在逻辑上可以分为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)

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

安装依赖包:

[root@django-db ~]# yum -y install gcc gcc-c++ make cmake ncurses-devel openssl-devel

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

[root@django-db ~]# 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选项显式指定。

[root@django-db ~]# cd /usr/local/src/ [root@django-db src]# [root@django-db src]# ll total 47960 -rw-r--r-- 1 root root 49110448 Oct 4 04:02 mysql-boost-5.7.24.tar.gz [root@django-db src]# [root@django-db src]# tar axf mysql-boost-5.7.24.tar.gz [root@django-db src]# [root@django-db 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 [root@django-db src]# [root@django-db src]# cd mysql-5.7.24/ [root@django-db mysql-5.7.24]# [root@django-db mysql-5.7.24]# ll /usr/local/mysql ls: cannot access /usr/local/mysql: No such file or directory [root@django-db mysql-5.7.24]# [root@django-db mysql-5.7.24]# [root@django-db 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的方式后台运行,避免因终端关闭而导致运行终止。

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

 创建mysql用户和组:

[root@django-db mysql-5.7.24]# id mysql id: mysql: no such user [root@django-db mysql-5.7.24]# [root@django-db mysql-5.7.24]# useradd mysql -s /sbin/nologin [root@django-db mysql-5.7.24]# [root@django-db mysql-5.7.24]# id mysql uid=1000(mysql) gid=1000(mysql) groups=1000(mysql) [root@django-db mysql-5.7.24]# [root@django-db mysql-5.7.24]#

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

[root@django-db 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 [root@django-db mysql-5.7.24]# [root@django-db 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):

[root@django-db mysql-5.7.24]# touch /usr/local/mysql/logs/{mysqld.pid,mysqld-err.log,mysqld-slw.log} [root@django-db mysql-5.7.24]#

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

[root@django-db mysql-5.7.24]# chown -R mysql:mysql /usr/local/mysql/ [root@django-db mysql-5.7.24]#

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

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

初始化数据库:

[root@django-db 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] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option. [root@django-db mysql-5.7.24]# 

编辑mysql配置文件:

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

[root@django-db mysql-5.7.24]# cd [root@django-db ~]# [root@django-db ~]# ll /etc/my.cnf -rw-r--r--. 1 root root 570 Aug 16 14:00 /etc/my.cnf [root@django-db ~]# [root@django-db ~]# cp -a /etc/my.cnf{,.ori} [root@django-db ~]# [root@django-db ~]# 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)添加到系统共享库查找路径列表的配置文件中:

[root@django-db ~]# echo "/usr/local/mysql/lib" >> /etc/ld.so.conf.d/mysql-v5.7.24.conf [root@django-db ~]# [root@django-db ~]# cat /etc/ld.so.conf.d/mysql-v5.7.24.conf /usr/local/mysql/lib [root@django-db ~]# [root@django-db ~]# ldconfig [root@django-db ~]#

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

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

启动mysql服务:

[root@django-db ~]# /etc/init.d/mysqld start Starting MySQL.. SUCCESS! [root@django-db ~]# 

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

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

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

3.2 - Django环境

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

3.2.1 - Python3

安装依赖包:

[root@django-web ~]# yum -y install gcc make libffi-devel readline-devel zlib-devel openssl-devel

下载python3源码包:

[root@django-web ~]# 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的方式后台运行,避免因终端关闭而导致运行终止。

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

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

[root@django-web Python-3.7.2]# which python3 /usr/local/bin/python3 [root@django-web Python-3.7.2]# [root@django-web 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 [root@django-web Python-3.7.2]# [root@django-web 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 [root@django-web Python-3.7.2]# [root@django-web Python-3.7.2]# [root@django-web Python-3.7.2]# echo '/usr/local/lib' >> /etc/ld.so.conf.d/python-v3.7.2.conf [root@django-web Python-3.7.2]# ldconfig [root@django-web Python-3.7.2]# [root@django-web Python-3.7.2]# [root@django-web Python-3.7.2]# python3 -V Python 3.7.2 [root@django-web Python-3.7.2]#

Python3解析器安装完成。

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

安装依赖包mysql-devel:

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

[root@django-web ~]# yum -y install mysql-devel

下载mysqlclient源码包:

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

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

[root@django-web ~]# cd /usr/local/src/ [root@django-web src]# [root@django-web 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 [root@django-web src]# [root@django-web src]# tar axf mysqlclient-1.3.14.tar.gz [root@django-web src]# cd mysqlclient-1.3.14/ [root@django-web mysqlclient-1.3.14]# python3 setup.py install

查看版本:

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

mysqlclient安装完成。

3.2.3 - django

下载源码包:

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

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

[root@django-web ~]# cd /usr/local/src/ [root@django-web src]# [root@django-web 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 [root@django-web src]# [root@django-web src]# tar axf Django-2.1.5.tar.gz [root@django-web src]# cd Django-2.1.5/ [root@django-web Django-2.1.5]# python3 setup.py install

查看版本:

[root@django-web Django-2.1.5]# python3 -m django --version 2.1.5 [root@django-web Django-2.1.5]#

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

[root@django-web Django-2.1.5]# which django-admin /usr/local/bin/django-admin [root@django-web Django-2.1.5]#

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

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

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

[root@django-web Django-2.1.5]# cd [root@django-web ~]# [root@django-web ~]# mkdir -p /opt/webs/t_django [root@django-web ~]# django-admin startproject t_app /opt/webs/t_django [root@django-web ~]# [root@django-web ~]# 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 [root@django-web ~]#

以上显示的目录层级结构中,/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

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

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

  • 将ALLOWED_HOSTS = []修改为ALLOWED_HOSTS = ['*'],添加允许访问的主机:
[root@django-web ~]# cd /opt/webs/t_django/ [root@django-web 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 [root@django-web t_django]# [root@django-web t_django]# [root@django-web t_django]# [root@django-web t_django]# cp -a t_app/settings.py{,.ori} [root@django-web t_django]# [root@django-web t_django]# sed -n '/^ALLOWED_HOSTS/p' t_app/settings.py ALLOWED_HOSTS = [] [root@django-web t_django]# sed -i '/^ALLOWED_HOSTS/ s|\[\]|\['\''*'\''\]|' t_app/settings.py [root@django-web t_django]# [root@django-web t_django]# sed -n '/^ALLOWED_HOSTS/p' t_app/settings.py ALLOWED_HOSTS = ['*'] [root@django-web t_django]#
  • 设置数据库信息,将数据库引擎由sqlite3修改为mysql,并设置数据库IP与端口,数据库名称,用户名与密码,以及时区信息(注意缩进格式保持一致):
[root@django-web t_django]# sed -i '/ENGINE/ s/sqlite3/mysql/' t_app/settings.py [root@django-web t_django]# sed -i '/ENGINE/{n;d}' t_app/settings.py [root@django-web t_django]# sed -i -e '/ENGINE/ a\ '\''HOST'\'': '\''192.168.9.130'\'',\n '\''PORT'\'': '\''3306'\'',\n '\''NAME'\'': '\''t_django_db'\'',\n '\''USER'\'': '\''u_django'\'',\n '\''PASSWORD'\'': '\''p_django'\'',\n '\''TIME_ZONE'\'': '\''Asia/Shanghai'\'',\n '\''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'),指定静态文件的根目录:
[root@django-web t_django]# sed -i '$a\STATIC_ROOT = os.path.join(BASE_DIR, '\''static'\'')' t_app/settings.py [root@django-web t_django]# [root@django-web 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/

[root@django-web t_django]# pwd /opt/webs/t_django [root@django-web 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 [root@django-web t_django]# [root@django-web t_django]# python3 manage.py collectstatic 119 static files copied to '/opt/webs/t_django/static'. [root@django-web t_django]# [root@django-web 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 [root@django-web t_django]# [root@django-web t_django]# du -sh static/ 1.7M static/ [root@django-web t_django]#

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

[root@django-web t_django]# mysql -h192.168.9.130 -P3306 -uu_django -pp_django -Dt_django_db -e "show tables;" [root@django-web t_django]# [root@django-web 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 [root@django-web 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 | +----------------------------+ [root@django-web t_django]# [root@django-web t_django]# ss -atn | grep :3306 ESTAB 0 0 192.168.9.129:44518 192.168.9.130:3306 [root@django-web t_django]#

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

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

[root@django-web t_django]# python3 manage.py runserver 0:8000 & [1] 30910 [root@django-web 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. [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web t_django]# ss -atn | grep :8000 [root@django-web 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)

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

3.3.1 - 安装与基本配置

安装依赖包:

[root@django-web ~]# yum -y install expat-devel pcre-devel

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

[root@django-web ~]# 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选项显式指定。

[root@django-web ~]# cd /usr/local/src/ [root@django-web src]# [root@django-web 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 [root@django-web src]# [root@django-web src]# [root@django-web src]# tar axf apr-1.6.5.tar.gz [root@django-web src]# cd apr-1.6.5/ [root@django-web apr-1.6.5]# ./configure && make && make install

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

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

[root@django-web apr-1.6.5]# cd .. [root@django-web src]# tar axf apr-util-1.6.1.tar.gz [root@django-web src]# cd apr-util-1.6.1/ [root@django-web apr-util-1.6.1]# ./configure --with-apr=/usr/local/apr && make && make install

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

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

[root@django-web apr-util-1.6.1]# cd .. [root@django-web src]# tar axf httpd-2.4.37.tar.gz [root@django-web src]# cd httpd-2.4.37/ [root@django-web 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中:

[root@django-web httpd-2.4.37]# ls /usr/local/apache2/ bin build cgi-bin conf error htdocs icons include logs man manual modules [root@django-web 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 [root@django-web httpd-2.4.37]# [root@django-web httpd-2.4.37]# which apachectl /usr/bin/which: no apachectl in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin) [root@django-web httpd-2.4.37]# [root@django-web httpd-2.4.37]# sed -i '/^PATH/ s|$|:/usr/local/apache2/bin|' ~/.bash_profile [root@django-web httpd-2.4.37]# source ~/.bash_profile [root@django-web httpd-2.4.37]# which apachectl /usr/local/apache2/bin/apachectl [root@django-web httpd-2.4.37]# [root@django-web httpd-2.4.37]# apachectl -v Server version: Apache/2.4.37 (Unix) Server built: Jan 17 2019 22:35:36 [root@django-web httpd-2.4.37]#

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

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

[root@django-web httpd-2.4.37]# cd .. [root@django-web src]# tar axf mod_wsgi-4.6.5.tar.gz [root@django-web src]# cd mod_wsgi-4.6.5/ [root@django-web 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):

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

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

  • 将默认的运行时用户和组daemon修改为www
[root@django-web ~]# ll /usr/local/apache2/conf/httpd.conf -rw-r--r-- 1 root root 18541 Jan 17 13:57 /usr/local/apache2/conf/httpd.conf [root@django-web ~]# [root@django-web ~]# cp -a /usr/local/apache2/conf/httpd.conf{,.ori} [root@django-web ~]# [root@django-web ~]# sed -n '/^User\|^Group/p' /usr/local/apache2/conf/httpd.conf User daemon Group daemon [root@django-web ~]# sed -i '/^User\|^Group/ s/daemon/www/' /usr/local/apache2/conf/httpd.conf [root@django-web ~]# [root@django-web ~]# sed -n '/^User\|^Group/p' /usr/local/apache2/conf/httpd.conf User www Group www [root@django-web ~]#
  • 添加ServerName为localhost:80
[root@django-web ~]# 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 [root@django-web ~]# [root@django-web ~]# sed -i '/^#ServerName/a\ServerName localhost:80' /usr/local/apache2/conf/httpd.conf [root@django-web ~]# [root@django-web ~]# 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 [root@django-web ~]#

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

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

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

[root@django-web ~]# apachectl configtest Syntax OK [root@django-web ~]# apachectl [root@django-web ~]# [root@django-web ~]# 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 :::* [root@django-web ~]#

浏览器访问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指令指定。

[root@django-web ~]# mkdir -p /opt/webs/documents [root@django-web ~]# 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虚拟主机配置文件:

[root@django-web ~]# sed -i '$a\Include conf/extra/httpd-django.conf' /usr/local/apache2/conf/httpd.conf [root@django-web ~]# sed -n '$p' /usr/local/apache2/conf/httpd.conf Include conf/extra/httpd-django.conf [root@django-web ~]#

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

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

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

查看访问日志:

[root@django-web ~]# 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路径对应的本地文件名。
[root@django-web ~]# cp -a /usr/local/apache2/conf/extra/httpd-django.conf /usr/local/apache2/conf/extra/httpd-django-embedded.conf [root@django-web ~]# [root@django-web ~]# vi /usr/local/apache2/conf/extra/httpd-django.conf <VirtualHost *:80> 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/ <Directory /opt/webs/t_django> Require all granted </Directory> </VirtualHost>
[root@django-web ~]# [root@django-web ~]# apachectl configtest Syntax OK [root@django-web ~]# apachectl [root@django-web ~]# 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 [root@django-web ~]# 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),每一个子进程又各自包含若干线程。

[root@django-web ~]# 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:

[root@django-web ~]# vi /usr/local/apache2/conf/extra/httpd-django.conf <VirtualHost *:80> 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/ <Directory /opt/webs/t_django> Require all granted </Directory> </VirtualHost> [root@django-web ~]# [root@django-web ~]# apachectl configtest Syntax OK [root@django-web ~]# apachectl graceful [root@django-web ~]# [root@django-web ~]# 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 [root@django-web ~]# [root@django-web ~]# pstree -a 60941 httpd ├─httpd │ └─17*[{httpd}] ├─httpd │ └─26*[{httpd}] ├─httpd │ └─26*[{httpd}] └─httpd └─26*[{httpd}] [root@django-web ~]# 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 [root@django-web ~]# [root@django-web ~]# ps -p 61088 -o pid,ppid,args PID PPID COMMAND 61088 60941 (wsgi:t_app) [root@django-web ~]#

从以上输出可以看出,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的运行时用户可写。

[root@django-web ~]# 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 [root@django-web ~]# [root@django-web ~]# 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的部署方式更加复杂与灵活。

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

安装依赖包:

[root@django-web ~]# yum -y install gcc make openssl-devel pcre-devel

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

[root@django-web ~]# wget \ > https://projects.unbit.it/downloads/uwsgi-2.0.17.1.tar.gz \ > 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模块以共享库的方式安装。

[root@django-web ~]# cd /usr/local/src/ [root@django-web 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 [root@django-web src]# [root@django-web src]# tar axf tengine-2.2.3.tar.gz [root@django-web src]# cd tengine-2.2.3/ [root@django-web tengine-2.2.3]# [root@django-web tengine-2.2.3]# ./configure \ > --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 && \ > make -j && make install

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

[root@django-web tengine-2.2.3]# ls /usr/local/nginx/ conf html include logs modules sbin [root@django-web tengine-2.2.3]# ls /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx [root@django-web 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) [root@django-web tengine-2.2.3]# [root@django-web tengine-2.2.3]# sed -i '/^PATH/ s|$|:/usr/local/nginx/sbin|' ~/.bash_profile [root@django-web tengine-2.2.3]# source ~/.bash_profile [root@django-web tengine-2.2.3]# which nginx /usr/local/nginx/sbin/nginx [root@django-web tengine-2.2.3]# nginx -v Tengine version: Tengine/2.2.3 (nginx/1.8.1) [root@django-web tengine-2.2.3]#

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

[root@django-web tengine-2.2.3]# cd .. [root@django-web src]# tar axf uwsgi-2.0.17.1.tar.gz [root@django-web src]# cd uwsgi-2.0.17.1/ [root@django-web uwsgi-2.0.17.1]# python3 setup.py install

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

[root@django-web uwsgi-2.0.17.1]# which uwsgi /usr/local/bin/uwsgi [root@django-web uwsgi-2.0.17.1]# [root@django-web uwsgi-2.0.17.1]# uwsgi --version 2.0.17.1 [root@django-web 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 项目包:入口文件:方法

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

查看日志:

[root@django-web ~]# 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文件。

[root@django-web ~]# mkdir -p /usr/local/nginx/conf/vhosts [root@django-web ~]# vi /usr/local/nginx/conf/vhosts/upstream.conf upstream t_django { server 127.0.0.1:8000; } [root@django-web ~]# 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上下文中包含以上两个配置文件。

[root@django-web ~]# 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\t$upstream_addr\t[$time_local]\t$request\t' '$status\t$body_bytes_sent\t$http_user_agent\t$http_referer\t' '$http_x_forwarded_for\t$request_time\t$upstream_response_time\t$remote_user\t' '$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:

[root@django-web ~]# 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 [root@django-web ~]# [root@django-web ~]# nginx [root@django-web ~]# [root@django-web ~]# ss -atn | grep 80 LISTEN 0 128 *:80 *:*

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

查看日志:

[root@django-web ~]# 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域套接字的方式启动:

[root@django-web ~]# uwsgi --stop /opt/webs/t_django/t_app.pid [root@django-web ~]# uwsgi \ > --socket /tmp/t_app.sock \ > --uid=www --gid=www \ > --master \ > --pidfile=/opt/webs/t_django/t_app.pid \ > --daemonize=/opt/webs/t_django/t_app.log \ > --chdir=/opt/webs/t_django \ > --module=t_app.wsgi:application \ > --processes=5 \ > --harakiri=20 \ > --max-requests=500 \ > --vacuum [root@django-web ~]# 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域套接字已被创建:

[root@django-web ~]# ll /tmp/t_app.sock srwxrwxrwx 1 www www 0 Jan 18 03:25 /tmp/t_app.sock [root@django-web ~]#

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

[root@django-web ~]# sed -n '/server/p' /usr/local/nginx/conf/vhosts/upstream.conf server 127.0.0.1:8000; [root@django-web ~]# [root@django-web ~]# sed -i '/server/ s|127.0.0.1:8000|unix:/tmp/t_app.sock|' /usr/local/nginx/conf/vhosts/upstream.conf [root@django-web ~]# [root@django-web ~]# sed -n '/server/p' /usr/local/nginx/conf/vhosts/upstream.conf server unix:/tmp/t_app.sock; [root@django-web ~]# [root@django-web ~]# 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 [root@django-web ~]# [root@django-web ~]# nginx -s reload [root@django-web ~]#

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

[root@django-web ~]# 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="$@" make -j ${CPU_CORES} ${OPTIONS} make install } # 检查第1个参数指定的路径是否位于系统环境变量PATH中,若不存在则返回0,反之则返回1 path_not_exists() { [[ ${1} != "" && ${1} != " " ]] && 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} ]] && return 1 done return 0 } # 安装python3解析器 install_python3() { local PYTHON_BIN="/usr/local/bin/python3" [[ -x ${PYTHON_BIN} ]] && \ { 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} ]] && \ echo '/usr/local/lib' > ${PYTHON_LIB_CONF} || \ echo '/usr/local/lib' >> ${DEFAULT_LIB_CONF} ldconfig # 若python3可执行文件的路径不在PATH中,则添加 path_not_exists "${PYTHON_BIN_DIR}" && \ sed -i "/^PATH/s|$|:${PYTHON_BIN_DIR}|" ${USER_PROFILE}; source ${USER_PROFILE} # 创建python3可执行文件的符号链接 [[ ! -L ${PYTHON_LINK} ]] && \ ln -s ${PYTHON_BIN} ${PYTHON_LINK} } install_mysqlclient() { python3 -c "import MySQLdb; print(MySQLdb.version_info)" &> /dev/null && \ { 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}" && \ 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} ]] && \ { 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}" && \ sed -i "/^PATH/s|$|:${NGINX_BIN_DIR}|" ${USER_PROFILE}; source ${USER_PROFILE} } install_uwsgi() { local UWSGI_BIN="/usr/local/bin/uwsgi" [[ -e ${UWSGI_BIN} ]] && \ { 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}" && \ sed -i "/^PATH/s|$|:${UWSGI_BIN_DIR}|" ${USER_PROFILE}; source ${USER_PROFILE} } install_django() { python3 -m django --version &> /dev/null && \ { 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} ]] && mkdir -p ${DJANGO_PROJECT_DIR} [[ ! -d ${DJANGO_PROJECT_DIR}/${DJANGO_APP} ]] && \ 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}''\'',\n '\''PORT'\'': '\'''${DJANGO_DB_PORT}''\'',\n '\''NAME'\'': '\'''${DJANGO_DB_NAME}''\'',\n '\''USER'\'': '\'''${DJANGO_DB_USER}''\'',\n '\''PASSWORD'\'': '\'''${DJANGO_DB_PASS}''\'',\n '\''TIME_ZONE'\'': '\''Asia/Shanghai'\'',\n '\''USE_TZ'\'': False' ${DJANGO_APP}/settings.py sed -i '$a\STATIC_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 & } # 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 '\t')" id -u ${WEB_USER} &> /dev/null || \ useradd ${WEB_USER} -N -s ${WEB_USER_SHELL} getent group ${WEB_GROUP} &> /dev/null || \ groupadd ${WEB_GROUP} id -gr ${WEB_USER} &> /dev/null || \ usermod -g ${WEB_GROUP} ${WEB_USER} [[ ! -d ${APACHE_DOC_ROOT_DIR} ]] && \ 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/,/<\/Directory\>/ s/^/#/' ${APACHE_BASE_CONF} sed -i '$a\\nServerName localhost:80' ${APACHE_BASE_CONF} sed -i '$a\Include '${APACHE_DJANGO_CONF}'' ${APACHE_BASE_CONF} [[ -f ${APACHE_DJANGO_CONF} ]] && \ cp -a ${APACHE_DJANGO_CONF}{,.ori} cat > ${APACHE_DJANGO_CONF} <<-EOF WSGISocketPrefix ${DJANGO_PROJECT_DIR}/${DJANGO_APP} <VirtualHost *:80> ${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}<Directory ${DJANGO_PROJECT_DIR}> ${TAB}${TAB}Require all granted ${TAB}</Directory> </VirtualHost> EOF } # mysql安装与基本配置 install_setup_mysql() { local MYSQL_BIN="/usr/local/mysql/bin/mysqld" [[ -x ${MYSQL_BIN} ]] && \ { 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} &> /dev/null || \ useradd ${MYSQL_USER} -N -s ${MYSQL_USER_SHELL} # 若mysql组不存在,则创建 getent group ${MYSQL_GROUP} &> /dev/null || \ groupadd ${MYSQL_GROUP} # 若mysql用户不是mysql组的成员,则加入 id -gr ${MYSQL_USER} &> /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} ]] && mkdir -p ${MYSQL_DATA_DIR} [[ ! -d ${MYSQL_LOGS_DIR} ]] && mkdir -p ${MYSQL_LOGS_DIR} [[ ! -f ${MYSQL_ERR_LOG} ]] && touch ${MYSQL_ERR_LOG} [[ ! -f ${MYSQL_SLW_LOG} ]] && touch ${MYSQL_SLW_LOG} [[ ! -f ${MYSQL_PID_FILE} ]] && touch ${MYSQL_PID_FILE} chown -R ${MYSQL_USER}:${MYSQL_GROUP} ${MYSQL_BASE_DIR} # 若mysql可执行文件的路径不在PATH中,则添加 path_not_exists "${MYSQL_BIN_DIR}" && \ 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 > /etc/my.cnf <<-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} ]] && \ echo ${MYSQL_LIB_DIR} > ${MYSQL_LIB_CONF} || \ echo ${MYSQL_LIB_DIR} >> ${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 &> /tmp/django-apache-daemon.log & 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}>>${LOCK_FILE}" flock -n ${LOCK_FD} && \ { echo "${BASHPID} - 运行开始"; echo ${BASHPID}>>${LOCK_FILE}; } || \ { echo "${BASHPID} - 运行失败,($(cat ${LOCK_FILE}))正在运行"; exit 1; } # 若程序成功执行完毕,则在退出前显式解锁,备份并清空锁文件 success() { echo "${BASHPID} - 运行完毕" flock -u ${LOCK_FD} eval "exec ${LOCK_FD}>&-" cp -a ${LOCK_FILE}{,.$(date +%Y-%m-%d-%H-%M-%S)} :>${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/

原文链接:https://my.oschina.net/superwjc/blog/3003027
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章