CentOS7搭建LNMP环境

前言:

几年前写过一篇CentOS6编译LNMP搭建高性能WEB环境的教程,如今CentOS7已经成熟,同时迎来了PHP7时代,部分生产环境需要PHP5.6+7.x双版本,故更新本篇教程

这篇教程适合喜欢自己动手编译定制化环境的同学参考,懒人以及时间比较宝贵的公司运维人员推荐 OneinStack 一键安装工具


【2020.05更新】

这篇教程最早源自多年前,我在个人站长时期配置WEB服务器环境的笔记,后来改写成了教程,如今SSLv1.3与HTTP2.0已成为主流趋势,配置新环境时发现已变化太多,例如对SSL协议握手的优化考虑、SSLv1.3与早期版本的兼容等,然而国内并没有一篇文章系统的讲述,所以用业余时间重写了教程,降低技术搭建WEB环境的时间成本


一、获取开源程序与系统升级

① 将CentOS的yum默认源更为更快的国内源(可跳过):

sudo -s
yum -y install wget
cd /etc/yum.repos.d/
wget http://mirrors.163.com/.help/CentOS7-Base-163.repo
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
mv /etc/yum.repos.d/CentOS7-Base-163.repo /etc/yum.repos.d/CentOS-Base.repo
yum clean all
yum makecache

② 用yum命令安装、升级所需的程序库:

yum -y upgrade
yum -y install gcc gcc-c++ autoconf automake libtool libjpeg libjpeg-devel libpng libpng-devel libwebp-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel ncurses ncurses-devel readline-devel curl curl-devel e2fsprogs e2fsprogs-devel libaio-devel.x86_64 krb5-devel libidn libidn-devel openssl openssl-devel openldap openldap-devel nss_ldap openldap-clients openldap-servers bison libevent-devel cmake iptables-services sqlite-devel lua-devel git patch ntp numactl wget

③ 程序源码包下载:

mkdir -p /data0/software
cd /data0/software
wget https://tengine.taobao.org/download/tengine-2.3.2.tar.gz
wget https://www.php.net/distributions/php-7.4.15.tar.gz
wget https://www.percona.com/downloads/Percona-Server-5.7/Percona-Server-5.7.30-33/binary/tarball/Percona-Server-5.7.30-33-Linux.x86_64.ssl101.tar.gz
wget https://github.com/jemalloc/jemalloc/archive/5.2.1.tar.gz -O jemalloc-5.2.1.tar.gz
wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz
wget https://ftp.pcre.org/pub/pcre/pcre-8.44.tar.gz
wget https://jaist.dl.sourceforge.net/project/mhash/mhash/0.9.9.9/mhash-0.9.9.9.tar.gz
wget https://jaist.dl.sourceforge.net/project/mcrypt/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz
wget https://jaist.dl.sourceforge.net/project/mcrypt/MCrypt/2.6.8/mcrypt-2.6.8.tar.gz
wget https://ftp.gnu.org/gnu/libiconv/libiconv-1.16.tar.gz
wget https://github.com/kkos/oniguruma/archive/v6.9.4.tar.gz -O oniguruma-6.9.4.tar.gz
wget https://github.com/vision5/ngx_devel_kit/archive/v0.3.1.tar.gz -O ngx_devel_kit-0.3.1.tar.gz
wget https://github.com/openresty/luajit2/archive/v2.1-20200102.tar.gz -O luajit2-2.1-20200102.tar.gz
wget https://github.com/openresty/lua-ssl-nginx-module/archive/v0.01rc3.tar.gz -O lua-ssl-nginx-module-0.01rc3.tar.gz
wget https://raw.githubusercontent.com/openresty/openresty/master/patches/openssl-1.1.1f-sess_set_get_cb_yield.patch
wget https://raw.githubusercontent.com/hakasenyang/openssl-patch/master/openssl-equal-1.1.1e-dev_ciphers.patch
wget https://raw.githubusercontent.com/pmkol/apad.pro/master/patch/tengine-2.3.2-ssl_cert_sees_cb_yield.patch
wget https://pecl.php.net/get/redis-5.3.3.tgz
wget http://download.redis.io/releases/redis-6.0.12.tar.gz

④ 升级OpenSSL 以支持 TLS 1.3:

patch BoringSSLOpenResty 提供的补丁,解决 Nginx 的 ssl_ciphers 报错问题,并让 OpenSSL 的 session get callback 支持 yield
( 即用于支持Nginx集群时 SSL Seesion ID 重用共享 )

#编译安装最新版本的OpenSSL
tar xzvf openssl-1.1.1g.tar.gz
cd openssl-1.1.1g/
patch -p1 < /data0/software/openssl-1.1.1f-sess_set_get_cb_yield.patch
patch -p1 < /data0/software/openssl-equal-1.1.1e-dev_ciphers.patch
./config shared zlib --prefix=/usr/local/openssl
make
make install
cd ../

