一、背景
很多公司都有做动态调度系统,有些是基于 mesos+docker,有些采用了 google 的 K8s,或者是自研的系统,这些系统有一个明显的特征就是服务实例的ip会频繁更换。这种容器化的部署方式和传统的服务部署形式不一样,原有的服务都是部署在某些物理机或者云主机上,这些物理机或者云主机的 ip 地址不会轻易更换,这样我们配置 nginx 做流量转发的时候就可以直接写ip。但是切换到这些容器化的系统后,服务的实例重启频繁,每一次重启后实例的 ip 就会发生变化,这样我们再用手动配置、变更后端 ip 的形式来做 nginx 的流量转发就基本上不可行了。这时我们需要想办法让发布后的实例ip自动更新到 nginx 的配置中去,并且能够让其自动生效。本模块正是基于前面的应用场景,用于解决后端实例 ip 频繁变化,无法将更新实时同步至 nginx 的配置中的问题。
二、模块架构
前期通过调研发现,有些公司采用了 etcd/consul+nginx 第三方模块(nginx-upsync-module)的方式来实现nginx零重启更新upst
在我们的模块有用到 dyups 这个 nginx 模块,dyups 模块是一个能够直接更新正在运行的 nginx 的 upstream 列表而不需要重新 reload nginx 配置的模块。这个模块通过开放一个接口,然后外部通过这个接口发起 post 或者 get 请求,直接更新或者获取对应 upstream 的后端列表。更加详细的用法可以浏览网址https://github.com/yzprofile/ngx_http_dyups_module查看这个模块的 github 介绍。但是由于 dyups 模块只能修改 nginx 的共享内存,不能持久化当前的upstream 配置到文件中,所以我们的模块另外一个核心的工作就是持久化 upstream 配置到配置文件中。
三、模块功能
本模块结合了我们公司常见业务的应用场景、日常使用中碰到的问题以及 dyups 的不足之处,主要实现了如下几个功能:
1)获取注册到zk中后端列表,并对获取到的列表数据格式化,保存到相应的 nginx 配置文件中,进行持久化
2)将保存到文件的后端服务器列表通过dyups模块的接口写入到 nginx upstream 模块的共享内存中,动态更新 upstream 里面的后端列表
3)当 zk 故障时,本模块将不再更新 nginx 的共享内存和本地 nginx 配置文件,使 nginx 的 upstre
4)支持读取多个 zk 集群的多个 zk 节点配置
四、模块工作流程

五、模块的使用
基础依赖:
支持 dyups 模块的 nginx
python 2.6/2.7
1、clone 模块代码到 nginx 机器上
这里我们将模块代码放到/home/work 目录下
cd /home/work
git clone http://v9.git.n.xiaomi.com/liuliqiu/nginx-upstream-reloader.git
2、执行模块源码目录中依赖安装脚本(nginx-upstream-reloader/install_venv.sh),这个脚本主要用于安装virtualvenv环境和依赖的第三方模块
cd nginx-upstream-reloader
bash install_venv.sh
3、修改 nginx-upstream-reloader 模块配置文件
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_na
有同学跟我反馈为什么要用 yaml 格式的配置文件,而不用json格式的配置文件,json 对格式要求没有yaml 严格,但是 yaml 的配置文件看起层级直观多了。
当我们启动该模块后,模块会拉取指定zk节点路径下的后端列表信息自动生成 upstream_name.upstream文件,如上述配置,模块在/home/w
upstream ocean-helloworld-upstream1 {
server ...;
server ...;
}
upstream ocean-helloworld-upstream2 {
server ...;
server ...;
}
upstream ocean-helloworld-upstream3 {
server ...;
server ...;
}
4、修改 nginx 配置文件
前面已经配置了 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;
}
}
5、修改 nginx 配置文件,开启 dyups 接口
这里添加一个单独的 server,监听本地地址的14443端口
server{
listen 127.0.0.1:14443;
server_name _;
location / {
dyups_interface;
}
}
6、启动zk动态发现模块和 nginx
这里需要先执行 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 上测试通过,适用于容器和物理机。