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

1. 概述

部署django和vue架在邏輯上能夠分爲web層與數據庫層:web前端經過實現了WSGI協議的模塊對python代碼進行解析,而python代碼中則經過特定於數據庫的操做接口對數據庫進行讀取與寫入。javascript

Django自身內置了輕量級的web服務器與sqlite數據庫,能夠用於簡單的代碼測試,並支持Apache httpd與Nginx做爲web前端,以及PostgreSQL/MySQL/Oracle等數據庫做爲後端存儲,用於實際的生產環境。php

本文分別以MySQL + Apache httpd + mod_wsgi與MySQL + Nginx + uwsgi爲例,經過源碼安裝的方式,簡單描述Django服務框架在Linux系統生產環境下的部署過程。css

2. 說明

1. 示例中包含兩臺服務器,操做系統版本均爲CentOS 7.6.1810,最小化全新安裝,無其餘項目運行。html

  • 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.gzvue

3. 示例中對源碼包進行安裝時,均使用默認的目標安裝路徑。在實際的生產環境中,爲避免覆蓋,安裝前務必確認目標安裝路徑與文件是否存在,若存在則應查看當前已安裝版本,並選擇其餘目錄。java

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 ~]#

安裝依賴包:node

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

下載集成boost庫的mysql源碼包:python

[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

源碼安裝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路徑對應的本地文件名。
<code class="language-apache">[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
&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;
[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
&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;
[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 \
&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模塊以共享庫的方式安裝。

[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 \
&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中:

[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 \
&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
[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 \
&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
[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} != "" &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}''\'',\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 &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 '\t')"

    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 '$a\Include '${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

相關文章
相關標籤/搜索