#替换旧版本的OpenSSL
mv /usr/bin/openssl /usr/bin/openssl.old
mv /usr/lib64/openssl /usr/lib64/openssl.old
mv /usr/lib64/libssl.so /usr/lib64/libssl.so.old
ln -s /usr/local/openssl/bin/openssl /usr/bin/openssl
ln -s /usr/local/openssl/include/openssl /usr/include/openssl
ln -s /usr/local/openssl/lib/libssl.so /usr/lib64/libssl.so

#配置库文件搜索路径
echo "/usr/local/openssl/lib" >> /etc/ld.so.conf
ldconfig

#查看OpenSSL版本
openssl version -a

补丁的小版本号有时会低于最新的 OpenSSL,一般不影响使用

[返回目录]


二、安装 MySQL 5.7

建议选择MySQL的分支版本Percona Server 5.7,在功能与性能上有显著提升。

① 编译安装Jemalloc:

tar zxvf jemalloc-5.2.1.tar.gz
cd jemalloc-5.2.1/
./autogen.sh
make
make install
cd ../
ln -s /usr/local/lib/libjemalloc.so.2 /usr/lib64/libjemalloc.so.2

② 安装MySQL:

创建mysql用户与用户组

/usr/sbin/groupadd mysql
/usr/sbin/useradd -g mysql mysql

安装Percona Server 5.7二进制版本

tar zxvf Percona-Server-5.7.30-33-Linux.x86_64.ssl101.tar.gz
mkdir -p /usr/local/webserver
mv Percona-Server-5.7.30-33-Linux.x86_64.ssl101 /usr/local/webserver/mysql
ln -s /usr/local/webserver/mysql/lib/libperconaserverclient.so.20 /usr/lib/libperconaserverclient.so.20

③ 一些相关设置:

创建MySQL数据库存放目录

mkdir -p /data0/mysql/3306/data/
mkdir -p /data0/mysql/3306/binlog/
chown -R mysql:mysql /data0/mysql/

添加环境变量

echo -e '\n\nexport PATH=/usr/local/webserver/mysql/bin:$PATH\n' >> /etc/profile && source /etc/profile

创建my.cnf配置文件

vi /usr/local/webserver/mysql/my.cnf

输入以下内容

#需要根据服务器硬件来设置,双核2G内存环境的my.cnf仅供参考
# The following options will be passed to all MySQL clients
[client]
port = 3306
socket = /data0/mysql/3306/mysql.sock
default-character-set = utf8mb4

# The MySQL server
[mysqld]
port = 3306
socket = /data0/mysql/3306/mysql.sock

basedir = /usr/local/webserver/mysql
datadir = /data0/mysql/3306/data
pid-file = /data0/mysql/3306/mysql.pid
user = mysql
bind-address = 0.0.0.0
server-id = 1

local_infile = OFF
transaction-isolation = READ-COMMITTED

init-connect = 'SET NAMES utf8mb4'
character-set-server = utf8mb4

#sql_mode = NO_ENGINE_SUBSTITUTION

#skip-name-resolve
#skip-networking
skip_symbolic_links = yes
back_log = 600

max_connections = 2000
max_connect_errors = 10000
open_files_limit = 65535
table_open_cache = 128
max_allowed_packet = 64M
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 = 16

query_cache_type = 1
query_cache_size = 8M
query_cache_limit = 2M

ft_min_word_len = 4

log_bin = /data0/mysql/3306/binlog/mysql-bin
binlog_format = mixed
expire_logs_days = 30
binlog_cache_size = 8M
max_binlog_cache_size = 2048M
max_binlog_size = 1024M

log_error = /data0/mysql/3306/mysql-error.log
slow_query_log = 1
long_query_time = 1
slow_query_log_file = /data0/mysql/3306/mysql-slow.log

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 = 1024M
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 = 8M
innodb_log_file_size = 512M
innodb_log_files_in_group = 3
innodb_max_dirty_pages_pct = 90
innodb_lock_wait_timeout = 120
innodb_flush_method = O_DIRECT

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 = 64M

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

初始化数据库

/usr/local/webserver/mysql/bin/mysqld --initialize-insecure --user=mysql --basedir=/usr/local/webserver/mysql --datadir=/data0/mysql/3306/data

④ 配置MySQL服务脚本:

创建服务脚本

vi /usr/lib/systemd/system/mysqld.service
#输入以下内容
[Unit]
Description=MySQL Server
Documentation=man:mysqld(7)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target

[Install]
WantedBy=multi-user.target

[Service]
User=mysql
Group=mysql

Type=forking

PIDFile=/data0/mysql/3306/mysqld.pid

# Disable service start and stop timeout logic of systemd for mysqld service.
TimeoutSec=0

# Start main service
ExecStart=/usr/local/webserver/mysql/bin/mysqld --defaults-file=/usr/local/webserver/mysql/my.cnf --daemonize --pid-file=/data0/mysql/3306/mysqld.pid $MYSQLD_OPTS

# Use this to switch malloc implementation
EnvironmentFile=-/etc/sysconfig/mysql

# Sets open_files_limit
LimitNOFILE = 5000

Restart=on-failure

RestartPreventExitStatus=1

PrivateTmp=false

加载Jemalloc动态库

