使用树莓派部署nginx服务器提供dns负载

之前买了一个小小的树莓派,一直在吃灰. 本来自己也有一个对外的博客域名,就想怎么样将此机器用起来.(本来就长年24小时开机,当简单的闹钟用)
本文章的目的是让树莓派工作起来,并没有一定技术上的特定优势,仅作技术验证以及耗电技术验证.
先说一下我这边的硬件和网络环境
1 linode云主机,cpu 1核,内存 1g,对外公网ip,地址新加坡(备案你懂的), nginx,mysql,php程序,之前是独立wordpress应用
2 树莓派3代,没部任何程序 上海电信家庭宽带 封80端口(后来发现的) 路由器可作内网转发

做负载均衡有很多种做法,根据当前的网络环境,以及硬件设备.对于像wordpress这种博客应用来说,有以下两种

1 后端应用负载
即使用一个前端nginx,后端通过挂一个upstream,将相应的请求代理到2个后端应用中.
这样的好处在于,应用始终访问一个对外地址,由统一的代理程序决定如何访问后端应用,并且也可以根据后端的实际情况设置权重.nginx上设置代理也简单.
不足之处,也有许多,主要是后端应用需要部署多套,并且相应的底层存储数据需要共享或者是复制.
1.1 在树莓派上就需要安装mysql,php这种应用,同时要开始公网数据同步,开销可能有点大.树莓派本身能不能支持mysql这种不算小型的数据库,内存和cpu占用都是未知问题.
1.2 前端代理访问,nginx之前安装在linode主机,当前也不准备更换,如果代理到树莓派,就意味着访问先从国内访问到linode,然后linode再内部访问到树莓派,中间的延时肯定会有问题
1.3 当然也可以将nginx放在树莓派国内电信上,不过鉴于电信宽带本身的稳定性(ip变化,断电,或者临时被封等),肯定比不上固定的云主机,而且树莓派一挂,整个博客即挂掉

2 访问前端负载
更往前一点就可以作dns负载, 通过dns解析域名时产生多个ip,让访问者随机选择1个进行访问.
好像在于,原linode应用不作任何调整,接下来就工作就是让访问树莓派时能够输出相应的内容即可
不足之处,在于当前的dns解析(使用的dnspod)当前还不能作权重,不能设置权重信息. 树莓派不用后端处理,那么就需要加速访问才能达到效果.

最后的方案就是dns负载,树莓派加速网络访问,整个步骤如下

  1. 搭建支持https以及lua脚本支持的nginx
  2. 加速GET请求处理
  3. 反向代理POST请求
  4. 反向代理wp-admin后台管理
  5. dns负载配置,ip动态更新
  6. 树莓派小尾巴

1 搭建支持https以及lua脚本支持的nginx

为什么要选支持lua脚本,因为树莓派本身不作后台处理,但又要承担一些技术应用,否则全部代理到linode,那基本上没什么效果,还增加网络成本. 因此,采用lua+redis加速相应的前端访问.
现在比较火的就是openresty,当前的做法就是直接下载相应的源码,手动编译,安装即可.
本文采用的版本为 1.11.2.4
编译参数为:

--prefix=/home/flym/data/openresty/nginx -j2 --with-cc-opt='-O2 -fno-optimize-sibling-calls' --with-openssl=/home/flym/data/soft/openssl-1.0.2l --with-http_v2_module --with-http_addition_module --with-http_ssl_module

主要是启用http2,因为网站原来就是使用lets crypt的证书.其它的 -j2 支持lua脚本

这里有一坑,注意上面的with-cc-opt选项, 主要解决是在树莓派下默认编译优化会导致 prxy_pass 出现 empty reply from server的问题,便代理不能正常工作.参考 issue: https://github.com/openresty/openresty/issues/240

编译好之后,证书方面,当前简单粗暴,直接将原linode的证书copy回来,原样配置起相应的nginx,做简单的映射之后即能正常工作.
经验证,上海电信宽带封80端口,但443端口可以使用,即意味着直接通过域名访问 http://www.iflym.com 不能访问,但访问 https时,可以访问. 因为上海电信封80,即意味着如何第一次直接通过域名访问就可能访问不了.但考虑到访客都是通过历史记录或者是搜索引擎访问,因此不存在此问题.(搜索引擎收录的都是https地址)

2 加速GET请求处理

为什么是GET请求,因为GET是幂等的,可以缓存.post无法缓存,只能代理,并且在中间加一层lua脚本技术上也挺复杂.
本文采用resty.http + openresty自带的redis组件,并参考网上的配置. 使用 http请求访问linode远程的http请求(这里不再访问https,以减少请求量),然后将相应的结果缓存到redis中,下一次访问时直接从redis中获取相应的结果即可.

考虑到界面可能有变化,这里redis缓存设置相应的过期时间为30分,即每30分重新访问一次远程数据(更优手段是自动同步,但这里没作)

