本文主要介绍了适用于nginx的zk动态后端发现模块(nginx-upstream-reloader)及其使用方法。
上篇文章回顾: SOAR 101 快速入门指南
不少公司都有作动态调度系统,有些是基于 mesos+docker,有些采用了 google 的 K8s,或者是自研的系统,这些系统有一个明显的特征就是服务实例的ip会频繁更换。这种容器化的部署方式和传统的服务部署形式不同,原有的服务都是部署在某些物理机或者云主机上,这些物理机或者云主机的 ip 地址不会轻易更换,这样咱们配置 nginx 作流量转发的时候就能够直接写ip。可是切换到这些容器化的系统后,服务的实例重启频繁,每一次重启后实例的 ip 就会发生变化,这样咱们再用手动配置、变动后端 ip 的形式来作 nginx 的流量转发就基本上不可行了。这时咱们须要想办法让发布后的实例ip自动更新到 nginx 的配置中去,而且可以让其自动生效。本模块正是基于前面的应用场景,用于解决后端实例 ip 频繁变化,没法将更新实时同步至 nginx 的配置中的问题。node
前期经过调研发现,有些公司采用了 etcd/consul+nginx 第三方模块(nginx-upsync-module)的方式来实现nginx零重启更新upstream的操做。咱们内部并无采用 etcd 或者 consul 来存储后端实例配置,而是普遍采用了zk服务来保存后端的配置。大部分业务都会将实例ip注册到 zk 中去,因此咱们的nginx须要从zk中拉取后端实例ip和端口。咱们公司内部也有同窗开发了 nginx 链接 zk 的模块,可是该模块是经过每一个worker 进程去链接zk,一个 nginx 可能有多个甚至几十个 worker 进程,会形成 zk 的链接数突增,给 zk集群带来很大的压力。后续经过调研发现了 dyups 这个模块,而后经过本身编码实现链接zk,从 zk 中拉取配置,再经过 dyups 模块的接口更新到 upstream 的共享内存,也能够实现零重启更新 nginx 的upstream列表。同时经过本身编码实现和 zk 交互的逻辑,也能够控制在 zk 不可用时执行的逻辑。python
在咱们的模块有用到 dyups 这个 nginx 模块,dyups 模块是一个可以直接更新正在运行的 nginx 的 upstream 列表而不须要从新 reload nginx 配置的模块。这个模块经过开放一个接口,而后外部经过这个接口发起 post 或者 get 请求,直接更新或者获取对应 upstream 的后端列表。更加详细的用法能够浏览网址nginx
本模块结合了咱们公司常见业务的应用场景、平常使用中碰到的问题以及 dyups 的不足之处,主要实现了以下几个功能:git
1)获取注册到zk中后端列表,并对获取到的列表数据格式化,保存到相应的 nginx 配置文件中,进行持久化github
2)将保存到文件的后端服务器列表经过dyups模块的接口写入到 nginx upstream 模块的共享内存中,动态更新 upstream 里面的后端列表web
3)当 zk 故障时,本模块将再也不更新 nginx 的共享内存和本地 nginx 配置文件,使 nginx 的 upstream 配置保持在 zk 故障前的状态docker
4)支持读取多个 zk 集群的多个 zk 节点配置json
基础依赖:后端
支持 dyups 模块的 nginxcentos
python 2.6/2.7
这里咱们将模块代码放到/home/work 目录下
cd /home/work
git clone http://v9.git.n.xiaomi.com/liuliqiu/nginx-upstream-reloader.git复制代码
cd nginx-upstream-reloader
bash install_venv.sh复制代码
venv 环境安装完成后,修改 nginx-upstream-reloader/conf 目录下的 upstream_zk_nodes.conf 配置文件,这个配置文件用于定义后端实例所在的目的 zk 集群和 zk 节点以及对应 nginx upstream 的名字,具体的修改方法分为以下两种状况:
1)多个后端服务注册在一个zk集群,按照以下配置
upstream_zk_nodes.conf
zk_servers: zk-hadoop-test01:11000,zk-hadoop-test02:11000
zk_nodes:
bonus-api: /web_services/com.miui.bonus.api.resin-web复制代码
zk_servers:后端服务注册的 zk 集群地址和端口
zk_nodes:upstream_name:后端服务注册的zk节点路径
当咱们启动后,模块会拉取指定zk节点路径下的后端列表信息自动生成 upstream_name.upstream 文件,如上述配置,咱们在指定的目录下(这个指定的目录能够在nginx-upstream-reloader/conf/main.conf配置文件的files_output_path选项控制,这里咱们将该选项为/home/work/nginx/site-enable)/home/work/nginx/site-enable下会生成一个 bonus-api.upstream 文件,文件的内容会以下:
upstream bonus-api {
server ....;
server ....;
}复制代码
2)多个后端服务器注册在不一样的 zk 集群
upstream_zk_nodes.conf
- zk_servers: tjwqstaging.zk.hadoop.srv:11000
zk_nodes:
ocean-helloworld-upstream1: /ocean/services/job.ocean-helloworld-nginx-upstream_service.ocean-helloworld-nginx-upstream_cluster.staging_pdl.oceantest_owt.inf_cop.xiaomi
ocean-helloworld-upstream2: /ocean/services/job.ocean-helloworld-nginx-upstream_service.ocean-helloworld-nginx-upstream_cluster.staging_pdl.oceantest_owt.inf_cop.xiaomi
- zk_servers: tjwqstaging.zk.hadoop.srv:11000
zk_nodes:
ocean-helloworld-upstream3: /ocean/services/job.ocean-helloworld-nginx-upstream_service.ocean-helloworld-nginx-upstream_cluster.staging_pdl.oceantest_owt.inf_cop.xiaomi复制代码
zk_servers:后端服务注册的 zk 集群地址和端口
zk_nodes upstream_name:后端服务注册的zk节点路径
有同窗跟我反馈为何要用 yaml 格式的配置文件,而不用json格式的配置文件,json 对格式要求没有yaml 严格,可是 yaml 的配置文件看起层级直观多了。
当咱们启动该模块后,模块会拉取指定zk节点路径下的后端列表信息自动生成 upstream_name.upstream文件,如上述配置,模块在/home/work/nginx/site-enable会生成一个 ocean-helloworld-upstream1.upstream、ocean-helloworld-upstream2.upstream、ocean-helloworld-upstream3.upstream三个文件,文件的内容会分别以下:
upstream ocean-helloworld-upstream1 {
server ...;
server ...;
}
upstream ocean-helloworld-upstream2 {
server ...;
server ...;
}
upstream ocean-helloworld-upstream3 {
server ...;
server ...;
}复制代码
前面已经配置了 nginx-upstream-reloader 模块链接zk节点获取后端配置后,自动生成 upstream 配置文件,因此咱们须要在 nginx 中 include 这些 upstream 配置文件在 server 块中才可使用这些 upstream。目前因为 dyups 模块的限制,须要将 upstream_name 设置为一个变量,而后在 proxy_pass指令中使用这个变量配置转发,具体能够参考下面的配置:
include /home/work/nginx/site-enable/ocean-helloword-upstream1.upstream;
include /home/work/nginx/site-enable/ocean-helloword-upstream2.upstream;
include /home/work/nginx/site-enable/ocean-helloword-upstream3.upstream;
server {
listen 80;
location /helloworld1 {
set $ups1 ocean-helloword-upstream1;
proxy_pass http://$ups;
}
location /helloworld2 {
set $ups2 ocean-helloword-upstream2;
proxy_pass http://$ups2;
}
location /helloworld3 {
set $ups3 ocean-helloword-upstream3;
proxy_pass http://$ups3;
}
}复制代码
这里添加一个单独的 server,监听本地地址的14443端口
server{
listen 127.0.0.1:14443;
server_name _;
location / {
dyups_interface;
}
}复制代码
这里须要先执行 nginx-upstream-reloader/start.sh 文件,启动 nginx-upstream-reloader 模块,而后再启动nginx,由于当咱们尚未启动 upstream-reloader 模块时,upstream 配置文件还未生成,但咱们 nginx 配置文件中已经 include 这些 upstream 配置文件,这时启动 nginx 就会报错
bash nginx-upstream-reloader/start.sh
/home/work/nginx/sbin/nginx #这里假设咱们的nginx安装在/home/work/nginx/目录下复制代码
目前该模块已经在 centos6 和 centos7 上测试经过,适用于容器和物理机。
本文首发于公众号“小米运维”,点击查看原文