echo "LD_PRELOAD=/usr/local/lib/libjemalloc.so.2" > /etc/sysconfig/mysql

加入启动项

systemctl enable mysqld

启动MySQL服务

systemctl start mysqld

解决MySQL与mariadb-libs的socket冲突

部分使用yum命令安装的扩展依赖mariadb-libs,而卸载mariadb-libs可能会导致依赖应用报错。当MySQL与mariadb-libs共存时,可使用如下方法测试是否冲突

mysql -u root -p
#输入正确的密码后若报错如下信息则有冲突
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
#解决方法
ln -s /data0/mysql/3306/mysql.sock /tmp/mysql.sock

设置数据库root用户密码

#友情提醒:最后的密码就不要跟着复制粘贴了
/usr/local/webserver/mysql/bin/mysqladmin password 12345678

[返回目录]


三、安装 Redis 6.0

① 升级gcc版本:

CentOS7默认的gcc版本是4.8.5,需升级gcc版本才可编译成功Redis 6.0版本,Redis 5.0及更早期版本请跳过这一步

升级到gcc 9.3
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash

#scl命令启用只是临时的,退出shell或重启就会恢复原系统gcc版本
#如果要长期使用gcc 9.3
echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile

② 创建redis用户与用户组:

/usr/sbin/groupadd redis
/usr/sbin/useradd -g redis redis

③ 编译安装Redis:

tar zxvf redis-6.0.12.tar.gz
cd redis-6.0.12/
make
make PREFIX=/usr/local/webserver/redis install
make clean
cp redis.conf /usr/local/webserver/redis/6379.conf
cd ../
mkdir -p /data0/redis/6379/data/
chown -R redis:redis /data0/redis/
chown -R redis:redis /usr/local/webserver/redis/

④ 修改Redis配置文件:

vi /usr/local/webserver/redis/6379.conf
#修改以下参数
bind 127.0.0.1
daemonize yes
pidfile /data0/redis/6379/redis.pid
logfile /data0/redis/6379/redis.log
dir /data0/redis/6379/data
maxmemory 256000000
io-threads 2
io-threads-do-reads yes

bind 仅本机使用填写127.0.0.1,后面可添加更多使用该redis的服务器IP
io-thread 数值设置为CPU线程数的1/2或3/4

⑤ 创建服务脚本:

vi /usr/lib/systemd/system/redis.service
#输入以下内容
[Unit]
Description=Redis daemon
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/webserver/redis/bin/redis-server /usr/local/webserver/redis/6379.conf
ExecReload=/usr/local/webserver/redis/bin/redis-server -s reload
ExecStop=/usr/local/webserver/redis/bin/redis-server -s stop
PrivateTmp=true
User=redis
Group=redis

[Install]
WantedBy=multi-user.target

加入启动项

systemctl enable redis

启动Redis服务

systemctl start redis

[返回目录]


四、安装 PHP 7.4

生产环境下,部分应用可能不支持PHP7.4版本,7.0~7.3版本均可参考,只需要修改当前PHP对应的extension_dir地址即可。

如部分更早期的应用需使用5.6版本,可参考《编译安装PHP5.6并与PHP7.0共存

① 安装所需的支持库:

tar zxvf libiconv-1.16.tar.gz
cd libiconv-1.16/
./configure --prefix=/usr/local
make
make install
make clean
cd ../

tar zxvf libmcrypt-2.5.8.tar.gz
cd libmcrypt-2.5.8/
./configure
make
make install
/sbin/ldconfig
cd libltdl/
./configure --enable-ltdl-install
make
make install
cd ../
make clean
cd ../

tar zxvf mhash-0.9.9.9.tar.gz
cd mhash-0.9.9.9/
./configure
make
make install
make clean
cd ../

ln -s /usr/local/lib/libmcrypt.la /usr/lib/libmcrypt.la
ln -s /usr/local/lib/libmcrypt.so /usr/lib/libmcrypt.so
ln -s /usr/local/lib/libmcrypt.so.4 /usr/lib/libmcrypt.so.4
ln -s /usr/local/lib/libmcrypt.so.4.4.8 /usr/lib/libmcrypt.so.4.4.8
ln -s /usr/local/lib/libmhash.a /usr/lib/libmhash.a
ln -s /usr/local/lib/libmhash.la /usr/lib/libmhash.la
ln -s /usr/local/lib/libmhash.so /usr/lib/libmhash.so
ln -s /usr/local/lib/libmhash.so.2 /usr/lib/libmhash.so.2
ln -s /usr/local/lib/libmhash.so.2.0.1 /usr/lib/libmhash.so.2.0.1
ln -s /usr/local/bin/libmcrypt-config /usr/bin/libmcrypt-config

tar zxvf mcrypt-2.6.8.tar.gz
cd mcrypt-2.6.8/
/sbin/ldconfig
./configure
make
make install
make clean
cd ../

cp -frp /usr/lib64/libldap* /usr/lib/
echo '/usr/local/lib' >> /etc/ld.so.conf && /sbin/ldconfig

