WEB应用

分享一个Nginx反向代理的另类应用案例

博客前面分享了一篇《分享一个Nginx正向代理的另类应用案例》,时隔不久,身为救火队员、万金油的博主又再一次接到了一个另「W」类「T」需「F」求: 场景和上次有些类似,也是部门引进的第三方应用,部署在各个网络区域,从OA办公区域无法直接访问。目前,运营人员都需要登陆Windows跳板机,才能打开这些应用的WEB控制台。既不方便,而且还有一定Windows服务器的维护工作量,于是找到我们团队,希望通过运维手段来解决。 拿到这个需求后,我先问了下各个应用的基本情况,得知每个应用的框架基本是一样的,都是通过IP+端口直接访问,页面path也基本一样,没有唯一性。然后拿到了一个应用WEB控制台地址看了下,发现html引用的地址都是相对路径。 乍一想,这用Nginx代理不好弄吧?页面path一样,没法根据 location 来反代到不同的后端,只能通过不同Nginx端口来区分,那就太麻烦了!每次他们新上一个应用,我们就得多加一个新端口来映射,这种的尾大不掉、绵绵不绝事情坚决不干,Say pass。 再一想,我就想到了上次那个正向代理另类应用方案,感觉可以拿过来改改做动态代理。原理也简单:先和用户约定一个访问形式,比如: Nginx代理地址为myproxy.oa.com,需要代理到IP为 192.168.2.100:8080 的控制器,用户需要访问http://myproxy.oa.com/192.168.2.100:8080/path。 动态代理的原理及实现: 1、Nginx从$request_uri变量中,通过斜杠提取第一段作为要反向代理的对象,即proxy_pass,提取后面的作为需要反代的路径; 2、对于Html、JS、CSS等资源引用则需要通过Nginx的替换模块,将路径替换为上述约定形式,比如:Html里面的href="/js/jquery.min.js",需要替换为href="http://myproxy.oa.com/192.168.2.100:8080/js/juqery.min.js",即所有资源地址都保证符合代理约定的形式,才能够正确走代理获取。 3、通过代理去访问,查看浏览器开发者工具中的Network和console,找到无法访问的地址,并分析引用的位置,写规则替换掉即可。 4、实际测试发现,他们的应用可能还会有https协议(...),而且是伪证书模式,只是开了https协议访问。上述设计模式下,https页面是无法打开的,这里需要兼容https后端才行,因此最后的约定形式简单修改为:如果是 http://myproxy.oa.com/https-192.168.2.101:8080/path,即:如果是https协议,需要在第一段path的IP前面加上https-来区分,而http协议则可加可不加。 基本通过上述几步,就完全搞定了,最终Nginx规则如下: Tips:实际调试过程中,可以给Nginx集成一个echo模块,可以将变量打印出来,方便调试。 最后,在Nginx服务器的/html目录放一个index.html导航页面,用户只需要访问代理地址http://myproxy.oa.com/,就能看到全部的后端节点,点击访问即可,真的不要太爽哦! 不得不再一次感慨,Nginx真是一个好东西,完美~
阅读全文
WEB应用

惊现Haproxy重复添加X-Forwarded-For问题(附官方解决办法)

最近在配置Haproxy代理的时候发现一个很有意思的事情:Haproxy在代理http请求会无脑加一个X-Forwarded-For(后文简称XFF),而不是将自身的IP地址加到已存在的XFF列表之后,WTF!还有这种神操作? 确认无误之后,我到Haproxy的github开了一个issue反馈了这个BUG(issue地址),最终了解到了Haproxy就是这样设计的,并得到了解决方案,而且这个issue的回复很有意思,特来博客分享下。 刚开始,我的issue提到Haproxy没有将自身IP地址append到已有的XFF列表之后,而是无脑又加了一个,这应该是个BUG: 官方开发GG回复让我升级到最新稳定版,我说升级了也是一样的,结果人家又回复我说Haproxy本来就是这样设计的: 我接着回复说Haproxy这样设计会导致很多开源程序获取真实IP异常,比如Twisted和packetbeat,但是官方说Haproxy 的设计100%符合HTTP协议标准balabala....既然都这样说了,我能说啥呢?只好用官方给的建议选项if none来拒绝Haproxy添加XFF,先解决我程序获取源IP错误的问题。 本以为这样就结束了,结果高潮来了! 一位很酷的法国大胡子发起了热心支援: 他的大概意思是说,既然Haproxy 100% 符合HTTP标准,那为啥没有遵循 XFF 的标准约定,将自身的IP地址添加到已存在的XFF列表末尾??也提到了多个XFF会导致很多程序无法读取,比如tomcat-8.5。为啥不加一个选项来选择使用多个XFF还是使用一个XFF等等balabala一顿说... 接着官方在HTTP标准上发起反驳解释(一大堆内容,此处不表),并在最后说明这样做是为了提高Haproxy性能,如果先判断是否存在XFF在高并发情况下性能大概下降2-3倍(非特指Haproxy)。 当然,官方最终还是给了一个解决办法,能够让Haproxy也像Nginx那样将自己的IP地址加到已有XFF之后,只需在Haproxy新增如下配置即可: 虽然官方给的答复可以解决问题,不过法国大胡子最后回复的一段话我觉得说得非常好: 大致意思是,我已经知道如何通过配置来解决,但是为啥 option forwardfor 这个选项没有和大部分人预期那样来设计与开发,而且搞得巨复杂? 然后直接给出他认为更好的开发设计,比如用 option forwardfor force 替代: 实现强制覆盖XFF,又比如用 option forwardfor append 来替代: 实现将自身IP地址加到已有的XFF之后。很明显,这样的设计才更有可读性,更好理解!他在最后还提到了,Apache/Nginx/Tomcat/Jetty/F5等等都是将自身IP地址添加到XFF之后,难道你Haproxy认为这些软件的使用率还不够高??喷得大快人心,再次赞一下法国大胡子! 总之,真的是很有意思的一个issue,同时也解决了问题,发现同样问题的朋友可以参考解决!
阅读全文
数据库