redis中缓存的数据包括 响应状态码,响应头以及具体的响应内容, 相应的key则以相应的uri路径为key.
响应状态码,以支持像远端发生301跳转的情况,这样redis中缓存301时也可以直接将301返回给前端处理.因为resty.http不会自行处理301请求(nginx也是不会处理,交由前端自行处理)
响应头,主要是将远程的响应不变地返回给前端,避免修改一些特定的响应信息. 这里需要注意的是,由远程返回的响应头中,几个与connection相关的需要删除掉,因为这几个响应头是非代理的,仅由代理和源之间使用,不可再返回到前端,否则即会出现解析问题.这里我主要设置了3个头不转发,分别为 Transfer-Encoding,Connection,Content-Length. 主要是传输编码和长度相关,这些字段会由nginx重新设置给前端(或者使用其它响应字段),如果再加上这些响应头,前端就变得不知什么解析.

另外,使用lua将响应头信息存储到redis,涉及到序列化问题,这里我采用了 cjson 这个模块,简单的encode和decode就解决问题

3 反向代理POST请求

因为考虑到post请求都是表单相关, 树莓派没有任何后端, 自己作表单处理比较麻烦,就由nginx直接反向代理回linode远端就好.

因此这里就有一个选择判断的情况了, 如果前端是GET请求,就交由lua脚本处理,如果是POST请求就反向代理回远端. 这种情况可以用复杂的匹配模式处理,但最简单的就是使用if指令.虽然if指令不被推荐,但用在这里最方便,就是一个2选1的情况,不存在互相交叉的情况. 我用了2个if+break来处理这个场景,即满足条件,处理,然后终止流程,如下参考所示:

    if ($request_method = GET) {
        content_by_lua_file conf/lua/flym.com.lua;
        break;
    }
    resolver 223.5.5.5;
    if ($request_method != GET) {
        proxy_pass http://internal.iflym.com;
        break;
    }

可以看到,上面用了一个非https的内部访问地址, 这里主要作用在于本身nginx已经处理了https协议,这里访问远端就直接走http协议了, 避免再访问https消耗性能.

4 反向代理wp-admin后台管理

考虑到wp-admin是整个wordpress的后台应用, 本身作负载的作用不大, 并且大量是post处理,这里就全交由linode远端来处理.因此,nginx里作一个简单的反向代理. 凡是以 /wp-admin 开头的请求,不管get还是post,全反向代理到远端.

这里有一点需要注意,因为我们这里是采用一个内部域名进行代理,远端是由wordpress php程序在处理. 如直接访问 /wp-admin,则默认的php 就是返回302跳转到 如 http://internal.iflym.com/login.php 类似的地址,这个地址浏览器是无法作解析的,并且这里也不是https协议. 经过google,我们可以在nginx作cgi处理时,传递一定的参数,让php接收相应的请求头信息,即重设请求头,这样php在重定向或者输出内容时,就了解正确的协议和域名了.相应的代理设置如下参考

    fastcgi_param   HTTPS               on;
    fastcgi_param   HTTP_SCHEME         https;
    fastcgi_param   SERVER_NAME www.iflym.com;
    fastcgi_param   HTTP_HOST www.iflym.com;

以上设置仅供参考,来源于多个地方,某些头可能并没有用. 参考 https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/

5 dns负载配置,ip动态更新

这个最简单,在dnspod上增加一条A记录,指向上海电信宽带的对外ip即可.如何查看?ip138.net可查看对外ip.

另外,因此对外ip经常变化, 因此可以作一个定时程序,使用dnspod api更新相应域名ip即可.参考 https://gist.github.com/chuangbo/833369

6 树莓派小尾巴

为了让树莓派看起来是在工作的,我加了一个小尾巴.使用nginx的 http_addition_module 模块,当前端应用是访问树莓派生成的内容时,会在尾巴增加一段话.如下:

<!-- generated by flym's raspberry pi 3. 本界面由flym的树苺派3生成 -->

就是将上面这段话保存到一个html片段文件中,然后在nginx使用以下指令

add_after_body /_header/header.html;

如果,你在访问本博客时,查看源代码,发现以上文本,就是树莓派返回的啦.

总结:

经过以上的处理,中间加上大量调试(主要是lua语言不熟+nginx各类配置),终于最终工作了. 最后简单验证了一下, 如果是第一次访问的话,会有一个比较明显的延迟,估计是后台lua在请求远端数据时比较慢(http协议容易被墙..), 后面有缓存了就非常快了. lua+redis的处理速度还是不需要怀疑的.

本文涉及到的 lua 脚本,如此连接可以下载参考.  lua+redis远程访问+存储

转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/computer-use/201708210001.html

相关文章:

作者: flym

I am flym,the master of the site:)

发表评论

邮箱地址不会被公开。 必填项已用*标注