#PHP7.0-7.3版本无需编译安装oniguruma
tar zxvf oniguruma-6.9.4.tar.gz
cd oniguruma-6.9.4/
./autogen.sh
./configure --libdir=/lib64
make
make install
make clean
cd ../

#PHP7.0-7.3版本无需升级libzip
yum -y remove libzip libzip-devel
yum -y install https://rpms.remirepo.net/enterprise/7/remi/x86_64/libzip-last-1.1.3-1.el7.remi.x86_64.rpm
yum -y install https://rpms.remirepo.net/enterprise/7/remi/x86_64/libzip-last-devel-1.1.3-1.el7.remi.x86_64.rpm

② 编译安装PHP7.4:

tar zxvf php-7.4.15.tar.gz
cd php-7.4.15/
./configure --prefix=/usr/local/webserver/php --with-config-file-path=/usr/local/webserver/php/etc --with-mysqli --with-pdo-mysql --with-mysql-sock=/data0/mysql/3306/mysql.sock --with-iconv-dir=/usr/local --with-freetype --with-jpeg --with-webp --with-zlib --with-libxml --disable-mysqlnd-compression-support --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --enable-mbregex --enable-fpm --enable-gd --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --with-zip --enable-soap --enable-mbstring --enable-opcache
make ZEND_EXTRA_LIBS=\'-liconv\'
make install
cp php.ini-production /usr/local/webserver/php/etc/php.ini
mv /usr/local/webserver/php/etc/php-fpm.conf.default /usr/local/webserver/php/etc/php-fpm.conf
mv /usr/local/webserver/php/etc/php-fpm.d/www.conf.default /usr/local/webserver/php/etc/php-fpm.d/www.conf
make clean

PHP7.3~7.0版本编译参数

./configure --prefix=/usr/local/webserver/php --with-config-file-path=/usr/local/webserver/php/etc --with-mysqli --with-pdo-mysql --with-mysql-sock=/data0/mysql/3306/mysql.sock --with-iconv-dir=/usr/local --with-freetype-dir --with-jpeg-dir --with-png-dir --with-webp-dir --with-zlib --with-libxml-dir=/usr --disable-mysqlnd-compression-support --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --enable-mbregex --enable-fpm --with-gd --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-zip --enable-soap --enable-mbstring --enable-opcache

③ 编译安装PHP7.4扩展模块:

编译安装openssl扩展

cd ext/openssl/
mv config0.m4 config.m4
/usr/local/webserver/php/bin/phpize
./configure --with-php-config=/usr/local/webserver/php/bin/php-config
make
make install
make clean
cd ../../../

注:当openssl版本较新时,编译PHP使用 –with-openssl 参数会报错,故采用该方法

编译安装redis扩展

tar zxvf redis-5.3.3.tgz
cd redis-5.3.3/
/usr/local/webserver/php/bin/phpize
./configure --with-php-config=/usr/local/webserver/php/bin/php-config
make
make install
make clean
cd ../

PHP扩展均可参照该方法编译安装
在 php.ini 中使用 extension = xxx.so 加载即可

④ 修改PHP配置文件:

修改 /usr/local/webserver/php/etc/php.ini 中的以下参数

disable_functions = passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server,fsocket,popen
short_open_tag = On
realpath_cache_size = 512k
expose_php = Off
max_execution_time = 600
memory_limit = 256M
post_max_size = 200M
upload_max_filesize = 10M
date.timezone = Asia/Shanghai
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379"
session.use_strict_mode = 1
opcache.enable=1
opcache.enable_cli = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 10000
opcache.max_wasted_percentage=5
opcache.use_cwd=1
opcache.validate_timestamps=1
opcache.revalidate_freq = 60
opcache.consistency_checks=0
extension_dir = "/usr/local/webserver/php/lib/php/extensions/no-debug-non-zts-20190902"

#并在下方添加
zend_extension = "/usr/local/webserver/php/lib/php/extensions/no-debug-non-zts-20190902/opcache.so"
extension = "openssl.so"
extension = "redis.so"

⑤ 创建www用户和用户组:

/usr/sbin/groupadd www
/usr/sbin/useradd -g www www

⑥ 设置php-fpm:

vi /usr/local/webserver/php/etc/php-fpm.conf

#修改以下参数
pid = run/php-fpm.pid
log_level = warning
emergency_restart_threshold = 20
emergency_restart_interval = 60s
process_control_timeout = 5s

保存后

vi /usr/local/webserver/php/etc/php-fpm.d/www.conf

#修改以下参数
user = www
group = www
listen = /dev/shm/php-cgi.sock
listen.backlog = -1
listen.owner = www
listen.group = www
listen.mode = 0660
pm = static
pm.max_children = 46
pm.start_servers = 31
pm.min_spare_servers = 16
pm.max_spare_servers = 46
pm.process_idle_timeout = 10s
pm.max_requests = 2048
pm.status_path = /status
slowlog = log/slow.log
request_terminate_timeout = 120
rlimit_files = 51200
rlimit_core = 0
catch_workers_output = yes

⑦ 创建服务脚本文件

