Nginx回炉(一)

简介

  • 上个系列直接深入 nginx,实战也少,不便于理解
  • 这里从基础到进阶,多配几个例子,再去看之前的内容,理解会更加深入

安装

  • 先从原始的 nginx 开源版本学起,也就是只提供了反向代理和负载均衡功能的 NGINX
  • 具体安装过程跳过
    • 使用 docker 安装要将配置文件映射出来,容器里面不好改
  • 新增配置,让 systemd 管理 nginx;或者说,配置成系统服务
    vi /usr/lib/systemd/system/nginx.service
    
    [Unit]
    Description=nginx - web server
    After=network.target remote-fs.target nss-lookup.target
    [Service]
    Type=forking
    # 这里注意,/usr/local/nginx/ 是安装目录才行,不是的话要改
    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
    ExecQuit=/usr/local/nginx/sbin/nginx -s quit
    PrivateTmp=true
    [Install]
    WantedBy=multi-user.target
    
  • 重新加载系统服务:systemctl daemon-reload
    • 把之前启动的 nginx 停掉:./nginx -s stop
    • 启动:systemctl start nginx.service
    • 开机启动:systemctl enable nginx.service

目录结构

工作流程

  • NGINX 的最基本工作流程
    1
  • 启动 nginx 就可以看到默认有 master+worker 进程,真正干活的是 worker
    2
  • 当重新加载配置时,master 会优雅关闭现有 worker 并重建新的 worker
  • 想深入了解 NGINX 进程管理可以看这篇文章

配置文件

  • 配置文件及语法可以看这篇文章,但是可能有点晦涩,这里结合案例讲解
  • 最小配置,就是把注释部分都删掉的 nginx.conf
    worker_processes  1;	# 应当和内核数匹配
    
    events {
        worker_connections  1024;	# 最大请求连接数
    }
    
    http {
        include       mime.types;	# 根据后缀,判断文件类型,带在HTTP报文头部,便于客户端解析
        default_type  application/octet-stream;	# 如果上面都不匹配,就用流传输
    
        sendfile        on;	# 使用 Linux 的高效网络传输,数据0拷贝
    
        keepalive_timeout  65;	# 保持长连接
    
    	# 虚拟主机
        server {
            listen       80;	# 监听服务器80端口
            server_name  localhost;	# 匹配域名或主机名(hosts可配置)
    
            location / {
                root   html;	# 指定资源目录(相对)
                index  index.html index.htm;	# 指定目录下文件
            }
            
            error_page   500 502 503 504  /50x.html;	# 请求 50x 这个文件
            location = /50x.html {	# 怎么找到这个文件呢?这里使用精准匹配
                root   html;
            }
        }
    }
    

虚拟主机

  • NGINX 可以配置多个虚拟主机监听多个端口,充分利用服务器资源
  • 我们把 http 里面的虚拟 server 复制一份改一下端口号测试,重启 NGINX
    1
  • 现在访问两个端口都能返回 index 页面

server_name

  • 这部分很重要,要看懂
  • 注:这个 server_name 是用来匹配请求的域名/主机名的
    • 从浏览器访问,如果使用虚拟机 IP 192.168.109.129 访问,不需要域名解析,也不经过 hosts 文件解析,直接到 nginx,但是这个 IP 不和 localhost 匹配,也不和任何 server_name 匹配,那就走第一个虚拟 server(按顺序的)
    • 在 80 端口定义两个虚拟 server 测试一下
      2
    • 虚拟机访问 http://console 返回 index2 页面(完整匹配);用 IP 访问,返回 index 页面(没匹配上)
    • 注:这里 console 是在 hosts 文件新增的主机名,还是指向我们本地的 IP
    • 也就是说,虽然请求在 host 解析 IP 然后被 nginx 监听(捕获)了,也就是说 nginx 是不能直接看到 hostname/domain 的,变成 IP(网卡地址) 后才到 nginx的,但 hostname还是能在 nginx 匹配的(请求报文中还带有hostname,可以抓包看一下)
    • 同理,域名(domain )被 DNS 解析了但也能在 nginx 匹配
    • 最终解析成 IP 是因为定位网卡需要 IP,然后根据网络协议(tcp/udp)指定的端口号路由到具体的某个应用程序,nginx 就位于这之间,具体位置是哪里呢?请看后续分解
  • server_name 后可以跟多个 domain/hostname,空格分隔即可
  • 既然是匹配,那就有通配符正则表达式可以用;也叫泛域名解析
    • 我们在 host 文件新增几个域名测试一下
      192.168.109.129 www.royspace.com www.royspace.cn xxx.royspace.xyz
      
    • nignx 配置如下
      3
    • 这个规则的意思是,只要以 www.royspace 开头即可,后面带什么都行
    • 类似的,如果将 * 写在前面,比如:*.royspace.com 就是以 royspace.com 结尾即可,前面啥都行
    • 这个 * 就是通配符,如果想用正则,需要在匹配字符串前面加 ~
    • 例如:~^[0-9]+\.royspace\.com$ 表示以数字开头
  • 泛域名解析用途广泛
    • 多用户二级域名
    • 短网址
    • httpdns

