惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

SecWiki News
SecWiki News
I
InfoQ
The Cloudflare Blog
人人都是产品经理
人人都是产品经理
博客园 - Franky
T
Tailwind CSS Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
量子位
博客园_首页
罗磊的独立博客
V
V2EX
李成银的技术随笔
大猫的无限游戏
大猫的无限游戏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
True Tiger Recordings
Vercel News
Vercel News
Cyberwarzone
Cyberwarzone
Cisco Talos Blog
Cisco Talos Blog
F
Fox-IT International blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
M
Microsoft Research Blog - Microsoft Research
Know Your Adversary
Know Your Adversary
爱范儿
爱范儿
The Register - Security
The Register - Security
G
Google Developers Blog
The Hacker News
The Hacker News
Malwarebytes
Malwarebytes
S
Securelist
博客园 - 三生石上(FineUI控件)
Jina AI
Jina AI
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
S
SegmentFault 最新的问题
博客园 - 叶小钗
F
Fortinet All Blogs
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
博客园 - 聂微东
T
Threatpost
博客园 - 【当耐特】
D
Docker
P
Privacy & Cybersecurity Law Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
G
GRAHAM CLULEY
V
Visual Studio Blog
C
Cisco Blogs
IT之家
IT之家
S
Security Archives - TechRepublic
Latest news
Latest news
阮一峰的网络日志
阮一峰的网络日志

蛮荆

如何获取更多的免费服务器 Kubernetes 调度器队列 - 设计与实现 Kubernetes 调度器 - 核心流程 Kubernetes Networking Model & CNI Kubernetes 控制器管理总结 Kubernetes CronJob 设计与实现 Kubernetes Job 设计与实现 Kubernetes HPA 设计与实现 Kubernetes Deployment 滚动更新实现原理 Kubernetes GC 设计与实现 Kubernetes Pod 驱逐 - 设计与实现 Kubernetes Daemonset 设计与实现 Kubernetes ReplicaSet 设计与实现 Kubernetes EndPoint 设计与实现 Kubernetes Informer 设计与实现 降本增效之应用优化 (三) 日志存储与检索 Kubernetes Pod 设计与实现 - 创建流程 Kubernetes 探针设计与实现 Unix 编程艺术名句摘录 Kubernetes - CRI 概述 Golang 编译速度为什么这么快? Kubernetes Pod 设计与实现 - Pause 容器 Kubernetes - kube-proxy 代理模式工程优化 Kubernetes 应用最佳实践 - 优雅关闭长连接 Kubernetes Service 类型和会话亲和性 Kubernetes 为什么需要 Ingress Kubernetes 架构 - 控制平面和数据平面 降本增效之应用优化 (二) 大报表 Go 语言如何获取 CPU 利用率 降本增效之应用优化 (一) Redis 业务规则引擎演变过程简述 微服务中的熔断算法 漏桶算法和令牌桶算法 jsonparser 为什么比标准库的 encoding/json 快 10 倍 ? zap 高性能设计与实现 HTTP Router 算法演进 布谷鸟过滤器 fastcache 高性能设计与实现 Web 常见的三个安全问题 ants Code Reading 布谷鸟过滤器 Go 线程安全 map 方案选型 布隆过滤器 死锁、活锁、饥饿、自旋锁 sync.Pool Code Reading Go 内存管理概述 Go netpoll Code Reading goroutine 泄漏与检测 time/Timer Code Reading GMP Scheduler Code Reading Go channel 的 15 条规则和底层实现 为什么 Linux “一切皆文件” context.Context Code Reading runtime/HACKING.md Goland 最佳实践 互联网开发与金庸武学 为什么 Redis 6.0 引入多线程模型? Kubernetes 应用最佳实践 - 金丝雀发布 容器中如何正确配置 GOMAXPROCS ? singleflight Code Reading sync.Map Code Reading sync.Cond Code Reading sync.WaitGroup Code Reading sync.RWMutex Code Reading sync.Mutex Code Reading sync.Once Code Reading Go 无锁编程 sync/atomic Code Reading goroutine 交替打印奇偶数 GODEBUG Go 并发模式 Go 汇编 UUID 通用技术选型 Kubernetes 应用最佳实践 - 水平自动伸缩 Go 高性能 Tips fasthttp 为什么比标准库 net/http 快 10 倍 ? 技术文章配图指南 ChatGPT 初体验 Docker 网络原理概览 iptables 的五表五链 Kubernetes 应用最佳实践 - 亲和性和污点容忍度 Go 的反射与三大定律 Docker 官方提供的最佳实践 Go 语言内置的设计模式 HTTP1 到 HTTP3 的工程优化 Kubernetes 应用最佳实践 - Sidecar 模式 Kubernetes 应用最佳实践 - init 容器和钩子函数 为什么 recover 必须在 defer 中调用? 为什么 defer 的执行顺序和注册顺序不同? Go map 设计与实现 Go 切片扩容底层实现 Go 语言中的零拷贝 Go Delve 云原生和边缘计算简介 Kubernetes Pod 服务质量等级 Kubernetes 应用最佳实践 - 探针 Kubernetes 应用最佳实践 - 资源请求和限制 CDN 原理 Kubernetes 应用最佳实践 - 开篇 缓存策略和模式
Nginx 快速安装优化指南
2018-10-23 · via 蛮荆