解决MySQL主从复制错误1595:Relay log write failure…

先交代下背景:我这边正在研究基于Docker的MariaDB Galera Cluster的自动化集群部署方案【以下简称MGC,后续有时间可以考虑分享下这个方案】,已经完成所有调试,想在生产环境部署一个MGC集群作为从先灰度测试。 生产环境主DB版本为MySQL5.5,新的MGC采用Mariadb最新10.3.12 stable版本,做好MGC集群,并导入一份从主DBdump出来的完整SQL之后,change master 开始创建主从,结果如下报错: 查了下资料,都是是磁盘满导致的,但是我这边磁盘空间、权限都没问题,于是继续检查了下MGC节点日志如下: 里面有一个关键信息:binlog_checksum failed,看来报错和这个有关系了。查了下资料,应该是Mariadb默认打开了 slave_sql_verify_checksum(MySQL版本默认关闭),而主从版本又不一致,导致checksum失败。 解决办法也很简单,在MGC各节点的配置中加上:slave_sql_verify_checksum=0 ,关闭这个特性即可,具体有没有负面影响暂未深入研究。
阅读全文
大数据

解决Nexus Docker仓库搜索镜像返回500错误

之前用Nexus给团队搭建了一个数据仓库,其中Docker仓库配置教程已经整理分享到了博客《Nexus3最佳实践系列:搭建Docker私有仓库》,但是一直有个小问题,使用docker search来搜索会返回500错误: 因为平常不怎么使用搜索,而且Nexus前台也有镜像浏览页面,所以一直没花时间去解决。年前封网好不容易有点空闲,寻思着解决下。 经过定位,发现了问题所在:Nexus里面的Docker有3种类型仓库:group、hosted、proxy,也就是组合仓库、本地仓库和代理仓库,其中组合仓库就是本地仓库和代理仓库的聚合,通过测试发现只有本地仓库才支持search请求,其他2种类型仓库都会500错误。所以,报错的直接原因就是我将search的请求转发到了组合仓库导致的。 解决办法非常简单,在nginx转发里面加一条规则即可: Ps:详见之前的文章:https://zhang.ge/5139.html 。 以下是定位过程,不感兴趣的可以忽略。。。 1、确认Nginx代理日志是正常打开状态,并tail -f idocker.io.log实时查看日志; 2、执行docker search发起搜索: 3、回过来查看日志:发现500错误的内容如下(我这边Nginx改成了json格式): 2个重要的信息:搜索请求使用的是GET方法,请求路径是 /v1/search?q=hello-world&n=25。 4、直接对仓库后端一次发起请求,首先试了下group组合仓库,端口8082: 终于看到了关键的报错: 大概意思是不支持SEARCH,于是继续试了下hosted本地仓库,端口8083: 很明显是可以的,然后再试了下proxy代理仓库,都不行。因此确定只有本地仓库是可以搜索的! 回头看了下Nginx代理的配置,发现我之前将所有GET都丢给了组合仓库,也就是转发了拉取请求,将PUT丢给了本地仓库,意思是转发了推送请求。看来,还需要多加一个规则,将搜索请求转发到本地仓库。 也就是得出了上文的规则: 这个规则明显要加到PUT转发规则之后,以覆盖之。 生效后,再次执行 docker search idocker.io/hello-world,结果如下: 问题得到解决~! 当然,还有点遗憾的是代理和组合仓库不支持搜索~希望后续Nexus版本能够考虑加上这个特性。
阅读全文
WEB应用

分享一个Nginx正向代理的另类应用案例

