《架构师》2016年11月
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

推荐文章|Article

京东Nginx平台化实践

作者 吴建苗

Nginx是优秀的HTTP和反向代理服务器,京东各部门都在广泛使用,但普遍都面临着一些问题:

• 配置复杂,专业性强。

• 配置文件无法批量修改且配置变更依赖重启操作。

• 不同应用依赖不同模块、配置项,管理混乱。

• 同一应用的Nginx无法批量、快速扩容。

所有问题的根源在于Nginx是一个单机系统,虽然模块化、高性能,但在互联网高速发展的今天,像京东这样拥有大规模Nginx、业务集群的场景下,所有问题都有可能被无限放大,针对这种现状我们设计研发了JEN(JD EXTENDED NGINX),截止目前JEN已覆盖京东金融大部分核心业务,如夺宝吧,卡超市,白条等。

一、整体结构

如图1,运维通过Web控制台做相应的配置操作,若是分流、限流等配置,则信息入库等待Nginx通过Restful API同步规则后开始生效;若是平滑升级、重启等强运维性操作,则Web控制台通过控制Ansible对Nginx进行相应操作(见图2)。

图1:JEN结构图

JEN特点:

• 支持Nginx自动发现,分组管理,状态监控。

• 统一入口,通过抽象配置,简化操作管控Nginx集群生命周期,并支持规则批量配置,操作批量执行。

• 扩展了原生Nginx的分流、限流功能,支持规则的内存实时同步,无需修改配置文件,更无需重启Nginx进程。

1.基础信息

Web上所有的展示和操作全部基于对基础信息的计算整合,主要包含两类:

• 分组信息(业务线、应用、机房、Nginx IP)

• Nginx属性,例如upstream信息,server_name,listen_port等,主要来源Nginx读取Nginx.conf内容后的信息上报(心跳)

对于分组信息,JEN支持以下两种方式填充:

• 调用外部服务的Restful API导入完整的基础信息。

• 对自动发现的Nginx做分组的手工编辑。

图2:Nginx和Web控制台多机房部署图

如图3,分组包括业务线、应用、机房、Nginx共四层关系,在大规模集群环境下可以通过这种关系并结合Nginx属性,支持对所有操作的批量执行,如批量修改配置文件,批量升级重启等,解放生产力。

2.规则获取

用户在Web控制台配置后,在Nginx端我们实现了全异步的模块支持定时向Web获取属于当前Nginx的规则信息,规则存储内存,即时生效,其中:

a)规则信息每个进程存储一份,避免进程间资源共享导致锁竞争。

b)版本号设计,保证规则和心跳的绝对顺序,不因丢包、延迟等网络因素导致版本错乱,而且在规则未变更时Nginx无需频繁解析大量规则信息而消耗CPU资源。

3.安全

JEN支持三类角色,每种角色支持不同的操作权限(默认是普通用户角色,无写权限),任何角色对Web的任何操作都会被记录,并在Web提供了入口支持多维度操作日志查询,便于审计。

图3:各分组间关系图

4.监控

我们实现了更为全面的监控信息采集与展示,包括:

a)扩展了tengine的主动探测模块,支持上游服务器的平均、当前延时统计。

b)通过与Web的心跳保持支持Nginx存活状态监控。

c)支持TCP连接信息,in/out流量,QPS,1xx到5xx回应报文等信息监控。

以上的监控信息支持分组统计(业务线、应用、机房)和大屏展示,便于相关人员(业务,运维)实时监控应用状态。

二、分流

概念:根据请求特征(IP, header中任意关键字)支持把某些特定请求分流到单个或多个上游服务器中,如图4。

图4:分流示例图

分流主要适用灰度发布,ab testing等场景,另外我们也对分流功能做了扩展,支持Web控制台一键启停上游服务器,便于当应用服务器需要维护或升级时,用户请求正常访问。

三、限流

京东618等大促,货物都提前堆积在购物车,等待零点秒杀,换成工程师的语言来说,就是前一秒的QPS很低,但是下一秒QPS非常高,流量大意味着机器负载高,若一个应用的一两台机器没有扛住,这样就会导致整个应用集群雪崩。

限流不可盲目,首先需要根据业务特点选择合适的限流算法(漏桶算法、令牌桶算法),其次需要结合历史流量、应用服务能力、营销力度等因素综合评定限流参数,最后决定以何种优雅的方式反馈用户。

