背景:为应对较大流量及高并发的业务场景,需要多台nginx做代理转发,针对这种情况,如果对nginx的站点配置文件进行修改,需要一台一台的去操作,如果有遗漏修改的很有可能造成生产事故,为避免这种情况的发生,本文介绍使用consul和consul-template实现对nginx配置文件进行统一管理,也是目前作者本人所在公司使用的一种解决方案。
consul介绍
Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其他工具(比如 ZooKeeper 等)。使用起来也较 为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。
consul-template介绍
Consul-Template是一个守护进程,用于实时查询Consul集群信息
Consul-Template可以更新文件系统上任意数量的指定模板,生成配置文件更新完成以后,可以选择运行shell命令执行更新操作,重新加载Nginx。
Consul-Template可以查询Consul中的服务目录、Key、Key-values等。
这种强大的抽象功能和查询语言模板可以使Consul-Template特别适合动态的创建配置文件
操作详解
agent安装
首先需要在所有的nginx服务器节点安装consul agent,并注册到consul集群
nginx upstream.conf文件管理
编写脚本,将consul-template作为守护进程启动
脚本文件名自定义(consul-init),需要可执行权限,然后执行 consul-init start
#!/bin/bash
# Source function library.
. /etc/init.d/functions
RETVAL=0
PROG="consul-template"
PID_FILE=/var/run/${PROG}.pid
check_status() {
pgrep -n ${PROG}
RETVAL=$?
echo $RETVAL
return $RETVAL
}
start(){
if check_status > /dev/null; then
echo "Service is already running"
exit 0
fi
consul-template -config /data/config/upstreams/upstream.conf &
RETVAL=$?
PID=`pgrep -f ${PROG}`
echo "${PID}" > ${PID_FILE}
return $RETVAL
}
stop(){
killproc -p ${PID_FILE} -d 10
RETVAL=$?
[ $RETVAL = 0 ] && rm -f ${PID_FILE}
return $RETVAL
}
reload(){
if [ -n $PID_FILE ];then
PID=`cat ${PID_FILE}`
kill -HUP $PID
else
echo "Service is not running"
fi
}
case "$1" in
start)
start
;;
stop)
stop
;;
reload)
reload
;;
esac
upstream.conf文件配置(即 consul-template -config /**/upstream.conf)
consul {
address = "127.0.0.1:8500"
retry {
enabled = true
attempts = 12
backoff = "250ms"
max_backoff = "1m"
}
ssl {
}
}
reload_signal = "SIGHUP"
kill_signal = "SIGINT"
max_stale = "10s"
log_level = "warn"
pid_file = "/var/run/consul-template.pid"
wait {
min = "5s"
max = "10s"
}
syslog {
enabled = true
facility = "LOCAL5"
}
deduplicate {
}
exec {
splay = "5s"
env {
pristine = false
}
reload_signal = ""
kill_signal = "SIGINT"
kill_timeout = "2s"
}
template {
source = "/data/config/upstreams/upstreams.ctmpl"
destination = "/opt/openresty/nginx/conf/servers/upstreams/upstreams.conf"
create_dest_dirs = true
command = "systemctl reload nginx"
command_timeout = "30s"
error_on_missing_key = false
perms = 0600
left_delimiter = "{{"
right_delimiter = "}}"
wait {
min = "2s"
max = "10s"
}
}
upstream模板文件配置(upstreams.ctmpl)
首先需要在consul中配置好对应的key/value
upstream {{key "test/backend_name"}} {
{{range ls "test/upstream"}}
server {{.Value}} max_fails=3 fail_timeout=60 weight=1;
{{end}}
}
以上完成后,可以执行 consul-init start ,正常启动后,可以去目的目录里面查看是否正常生成了upstream.conf文件,文件内容及为consul中事先配置好的内容,增删key,upstream中会动态更新
站点配置文件管理
借助python脚本(flushnginx),实现对站点配置文件的批量更新
#!/opt/python3/bin/python3.6
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#dingzk
import os,sys,yaml,re
def fn(_dict, depth):
for k, v in _dict.items():
if depth == 1:
yield k, v
else:
yield from ((k, *q) for q in fn(v, depth - 1))
if __name__=="__main__":
if len(sys.argv) != 2:
print("参数错误")
else:
cfgfile = "/data/config/template.yaml"
cfgregion = "external"
cfgname = sys.argv[1]
if os.path.exists(cfgfile):
f = open(cfgfile, encoding='utf-8')
cfg = yaml.load(f,Loader=yaml.FullLoader)
for i in cfg[cfgregion]['config']:
source = i['source']
destination = i['destination']
if cfgname == "all":
print(source,destination)
status = True
if os.system('consul-template -template %s:%s -once' % (source,destination)) > 0:
print("配置文件生成失败")
sys.exit()
else:
if re.search(cfgname,source):
print(source,"---->",destination)
status = True
if os.system('consul-template -template %s:%s -once' % (source,destination)) > 0:
print("配置文件生成失败")
sys.exit()
break
else:
status = False
if status:
if os.system('/opt/openresty/nginx/sbin/nginx -t') == 0:
os.system('systemctl reload nginx')
print("更新成功")
else:
print("配置文件测试失败")
else:
print("配置文件不存在")
else:
print("yaml文件不存在")
维护一个template.yaml文件,里面包含配置文件模板及目标文件路径
---
external:
config:
- source: "/data/config/test.ctmpl"
destination: "/opt/openresty/nginx/conf/servers/test.conf"
配置文件模板
server {
listen 80;
server_anme test.com.cn;
}
执行脚本
python flushnginx test