最近接到了一个需求:通过Nginx代理把现网一个自研代理程序给替换掉,感觉有点意思,也有所收益,简单分享下。 需求背景 部门的生产环境异常复杂,有部分第三方引入的系统位于特殊网络隔离区域,请求这些系统需要通过2层网络代理,如图所示: 中心源系统请求目标系统API的形式各异,我简单收集了下,至少有如下3种: 目前开发GG是用 lighthttp 二次开发实现了这个需求(猜测用到了一堆判断和转发逻辑),存在一定的后期维护工作量,而且这个GG已经转岗去其他部门了,现任开发GG就想直接通过 Nginx 代理来实现,淘汰这个组件,因此就将这个需求丢给了我这个运维了。 需求分析 拿到需求后,我分析了下,应该需要使用正向代理来实现,我们来看下普通的一级正向代理写法: 这个规则的意思是将所有请求都代理到请求对应的主机。这个在内网正向代理上网的时候会用到,这时候用户只需要将你提供的代理设置为http_proxy,就可以访问到直接访问不到的站点。 看起来好像可以满足需求了,But...实际需求是要经过2层代理,那第一层代理的$host必须是固定为第二层代理的地址了!而且Nginx也不支持类似http_proxy的设置,所以照搬正向代理是行不通的。 最终解决 既然正向代理涉及到自动提取目标主机、端口以及请求的特性,那我们就自己设计一个请求方式,方便使用Nginx自带规则来提取并自动代理。 我和开发约定了一个请求方式(之前也用了类似约定),方便Nginx来提取变量并自动代理: 将真正需要请求的API拆成: ?schema=http&host=主机:端口&proxy_url=请求路径及参数,然后请求到第一级Nginx代理服务,一级代理将请求原样传给Nginx二级代理,然后在二级代理上通过正则提取schema、host和proxy_url,并代理请求,即可满足需求。 Nginx一级代理规则(反向代理):反向代理到2个二级代理 Nginx二级代理规则(正向代理):自动提取url里面约定的协议、目标主机和url并代理 最后再套了一层负载均衡,最终生产环境的拓扑如下: 利用Nginx代理,非常轻量的替代了之前开发GG研发的程序,而且后期维护工作量基本可以忽略不计,其中涉及到的安全措施这里就略去不提了,请自行脑补。
阅读全文
操作系统

Linux运维基础技能: 接入层与网络基础

本系列文章一共三篇,分别为《脚本编程与 Linux 命令》、《接入层与网络基础》和《 MySQL 与 SQL 优化》,由腾讯高级工程师 luaruan(阮永顺) 原创、张戈博客整理分享,如有勘误请在博客留言。 希望对需要学习、面试 Linux 运维的同学有所帮助。 MSS与MTU的区别,默认大小各是多少? 标准以太网接口缺省的MTU(最大传输单元)为1500 字节,是最大帧1518减去源宿的MAC、FCS后最大的IP packet大小; MTU减去20字节IP包头减去20字节TCP包头 ,即是MSS,1460字节 一台交换机要保证接口MTU的一致性。如果在一个VLAN上、或整个交换机都采用同样的MTU,避免一些奇怪的问题 参考:https://www.zhihu.com/question/21524257 TIME_WAIT 与CLOSE_WAIT 的区别 主动关闭连接的一方进入TIME_WAIT ,若客户端高并发访问HTTP接口又没有使用会话或者线程池机制,可能会导致本地端口耗尽。 CLOSE_WAIT 出现在被动断开连接。可能常见在Server端,例如在Netty的I/O线程里做DB读写、日志记录这种不可控的阻塞行为,可能会引发客户端超时。 如何查看机器上所有的tcp连接? natstat -ant netstat -antp 如何统计time_wait 状态的连接? IP 包头大小? 20 字节 url.cn 是几级域名? -- 二级 www.qq.com 是几级域名? -- 三级 url.cn 可以CNAME到 demo.te.isc.demo-cloud.net 吗? 不建议这么操作,实际上很多场合是不可以这么操作的。 https://serverfault.com/questions/613829/why-cant-a-cname-record-be-used-at-the-apex-aka-root-of-a-domain 递归解析与迭代解析的区别? 先理解13组根DNS服务器、顶级域DNS服务器(各解析.com .net .gov 等)、权威DNS服务器、本地DNS服务器。 考虑浏览器客户端访问www.qq.com ,会向小区宽带的本地DNS查询域名解析IP ,而本地DNS会向根DNS、顶级域DNS、权威DNS逐个查询。 从客户端到本地DNS的查询是递归的;而其余的查询是迭代的。(参考《自顶向下理解计算机网络》89页) x-forwarded-for 与 remote_addr 的区别? X-Forwarded-For: client1, proxy1, proxy2 <proxy1>, <proxy2>如果一个请求经过了多个代理服务器,那么每一个代理服务器的IP地址都会被依次记录在内。也就是说,最右端的IP地址表示最近通过的代理服务器,而最左端的IP地址表示最初发起请求的客户端的IP地址。 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/X-Forwarded-For 鉴于伪造这一字段非常容易,应该谨慎使用X-Forwarded-For字段。正常情况下XFF中最后一个IP地址是最后一个代理服务器的IP地址 remote_addr 是通讯客户端与服务器实际进行TCP通信的IP IPv4地址分类 (IPv4地址32位) A 类 0.0.0.0/8 127.255.255.255 结束 B 类 128.0.0.0/16 191.255.255.255 结束 C 类 192.0.0.0/24 D 类 224.0.0.0~239.255.255.255 192.168.2.47/26 这个子网的开始IP 和结束IP是? (32-26)=6 , 2^6 = 64...
阅读全文