2018-10-23 Nginx

Nginx 安装

官方下载稳定版本

官方下载地址: https://nginx.org/en/download.html

安装依赖环境

  1. 安装 gcc 环境用于编译
yum install gcc-c++
  1. 安装 PCRE 库用于解析正则表达式
yum install -y pcre pcre-devel
  1. 安装 zlib 用于压缩和解压缩
yum install -y zlib zlib-devel
  1. 安装 SSL 用于 https
yum install -y openssl openssl-devel

解压 & 预编译

tar -zxvf nginx-1.16.1.tar.gz

编译之前,先创建 Nginx 工作目录,如果不创建,在启动 nginx 的过程中会报错。

mkdir -p /var/nginx 

在 Nginx 工作目录输入如下命令进行配置,目的是为了创建 Makefile 文件。

./configure \
--prefix=/usr/local/nginx \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/nginx/client \
--http-proxy-temp-path=/var/nginx/proxy \
--http-fastcgi-temp-path=/var/nginx/fastcgi \
--http-uwsgi-temp-path=/var/nginx/uwsgi \
--http-scgi-temp-path=/var/nginx/scgi \
--with-http_ssl_module

然后开始安装 HTTPS 证书:

将 SSL 证书 *.crt 和私钥 *.key 文件拷贝到 /usr/local/nginx/conf 目录中,修改配置文件,新增监听 443 端口:

server {
    listen       443;
    server_name  www.example.com;
    # 开启 ssl
    ssl     on;
    # 配置 ssl 证书
    ssl_certificate      example.com.crt;
    # 配置证书秘钥
    ssl_certificate_key  example.com.key;
    # ssl 会话cache
    ssl_session_cache    shared:SSL:1m;
    # ssl会话超时时间
    ssl_session_timeout  5m;
    # 配置加密套件,写法遵循 openssl 标准
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass backend_service_name;
        index  index.html index.htm;
    }
}

编译安装

make
make install

安装完成之后,可以进入 sib 目录执行相关操作。

# 启动 Nginx
$ nginx


# 停止 Nginx
$ /nginx -s stop


# 重新加载配置
$ nginx -s reload

优化配置

优化 worker 进程数

Nginx 有 master 和 worker 两种进程,master 进程用于管理 worker 进程,worker 进程用于 Nginx 服务,参数 worker_processes 表示需要启动的进程数量 (默认为 1)。

# 设置为 auto 自动匹配进程数

worker_processes auto;

绑定 CPU

默认情况下,Nginx 的多个进程有可能运行在某一个 CPU 或 CPU 的某一核上,导致 Nginx 进程使用硬件的资源不均,因此绑定 Nginx 进程到不同的 CPU 上是为了充分利用硬件的多 CPU 多核资源。

worker_processes 4;

# 为每个进程分配一个 CPU 核 
worker_cpu_affinity 0001 0010 0100 1000;

# 自动分配
# worker_cpu_affinity auto;

事件处理模型

# Linux 使用 epoll IO 多路复用模型

events {
  use epoll;
}

当然也可以不指定事件处理模型,Nginx 会自动选择最佳的事件处理模型。