vi /usr/lib/systemd/system/php-fpm.service
#输入以下内容
[Unit]
Description=The PHP FastCGI Process Manager
After=syslog.target network.target

[Service]
Type=forking
PIDFile=/usr/local/webserver/php/var/run/php-fpm.pid
ExecStart=/usr/local/webserver/php/sbin/php-fpm --fpm-config /usr/local/webserver/php/etc/php-fpm.conf
ExecReload=/bin/kill -USR2 $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

加入启动项

systemctl enable php-fpm

启动php-fpm服务

systemctl start php-fpm

[返回目录]


五、安装 Nginx 1.17

建议选择Nginx的分支版本Tengine,拥有更强的性能与扩展。

① 编译安装LuaJIT:

使Nginx支持Lua扩展

tar zxvf luajit2-2.1-20200102.tar.gz
cd luajit2-2.1-20200102/
make install PREFIX=/usr/local/luajit
cd ../
echo -e '\n\nexport LUAJIT_LIB=/usr/local/luajit/lib\nexport LUAJIT_INC=/usr/local/luajit/include/luajit-2.1\n' >> /etc/profile && source /etc/profile
echo '/usr/local/luajit/lib' >> /etc/ld.so.conf.d/luajit.conf && /sbin/ldconfig

② 编译安装Tengine:

patch OpenResty 的Nginx补丁

使Nginx支持 ssl_session_fetch_by_lua*ssl_certificate_by_lua* 参数,一般用于Nginx集群时 SSL Seesion ID 重用共享,降低服务器压力

OpenResty官方提供的两个patch不支持Tengine
tengine-2.3.2-ssl_cert_sees_cb_yield.patch 进行了修正与合并

tar zxvf tengine-2.3.2.tar.gz
tar zxvf pcre-8.44.tar.gz
tar zxvf ngx_devel_kit-0.3.1.tar.gz
tar zxvf lua-ssl-nginx-module-0.01rc3.tar.gz
git clone https://github.com/google/ngx_brotli.git && cd ngx_brotli && git submodule update --init && cd ../
cd tengine-2.3.2/
patch -p1 < /data0/software/tengine-2.3.2-ssl_cert_sees_cb_yield.patch
./configure --user=www --group=www --prefix=/usr/local/webserver/nginx --with-pcre=/data0/software/pcre-8.44 --with-pcre-jit --with-openssl=/data0/software/openssl-1.1.1g --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-threads --with-file-aio --with-stream --with-stream_ssl_module --with-http_v2_module --with-http_ssl_module --add-module=modules/ngx_http_concat_module --add-module=modules/ngx_http_trim_filter_module --add-module=modules/ngx_http_slice_module --add-module=modules/ngx_http_lua_module --add-module=/data0/software/lua-ssl-nginx-module-0.01rc3 --add-module=/data0/software/ngx_devel_kit-0.3.1 --with-ld-opt='-Wl,-rpath,/usr/local/luajit/lib -ljemalloc' --add-module=/data0/software/ngx_brotli
make
make install
make clean
mkdir -p /data0/htdocs/conf/
mkdir -p /data0/htdocs/ssl/
cd ../

上述编译参数已安装常用模块,可以根据业务需求在 ./configure 中添加编译参数,安装更多模块

若不使用LUA模块,可参考如下参数编译

./configure --user=www --group=www --prefix=/usr/local/webserver/nginx --with-pcre=/data0/software/pcre-8.44 --with-pcre-jit --with-openssl=/data0/software/openssl-1.1.1k --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-threads --with-file-aio --with-stream --with-stream_ssl_module --with-http_v2_module --with-http_ssl_module --add-module=modules/ngx_http_concat_module --add-module=modules/ngx_http_trim_filter_module --add-module=modules/ngx_http_slice_module --with-ld-opt='-ljemalloc' --with-jemalloc=/data0/software/jemalloc-5.2.1 --add-module=/data0/software/ngx_brotli

Tengine 自身实现的模块,自2.3.x版本后已全部剥离到 modules 目录下,应使用如下方法添加编译

#Tengine <module_name> 名称查看
ls /data0/software/tengine-2.3.2/modules/
#Tengine <module_name> 添加编译
--add-module=modules/<module_name>

Nginx 第三方模块应使用如下方法添加编译

--add-module=/data0/software/<module_name>
#/data0/software/ 为插件解压缩后所在的绝对目录地址
#<module_name> 为插件解压缩后的文件夹名称

③ 创建Tengine日志目录:

mkdir -p /data1/logs/
chmod +w /data1/logs/
chown -R www:www /data1/logs/

④ 创建Tengine配置文件:

mv /usr/local/webserver/nginx/conf/nginx.conf  /usr/local/webserver/nginx/conf/nginx.conf.old
vi /usr/local/webserver/nginx/conf/nginx.conf

输入并保存以下内容

user  www www;
worker_processes  auto;
worker_cpu_affinity  auto;

error_log  /data1/logs/nginx_error.log  crit;
pid        /usr/local/webserver/nginx/nginx.pid;

worker_rlimit_nofile  65535;

events {
    use epoll;
    worker_connections  65535;
}

