nginx是一个特别受欢迎的开源软件,但它有一些特性只有在商业版里才有,所以就有了一些好事者自己编写了一些和商业版相似功能的模块,这才开源的nginx功能更能接近商业版。这里介绍nginx源码编译安装并引入了一些优秀的第三方模块的编译过程,可供参考。
前言
线上环境有多套nginx作为应用的负载均衡器,因历史原因,nginx存在各种遗留问题,版本不统一,调度算法单一,未实现对上游服务器的健康检测,配置缩进不规范,所有的
upstream,server都配置在nginx.conf文件中,看起配置比较冗长。为了实现环境部署的规范化,需要规范nginx的目录结构、配置文件的缩进,并制作一个nginx环境的部署模板,
在当有需要一个nginx环境需求时能通过简单的操作就能部署一个功能完整的nginx环境。
源码编译安装nginx
目前nginx的稳定版本为“nginx-1.8.1”,以此版本来编译安装。nginx有许多非常好的特性,在查看官方帮助文档时,有时看到一个功能特别强大的指令时,正当你在欢喜时,你
又会发现有如下的说明:
This directive is available as part of our commercial subscription .
不知道此你有何感想。好在开源的力量是不可估量的,nginx许多商业化版本中才支持的功能在github上都能找到类似的开源项目。这次制作nginx为模板时也加进了几个第三方模块,
使nginx的功能更完善。
我的编译平台如下:
1 2 3 4 root@nginx-02:~/tools 3.16.0-4-amd64 root@nginx-02:~/tools
引入的第三方模块列表如下:
此模块能实现对上游服务器的基于tcp、http、ssl、hello、mysql、ajp、fastcgi的健康检测,并能把被标记为down的主机踢出负载调度。
此模块实现nginx的四层调度,nginx-1.9已原生支持四层调度了,但目前不是稳定版本。此模块还提供了一个对四层调试的状态监控页面。
此模块增强了round-robin负载均衡算法,可以跟踪后端服务器的负载来分发请求,觉得有点类似least_conn算法。
此模块实现了session保持,比ip_hash的粒度更小,它是基于用户浏览器cookie来保持会话。
此模块实现日志过虑功能,此模块是做好模板后才有这样的功能需求,所以下边的演示没有加入此模块。
第三方模块安装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 root@nginx-02:~/tools /root/tools root@nginx-02:~/tools nginx-1.8.1 nginx-1.8.1.tar.gz part3 root@nginx-02:~/tools/part3 /root/tools/part3 root@nginx-02:~/tools/part3 total 524 drwxrwxr-x 4 root root 4096 Aug 6 2015 nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d -rw-r--r-- 1 root root 120553 Mar 8 09:53 nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d.tar.gz drwxr-xr-x 7 root root 4096 Aug 18 2015 nginx_tcp_proxy_module-master -rw-r--r-- 1 root root 213069 Mar 8 10:28 nginx_tcp_proxy_module-master.zip drwxr-xr-x 6 root root 4096 Jul 1 2015 nginx_upstream_check_module-master -rw-r--r-- 1 root root 167495 Mar 8 16:50 nginx_upstream_check_module-master.zip drwxr-xr-x 2 root root 4096 Apr 8 2012 nginx-upstream-fair-master -rw-r--r-- 1 root root 10845 Mar 8 09:28 nginx-upstream-fair-master.zip
特别注意
各个模块的安装方法请参数源码目录下的帮助文档,经过反复验证,在安装以上第三方模块时,有些坑是值得注意的。nginx_upstream_check_module与nginx_tcp_proxy_module
不能同时导入到nginx源码,不然在make时会报错,所以请严格按照以下顺序来安装。
第一步:先解决编译安装nginx的依赖并创建运行nginx work进程的用户
1 root@nginx-02:~/tools/nginx-1.8.1
libpcre3 libpcre3-dev HTTP rewrite module requires the PCRE library
1 root@nginx-02:~/tools/nginx-1.8.1
第二步:导入第三方nginx_upstream_check_module模块
1 2 3 4 5 6 7 root@nginx-02:~/tools/nginx-1.8.1 patching file src/http/modules/ngx_http_upstream_hash_module.c Hunk patching file src/http/modules/ngx_http_upstream_ip_hash_module.c patching file src/http/modules/ngx_http_upstream_least_conn_module.c patching file src/http/ngx_http_upstream_round_robin.c patching file src/http/ngx_http_upstream_round_robin.h
nginx-upstream-fair-master和nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d模块不需要做额外处理,只需要在编译时用–add-module指令加入即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 root@nginx-02:~/tools/nginx-1.8.1 --user=nginx \ --group=nginx \ --with-file-aio \ --pid-path=/var/run/nginx18.pid \ --lock-path=/var/lock/subsys/nginx18 \ --with-http_ssl_module \ --with-http_flv_module \ --with-http_mp4_module \ --with-http_gunzip_module \ --with-http_gzip_static_module \ --with-http_stub_status_module \ --with-http_auth_request_module \ --with-http_realip_module \ --with-http_secure_link_module \ --http-client-body-temp-path=/var/tmp/nginx18/client \ --http-proxy-temp-path=/var/tmp/nginx18/proxy \ --http-fastcgi-temp-path=/var/tmp/nginx18/fastcgi \ --http-uwsgi-temp-path=/var/tmp/nginx18/uwsgi \ --http-scgi-temp-path=/var/tmp/nginx18/scgi \ --add-module=../part3/nginx-upstream-fair-master \ --add-module=../part3/nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d \ --add-module=../part3/nginx_upstream_check_module-master root@nginx-02:~/tools/nginx-1.8.1 root@nginx-02:~/tools/nginx-1.8.1 root@nginx-02:~/tools/nginx-1.8.1 root@nginx-02:~/tools/nginx-1.8.1 /usr/local /nginx18/ ├── conf │ ├── fastcgi.conf │ ├── fastcgi.conf.default │ ├── fastcgi_params │ ├── fastcgi_params.default │ ├── koi-utf │ ├── koi-win │ ├── mime.types │ ├── mime.types.default │ ├── nginx.conf │ ├── nginx.conf.default │ ├── scgi_params │ ├── scgi_params.default │ ├── uwsgi_params │ ├── uwsgi_params.default │ └── win-utf ├── html │ ├── 50x.html │ └── index.html ├── logs └── sbin └── nginx
查看nginx编辑进的模块:
第四步:导入第三方nginx_tcp_proxy_module模块
1 2 3 4 5 6 7 8 9 root@nginx-02:~/tools/nginx-1.8.1 patching file src/core/ngx_log.c Hunk patching file src/core/ngx_log.h Hunk Hunk patching file src/event/ngx_event_connect.h Hunk Hunk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 root@nginx-02:~/tools/nginx-1.8.1 --user=nginx \ --group=nginx \ --with-file-aio \ --pid-path=/var/run/nginx18.pid \ --lock-path=/var/lock/subsys/nginx18 \ --with-http_ssl_module \ --with-http_flv_module \ --with-http_mp4_module \ --with-http_gunzip_module \ --with-http_gzip_static_module \ --with-http_stub_status_module \ --with-http_auth_request_module \ --with-http_realip_module \ --with-http_secure_link_module \ --http-client-body-temp-path=/var/tmp/nginx18/client \ --http-proxy-temp-path=/var/tmp/nginx18/proxy \ --http-fastcgi-temp-path=/var/tmp/nginx18/fastcgi \ --http-uwsgi-temp-path=/var/tmp/nginx18/uwsgi \ --http-scgi-temp-path=/var/tmp/nginx18/scgi \ --add-module=../part3/nginx-upstream-fair-master \ --add-module=../part3/nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d \ --add-module=../part3/nginx_upstream_check_module-master \ --add-module=../part3/nginx_tcp_proxy_module-master root@nginx-02:~/tools/nginx-1.8.1
拷贝重新编译好的nginx二进制文件到nginx的安装目录,如下:
1 2 3 4 5 root@nginx-02:~/tools/nginx-1.8.1 auto CHANGES CHANGES.ru conf configure contrib html LICENSE Makefile man objs README src root@nginx-02:~/tools/nginx-1.8.1 addon autoconf.err Makefile nginx nginx.8 ngx_auto_config.h ngx_auto_headers.h ngx_modules.c ngx_modules.o src root@nginx-02:~/tools/nginx-1.8.1
再次查看编译进nginx的模块:
很明显多了nginx_tcp_proxy_module这个模块。
第六步:提供systemctl控制的启动脚本及收尾工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 root@nginx-02:~/tools/nginx-1.8.1 [Unit] Description=nginx - high performance web server Documentation=http://nginx.org/en/docs/ After=network.target [Service] Type=forking PIDFile=/var/run/nginx18.pid ExecStartPre=/usr/local /nginx18/sbin/nginx -t -c /usr/local /nginx18/conf/nginx.conf ExecStart=/usr/local /nginx18/sbin/nginx -c /usr/local /nginx18/conf/nginx.conf ExecReload=/usr/local /nginx18/sbin/nginx -s reload ExecStop=/usr/local /nginx18/sbin/nginx -s stop TimeoutStopSec=5 KillMode=mixed [Install] WantedBy=multi-user.target root@nginx-02:~/tools/nginx-1.8.1 Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /lib/systemd/system/nginx.service. root@nginx-02:~/tools/nginx-1.8.1 root@nginx-02:~/tools/nginx-1.8.1 LISTEN 0 128 *:80 *:* users:(("nginx" ,pid=10935,fd=6),("nginx" ,pid=10934,fd=6)) root@nginx-02:~/tools/nginx-1.8.1 root@nginx-02:~/tools/nginx-1.8.1 export PATH=/usr/local /nginx18/sbin:$PATH root@nginx-02:~/tools/nginx-1.8.1
关于线程池
线程池在nginx-1.8是支持的,但在当我启用了“–with-threads”功能,并加增加“–add-module=…/part3/nginx_tcp_proxy_module-master”模块后,make时会报如下错:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ../part3/nginx_tcp_proxy_module-master/ngx_tcp_upstream_round_robin.c: In function ‘ngx_tcp_upstream_get_round_robin_peer’: ../part3/nginx_tcp_proxy_module-master/ngx_tcp_upstream_round_robin.c:459:16: error: ‘ngx_event_t’ has no member named ‘lock’ c->read ->lock = c->read ->own_lock; ^ ../part3/nginx_tcp_proxy_module-master/ngx_tcp_upstream_round_robin.c:459:32: error: ‘ngx_event_t’ has no member named ‘own_lock’ c->read ->lock = c->read ->own_lock; ^ ../part3/nginx_tcp_proxy_module-master/ngx_tcp_upstream_round_robin.c:460:17: error: ‘ngx_event_t’ has no member named ‘lock’ c->write->lock = c->write->own_lock; ^ ../part3/nginx_tcp_proxy_module-master/ngx_tcp_upstream_round_robin.c:460:34: error: ‘ngx_event_t’ has no member named ‘own_lock’ c->write->lock = c->write->own_lock; ^ objs/Makefile:1298: recipe for target 'objs/addon/nginx_tcp_proxy_module-master/ngx_tcp_upstream_round_robin.o' failed make[1]: *** [objs/addon/nginx_tcp_proxy_module-master/ngx_tcp_upstream_round_robin.o] Error 1 make[1]: Leaving directory '/root/tools/nginx-1.8.1' Makefile:8: recipe for target 'build' failed make: *** [build] Error 2
只好作罢,如果真的业务需要启用threads,再去掉“nginx_tcp_proxy_module”模块后重新编译。
规范
至此,我们编译安装的nginx已准备好。在生产环境中,gninx的配置文件改动得比较频繁,如果没有规划好配置文件该怎么修改?各个目录该存放什么文件?缩进是否要严格要求?
随时间的推移,nginx的配置文件将变得不易维护,所以约定如下:
nginx.conf配置文件只启用nginx健康检测的页面的虚拟主机,并使用include语句把“/usr/local/nginx18/conf”目录下的"layer4"和“layer7”目录下的任何以“.conf”结尾的文件
包含进来,目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 root@nginx-02:/usr/local /nginx18/conf /usr/local /nginx18/conf root@nginx-02:/usr/local /nginx18/conf total 68 -rw-r--r-- 1 root staff 1034 Mar 9 22:03 fastcgi.conf -rw-r--r-- 1 root staff 1034 Mar 9 22:03 fastcgi.conf.default -rw-r--r-- 1 root staff 964 Mar 9 22:03 fastcgi_params -rw-r--r-- 1 root staff 964 Mar 9 22:03 fastcgi_params.default -rw-r--r-- 1 root staff 2837 Mar 9 22:03 koi-utf -rw-r--r-- 1 root staff 2223 Mar 9 22:03 koi-win drwxr-sr-x 2 root staff 4096 Mar 9 22:03 layer4 drwxr-sr-x 2 root staff 4096 Mar 9 22:03 layer7 -rw-r--r-- 1 root staff 3957 Mar 9 22:03 mime.types -rw-r--r-- 1 root staff 3957 Mar 9 22:03 mime.types.default -rw-r--r-- 1 root staff 3480 Mar 9 22:03 nginx.conf -rw-r--r-- 1 root staff 2656 Mar 9 22:03 nginx.conf.default -rw-r--r-- 1 root staff 596 Mar 9 22:03 scgi_params -rw-r--r-- 1 root staff 596 Mar 9 22:03 scgi_params.default -rw-r--r-- 1 root staff 623 Mar 9 22:03 uwsgi_params -rw-r--r-- 1 root staff 623 Mar 9 22:03 uwsgi_params.default -rw-r--r-- 1 root staff 3610 Mar 9 22:03 win-utf
上边规划的layer4和layer7两个目录是分别放置4层调度和7层调度时各配置文件,文件以“.conf”结尾;
各虚拟主机的配置文件名称以“server_name”中的值来命名,点号转换成“_”,如“server_name www.test.cn ”,那相应的配置文件为“www_test_cn.conf”,在layer4目录下的文件也
类似,只是它没有“server_name”字段,以upstream字段的值来命名配置文件,如“upstream cmdserver”,那配置文件为“cmdserver.conf”;
layer7目录下各虚拟主机产生的访问日志和错误日志名称单独规划,参照配置文件的名称,日志名称如“www_test_cn.access.log”和"www_test_cn.error.log";
配置文件中的代码缩进严格按照各代码块的层级关系缩进。
配置示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 root@nginx-02:/usr/local /nginx18/conf /usr/local /nginx18/conf root@nginx-02:/usr/local /nginx18/conf user nginx; worker_processes 1; pid /var/run/nginx18.pid; events { worker_connections 65535; } 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"' '"$upstream_cache_status"' ; access_log logs/access.log main; proxy_temp_path /usr/local /nginx18/proxy_temp; proxy_cache_path /usr/local /nginx18/proxy_cache levels=1:2 keys_zone=cache_one:100m inactive=2d max_size=2g; sendfile on; keepalive_timeout 65; client_header_buffer_size 4k; proxy_headers_hash_max_size 512; proxy_headers_hash_bucket_size 128; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } location /layer4-status { tcp_check_status; access_log off; } location /layer7-status { check_status; access_log off; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } include layer7/*.conf; } include layer4/*.conf; root@nginx-02:/usr/local /nginx18/conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 root@nginx-02:/usr/local /nginx18/conf/layer4 /usr/local /nginx18/conf/layer4 root@nginx-02:/usr/local /nginx18/conf/layer4 cmdserver.conf root@nginx-02:/usr/local /nginx18/conf/layer4 tcp { upstream cmdserver { ip_hash; server 172.31.11.70:9100; server 172.31.11.71:9100; check interval=3000 rise=2 fall=5 timeout=1000 type =tcp; } server { listen 8500; proxy_pass cmdserver; } } root@nginx-02:/usr/local /nginx18/conf/layer4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 root@nginx-02:/usr/local /nginx18/conf/layer7 /usr/local /nginx18/conf/layer7 root@nginx-02:/usr/local /nginx18/conf/layer7 www_test_cn.conf root@nginx-02:/usr/local /nginx18/conf/layer7 upstream test_server { sticky; server 172.31.0.10:80 weight=1; server 172.31.11.96:80 weight=1 down; check interval=5000 rise=2 fall=5 timeout=1000 type =http port=80; check_http_send "HEAD / HTTP/1.0\r\n\r\n" ; check_http_expect_alive http_2xx http_3xx; } server { listen 8080; client_max_body_size 50M; server_name www.test.cn; access_log logs/www_test_cn.access.log main; error_log logs/www_test_cn.error.log debug; location ~ .*\.(gif|jpg|html|css|js|ico|swf|pdf)(.*) { proxy_pass http://test_server; proxy_set_header X-Forwarded-For $remote_addr ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; proxy_set_header Host $host ; proxy_redirect off; proxy_cache cache_one; add_header Nginx-Cache $upstream_cache_status ; proxy_cache_valid 200 304 301 302 8h; proxy_cache_valid 404 500 1m; proxy_cache_valid any 2d; proxy_cache_key $host $uri $is_args $args ; expires 30d; } location / { proxy_pass http://test_server; proxy_set_header X-Real-IP $remote_addr ; proxy_set_header X-Forwarded-For $remote_addr ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; proxy_set_header Host $host ; proxy_redirect off; set_real_ip_from 172.31.0.0/16; set_real_ip_from 172.31.0.100; real_ip_header X-Real-IP; } } root@nginx-02:/usr/local /nginx18/conf/layer7
总结
以后若有nginx环境的需要,可以直接把“/usr/local/nginx18”目录拷贝到目标主机,再把“/lib/systemd/system/nginx.service”文件拷贝到目标主机,一个nginx环境就可以运行了。