反向代理

  • 什么是反向代理
    • 简而言之就是,客户端的请求由 NGINX 决定转发到哪
      1
  • 后面会介绍 Linux 内置的 LVS 负载均衡器,和 NGINX 的隧道式代理不同
    • 大文件传输会是 NGINX 的性能瓶颈,可以使用 DR 等方案解决
  • 使用 NGINX 的架构
    • 传统架构
      2
      3
      4
    • 中小型互联网项目架构(pass)
  • 配置反向代理:proxy_pass
    • 转发给百度的域名,就是请求百度的首页;参数还是会带上的
      5

负载均衡

  • 给多个 server(上游服务器)建立变量,配置负载均衡
    1
  • 注:配置证书之前,不支持代理 HTTPS
  • 负载均衡策略
    • 上面的配置默认采用轮询策略
    • 权重策略
      upstream httpd {
      	server 127.0.0.1:8050 weight=10 down;
      	server 127.0.0.1:8060 weight=1;
      	server 127.0.0.1:8070 weight=1 backup;
      }
      
    • down、backup 策略(不常用)
    • ip_hash(解决session问题)、fair、leastconn 策略(不常用)

动静分离

  • 静态资源放在 nginx,不向后端请求,提高效率
  • 小型的展示类型的网站比较常用

alias

  • nginx 作为静态资源服务器时会使用 root 或 alias,有啥区别呢?
  • alias指定的目录是准确的,即 location 匹配后,文件直接是在alias目录下查找的,不会再去指定的目录的子目录找;访问 /css/a.css,那么这个文件就在 /usr/local/nginx/static/css 下
    location /css {
    	alias /usr/local/nginx/static/css;
    	index index.html index.htm;
    }
    
  • root指定的目录是 location 匹配访问的 path 目录的上一级目录,这个path目录一定要是真实存在root指定目录下的;访问 /css/b.css,/usr/local/nginx/static 下面必须有个叫 css 的文件夹存放着 b.css
    location ~* /(css|img|js) {
    	root /usr/local/nginx/static;
    	index index.html index.htm;
    }
    
  • 使用alias标签的目录块中不能使用 rewrite 的break(原因未知)

location

  • 虚拟 server 内部依赖 location 关键字匹配 URI,进而反向代理、负载均衡
  • location 的匹配规则
    • / 通用匹配,任何请求都会匹配到
    • = 精准匹配,请求地址必须和这里的规则完全一样才能匹配到
    • ~ 正则匹配,区分大小写
    • ~* 正则匹配,不区分大小写
    • ^~ 非正则匹配
  • 匹配规则的优先级
    • 多个正则,直接按定义的先后顺序匹配,成功后就不会继续往后面匹配
      • nginx 中很多地方都用了按定义顺序优先匹配的规则,例如:server_name 匹配
    • 通用匹配,一直往下,直到找到匹配度最高的(最大前缀匹配)
    • 非正则与正则同时存在,如果正则匹配成功,则不会再执行普通匹配
    • 所有规则同时存在,= > ^~ > 正则 > 通用
    • 口诀:精准禁则先,正则最长后