Nginx在实现上通过共享内存共享限流中间信息的方式来达到多进程间的状态统一。在JEN设计初衷,原本计划和分流一致,即每个进程存储一份限流规则,限流只在当前进程内限流,但不可避免的会出现如下问题:

• 每个进程“你限你的,我限我的”,信息不一致进而导致限流不准确。

• 类似用户ID的限流,在京东这样拥有庞大日活用户的场景下,每个进程需要开辟足够大的内存才能避免限流算法中对于红黑树节点的频繁置换,这样一来Nginx占用内存就会随着进程数成倍扩大。

我们的做法:

预分配共享内存,Nginx获取到限流规则时动态适配一块共享内存。

规则共享,生效后实时同步至所有进程,规则链保证所有旧版本规则只有在当前流量更新之后才会删除,如图5所示。

图5:规则链

我们在限流功能上的几点扩展,支持错误页定制,除了返回Nginx静态页,还支持302错误页重定向,根据在Web控制台的配置可以重定向到任何外部链接,但302重定向存在一个问题:用户浏览器的URL和内容都发生了变更,意味着用户需要重新输入URL重新请求或者是重复之前的操作步骤,用户体验差可能导致用户放弃此次购买行为而转投它家。在逻辑上我们通过Nginx的subrequest机制支持返回内容发生变更而URL保持不变,这样一来每当用户被限流,只需重新刷新页面即可重复之前的操作步骤,如图6所示。

图6:两种错误页对比

通过扩展限流算法支持限流后一段时间不可用,例如按IP限流且某个IP已经触发限流,则支持该IP一段时间内不可访问,无需重新通过算法计算。

同步实现了黑名单、白名单功能,通过白名单避免一些复杂场景下的限流“误杀”(例如nat网络下按ip限流)。

四、运维特性

运维特性主要指Nginx的安装、升级、配置文件修改、启停等操作,运维特性与之前介绍内容的最大区别在于需要重启操作,所以结合第三方工具Ansible是比较合适的想法(Ansible相对于Puppet等运维工具,其迁移成本相对较小)。

在实际生产中Ansible和Web为避免单点需要集群部署,我们的方案是:Web和Ansible在同一PC上部署,相关数据改用DB存储替代Ansible本地文件存储,通过这种简单的改造可以方便Ansible和Web这组“套件”进行扩容。

图7:自动化运维操作逻辑图

如图7,用户通过Web操作控制Ansible对Nginx进行升级、重启等操作,Web是Nginx操作的统一入口,这是平台化的重要意义所在,可以放弃SSH, Shell甚至是监控系统,开始在JEN自给自足了。

通过主动拉取或者是用户在页面导入、手工配置,JEN会为所有Nginx存储配置文件,这样不仅原本因为每个应用都依赖不同的配置项而导致管理混乱的局面得到了改善,而且也可以方便的对配置文件做些扩展,例如历史记录追溯,配置比对,配置复用,操作回滚等。

在页面执行相关操作时,Web会读取Ansible的标准输出并在页面实时展示,为了让使用者以相对友好的方式获知进度我们对Ansible做了优化:

• 丰富了标准输出的内容,尽量细化到每一个步骤。

• 格式化标准输出,便于Web获取和展示。

Nginx在生产环境大规模部署,倘若因为一些原因导致Nginx大规模异常,这是我们不希望看到的,所以在可靠性方面,JEN也提供了多种机制来保证:

1.三层错误校验,保证只有在完全正确的情况下才会重启和更新进程,中途发生任何错误不影响线上服务

a)在Web填充表单时做第一层校验。

b)在目标机器做操作时做第二层检测,例如先执行Nginx -t校验。

c)执行完毕做第三层校验,例如端口是否启动,进程数是否一致等。

2.灰度执行

a)单个Nginx依次执行,有任何异常立即中断开始人工介入。

b)按百分比支持批量执行,例如某个机房的Nginx先升级10%。

五、总结

以上整理了京东在Nginx平台化方面的一些实践,JEN提供了统一入口管控整个Nginx生命周期,并支持规则的批量修改即时生效,我们希望这些实践经验能对所有读者产生帮助。

作者介绍

吴建苗,目前就职于京东金融杭州研发中心,负责Nginx, MQ项目,对高性能服务器开发和调优具有浓厚兴趣。欢迎沟通交流(微信号wujm1230)。