单个进程允许的最大连接数

控制 Nginx 单个进程允许的最大连接数的参数为 worker_connections,这个参数要根据服务器性能和内存使用量来调整。

进程的最大连接数受 Linux 系统进程打开的最大文件数的限制,只有执行了 “ulimit -HSn 65535” 之后,worker_connections 才能生效。

连接数包括代理服务器的连接、客户端的连接等,Nginx 总并发连接数 = worker_processes * worker_connections, 测试完之后记得根据不同业务场景进行压测并调整。

# 设置单个进程允许的最大连接数为 65536

worker_processes 65535;

单个进程允许的最大文件数

# 设置单个进程打开的最大文件数为 65535 

worker_rlimit_nofile 65535;    

开启高效文件传输模式

sendfile 参数用于开启文件的高效传输模式,该参数实际上是激活了 sendfile() 功能。

sendfile() 是作用于两个文件描述符之间的数据拷贝函数,这个拷贝操作是在内核之中的,被称为 “零拷贝” 。sendfile 比 read 和 write 函数要高效得多,因为 read 和 write 函数要把数据拷贝到应用层再进行操作。

tcp_nopush 参数用于激活 Linux 上的 TCP_CORK socket 选项,尽可能发送大块的数据,减少数据包的数量,此选项仅仅当开启 sendfile 时才生效, tcp_nopush 参数可以把 http response header 和文件的开始部分放在一个文件里发布,以减少网络报文段的数量。

sendfile on;

tcp_nopush on;

连接超时时间

连接超时的作用

  • 将无用的连接设置为尽快超时,可以保护服务器的系统资源(CPU、内存、磁盘)
  • 当连接很多时,及时断掉那些建立好的但又长时间不做事的连接,以减少其占用的服务器资源
  • 如果黑客攻击,会不断地和服务器建立连接,因此设置连接超时以防止大量消耗服务器的资源
  • 如果用户请求了动态服务,则 Nginx 就会建立连接,请求 FastCGI 服务以及后端数据库服务,设置连接超时,使得在用户容忍的时间内返回数据

连接超时存在的问题

  • 服务器建立新连接是要消耗资源的,因此,连接超时时间不宜设置得太短,否则会造成并发很大,导致服务器瞬间无法响应用户的请求
  • 有些 PHP 站点会希望设置成短连接,因为 PHP 程序建立连接消耗的资源和时间相对要少些
  • 有些 Java 站点会希望设置成长连接,因为 Java 程序建立连接消耗的资源和时间要多一些,这是由语言的运行机制决定的

设置超时时间

  • keepalive_timeout:用于设置客户端连接保持会话的超时时间,超过这个时间服务器会关闭该连接
  • client_header_timeout:用于设置读取客户端请求头数据的超时时间,如果超时客户端还没有发送完整的 header 数据,服务器将返回 “Request time out (408)” 错误
  • client_body_timeout:用于设置读取客户端请求主体数据的超时时间,如果超时客户端还没有发送完整的主体数据,服务器将返回 “Request time out (408)” 错误
  • send_timeout:用于指定响应客户端的超时时间,如果超过这个时间,客户端没有任何活动,Nginx 将会关闭连接
  • tcp_nodelay:默认情况下当数据发送时,内核并不会马上发送,可能会等待更多的字节组成一个数据包,这样可以提高 I/O 性能,但是,在每次只发送很少字节的业务场景中,使用 tcp_nodelay 功能,等待时间会比较长

限制上传文件的大小

client_max_body_size 128M;

如果客户端文件超过 128MB, 就会收到 413 的错误响应码。

gzip 压缩

# 开启 gzip 压缩功能
gzip on;

# 设置允许压缩的页面最小字节数,页面字节数从 header 头的 Content-Length 中获取。默认值是 0,表示不管页面多大都进行压缩。
# 建议设置成大于 1K,如果小于 1K 可能会越压越大
gzip_min_length 1k;

# 压缩缓存区大小。表示申请 4 个单位的位 16K 的内存作为压缩结果流缓存,
# 默认值是申请与原始数据大小相同的内存空间来存储 gzip 压缩结构
gzip_buffers 4 16k;