Rewrite

  • 隐藏后端服务器的真实地址,前端只需访问包含关键信息的静态页面(http://ip/3.html),就能跳转到真实的后端地址(http://ip/index.jsp?pageNum=3)
  • 这里使用了正则匹配,前面匹配到的参数可以在后面使用
    1
  • break 表示本条规则匹配完成即终止,不再匹配后面的任何规则
  • last 本条规则匹配完成后,继续向下匹配新的 location URI 规则(先不转发)
  • 这里遇到个问题
    • 直接访问:http://192.168.109.129:8080/index.html?pageNum=666 可以显示页数
    • 访问:http://192.168.109.129:8080/666.html 就不显示
    • 分析:proxy_pass 会带上 location / 匹配到的完整 URI,包括 query 参数;如果直接带上 666.html 128 是会报错的,说明 rewrite 还是起作用的了;直接传 pageNum 生效是因为 rewrite 没匹配上,直接代理过去了
    • 所以应该是 rewrite 匹配上并重写了,但重写失败,传递了空字符串或者 $1 出现问题(只写了 index.html)
    • 将 rewrite 中 pageNum 写死也未显示,将 index.html 改为 index.jsp 出现 tomcat 界面
    • 说明丢失了 query_string 参数,rewrite 只是重写 URI 的,不负责参数,并不背锅
      • why? 需要了解更多底层知识
        upstream us {
        	server 192.169.109.130:80 weight=8 down;
        	server 192.168.109.128:8080 weight=1 backup;
        }
        
        server {
            listen       80;
            server_name  localhost;
        
            location / {
        	#if ($request_uri ~ ^/([0-9]+).html$){
        		#set $a $1;
        	#}
        	 rewrite ^/([0-9]+).html$ /index.html?pageNum=$1 break;
        	 #proxy_set_header Host $host;
        	 #proxy_set_header X-Real-IP $remote_addr;
        	 #proxy_pass  http://192.168.109.128:8080/;
        	 proxy_pass http://us?$args;
        	 #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            }
        
        	location ~*/(css|images|js|fonts) {
        	 root /usr/local/nginx/static;
        	 index index.html index.htm;
        	}
        
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
        
    • 注:location 匹配 URI,GET 参数存在内置变量 $query_string 里,但这个变量不能手动修改

防火墙

  • 应用服务器的防火墙配置
    • 目前,我们的结构如图所示
      1
    • 129 反向代理了 128 这台应用服务器,并隐藏了后端服务器的真实地址,且作为静态资源服务器直接返回 129 上的一些128所需的样式文件,也做了负载均衡,但目前只启用了 128 作为 server,其他的先标为 down
    • 测试源码放在这里需要在 128 部署 tomcat 服务器
    • 128 返回的页面再次向静态资源服务器请求(href 相对站点定位),对 client 来说,站点就是 nginx
    • 目前我们关闭了防火墙,内网还是可以直接访问 128(虽然没有样式),但我们想要的效果是:只有通过 nginx(129:8080)才能访问 128,这就需要打开防火墙并配置
  • 相关命令(给 128 应用服务器设置)
    systemctl start firewalld
    # 指定端口和 ip 访问
    firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="192.168.109.129" port protocol="tcp" port="8080" accept"
    # 重载规则
    firewall-cmd --reload
    # 重启
    systemctl restart firewalld
    # 移除规则
    firewall-cmd --permanent --remove-rich-rule="rule family="ipv4" source address="192.168.109.129" port="8080" protocol="tcp" accept"
    
  • OK,现在只有 129 才能访问
  • 此后,我们称 nginx 这台服务器为网关服务器
  • 目前的配置(有上面说的那个rewrite问题,为啥别人的可以?排除浏览器原因,是否在nginx-1.21就可以)
    upstream us {
    	server 192.169.109.130:80 weight=8 down;
    	server 192.168.109.128:8080 weight=1 backup;
    }
    
    server {
        listen       8080;
        server_name  localhost;
    
        location / {
        	# proxy之前定义
    		rewrite ^/([0-9]+).html$ /index.html?pageNum=$1 break;
    		proxy_pass  http://us;
        }
    
    	location ~*/(css|images|js|fonts) {
    		root /usr/local/nginx/static;
    		index index.html index.htm;
    	}
    
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
    

防盗链

  • 在浏览器中,如果我们请求的资源返回后,再去请求其他的资源,比如上面说的 nginx 作为网关服务器,返回了 128 的资源后,这个页面还需要加载一些静态资源(css/js/imgs),就会自动的再次向作为静态资源服务器的 nginx 发起请求;此时,浏览器就会根据 HTTP 协议规定的,给我们在 Header 中携带 Referer ,值就是我们第一次请求时的域名,表明这次请求的“推荐人”还是这个域(129)
  • 想要通过 referer 限制资源的访问,要在 nginx 中需要验证防盗链的 location 中配置
    valid_referers 192.168.109.129;	# 这里暂时设置为 ip
    	if ($invalid_referer) {
    	return 403;
    }
    
  • 此时访问 129 没问题,直接访问应用服务器(130,得是没启动防火墙的才行)不能加载静态资源
    • 必须有 referer,还不能错
  • 直接访问 130 的资源地址(不访问到应用服务器,也就没有 referer)也不行,控制台可以看到 403 forbidden;但如果我们需要直接访问(没有 referer的时候)能拿到静态资源,可以配置:
    • 也就是说,可以没有,但有的话不能错
      valid_referers none 192.168.109.129;	# 这里暂时设置为 ip
      	if ($invalid_referer) {
      	return 403;
      	# 可以通过 rewrite 返回图片
      }
      

curl

  • 使用 curl 发起测试请求可以有效避免缓存问题
  • 常用命令
    # 只返回头信息
    curl -I http://192.168.44.101/img/logo.png	# 上面设置 none,即可以没有referer
    # 设置 referer
    curl -e "http://baidu.com" -I http://192.168.109.129/img/logo.png	# 这个referer不对,应该返回forbidden
    

error page

  • 可以自定义返回的错误页面
    server {
       	listen       82;
        server_name  localhost;
    
        location / {
     		return 401;
        }
    
        error_page  401   /401.html;
        location = /401.html {
            root   html;
        }
    }
    

高可用

  • 应用服务器可以通过搭集群的方式,均衡负载,并让系统能够一直提供服务(应对单点故障)
  • 网关服务器(NGINX)作为流量转发中心,自然也需要考虑高可用
  • 常见的高可用方案如图所示
    1
  • 对外(client),我们只有一个 IP(200),因为一块网卡是可以设置多个 IP 的,这个虚拟 IP 在正常情况下也就设置在主机(100),也叫 Master
  • 当正在使用的网关服务器宕机后,就会重新竞选出 Master,这个虚拟 IP 就会设置在新的主机上
  • NGINX 通过配置 Keepalived 实现 HA
  • 安装:yum -y install keepalived
    • 也通过下载源码编译安装
  • 最小配置
    # vim /etc/keepalived/keepalived.conf
    global_defs {
       router_id roy666	# 129
    }
    
    vrrp_instance myHA {
        state MASTER	# 这台机器先作为 master
        interface ens33	# 和本机网卡名要一致
        virtual_router_id 51
        priority 100	# 竞选时的权重
        advert_int 1
        authentication {	# 每个机器都要装keepalived,可能有很多机器用于不同类型服务器的HA,通过这个分组,同一组保持一致
            auth_type PASS
            auth_pass 1111
        }
        virtual_ipaddress {	# 相当于图上 44.200
            192.168.109.133	# 注意不要和其他服务器冲突
        }
    }
    
    # 备份机
    global_defs {
       router_id roy777	# 192.168.109.128 那台机器
    }
    
    vrrp_instance myHA {
        state BACKUP	# 备份机
        interface ens33
        virtual_router_id 51	# 要和master一致,确保在同一组
        priority 50
        advert_int 1
        authentication {		# 要和master一致
            auth_type PASS
            auth_pass 1111
        }
        virtual_ipaddress {
            192.168.109.133
        }
    }
    
  • 启动 Keepalived,在 Master 上能看到
    2
  • 启动 BACKUP 的 Keepalived
    3
  • 测试
    • 在 cmd ping 这个虚拟 IP,然后关掉 Master(init 0)
      4
    • 在短暂的一次请求超时后,在另一台机器雄起
      5
  • 可以了解一下 ARP 协议

升级

  • Keepalived 默认监听主机的 keepalived 进程是否存在来判断是否出问题
  • 有时 NGINX 服务器报错并不能让给 k 进程 down 掉,我们可以写一个脚本放在每个机器上,监听 nginx 的 error log,一旦出错,就 down 掉 k 进程,选择其他主机
  • 当然,nginx 在 Master 上报错并不能保证在 Backup 上就没问题,还是要赶紧排错

加密算法

  • 互联网信息的加密传输
    • 对称加密
    • 非对称加密:更安全,需要公私钥;但也不是绝对的安全,伪装成客户端和服务端就有可能篡改所有信息
      1
  • 那如何保证互联网传输的安全性?核心逻辑是:有一个不在互联网中传输的可信任的东西作为证人,这个东西就是 CA 机构
  • 基本流程
    • 服务器向 CA 机构申请证书(花钱买)
    • 客户请求服务器时,给客户端颁发证书,这个证书(用CA的公钥才能解开)里带着服务器的公钥
    • 客户端后续的网络请求都用这个公钥加密,只有服务端的私钥才能解密
  • 为什么就安全了呢?
    • 未使用证书前,给客户端发公钥的可能是坏人,坏人拿着一套假的公私钥玷污数据,然后假冒客户端,将玷污后的数据用真公钥加密再给服务端,服务端并不能发现异常,坏人达到目的
    • 使用证书后,坏人也能拿到证书并解开获取服务器的公钥,但是想要再伪装成 CA 机构加密出假证书是不可能的,因为只要客户端保证使用的是正版操作系统,那只有 CA 证书是真的,用内置 CA 公钥解密才不会出现问题,进而拿到真实的服务器的公钥,保证了这一步,就打破了坏人的伪造链
    • 也就是说,这个不在互联网中传输的证人就是我们的正版操作系统/浏览器(里面内置的CA公钥)和 CA(私钥)
      2
    • 因此,要防止坏人伪造成 CA,给我们颁发恶意证书,哄骗我们使用假公钥,那就一定要使用正版OS 和浏览器,对证书要敏感,不能轻易信任
    • 颁发给客户端的证书,其实就是 CA 用私钥加密公钥后的密文,这个公钥是 CA 根据我们提交的域名生成的,当然,也有对应的私钥(服务器加密信息使用),服务器安装 CA 签发的 SSL 证书就可以拿到(也可直接下载);但是 CA 的私钥是用来传递证书或者说传递 CS 之间通信的公钥的,只用一次,也不会给服务器,只要验证了是真实证书,client 获取了真实服务器公钥就等于获取了互信;后续用客户端公钥和服务器下载的私钥即可加密通信
    • 别搞混:CA 私钥/公钥,服务器公钥/私钥,都是由 CA 生成
    • 也就是说,给服务器的证书只有一份,连接服务器的客户端拿的都是相同的公钥
    • 如何保证服务端传给客户端的数据不被坏人用真实公钥解密呢?
      • 首先,服务器返回的信息一定要脱敏,被拦截了影响不大
      • 坏人解密后不能再加密了,传递到客户端也不会被相信,也就是无法篡改数据

自签名

  • 了解即可,不常用,因为存在安全隐患
  • 自签名,就是在服务器生成一对公私钥,公钥包装成证书,私钥就相当于服务器私钥
  • 安装证书拿到公钥开始加密传输数据,服务器用私钥加解密
  • 因为不需要第三方机构验证证书真实性(本来就是伪造的),也就没了上述内置的解开证书的 CA 公钥,没了 CA 私钥加密服务器公钥这一层

申请证书

  • 申请正规机构的证书
  • 先买一个域名
    1
  • 为了更真实,可以再买个 VPS(云服务器)
    • 买个一个月最低配置即可
  • 配置服务器(安装软件)
    • lnmp 是一套用的比较多的搭配(Linux+nginx+MySQL+PHP)
    • 推荐通过这里安装,省事
      2
  • 后续的配置以及域名解析、泛域名解析暂且不表
  • 给域名申请证书
    • 还是在阿里云买
      3
    • 很显然,大机构的证书买不起,那就选免费的那个,时长一年
      4
    • 这里是系统自动验证我这个服务器的安全性,也可以通过手动上传个东西到指定位置做为验证
      5
    • 下载证书,可以选择服务器类型;我们就选 NGINX
      6
    • 会得到 pem 证书文件和 key 私钥,直接在 nginx 配置
      server {
      	listen 443 ssl;
      	server_name localhost;
      	
      	ssl_certificate	/usr/local/nginx/conf/xxx.crt	# 颁发给客户端,浏览器内置的ca公钥会验证并解析出服务器公钥加密通讯
      	ssl_certificate_key	/usr/local/nginx/conf/xxx.key # 服务器私钥(CA生成)
      }
      
    • 就可以用 HTTPS 访问啦
  • 目前,nginx 作为网关服务器配置了 HTTPS 传输,接收外网客户端请求,应用服务器可以和 nginx 在同一个内网使用 HTTP 传输
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Roy_Allen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值