Nginx 安装
安装前准备
预先安装额外的依赖
yum -y install pcre-devel
yum -y install openssl openssl-devel
yum -y install gcc gcc-c++ autoconf automake make
下载最新版本的 nginx 源码文件
在 nginx 官网下载 Stable version
的源码包。
- 在 /usr/local/ 下创建 nginx ⽂件夹并进⼊
cd /usr/local/
mkdir nginx
cd nginx
- 将 Nginx 安装包解压到 /usr/local/nginx 中即可
tar -zxvf /home/nginx-1.18.0.tar.gz -C /usr/local/nginx
解压完之后, /usr/local/nginx ⽬录中会出现⼀个 nginx-1.18.0 的⽬录
开启 Brotli 压缩算法
Brotli 是一种通用的无损压缩算法。
它结合使用 LZ77 算法的一个现代变体、霍夫曼编码编码和二阶上下文建模来压缩数据,提供与目前可用的最佳通用压缩算法相当的压缩率。Brotli 提供比 gzip 更好(小)的压缩率,并具有与 deflate 相当的压缩速度。但 brotli 压缩速度比 Gzip 压缩慢,因此 gzip 可能更适合于压缩不可缓存的内容。
提示
Brotli 与大多数现代浏览器兼容,但只能再 https 下使用。
下载 Brotli
我们将 google/ngx_brotli
和 google/brotli
下载到 home/nginx
目录。
cd /home/nginx
wget https://github.com/google/ngx_brotli/archive/refs/tags/v1.0.0rc.tar.gz -O /home/ngx_brotli-1.0.0rc.tar.gz
wget https://github.com/google/brotli/archive/refs/tags/v1.0.9.tar.gz -O /home/brotli-1.0.9.tar.gz
# 解压文件备用
tar -zxvf /home/ngx_brotli-1.0.0rc.tar.gz -C ./
tar -zxvf /home/brotli-1.0.9.tar.gz -C ./
# 将 brotli 放入 ngx_brotli 的 deps/brotli 目录中
mv brotli-1.0.9/* ./ngx_brotli-1.0.0rc/deps/brotli 目录中
# 将 ngx_brotli 移动到 nginx 的目录
mv ngx_brotli-1.0.0rc /usr/local/nginx/ngx_brotli
您的服务器上下载GitHub资源可能很慢,所以我准备了完整的安装包下载。解压后即可使用
wget https://gitee.com/WangJincheng4869/ngx_brotli/raw/master/ngx_brotli.tar.gz tar -zxvf ngx_brotli.tar.gz -C /usr/local/nginx/ # 将 ngx_brotli 移动到 nginx 的目录 mv ngx_brotli-1.0.0rc /usr/local/nginx/ngx_brotli
编译安装Nginx
cd nginx-1.18.0
./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-pcre --with-http_gzip_static_module --add-module=/usr/local/nginx/ngx_brotli
make && make install
安装完成后,Nginx的可执⾏⽂件位置位于
/usr/local/nginx/sbin/nginx
注意其配置⽂件位于:
/usr/local/nginx/conf/nginx.conf
查看压缩配置是否生效
浏览器控制台查看
- 按下
F12
或Ctrl + Shift + I
打开开发者工具 - 切换
Network
(中文下为网络
),刷新页面。 - 找到一个
js
或css
文件查看Response Headers
(中文为响应头
) 选项中的Content-Encoding
的选项,值应为Content-Encoding: br
或Content-Encoding: gzip
,如果没有则代表压缩未启用(要查看原文件大小,我们推荐的配置文件中,小于1K的文件不进行压缩,故小于1K的文件没有Content-Encoding
信息为正常现象)
curl 命令查询
可使用以下命令进行测试,将 url 中的路径替换为项目文件的一个 js 或 css 文件,html 文件也可以。
curl -H "Accept-Encoding:gzip,br" -I https://wxspirit.simperfect.com/basic-paper-cloud-docs/
会返回类似以下信息,其中第 10 行内容 Content-Encoding: br
代表压缩已经生效。
HTTP/1.1 200 OK
Date: Tue, 12 Dec 2023 03:29:32 GMT
Content-Type: text/html
Connection: keep-alive
Last-Modified: Sat, 09 Dec 2023 09:49:22 GMT
Vary: Accept-Encoding
ETag: W/"65743822-9bf9"
X-XSS-Protection: 1
X-Content-Type-Options: nosniff
Content-Encoding: br
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Content-Security-Policy: default-src 'self' http: https: data: blob: 'unsafe-inline' 'unsafe-eval'
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
服务注册
创建配置文件
vim /usr/lib/systemd/system/nginx.service
把下面文件内容编写在 nginx.service
文件中
[Unit]
Description=nginx
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target
重新加载系统服务
systemctl daemon-reload
设置开机启动
systemctl enable nginx.service
返回类似Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.
的消息,代表注册成功!
[Unit]配置文件参数介绍
Description
有利于人类阅读的、对单元进行简单描述的字符串。将被 systemd 或其他程序用来标记此单元, 这个字符串应该只用于识别此单元即可,不需要过分说明。例如 "
Apache2 Web Server
" 就是一个好例子,而 "high-performance light-weight HTTP server
" (太通用) 与 "Apache2
" (信息太少) 则是两个坏例子。因为 systemd 将会把这个字符串用于状态信息中("Starting *
description*...
", "Started *
description*.
", "Reached target *
description*.
", "Failed to start *
description*.
"),所以这个字符串应该表现的像一个名词, 而不是一个完整的句子或带有动词的短语。比如 "exiting the container
" 或 "updating the database once per day.
" 就是典型的坏例子。
After
强制指定单元之间的先后顺序,接受一个空格分隔的单元列表。
假定
foo.service
单元包含After=bar.service
设置, 那么当两个单元都需要启动的时候,foo.service
将会一直延迟到bar.service
启动完毕之后再启动。 注意,停止顺序与启动顺序正好相反,也就是说, 只有当foo.service
完全停止后,才会停止bar.service
单元。
如果想了解更多,可以移步至 http://www.jinbuguo.com/systemd/systemd.unit.html查看更多
文件中After参数值介绍
network.target
此目标单元用于表明网络可用, 但是由于其定义的脆弱性, 它事实上只有一个用途: 在关机时, 确保所有包含
After=network.target
的单元 都在关闭网络(无论网络实际处于何种状态)之前被优先关闭。 因此对于那些需要在关闭时访问网络的服务来说, 应该将其自身排在此目标单元之后(但是不要包含此目标单元)。
remote-fs.target
专用于集合远程文件系统挂载点的目标单元,其他与
local-fs.target
相似。
After=remote-fs.target
将被自动添加到所有引用了 "$remote_fs
" 的SysV初始化脚本单元中。
nss-lookup.target
专用于标记所有主机与网络的名字查找服务都已可用的目标单元。 注意,这不包括 UNIX 用户/组的名字查找功能(这是
nss-user-lookup.target
的功能)。 所有依赖于主机与网络的名字查找服务的服务单元,都必须排在该目标单元之后启动, 但却不应该包含它。After=nss-lookup.target
将被自动添加到所有引用了 "$named
" 的SysV初始化脚本单元中。
如果想了解更多,可以移步至 http://www.jinbuguo.com/systemd/systemd.special.html查看更多
[Service]配置文件参数介绍
Type
设置进程的启动类型。必须设为
simple
,exec
,forking
,oneshot
,dbus
,notify
,idle
之一:
- 如果设为
simple
(当设置了ExecStart=
、 但是没有设置Type=
与BusName=
时,这是默认值), 那么ExecStart=
进程就是该服务的主进程, 并且 systemd 会认为在创建了该服务的主服务进程之后,该服务就已经启动完成。 如果此进程需要为系统中的其他进程提供服务, 那么必须在该服务启动之前先建立好通信渠道(例如套接字), 这样,在创建主服务进程之后、执行主服务进程之前,即可启动后继单元, 从而加快了后继单元的启动速度。 这就意味着对于simple
类型的服务来说, 即使不能成功调用主服务进程(例如User=
不存在、或者二进制可执行文件不存在), systemctl start 也仍然会执行成功。exec
与simple
类似,不同之处在于, 只有在该服务的主服务进程执行完成之后,systemd 才会认为该服务启动完成。 其他后继单元必须一直阻塞到这个时间点之后才能继续启动。换句话说,simple
表示当fork()
函数返回时,即算是启动完成,而exec
则表示仅在fork()
与execve()
函数都执行成功时,才算是启动完成。 这就意味着对于exec
类型的服务来说, 如果不能成功调用主服务进程(例如User=
不存在、或者二进制可执行文件不存在), 那么 systemctl start 将会执行失败。- 如果设为
forking
,那么表示ExecStart=
进程将会在启动过程中使用fork()
系统调用。 也就是当所有通信渠道都已建好、启动亦已成功之后,父进程将会退出,而子进程将作为主服务进程继续运行。 这是传统UNIX守护进程的经典做法。 在这种情况下,systemd 会认为在父进程退出之后,该服务就已经启动完成。 如果使用了此种类型,那么建议同时设置PIDFile=
选项,以帮助 systemd 准确可靠的定位该服务的主进程。 systemd 将会在父进程退出之后 立即开始启动后继单元。oneshot
与simple
类似,不同之处在于, 只有在该服务的主服务进程退出之后,systemd 才会认为该服务启动完成,才会开始启动后继单元。 此种类型的服务通常需要设置RemainAfterExit=
选项。 当Type=
与ExecStart=
都没有设置时,Type=``oneshot
就是默认值。dbus
与simple
类似,不同之处在于, 该服务只有获得了BusName=
指定的 D-Bus 名称之后,systemd 才会认为该服务启动完成,才会开始启动后继单元。 设为此类型相当于隐含的依赖于dbus.socket
单元。 当设置了BusName=
时, 此类型就是默认值。notify
与exec
类似,不同之处在于, 该服务将会在启动完成之后通过sd_notify()
之类的接口发送一个通知消息。systemd 将会在启动后继单元之前, 首先确保该进程已经成功的发送了这个消息。如果设为此类型,那么下文的NotifyAccess=
将只能设为非none
值。如果未设置NotifyAccess=
选项、或者已经被明确设为none
,那么将会被自动强制修改为main
。注意,目前Type=``notify
尚不能与PrivateNetwork=``yes
一起使用。idle
与simple
类似,不同之处在于, 服务进程将会被延迟到所有活动任务都完成之后再执行。 这样可以避免控制台上的状态信息与shell脚本的输出混杂在一起。 注意:(1)仅可用于改善控制台输出,切勿将其用于不同单元之间的排序工具; (2)延迟最多不超过5秒, 超时后将无条件的启动服务进程。建议对长时间持续运行的服务尽可能使用
Type=simple
(这是最简单和速度最快的选择)。 注意,因为simple
类型的服务 无法报告启动失败、也无法在服务完成初始化后对其他单元进行排序, 所以,当客户端需要通过仅由该服务本身创建的IPC通道(而非由 systemd 创建的套接字或 D-bus 之类)连接到该服务的时候,simple
类型并不是最佳选择。在这种情况下,notify
或dbus
(该服务必须提供 D-Bus 接口) 才是最佳选择, 因为这两种类型都允许服务进程精确的安排 何时算是服务启动成功、何时可以继续启动后继单元。notify
类型需要服务进程明确使用sd_notify()
函数或类似的API, 否则,可以使用forking
作为替代(它支持传统的UNIX服务启动协议)。 最后,如果能够确保服务进程调用成功、服务进程自身不做或只做很少的初始化工作(且不大可能初始化失败), 那么exec
将是最佳选择。 注意,因为使用任何simple
之外的类型都需要等待服务完成初始化,所以可能会减慢系统启动速度。 因此,应该尽可能避免使用simple
之外的类型(除非必须)。另外,也不建议对长时间持续运行的服务使用idle
或oneshot
类型。
PIDFile
该服务PID文件的路径(一般位于
/run/
目录下)。 强烈建议在Type=forking
的情况下明确设置此选项。 如果设为相对路径,那么表示相对于/run/
目录。 systemd 将会在此服务启动完成之后,从此文件中读取主服务进程的PID 。 systemd 不会写入此文件,但会在此服务停止后删除它(若仍然存在)。 PID文件的拥有者不必是特权用户, 但是如果拥有者是非特权用户,那么必须施加如下安全限制: (1)不能是一个指向其他拥有者文件的软连接(无论直接还是间接); (2)其中的PID必须指向一个属于该服务的进程。
ExecStartPre
设置在执行
ExecStart=
之前/后执行的命令行。 语法规则与ExecStart=
完全相同。 如果设置了多个命令行, 那么这些命令行将以其在单元文件中出现的顺序 依次执行。如果某个无 "
-
" 前缀的命令行执行失败, 那么剩余的命令行将不会被继续执行, 同时该单元将变为失败(failed)状态。仅在所有无 "
-
" 前缀的ExecStartPre=
命令全部执行成功的前提下, 才会继续执行ExecStart=
命令。注意,不可将
ExecStartPre=
用于 需要长时间执行的进程。 因为所有由ExecStartPre=
派生的子进程 都会在启动ExecStart=
服务进程之前被杀死。
ExecStart
在启动该服务时需要执行的 命令行(命令+参数)。 有关命令行的更多细节, 可参见后文的"命令行"小节。
除非
Type=oneshot
,否则必须且只能设置一个命令行。 仅在Type=oneshot
的情况下,才可以设置任意个命令行(包括零个), 多个命令行既可以在同一个ExecStart=
中设置,也可以通过设置多个ExecStart=
来达到相同的效果。 如果设为一个空字符串,那么先前设置的所有命令行都将被清空。 如果不设置任何ExecStart=
指令, 那么必须确保设置了RemainAfterExit=yes
指令,并且至少设置一个ExecStop=
指令。 同时缺少ExecStart=
与ExecStop=
的服务单元是非法的(也就是必须至少明确设置其中之一)。命令行必须以一个可执行文件(要么是绝对路径、要么是不含任何斜线的文件名)开始, 并且其后的那些参数将依次作为"argv[1] argv[2] …"传递给被执行的进程。 可选的,可以在绝对路径前面加上各种不同的前缀表示不同的含义:
前缀 效果 " @
"如果在绝对路径前加上可选的 " @
" 前缀,那么其后的那些参数将依次作为"argv[0] argv[1] argv[2] …"传递给被执行的进程(注意,argv[0] 是可执行文件本身)。" -
"如果在绝对路径前加上可选的 " -
" 前缀,那么即使该进程以失败状态(例如非零的返回值或者出现异常)退出,也会被视为成功退出,但同时会留下错误日志。" +
"如果在绝对路径前加上可选的 " +
" 前缀,那么进程将拥有完全的权限(超级用户的特权),并且User=
,Group=
,CapabilityBoundingSet=
选项所设置的权限限制以及PrivateDevices=
,PrivateTmp=
等文件系统名字空间的配置将被该命令行启动的进程忽略(但仍然对其他ExecStart=
,ExecStop=
有效)。" !
"与 " +
" 类似(进程仍然拥有超级用户的身份),不同之处在于仅忽略User=
,Group=
,SupplementaryGroups=
选项的设置,而例如名字空间之类的其他限制依然有效。注意,当与DynamicUser=
一起使用时,将会在执行该命令之前先动态分配一对 user/group ,然后将身份凭证的切换操作留给进程自己去执行。" !!
"与 " !
" 极其相似,仅用于让利用 ambient capability 限制进程权限的单元兼容不支持 ambient capability 的系统(也就是不支持AmbientCapabilities=
选项)。如果在不支持 ambient capability 的系统上使用此前缀,那么SystemCallFilter=
与CapabilityBoundingSet=
将被隐含的自动修改为允许进程自己丢弃 capability 与特权用户的身份(即使原来被配置为禁止这么做),并且AmbientCapabilities=
选项将会被忽略。此前缀在支持 ambient capability 的系统上完全没有任何效果。"
@
", "-
" 以及 "+
"/"!
"/"!!
" 之一,可以按任意顺序同时混合使用。 注意,对于 "+
", "!
", "!!
" 前缀来说,仅能单独使用三者之一,不可混合使用多个。 注意,这些前缀同样也可以用于ExecStartPre=
,ExecStartPost=
,ExecReload=
,ExecStop=
,ExecStopPost=
这些接受命令行的选项。如果设置了多个命令行, 那么这些命令行将以其在单元文件中出现的顺序依次执行。 如果某个无 "
-
" 前缀的命令行执行失败, 那么剩余的命令行将不会被继续执行, 同时该单元将变为失败(failed)状态。当未设置
Type=forking
时, 这里设置的命令行所启动的进程 将被视为该服务的主守护进程。
ExecReload
这是一个可选的指令, 用于设置当该服务 被要求重新载入配置时 所执行的命令行。 语法规则与
ExecStart=
完全相同。另外,还有一个特殊的环境变量
$MAINPID
可用于表示主进程的PID, 例如可以这样使用:/bin/kill -HUP $MAINPID
注意,像上例那样,通过向守护进程发送复位信号, 强制其重新加载配置文件,并不是一个好习惯。 因为这是一个异步操作, 所以不适用于需要按照特定顺序重新加载配置文件的服务。 我们强烈建议将
ExecReload=
设为一个 能够确保重新加载配置文件的操作同步完成的命令行。
ExecStop
这是一个可选的指令, 用于设置当该服务被要求停止时所执行的命令行。 语法规则与
ExecStart=
完全相同。 执行完此处设置的所有命令行之后,该服务将被视为已经停止, 此时,该服务所有剩余的进程将会根据KillMode=
的设置被杀死(参见 systemd.kill(5))。 如果未设置此选项,那么当此服务被停止时, 该服务的所有进程都将会根据KillSignal=
的设置被立即全部杀死。 与ExecReload=
一样, 也有一个特殊的环境变量$MAINPID
可用于表示主进程的PID 。一般来说,不应该仅仅设置一个结束服务的命令而不等待其完成。 因为当此处设置的命令执行完之后, 剩余的进程会被按照
KillMode=
与KillSignal=
的设置立即杀死, 这可能会导致数据丢失。 因此,这里设置的命令必须是同步操作,而不能是异步操作。注意,仅在服务确实启动成功的前提下,才会执行
ExecStop=
中设置的命令。 如果服务从未启动或启动失败(例如,任意一个ExecStart=
,ExecStartPre=
,ExecStartPost=
中无 "-
" 前缀的命令执行失败或超时), 那么ExecStop=
将会被跳过。 如果想要无条件的在服务停止后执行特定的动作,那么应该使用ExecStopPost=
选项。 如果服务启动成功,那么即使主服务进程已经终止(无论是主动退出还是被杀死),也会继续执行停止操作。 因此停止命令必须正确处理这种场景,如果 systemd 发现在调用停止命令时主服务进程已经终止,那么将会撤销 $MAINPID 变量。重启服务的动作被实现为"先停止、再启动"。所以在重启期间,将会执行
ExecStop=
与ExecStopPost=
命令。 推荐将此选项用于那些必须在服务干净退出之前执行的命令(例如还需要继续与主服务进程通信)。当此选项设置的命令被执行的时候,应该假定服务正处于完全正常的运行状态,可以正常的与其通信。 如果想要无条件的在服务停止后"清理尸体",那么应该使用ExecStopPost=
选项。
PrivateTmp
PrivateTmp用于设置是否使用私有的tmp目录,为true表示,会使用私有的tmp目录。该目录会在服务启动时创建该目录,并且在关闭服务时删除该目录。
如果想了解更多,可以移步至 http://www.jinbuguo.com/systemd/systemd.service.html查看更多
[Install]配置文件参数介绍
WantedBy
接受一个空格分隔的单元列表, 表示在使用
systemctl enable
启用此单元时, 将会在每个列表单元的.wants/
或.requires/
目录中创建一个指向该单元文件的软连接。 这相当于为每个列表中的单元文件添加了Wants=此单元
或Requires=此单元
选项。 这样当列表中的任意一个单元启动时,该单元都会被启动。
如果想了解更多,可以移步至 [Install] 小节选项查看更多
multi-user.target
专用于启动多用户命令行环境(而非图形界面)的目标单元。 通常包含在
graphical.target
中。所有用于实现多用户命令行环境的单元的 "
[Install]
" 小节中, 都应该包含WantedBy=multi-user.target
指令, 以确保这些单元被添加到了multi-user.target
单元中。
相关命令(不局限与nginx,通用的命令)
systemctl is-enabled nginx.service #查询服务是否开机启动
systemctl enable nginx.service #开机运行服务
systemctl disable nginx.service #取消开机运行
systemctl start nginx.service #启动服务
systemctl stop nginx.service #停止服务
systemctl restart nginx.service #重启服务
systemctl reload nginx.service #重新加载服务配置文件
systemctl status nginx.service #查询服务运行状态
systemctl --failed #显示启动失败的服务
动态编译 Brotli(扩展内容,了解即可)
以下是扩充内容,不是必要步骤,上述其实已经完成了 nginx 的安装。如果按照上述步骤完成安装,则可以忽略本节内容。
nginx自1.9.11以后版本后支持动态模块,自此,给nginx添加模块再也不用重新编译nginx了,通过动态模块,你可以在运行时有有选择性的加载第三方或Nginx官方模块。新的实现方式通过API模块保持尽可能的向后兼容。
先进入解压后的nginx安装包目录,配置configure
,然后用make modules
。如果你的源码文件已经删除了,那么就要查看当前 nginx 版本后下载源码后继续
cd nginx-1.18.0
./configure --with-compat --add-dynamic-module=/usr/local/nginx/ngx_brotli --prefix=/usr/local/nginx --with-http_ssl_module --with-pcre --with-http_gzip_static_module
make modules
参数语法:--add-dynamic-module=[模块源码所在目录的绝对路径]
查看编译好的模块
ls ./objs/*.so
输出
./objs/ngx_http_brotli_filter_module.so ./objs/ngx_http_brotli_static_module.so
备份原始 nginx 执行文件
mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old
将编译好的模块文件复制到nginx动态模块加载目录
cd /usr/local/nginx
mkdir modules
mv nginx-1.22.1/objs/*.so ./modules
替换 nginx 执行文件
cp /usr/local/nginx/nginx-1.22.1/objs/nginx /usr/local/nginx/sbin/
注册 Brotli 模块
为了方便管理nginx动态模块,建议新建一个modules.conf
文件,单独管理动态模块。
cd conf/
vim modules.conf
在modules.conf
文件中写入以下内容
# Brotli模块
load_module /usr/local/nginx/modules/ngx_http_brotli_filter_module.so;
load_module /usr/local/nginx/modules/ngx_http_brotli_static_module.so;
在/usr/local/nginx/conf/nginx.conf
配置文件里引入modules.conf
文件,找到以下内容并修改:
#pid logs/nginx.pid;
include modules.conf;