# 压缩版本 (默认 1.1),用于设置识别 HTTP 协议版本,默认是 1.1,目前大部分浏览器都支持 GZIP 解压,使用默认即可
gzip_http_version 1.1;

# 压缩比率。用来指定 gzip 压缩比,1 压缩比最小,处理速度最快;9 压缩比最大,传输速度快,但处理最慢,也比较消耗 CPU 资源
gzip_comp_level 2;

# 用来指定压缩的类型
gzip_types text/plain text/css text/xml application/javascript;

# vary header 支持。该选项可以让前端的缓存服务器缓存经过 gzip 压缩的页面,例如用 Squid 缓存经过 Nginx 压缩的数据
gzip_vary on;

需要注意的是: 图片和视频文件在启用压缩前需要二次确认,因为这些文件大多数都已经被压缩过了,如果再次压缩可能会适得其反。

断点续传

视频等大文件可以多线程加载,支持断点续传并提高性能。

server {
  ...
  
  location ~ ^/(mp4|avi/m3u8) {
    add_header Access-Control-Allow-Origin *;
    add_header Accept-Ranges bytes;
    root /var/www/...;
    access_log off;
    expires 30d;
  }
}

expires 缓存期限

server {
    ...

    location / {
        proxy_pass http://tomcats;
            expires 10s;      # 浏览器缓存10秒钟
            #expires @22h30m  # 在晚上10点30的时候过期
            #expires -1h      # 缓存在一小时前时效
            #expires epoch    # 不设置缓存
            #expires off      # 缓存关闭,浏览器自己控制缓存
            #expires max      # 最大过期时间
    }
}

配置防盗链

主要通过请求的 Referer 头部来确定。

server {
    ...
    
    location ~* .*\.(gif|jpg|ico|png|css|svg|js)$ {
        valid_referers none blocked  *.example.com;
        if ($invalid_referer) {
            return 403;
        }
        
        # 合法请求
        ...
    }
}

更好的做法是使用 map 模块或 ngx_http_referer_module 模块来实现,这样可以避免使用 if 语句可能引发的问题。

map $http_referer $invalid_referer {
    default 1;
    "~*^https?://(www\.)?example\.com" 0;
}

server {
    ...

    location ~ \.(jpg|jpeg|png|gif)$ {
        if ($invalid_referer) {
            return 403;
        }
        
        # 合法请求
        ...
    }
}

上面的配置通过使用 map 模块,将引用者与合法的域名进行匹配,这样可以更加灵活地配置防盗链规则。

server 代码块

server 代码块位于 http 代码块内部,可以添加多个 server,每一个 server 都可以用来配置一个虚拟主机。也就是说,每一个 server 代表了一个虚拟服务器的配置信息。

server 中的主要配置有:

  • listen 虚拟主机监听的端口
  • server_name 虚拟主机的域名或 IP 地址,可以配置多个(用空格隔开)
  • root 虚拟主机的根目录
  • index 虚拟主机的首页,也可以用 location 代码块来配置
  • access_log 虚拟主机的访问日志
  • error_log 虚拟主机的错误日志
  • error_page 错误页面

location 代码块

location 代码块位于 server 代码块内部,用于配置虚拟主机的 URI,每一个 server 可以配置多个 location, 根据不同的 URI 配置来处理不同的请求。

全局变量

  • $args #这个变量等于请求行中的参数
  • $content_length #请求头中的 Content-length 字段
  • $content_type #请求头中的 Content-Type 字段
  • $document_root #当前请求在 root 指令中指定的值
  • $host #请求主机头字段,否则为服务器名称
  • $http_user_agent #客户端 agent 信息
  • $http_cookie #客户端 cookie 信息
  • $limit_rate #这个变量可以限制连接速率
  • $request_body_file #客户端请求主体信息的临时文件名
  • $request_method #客户端请求的动作,通常为 GET 或 POST
  • $remote_addr #客户端的 IP 地址
  • $remote_port #客户端的端口
  • $remote_user #已经经过 Auth Basic Module 验证的用户名
  • $request_filename #当前请求的文件路径,由 root 或 alias 指令与 URI 请求生成
  • $ query_string #与$args 相同
  • $scheme #HTTP 方法(如 http,https)
  • $server_protocol #请求使用的协议,通常是 HTTP/1.0 或 HTTP/1.1
  • $server_addr #服务器地址,在完成一次系统调用后可以确定这个值
  • $server_name #服务器名称
  • $server_port #请求到达服务器的端口号
  • $ request_uri #包含请求参数的原始 URI,不包含主机名,如:”/foo/bar.php?arg=baz”
  • $ uri #不带请求参数的当前 URI,$uri 不包含主机名,如”/foo/bar.html”
  • $ document_uri #与$uri 相同