thread_pool default threads=32 max_queue=65536;

http {
    include           mime.types;
    default_type      application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    log_format  https '$remote_addr - $remote_user [$time_local] '
                      '$ssl_protocol/$ssl_cipher <$ssl_session_id> $ssl_session_reused '
                      '"$request" $status $body_bytes_sent '
                      '"$http_referer" "$http_user_agent" "$http_x_forwarded_for"';

    sendfile          on;
    tcp_nopush        on;
    tcp_nodelay       on;
    keepalive_timeout 65;

    trim              off;

    gzip              on;
    gzip_min_length   1k;
    gzip_buffers      4 16k;
    gzip_http_version 1.0;
    gzip_comp_level   4;
    gzip_vary         on;
    gzip_disable      "MSIE [1-6]\.(?!.*SV1)";
    gzip_types        text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml text/javascript application/javascript application/x-javascript text/x-json application/json application/x-web-app-manifest+json text/css text/plain text/x-component font/eot font/opentype font/otf font/truetype font/ttf application/x-font-ttf application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype image/x-icon image/x-win-bitmap;

    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 128k;

    server_tokens  off;
    limit_req_zone $binary_remote_addr zone=qps:10m rate=1r/s;

    server {
            listen 80 default_server;
            listen 443 default_server ssl;
            server_name _;
            access_log /data1/logs/unknown.log https;
            ssl_certificate /data0/htdocs/ssl/default.pem;
            ssl_certificate_key /data0/htdocs/ssl/default.key;
            ssl_prefer_server_ciphers on;
            ssl_protocols TLSv1.2 TLSv1.3;
            ssl_ciphers [TLS13+AESGCM+AES128|TLS13+AESGCM+AES256|TLS13+CHACHA20]:[EECDH+ECDSA+AESGCM+AES128|EECDH+ECDSA+CHACHA20]:EECDH+ECDSA+AESGCM+AES256:EECDH+ECDSA+AES128+SHA:EECDH+ECDSA+AES256+SHA:[EECDH+aRSA+AESGCM+AES128|EECDH+aRSA+CHACHA20]:EECDH+aRSA+AESGCM+AES256:EECDH+aRSA+AES128+SHA:EECDH+aRSA+AES256+SHA:RSA+AES128+SHA:RSA+AES256+SHA:RSA+3DES;
            limit_req zone=qps burst=2;
            return 444;
    }

include /data0/htdocs/conf/*.conf;
}

生成 default.key 与 default.pem

#使nginx.conf的default_server不报错
openssl genrsa -out /data0/htdocs/ssl/default.key
openssl req -new -x509 -key /data0/htdocs/ssl/default.key -out /data0/htdocs/ssl/default.pem -days 3650

一般情况下 nginx.conf 配置完成后无需修改,只需使用default_server为80/443端口添加默认站点,返回为444状态禁止nginx未绑定的域名访问即可

建议在 include 指向的目录中添加新站点,这样有助nginx的运维

⑤ 添加站点:

在include指向的 /data0/htdocs/conf/ 目录下,生成 .conf 结尾的站点配置文件,检测后reload nginx即可完成站点的添加

#添加变量环境
echo -e '\nexport PATH=/usr/local/webserver/nginx/sbin:$PATH\n' >> /etc/profile && source /etc/profile

#检测配置文件
nginx -t

#加载配置文件
nginx -s reload

同理删除对应的.conf文件后reload nginx即可永久删除站点,也可以更改.conf文件的扩展名(例如.conf.del)临时删除站点

⑥ 添加HTTP测试站点:

#创建测试站点目录
mkdir -p /data0/htdocs/wwwroot/
chmod +w /data0/htdocs/wwwroot/
chown -R www:www /data0/htdocs/wwwroot/

#生成测试站点配置文件
vi /data0/htdocs/conf/wwwroot.conf

输入并保存以下内容

server {
        listen 80 reuseport;
        server_name  ip_or_domain.name;
        root   /data0/htdocs/wwwroot;
        index  index.html index.htm index.php;

        #charset gb2312;

        client_max_body_size 0;

        #limit_req zone=qps burst=5;

        #trim on;
        #trim_js on;
        #trim_css on;

        access_log  /data1/logs/wwwroot-logs.log  main;

        gzip              on;

        brotli            on;
        brotli_comp_level 6;
        brotli_static     on;
        brotli_min_length 1k;
        brotli_types      text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml text/javascript application/javascript application/x-javascript text/x-json application/json application/x-web-app-manifest+json text/css text/plain text/x-component font/eot font/opentype font/otf font/truetype font/ttf application/x-font-ttf application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype image/x-icon image/x-win-bitmap;

        location ~ \.php$ {
            try_files $uri =404;
            fastcgi_pass  unix:/dev/shm/php-cgi.sock;
            fastcgi_index index.php;
            include fastcgi.conf;
        }

        location /status {
            stub_status  on;
            access_log  off;
        }

        # Rewrite rules
        location / {
            try_files $uri $uri/ /index.php$is_args$args;
        }

        location ~* \.(jpg|jpeg|gif|png|bmp|swf|ico|rar|zip|7z|tgz|tar.gz|txt|flv|mid|doc|docx|pptx|ppt|xlsx|xls|pdf|mp3|wma)$ {
            expires     30d;
            access_log  off;
            aio         threads;
            directio    5m;
        }

        location ~* \.(js|css)?$ {
            expires     24h;
            access_log  off;
            concat      on;
            concat_max_files 20;
        }

        location ~* \.(eot|ttf|woff|woff2|svg)$ {
            expires     max;
            access_log  off;
            #add_header Access-Control-Allow-Origin *;
            #add_header Access-Control-Allow-Headers X-Requested-With;
            #add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
        }

        location = /favicon.ico { 
            log_not_found off; 
        }

        location = /robots.txt {
            #deny all;
            log_not_found off;
        }

        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        location ~ /\.ht {
            deny  all;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}
}

配置文件已做优化,只需要修改 server_name、root、access_log 三个参数即可,其它参数请根据实际需求调整或添加

测试站点目录一般用于存放php探针、phpmyadmin等工具,或测试web应用

将 server_name 设置成服务器外网IP时,可通过IP访问该测试站点。安全考虑,日常不使用时应重命名 wwwroot 目录,这样可以在不reload nginx的情况下禁止访问

注意:同一端口有多个站点时,listen指令后的reuseport参数只需要添加一次, 否则NGINX会报错。

示例:

server {
listen 80 reuseport;
...
}

server {
listen 80;
...
}

⑦ 添加HTTPS业务站点:

#创建业务站点目录
mkdir -p /data0/htdocs/sitename.com/
chmod +w /data0/htdocs/sitename.com/
chown -R www:www /data0/htdocs/sitename.com/

#生成业务站点配置文件
vi /data0/htdocs/conf/sitename.com.conf

输入并保存以下内容(将sitename.com替换成业务使用的域名)

server {
        listen                      80;
        listen                      443 ssl http2 reuseport;
        server_name                 sitename.com;
        root                        /data0/htdocs/sitename.com;
        index                       index.html index.htm index.php;

        resolver                    223.5.5.5 8.8.8.8 valid=300s;
        resolver_timeout            5s;

        ssl_stapling                on;
        ssl_stapling_verify         on; 
        ssl_trusted_certificate     /data0/htdocs/ssl/sitename.pem;
        ssl_certificate             /data0/htdocs/ssl/sitename.pem;
        ssl_certificate_key         /data0/htdocs/ssl/sitename.key;
        ssl_session_tickets         on;
        ssl_session_ticket_key      /data0/htdocs/ssl/session_ticket.key;
        ssl_session_timeout         1440m;
        ssl_session_cache           shared:SSL:128m;
        ssl_protocols               TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers   on;
        ssl_ciphers [TLS13+AESGCM+AES128|TLS13+AESGCM+AES256|TLS13+CHACHA20]:[EECDH+ECDSA+AESGCM+AES128|EECDH+ECDSA+CHACHA20]:EECDH+ECDSA+AESGCM+AES256:EECDH+ECDSA+AES128+SHA:EECDH+ECDSA+AES256+SHA:[EECDH+aRSA+AESGCM+AES128|EECDH+aRSA+CHACHA20]:EECDH+aRSA+AESGCM+AES256:EECDH+aRSA+AES128+SHA:EECDH+aRSA+AES256+SHA:RSA+AES128+SHA:RSA+AES256+SHA:RSA+3DES;

        add_header Strict-Transport-Security "max-age=15768000; includeSubdomains";
        add_header X-Content-Type-Options "nosniff";
        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-XSS-Protection "1; mode=block";

        #charset gb2312;

        client_max_body_size 0;

        #limit_req zone=qps burst=5;

        trim on;
        trim_js on;
        trim_css on;

        access_log  "pipe:rollback /data1/logs/sitename/sitename.com.log interval=1h baknum=48 maxsize=2G" https buffer=64k flush=5s;

        gzip              on;

        brotli            on;
        brotli_comp_level 6;
        brotli_static     on;
        brotli_min_length 1k;
        brotli_types      text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml text/javascript application/javascript application/x-javascript text/x-json application/json application/x-web-app-manifest+json text/css text/plain text/x-component font/eot font/opentype font/otf font/truetype font/ttf application/x-font-ttf application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype image/x-icon image/x-win-bitmap;

        location ~ \.php$ {
            try_files $uri =404;
            fastcgi_pass  unix:/dev/shm/php-cgi.sock;
            fastcgi_index index.php;
            include fastcgi.conf;
        }

        # Rewrite rules
        location / {
            if ($server_port = 80 ) {
                return 301 https://$host$request_uri;
            }
            try_files $uri $uri/ /index.php$is_args$args;
        }

        location ~* \.(jpg|jpeg|gif|png|bmp|swf|ico|rar|zip|7z|tgz|tar.gz|txt|flv|mid|doc|docx|pptx|ppt|xlsx|xls|pdf|mp3|wma)$ {
            valid_referers none blocked sitename.com *.sitename.com *.baidu.com *.baiducontent.com;
            if ($invalid_referer) {
                 return 412;
            }
            expires     30d;
            access_log  off;
            aio         threads;
            directio    5m;
            break;
        }

        location ~* \.(js|css)?$ {
            expires     7d;
            access_log  off;
            concat      on;
            concat_max_files 20;
        }

        location ~* \.(eot|ttf|woff|woff2|svg)$ {
            expires     max;
            access_log  off;
            #add_header Access-Control-Allow-Origin *;
            #add_header Access-Control-Allow-Headers X-Requested-With;
            #add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
        }

        location = /favicon.ico { 
            log_not_found off; 
        }

        location = /robots.txt {
            #deny all;
            log_not_found off;
        }

        location ~ /\.(?!well-known).* {
            deny all;
        }

        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}
}

与HTTP站点相比,增加了对 SSL v1.3 与 HTTP 2.0 的支持,并做了生产环境的优化,参数可以在该模板的基础上,根据业务需求调整或添加

但必须将以下参数修改为实际业务的参数

server_name
root
ssl_trusted_certificate
ssl_certificate
ssl_certificate_key
access_log

生成 session_ticket.key

openssl rand 48 > /data0/htdocs/ssl/session_ticket.key

单台nginx服务器使用,仅需不定期更换 session_ticket.key 秘钥既可。如果需要管理一整套nginx集群,可使用 lua-ssl-nginx-module 模块,实现集群层面上的密钥轮换

上传证书与秘钥

#证书可以在阿里云、腾讯云等平台免费申请
#一般将证书上传至 /data0/htdocs/ssl/ 目录即可

#或复制证书与秘钥的内容创建文件
vi /data0/htdocs/ssl/sitename.pem
vi /data0/htdocs/ssl/sitename.key

ssl_trusted_certificate 应配置为ca根证书+和中级ca证书的列表

一般国内申请的免费证书只会下载到一个pem文件,已包含全部证书列表,所以 ssl_trusted_certificate 和 ssl_certificate 使用同一个pem证书即可

⑧ 创建服务脚本:

vi /usr/lib/systemd/system/nginx.service
#输入以下内容
[Unit]
Description=The nginx HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/usr/local/webserver/nginx/nginx.pid
ExecStartPre=/usr/local/webserver/nginx/sbin/nginx -t
ExecStart=/usr/local/webserver/nginx/sbin/nginx
ExecReload=/usr/local/webserver/nginx/sbin/nginx -s reload
ExecStop=/usr/local/webserver/nginx/sbin/nginx -s stop
PrivateTmp=true

[Install]
WantedBy=multi-user.target

设置systemd延时防止pid报错

mkdir -p /etc/systemd/system/nginx.service.d
printf "[Service]\nExecStartPost=/bin/sleep 0.1\n" > /etc/systemd/system/nginx.service.d/override.conf
systemctl daemon-reload

加入启动项

systemctl enable nginx

启动Tengine服务

systemctl start nginx

⑨ 运维问题补充:

在HTTPS日志格式中,加入了 $ssl_session_reused 以便查看 ssl seesion 的复用率,但当站点开启HTTP 2.0支持时, 短时间内重新访问页面,日志会返回未重用状态

原因如下,可以临时关闭HTTP 2.0,使用HTTP 1.1进行测试

In HTTP/2 connections are usually kept open for a long time, and you
aren't likely to see actual SSL session reuse due to this - in most
cases you will see just another request in an already opened
connection.

[返回目录]


六、设置与优化 CentOS 7

① 配置防火墙:

如果安装了FirewallD,需要关闭FirewallD服务

systemctl stop firewalld
systemctl disable firewalld

配置iptables防火墙

vi /etc/sysconfig/iptables

#修改防火墙配置文件规则如下
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -s 127.0.0.1/32 -d 127.0.0.1/32 -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp --icmp-type any -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -j REJECT --reject-with icmp-port-unreachable
-A OUTPUT -j ACCEPT
COMMIT

#如需外部访问MySQL应增加一条3306端口规则
-A INPUT -p tcp -m tcp --dport 3306 -j ACCEPT

#开启防火墙
systemctl enable iptables
systemctl restart iptables

如果使用的是支持硬件防火墙设置的云主机,可以关闭 iptables

② 开启时间同步:

systemctl enable ntpd
systemctl start ntpd

#使用中国时区(一般无需修改)
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

③ 修改ulimit连接数最大值:

echo -e '\n\nulimit -SHn 65535\n' >> /etc/profile && source /etc/profile

④ 禁止Crontab产生邮件:

crontab -e
#在第一行输入
MAILTO=""

可以避免 /var/mail/root 体积快速增长

⑤ 系统性能优化:

vi /etc/sysctl.conf
#在最下方添加
net.ipv4.tcp_slow_start_after_idle 0
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 65536
net.core.netdev_max_backlog =  32768
net.core.somaxconn = 32768
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_fin_timeout = 30
net.ipv4.ip_local_port_range = 1024  65535
vm.swappiness = 10

使配置立即生效

sysctl -p

至此,一台高性能的Web服务器配置完毕了!