Linux(CentOS7)系统中部署Django web框架
1. 概述
部署django和vue架在逻辑上可以分为web层与数据库层:web前端通过实现了WSGI协议的模块对python代码进行解析,而python代码中则通过特定于数据库的操作接口对数据库进行读取与写入。
Django自身内置了轻量级的web服务器与sqlite数据库,可以用于简单的代码测试,并支持Apache httpd与Nginx作为web前端,以及PostgreSQL/MySQL/Oracle等数据库作为后端存储,用于实际的生产环境。
本文分别以MySQL + Apache httpd + mod_wsgi与MySQL + Nginx + uwsgi为例,通过源码安装的方式,简单描述Django服务框架在Linux系统生产环境下的部署过程。
2. 说明
1. 示例中包含两台服务器,操作系统版本均为CentOS 7.6.1810,最小化全新安装,无其他项目运行。
- django-web(192.168.9.129):web前端,安装的组件包括Python解析器,mysqlclient数据库操作接口,apache/nginx web服务器,WSGI协议处理模块。
- django-db(192.168.9.130):后端存储,安装mysql。
2. 示例中所使用的源码包均位于/usr/local/src,列表如下:
Python-3.7.2.tgz
Django-2.1.5.tar.gz
mysql-boost-5.7.24.tar.gz
mysqlclient-1.3.14.tar.gz
httpd-2.4.37.tar.gz
apr-1.6.5.tar.gz
apr-util-1.6.1.tar.gz
mod_wsgi-4.6.5.tar.gz
tengine-2.2.3.tar.gz
uwsgi-2.0.17.1.tar.gz
3. 示例中对源码包进行安装时,均使用默认的目标安装路径。在实际的生产环境中,为避免覆盖,安装前务必确认目标安装路径与文件是否存在,若存在则应查看当前已安装版本,并选择其他目录。
3. 步骤
3.1 - 后端存储(MySQL)
[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路径对应的本地文件名。
<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
<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/
github项目地址:https://github.com/superwujc
尊重原创,欢迎转载,注明出处:https://my.oschina.net/superwjc/blog/3003027
Linux(CentOS7)系统中部署Django web框架的更多相关文章
- 在裸机centos7系统中部署django项目的过程
概要 本文用一台安装了centos7.5系统的裸奔Linux机器(当然是虚拟机)详细讲解从无到有部署django项目的过程. 安装必要的工具 配置yum源 至于什么是yum源大家请自行百度,本人用的是 ...
- 教程:Visual Studio 中的 Django Web 框架入门
教程:Visual Studio 中的 Django Web 框架入门 Django 是高级 Python 框架,用于快速.安全及可扩展的 Web 开发. 本教程将在 Visual Studio 提供 ...
- Linux CentOS7系统中phpMyAdmin安装配置
今天介绍的是如何在Linux CentOS7系统中配置phpMyAdmin. 目录 环境准备 安装包 基本设置 网站预览 环境准备 linux centos7系统 ssh软件 php语言环境 mysq ...
- Linux CentOS7系统中mysql8安装配置
mysql是世界上最流行的关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司所有.今天我将记录一下如何在Linux centos7系统上安装和配置MySQL. 目录 环境准 ...
- Linux CentOS7系统中ssh的用法
大家都知道,公司买上服务器,不可能实时在线操作虚拟机,也没有那个时间和精力登录到公司的云服务商官网进行操作,一来不安全,二来也效率不高. 如果是购买的虚拟主机,你可以使用ftp进行本地程序文件传输和从 ...
- 【JVM】linux上tomcat中部署的web服务,时好时坏,莫名其妙宕机,报错:There is insufficient memory for the Java Runtime Environment to continue.
=========================================================================================== 环境: linu ...
- Linux CentOS7系统中php安装配置
本篇讲解如何配置php开发环境,让你的php代码可以正常的在网页中运行. 准备工作 linux centos7操作系统 ssh软件 nginx php资源 想要了解更多关于php的内容,请访问: ph ...
- 在Linux CentOS7系统中搭建LNMP
LNMP就是Linux+Nginx+MySQL+PHP,既然是在Linux CentOS7那么Linux就是已经安装好了.所以接下百度一下接下来的教程,整理测试如下: 教程是centos6.2的有点老 ...
- linux(Centos7系统)中安装JDK、Tomcat、Mysql
安装前准备两个工具:(360可以安装) 1.JDK的安装 使用yum命令安装 .查看是否已安装JDK # yum list installed |grep java .卸载CentOS系统Java环境 ...
随机推荐
- Python学习日记(十)—— 杂货铺(全局变量补充、Python参数传递、字符串格式化、迭代器、生成器)
全局变量补充 python自己添加了些全局变量 print(vars()) """结果: {'__name__': '__main__', '__doc__': None ...
- 循环引擎 greenlet 没有显式调度的微线程,换言之 协程
小结: 1. micro-thread with no implicit scheduling; coroutines, in other words. 没有显式调度的微线程,换言之 协程 2. 一个 ...
- 003-多线程-JUC集合-Set-CopyOnWriteArrayList
一.概述 它是线程安全的无序的集合,可以将它理解成线程安全的HashSet.有意思的是,CopyOnWriteArraySet和HashSet虽然都继承于共同的父类AbstractSet:但是,Has ...
- (翻译) closures-are-not-complicated
总计:读完这篇文章需要20分钟 这篇文章讲解了闭包的一些内容,作者是拿ES5规范中的一些名词来讲的. 所以可能和博客上一篇文章中提到的binding object, (lexical enviro ...
- git撤销某次提交
1.查询提交记录.进入目录,查看某人在此目录下的commit: panxi@ww-bj-panxi MINGW64 [path]/Business (feature/v2.3) $ git log f ...
- [Graphics] UIColor created with component values far outside the expected range, Set a breakpoint on UIColorBreakForOutOfRangeColorComponents to debug. This message will only be logged once.
用了别人的代码,一直总有一个报错,一开始没注意,最近项目快完期了,得处理下警告之类的东西, 后面发现之前那个大神代码是这样写的 [SVProgressHUD setBackgroundColor:[U ...
- js 匿名函数 js-函数定义方法
1.任何函数都是有返回值的,没有返回值的,在某些语言里称之为过程例如PL/SQL 2.js中的函数如果没有return 关键字指明给出的返回值,那么当调用完函数后,会返回“undefined" ...
- 服务器watchdog看门狗的理解
1.什么是watchdog?watchdog,中文名称叫做“看门狗”,全称watchdog timer,从字面上我们可以知道其实它属于一种定时器.然而它与我们平常所接触的定时器在作用上又有所不同.普通 ...
- skywalking 的安装部署及其远程应用
环境配置 centos 7.6 jdk 1.8 elasticsearch5.6.8 skyWalking3.2.6 1.安装elasticsearch wget https://artifacts. ...
- 《精通并发与Netty》学习笔记(06 - Apache Thrift使用简介)
一.概述 Apache Thrift 是 Facebook 实现的一种高效的.支持多种编程语言的远程服务调用的框架.Thrift是由Facebook开发的,并在2008年捐给了Apache基金会,成为 ...