今天聊聊nginx配置的自动化管理。主要聊聊etcd和confd,之前一直使用puppet 管理nginx的配置文件,现在很多朋友也将etcd+confd作为配置文件自动管理的一种方法。

什么是confd?

       confd 是一个轻量级的配置管理工具,可以将配置信息存储在etcd、consul、dynamodb、redis以及zookeeper等。confd定期会从这些存储节点pull最新的配置,然后reload服务,完成配置文件的update。


什么是etcd?

      etcd 大家应该都听过吧,最近比较火的一项技术。它是一个用语共享配置和服务的高可用key-value存储系统,由CoreOS使用开发并作为CoreOS的基础服务启动。它的具体特点在这里就不在过多阐述,如果想更加深入学习etcd.可以查看它的官方文档.

系统架构设计图:

      


I. 首先通过etcd ClientApi 将一些可变的配置信息push 到etcd.

II. confd 设置模版,通过etcd获取指定配置信息,生成最新配置文件.

III. reload nginx服务,完成nginx自动更新配置.


往etcd push数据:

     etcd的具体安装这里就不在多说了,详细了解etcd可以查看官方文档

      etcd服务启动以后,现在需要将数据灌进etcd Server服务器,然后才可以让confd 从etcd获取数据。我用python简单封装了一个etcd灌数据的工具:

#!/usr/bin/env python

from __future__ import unicode_literals
from optparse import OptionParser
import etcd
import sys


class NginxEtcd(object):
    """Configuration with etcd storage nginx. Provide push, pull function."""

    def __init__(self, host='', port=4001, *args, **kwargs):
        self.host = host
        self.port = port

    @property
    def _conn_etcd(self):
        """connection etcd Server."""
        client = etcd.Client(host=self.host, port=self.port)
        return client

    def set_config(self, key='', value='', *args, **kwargs):
        """set configuration."""
        client = self._conn_etcd
        try:
            if self.get_config(key) and isinstance(self.get_config(key), str):
                client.update(key, value)
                return_message = {'code': 200, 'message': 'set success.'}
            elif self.get_config(key) and isinstance(self.get_config(key), dict):
                return_message = self.get_config(key)
            else:
                client.write(key, value)
                return_message = {'code': 200, 'message': 'set success.'}
        except Exception as e:
            return_message = {'code': 500, 'message': e}
        finally:
            return return_message

    def get_config(self, key):
        """get configuration."""
        try:
            client = self._conn_etcd
            return_message = client.get(key).value
        except Exception as e:
            return_message = {'code': 500, 'message': e}
        finally:
            return return_message

def ops():
    parser = OptionParser(usage="usage: %prog -host HOST -port PORT -key KEY -value VALUE")
    parser.add_option('-H', "--host", default='127.0.0.1', action='store', type='string', dest="host", help="etcd server ipaddress.")
    parser.add_option('-p', "--port", default=4001, action='store', type='int', dest="port", help="etcd listen port.")
    parser.add_option('-k', "--key", default='', action='store', type='string', dest="key", help="etcd key name.")
    parser.add_option('-v', "--value", default='', action='store', type='string', dest="value", help='etcd key value.')
    parser.add_option('-t', "--type", default='', action='store', type='string', dest='type', help="Operating etcd Type.")
    return parser.parse_args()

def main():
    options, args = ops()
    if options.host and options.port:
        client = NginxEtcd(host=options.host, port=options.port)
    if options.type == 'set'  and client:
        data = client.set_config(key=options.key, value=options.value)
    if options.type == 'get' and client:
        data = client.get_config(options.key)
    return data

if __name__ == "__main__":
    print(main())

        现在可以通过该脚本,将你要更改的配置信息更新到etcd。该脚本的具体使用方法,可以查看帮助文档。


confd 配置管理:

       confd的安装在这里就不在多说,so  easy!。如果详细了解confd,可以参考confd的官方手册.

        confd安装完成以后,需要创建一个配置文件和一个模版文件。

mkdir -p /etc/confd/{conf.d,templates}

confd配置文件:

    现在我们需要配置confd的配置文件:

vim /etc/confd/conf.d/nginx.toml

#nginx.toml具体内容

[template]
src = "nginx.tmpl"
dest = "/etc/nginx/conf.d/app1.conf"
keys = [
    "/www/app1"
]

owner = "root"
mode = "0644"
check_cmd = "/usr/sbin/ngixn -t"
relaod_cmd = "/usr/sbin/service nginx reload"

confd 模版文件:

       创建我们在/etc/confd/conf.d/nginx.toml提到的模版文件,将其放在/etc/confd/templates目录下.

upstream app1_pool {
    {{ range getvs "/www/app1/*" }}
        server {{ . }};
    {{ end }}
}

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;
    
    access_log /var/log/nginx/access.log upstreamlog;
   
    location / {
        proxy_pass http://app1_pool;
        proxy_redirect off;
        proxy_set_header  Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    }
}


创建confd启动脚本:

        现在需要创建一个cond启动脚本,这个脚本主要有两个功能:

        I. 在nginx 后端Server启动之前启动

        II. 监听etcd的变化情况,然后重启nginx,以确保nginx配置的后端Server都是ok.

#!/bin/bash

set -eo pipefail

export ETCD_PORT=${ETCD_PORT:-4001}
export HOST_IP=${HOST_IP:-x.x.x.x}
export ETCD=$HOST_IP:$ETCD_PORT

echo "[nginx] booting container. ETCD: $ETCD."

util confd -onetime -node  $ETCD -config-file /etc/confd/conf.d/nginx.toml; do
    echo "[nginx] waiting for confd to create initial nginx configuration."
    sleep 5
done

confd -interval 10 -node $ETCD -config-file /etc/confd/conf.d/nginx.toml &
echo "[nginx] confd is now monitoring etcd for changes."

echo "[nginx] staring nginx service ..."
service nginx restart 

           接下来, 从etcd中读取数据,并使用confd实例化nginx的配置文件。我们使用一个until循环来不停的执行这个操作。最后执行完该脚本会自动生成一个nginx的配置文件。

  

     etcd+confd具体的性能现在还没有进行测试。



  1. push工具是我不会用还是有bug呢?[root@node4 ~]# ./pushData.py -host 127.0.0.1 -port 4001 -key ‘123’ -value ‘123’Usage: pushData.py -host HOST -port PORT -key KEY -value VALUEOptions: -h, –help show this help message and exit -H HOST, –host=HOST etcd server ipaddress. -p PORT, –port=PORT etcd listen port. -k KEY, –key=KEY etcd key name. -v VALUE, –value=VALUE