本人是分布式的新手,在实际工作中遇到了需要动态修改nginx的需求,因此写下实现过程中的想法。Nginx功能强大且灵活,所以这些权当抛砖引玉,希望可以得到大家的讨论和指点。(具体代码在 https://andy-zhangtao.github.io/nginx2svg/ )

如何动态配置Nginx参数

Nginx参数众多,并且配置是非灵活,因此要达到完美的自动化配置是一件很有挑战性的事情,这个工具并不能十分完美的自动化调整参数。目前支持自动化修改的参数有:

  • server
  • upstream
  • proxy_pass
  • root

下面将介绍Nginx2Svg是如何实现自动化修改参数的。

预备知识

为了更好的理解Nginx2Svg,需要一些很简单的预备知识。 首先需要了解Nginx的配置文件格式,一个典型的Nginx配置文件(假设此处Nginx作为7层反向负载使用)看起来应该是下面的样子:

# 抄自nginx官网 http://nginx.org/en/docs/example.html
1 user www www;
2
3 worker_processes 2;
4
5 pid /var/run/nginx.pid;
6
7 # [ debug | info | notice | warn | error | crit ]
8
9 error_log /var/log/nginx.error_log info;
10
11 events {
12 worker_connections 2000;
13
14 # use [ kqueue | epoll | /dev/poll | select | poll ];
15 use kqueue;
16 }
17
18 http {
19
20 include conf/mime.types;
21 default_type application/octet-stream;
22
23
24 log_format main '$remote_addr - $remote_user [$time_local] '
25 '"$request" $status $bytes_sent '
26 '"$http_referer" "$http_user_agent" '
27 '"$gzip_ratio"';
28
29 log_format download '$remote_addr - $remote_user [$time_local] '
30 '"$request" $status $bytes_sent '
31 '"$http_referer" "$http_user_agent" '
32 '"$http_range" "$sent_http_content_range"';
33
34 client_header_timeout 3m;
35 client_body_timeout 3m;
36 send_timeout 3m;
37
38 client_header_buffer_size 1k;
39 large_client_header_buffers 4 4k;
40
41 gzip on;
42 gzip_min_length 1100;
43 gzip_buffers 4 8k;
44 gzip_types text/plain;
45
46 output_buffers 1 32k;
47 postpone_output 1460;
48
49 sendfile on;
50 tcp_nopush on;
51 tcp_nodelay on;
52 send_lowat 12000;
53
54 keepalive_timeout 75 20;
55
56 #lingering_time 30;
57 #lingering_timeout 10;
58 #reset_timedout_connection on;
59
60
61 server {
62 listen one.example.com;
63 server_name one.example.com www.one.example.com;
64
65 access_log /var/log/nginx.access_log main;
66
67 location / {
68 proxy_pass http://127.0.0.1/;
69 proxy_redirect off;
70
71 proxy_set_header Host $host;
72 proxy_set_header X-Real-IP $remote_addr;
73 #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
74
75 client_max_body_size 10m;
76 client_body_buffer_size 128k;
77
78 client_body_temp_path /var/nginx/client_body_temp;
79
80 proxy_connect_timeout 70;
81 proxy_send_timeout 90;
82 proxy_read_timeout 90;
83 proxy_send_lowat 12000;
84
85 proxy_buffer_size 4k;
86 proxy_buffers 4 32k;
87 proxy_busy_buffers_size 64k;
88 proxy_temp_file_write_size 64k;
89
90 proxy_temp_path /var/nginx/proxy_temp;
91
92 charset koi8-r;
93 }
94
95 error_page 404 /404.html;
96
97 location = /404.html {
98 root /spool/www;
99 }
100
101 location /old_stuff/ {
102 rewrite ^/old_stuff/(.*)$ /new_stuff/$1 permanent;
103 }
104
105 location /download/ {
106
107 valid_referers none blocked server_names *.example.com;
108
109 if ($invalid_referer) {
110 #rewrite ^/ http://www.example.com/;
111 return 403;
112 }
113
114 #rewrite_log on;
115
116 # rewrite /download/*/mp3/*.any_ext to /download/*/mp3/*.mp3
117 rewrite ^/(download/.*)/mp3/(.*)\..*$
118 /$1/mp3/$2.mp3 break;
119
120 root /spool/www;
121 #autoindex on;
122 access_log /var/log/nginx-download.access_log download;
123 }
124
125 location ~* \.(jpg|jpeg|gif)$ {
126 root /spool/www;
127 access_log off;
128 expires 30d;
129 }
130 }
131 }

从18行到131行属于http配置内容,在这部分参数中,第61行到130行属于server配置内容,(一个server对应一个虚拟主机),server的参数属于http参数的子集,当相同参数出现时,server优先级会高于http。按照作用域来做类比,http就是全局变量,server就是局部变量。

所以18行到60行属于全局变量,而61行到130则属于局部变量。 为了简化后面的操作,我们可以简化httpserver之间的包含关系,如下:

     1	user  nginx;
2 worker_processes 1;
3
4 error_log /var/log/nginx/error.log warn;
5 pid /var/run/nginx.pid;
6
7
8 events {
9 worker_connections 1024;
10 }
11
12
13 http {
15 include /etc/nginx/mime.types;
16 default_type application/octet-stream;
17
18 log_format main '$remote_addr - $remote_user [$time_local] '
19 '"$request" $status $bytes_sent '
20 '"$http_referer" "$http_user_agent" '
21 '"$gzip_ratio"';
22
23 log_format download '$remote_addr - $remote_user [$time_local] '
24 '"$request" $status $bytes_sent '
25 '"$http_referer" "$http_user_agent" '
26 '"$http_range" "$sent_http_content_range"';
27
28 access_log /var/log/nginx/access.log main;
29
30 sendfile on;
31
32 keepalive_timeout 65;
33
34
35 server {
36 listen 80 default_server;
37 server_name _;
38
39 location /status {
40 vhost_traffic_status_display;
41 vhost_traffic_status_display_format html;
42 }
43 }
44
45 include /etc/nginx/conf.d/*.conf;
46 }

通过include引入其它server配置文件,而上面的内容可以作为nginx.conf全局默认配置文件,基本就不再修改了。而以后我们所要动态修改的配置文件就是/etc/nginx/conf.d/*.conf这部分。

配置规则

如果要达到自动化配置的目标,那么就需要设定一些规则。 下面是为了满足自动化而设置的规则:

  • 配置文件规则

    • 必须存在server_name。
    • 文件名以[server name].conf进行命名。 假设server_name为example.com, 则配置文件名就是example.com.conf。
    • 一个文件有并且只有一个server段
  • 配置内容规则
    • 同一个配置文件中location不重复(正则表达式不在限制范围内)

解析规则

在满足上述两个规则的前提下,我们来看如何实现Nginx参数的自动化配置。首先要明确实现nginx自动化配置的难点在哪里? 基于我的使用经验来看,难点在于以下三点:

  • nginx配置相当灵活,属于非结构化语义

    虽然nginx明确了配置文件的内容和格式,但在配置上可以任意组合(在执行nginx -t或者reload时才会真正验证)。因此配置文件只规定了最低门槛的结构范式,而并没有规定严谨的配置格式,造成了只要符合语义都可以验证成功。这一点在使用者眼里是非常灵活的优点,但从自动化角度来说则是很大的痛点,因为找不到一个统一的解析格式来理解语义。

  • 验证和回滚

    nginx是基于文本来进行配置的,每一次修改都是通过IO操作生成文本配置文件而后在加载在每个worker中。 因此当验证失败时,如何将新增/删除的内容恢复到上一个版本中,就变成了一个问题。

  • 个性化配置

    在真实业务场景中,nginx配置必然无法做到一个配置吃遍天。当某些server需要添加个性化配置参数时,如何平衡个性化配置和自动化配置,也变成了一个需要考虑的问题。

当找到上述三个问题的答案时,大体就可以满足自动化配置的要求了。

首先来看第一个问题。

如果因为nginx配置灵活而导致正面解析nginx配置文件是一个很困难的事情,那么可以尝试换个角度来理解这个问题。 如果变化很多而不容易解析,那么就不要让它变化了

具体怎么理解呢? nginx是通过语义来验证的,也就是nginx自身其实对结构不敏感的(可以反向证明,如果nginx是依赖结构来理解配置的,那么它应该会规定严谨的配置结构)。所以我们可以事先定义好每个配置文件的配置格式,如下:

     1
2
3 upstream 5d148ba37f325500011770af {
4 server xxxxx ;
5 }
6
7
8 server{
9
10 server_name web1.example.com;
11
12
13
14
15 location /server1 {
16 proxy_pass http://5d148ba37f325500011770af;
17 proxy_set_header X-Real-IP $remote_addr;
18 proxy_set_header Host $host;
19 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
20 proxy_next_upstream error timeout http_500 http_502 http_503 http_504 non_idempotent;
21
22
23
24 }
25
26 }
27

每个配置文件都规定好配置结构如下:

  • upstream都统一放置在server之前
  • server_name放置在location之前
  • proxy_pass 放置在每个location首行

当每个配置文件都满足上述三个条件时,自动化解析程序就可以按照设定好的规则解析并尝试理解每段语义。

只解析文件还不够,还需要能动态修改才可以。 再回到上面的配置内容,里面的变量有三部分,按照从上往下依次是:

  1. upstream的server IP列表
  2. server_name中的domain列表
  3. location列表

动态修改更准确的就是如何动态修改上面三部分值,这三部分的关联关系如下:


+-------------+
| server_name |
| domain1 |
| domain2 | +-----------------+ +-----------------+
| domain3 |---------------> | location1 |--------------> | upstream1 |
| ....... | +-----------------+ +-----------------+
| domainN |
+-------------+
+-----------------+ +-----------------+
| location2 |--------------> | upstream2 |
+-----------------+ +-----------------+ +-----------------+ +-----------------+
| locationN |--------------> | upstreamN |
+-----------------+ +-----------------+

同一个组的server_name共享所有的location数据,而每一个location则通过proxy_pass指向特定的upstream(可以是不同的,也可以是相同的upstream)。

从上图可以看出server_namelocation在一个作用域中(在同一个{}中)而upstream则游离在外。

三个问题中,server_name可以通过server_name准确定位,location也可以准确定位,此时如何从location通过proxy_pass定位到upstream则变成了当前的难点。

在实际使用过程中,我通过添加锚点来解决这个问题,具体来说就是增加一组upstream辅助定位数据,例如下图中的数据:

     1
2 ### [5d148ba37f325500011770af]-[/]-[upstream]-[start]
3 upstream 5d148ba37f325500011770af {
4 server xxxxx ;
5 }
6 ### [5d148ba37f325500011770af]-[/]-[upstream]-[end]
7
8 server{
9
10 server_name web1.example.com;
11
12
13
14
15 location /server1 {
16 proxy_pass http://5d148ba37f325500011770af;
17 proxy_set_header X-Real-IP $remote_addr;
18 proxy_set_header Host $host;
19 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
20 proxy_next_upstream error timeout http_500 http_502 http_503 http_504 non_idempotent;
21
22
23
24 }
25
26 }
27

第二行和第六行就是添加的锚点。 锚点数据需要满足的条件是:

  • 同一个配置文件中不重复
  • 有良好的区分度

因此设计了上述的锚点数据,其格式如下:

    ### [5d148ba37f325500011770af]-[/]-[upstream]-[start]
----------------------------------------------------
### [24位随机数]-[/]-[upstream]-[开始/结束标示]
① ② ③ ④ ① 三个#开头
② 满足锚点,upstream名称和proxy_pass一致,也就是第二行,第三行和第十六行使用同一个24位随机数
③ 固定格式,用来保证和其它注释信息不重复
④ start表示upstream开始, end表示upstream结束。

因此一个完整的自动化配置流程如下:

    // 假设配置web1.example.com的/server1 反向配置

    if web1.example.com.conf 存在
逐行读取文件内容 if 找到 server1的location行
解析 proxy_pass,找到 24位随机数 从头开始读取文件内容 if 找到 ### [xxxx]-[/]-[upstream]-[start]
找到锚点,此行往下两行是ip列表,开始修改
else
没找到锚点,配置文件出错,人工介入
else
// 当前没有此location配置,新建location和upstream
新建location配置
新建相匹配的upstream配置 else
// 当前没有此域名配置,新建一个
创建 web1.example.com.conf,内容按照既定格式创建

个性化支持

从上面的解析规则来看,如果要支持个性化支持,那么在理解语义时要做到适可而止,也就是只需要解析到需要的数据就可以了,其它数据原样复制。例如用户在location中添加了个性化参数(需要满足配置规则第三条),那么只要解析出proxy_pass就可以,后续的数据原样复制不要做变更。

新手学分布式-动态修改Nginx配置的一些想法的更多相关文章

  1. redis动态修改参数配置

    ./redis-cli -h 10.10.10.11 -p 6401   save  # 保存当前快照   # 列出所有当前配置 config get *   # 查看指定配置 config get  ...

  2. zookeeper 动态管理nginx配置

    假设我们有一个场景,所有服务器共享同一份配置文件,我们肯定不可能单独手动维护每台服务器,这时可以利用zookeeper的配置管理功能. 环境:python + nginx + zookeeper 目的 ...

  3. Elasticsearch 动态修改replica配置、增删replica

    1. 获取当前所有index配置 curl -XGET http://localhost:9200/_settings 2. 获取某些index的配置 curl -XGET http://localh ...

  4. 通过web页面修改nginx配置

    资源路径:https://download.csdn.net/download/song_yan_/12002460 nginx动态配置 一.页面展示 二.前端代码 (1)jsp页面(nginxCon ...

  5. Linux服务器上迁移项目路径,修改nginx配置,迁移及备份MongoDB数据库流程 (超详细)!!!

    缘由:客户服务器项目路径不是很合理,导致Jenkins自动部署时还需要添加路径后再更新部署,所以需要把项目路径统一和规范化. 迁移项目路径,保证路径合规,同时做好备份和迁移.迁移后先安装好依赖. 项目 ...

  6. docker下安装nginx,启动ngixn,修改nginx配置等--超详细

    1.获取nginx版本 docker中nginx版本信息:https://hub.docker.com/_/nginx?tab=tags&page=1&ordering=last_up ...

  7. 修改Nginx配置参数解决http状态码:413上传文件大小限制问题

    修改Nginx上传文件大小限制 我们使用ngnix做web server的时候,nginx对上传文件的大小有限制,默认是1M. 当超过大小的时候会报413(too large)错误.这个时候我们要修改 ...

  8. 新手学分布式 - Envoy Proxy XDS Server动态配置的一点使用心得

    Envoy Proxy 动态API的使用总结 Envoy Proxy和其它L4/L7反向搭理工具最大的区别就是原生支持动态配置. 首先来看一下Envoy的大致架构 从上图可以简单理解:Listener ...

  9. nginx配置入门

    谢谢作者的分享精神,原文地址:http://www.nginx.cn/591.html nginx配置入门 之前的nginx配置是对nginx配置文件的具体含义进行讲解,不过对于nginx的新手可能一 ...

随机推荐

  1. spring组件注册

    基于注解和类的组件注册 @Conditional 作用:按照一定的条件进行判断,如果满足条件的话就给spring容器中注册bean ​ 该注解既可以标注到方法上面,也可以标注到类上面(只有满足条件时, ...

  2. 【Offer】[23] 【链表中环的入口结点】

    题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 一个链表中包含环,如何找出环的入口结点?  思路分析 判断链表中是否有环:用快慢指针的方法,慢指针走一步,快指针走两步,如果快指针追上 ...

  3. 手工释放服务器的swap分区缓存

    时间     恢复时间 状态 信息 主机 问题 • 严重性 持续时间 确认 动作 2019-03-21 20:29:30     09:51:30     -ai-代理 Lack of free sw ...

  4. 数论 Day 12

    数论是个好东西 今天讲的是组合计数 组合计数 组合数学主要是研究一组离散对象满足一定条件的安排的存在性.构造及计数问题.计数理论是狭义组合数学中最基本的一个研究方向,主要研究的是满足一定条件的排列组合 ...

  5. openlivewriter安装配置

    一.前言 最近工作比较忙,同时也在构思下面我应该写哪方面的文章.上一篇文章,我直接在博客园管理后台自带的编辑器写的,确实比较麻烦,于是我就打算使用官方推荐的客户端“Open Live Writer”. ...

  6. ELKBR部署检测项目日志

    ELK filebeat:具有日志收集功能,相比logstash,+filebeat更轻量,占用资源更少,适合客户端使用. redis消息队列选型:Redis 服务器通常都是用作 NoSQL 数据库, ...

  7. asp.net core razor自定义taghelper

    又一个新的名词(taghelper),这个名词在netcore razor中也替代了(Htmlhelper),通过taghelper是可以操作html标签.条件输出.更是自由添加内外元素.当然也内置了 ...

  8. mybatis-geneator

    一.简介 在使用mybatis时我们需要重复的去创建pojo类.mapper文件以及dao类并且需要配置它们之间的依赖关系,比较麻烦且做了大量的重复工作,mybatis官方也发现了这个问题, 因此给我 ...

  9. Win10家庭版安装Docker

    1.下载Docker Toolbox 下载地址:http://mirrors.aliyun.com/docker-toolbox/windows/docker-toolbox/ 2.安装Docker ...

  10. Docker学习之Dockerfile

    通过编写简单的文件创建docker镜像 dockerfile 用来创建docker镜像. 格式 : FROM alpine:latest MAINTAINER XSW CMD echo "h ...