安全配置

隐藏版本号

图片来源: 网络

修改默认用户

首先查看 Nginx 服务对应的默认用户:

grep '#user' nginx.conf.default

图片来源: 网络

  1. 为 Nginx 创建新用户
useradd nginx -s /sbin/nologin -M # 不需要有系统登录权限,应当禁止其登录能力
  1. 配置 Nginx 刚创建的用户

第一种是直接修改配置文件参数,将默认的 #user nobody; 修改为如下内容:

user nginx nginx;

第二种方法 (推荐使用) 为直接在编译 Nginx 软件的时候指定编译的用户名和组,这样无论配置文件中是否加参数,用户都是 nginx (固定的)。

./configure \
--user=nginx \
--group=nginx \
--prefix=/usr/local/nginx-1.18.0 \
--with-http_v2_module \
--with-http_ssl_module \
--with-http_stub_status_module

日志切割

创建一个 runlog.sh 文件,按天切割

# 日志文件路径
LOGPATH=/usr/local/nginx/logs/access.log

BASEPATH=/usr/local/nginx/logs/access/$(date -d yesterday +%Y%m)

mkdir -p $BASEPATH

BACKUP=$BASEPATH/$(date -d yesterday +%Y%m%d).access.log

mv $LOGPATH $BACKUP
touch $LOGPATH
/usr/local/nginx/sbin/nginx -s reopen

图片来源: 网络

日志切割脚本配合定时任务,每天 0 点切割一次

0 0 * * * sh /usr/local/nginx/logs/runlog.sh

图片来源: 网络

robots.txt 机器人协议

通过 Robots 协议告诉搜索引擎 (爬虫) 哪些页面可以抓取,哪些页面不能抓取。

server {
    ...

    location = /robots.txt {
        alias /path/to/your/robots.txt;
    }
}

# robots.txt 文件内容

User-agent: *
Disallow: /private/
Disallow: /restricted/

Robots.txt 文件指示所有爬虫(User-agent: *)不应该爬取 /private/ 和 /restricted/ 路径下的所有内容。

反向代理

upstream [proxyName] {
    server 192.168.1.173:8080;
    server 192.168.1.174:8080;
    server 192.168.1.175:8080;
}

# proxy_cache_path 设置缓存保存的目录的位置
# keys_zone设置共享内以及占用的空间大小
# mas_size 设置缓存最大空间
# inactive 缓存过期时间,错过此时间自动清理
# use_temp_path 关闭零时目录
proxy_cache_path /usr/local/nginx/upsteam_cache keys_zone=mycache:5m max_size=1g inactive=8h use_temp_path=off;

server {
    listem  80;
    server_name www.tomcats.com;
    # 开启并使用缓存
    proxy_cache mycache;
    # 针对200和304响应码的缓存过期时间
    proxy_cache_valid 200 304 8h;

    location / {
        proxy_pass backend_service_name;
    }
}

负载均衡

加权轮询

upstream [proxyName] {
    server 192.168.1.173:8080 weight=1;
    server 192.168.1.174:8080 weight=5;
    server 192.168.1.175:8080 weight=2;
}

IP Hash

upstream [proxyName] {
    ip_hash

    server 192.168.1.173:8080;
    server 192.168.1.174:8080;
    server 192.168.1.175:8080;
}

使用注意: 不能把后端服务器直接移除,只能标记 down。

Url hash

upstream [proxyName] {
    hash $request_url;

    server 192.168.1.173:8080;
    server 192.168.1.174:8080;
    server 192.168.1.175:8080;
}

最小连接

upstream [proxyName] {
    least_conn;

    server 192.168.1.173:8080;
    server 192.168.1.174:8080;
    server 192.168.1.175:8080;
}

扩展阅读