title: Ansible自定义插件获取指定远端配置 date: 2018-05-11 tags: [Ansible,Python]

**概述:**将ansible部署与变量控制拆分,解决应用与主机的规划问题。其中使用Ansible本地fact实现应用主机规划,使用自定义的Ansiblelookup插件来实现从远端获取变量。通过这种方式可以提高ansible代码的利用率和普适性,降低运维的配置管理工作复杂度。可以关注下以下几点

  • 本地git注册文件的目录结构
  • ansible的local fact与git中注册目录的对应关系
  • ansible的playbook配置模板与获取fact的配置关系

设计好以上几点可以大幅降低不必要的麻烦,设计的时候最好充分考虑后期扩展问题。

本地fact的设置与使用

Ansible支持为某个主机设置fact的机制,可以将一个或多个文件放置在目标主机的/dev/ansible/facts.d目录下。以下形式ansible会自动识别

  • .ini格式
  • JSON格式
  • 可以不加参数形式执行,并在标准输出中输出JSON的可执行文件

举个例子

  • 本地fact如下/etc/ansible/facts.d/example.fact
1
2
3
[book]
title=hello
author=arvon
  • 在playbook如下使用
1
2
- name: print local fact
  debug: msg="Title is {{ ansible_local.example.book.title }}"

通过脚本注册配置到远端etcd

使用git结合jenkins并调用自定义python脚本进行注册。我这里做的是将git上规划的信息按原有目录结构注册到etcd。其中脚本可以在【我的Github】获取。

  • 另外通过etcd-view可以方便的查看注册信息,也可以再web上进行手动微调
  • 目前etcd-view还没有提供账户认证机制,可以考虑加一层nginx认证

获取etcd上的配置信息

我这里是通过etcd来进行这些变量的管理,思路就是把每个应用所需的配置变量从远端读取封装为dict,然后通过本地fact与字典获取对应关系,从而做到指定的应用获取指定的配置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
###############################################################################
#Author: arvon
#Email: arvon2014@gmail.com
#Date: 2018/05/05
#Description: register etcd v3 and used by ansible lookup
#Notes:
###############################################################################
from ansible.plugins.lookup import LookupBase
import etcd3
import os
'''
Require and Document
pip install etcd3
V3-api: http://python-etcd3.readthedocs.io/en/latest/usage.html
'''
class Etcd_v3:
    def __init__(self, prefix, shardsinfo, ANSIBLE_ETCD_URL, type_module):
        self.prefix = prefix
        #self.version = version
        self.shardsinfo = shardsinfo
        self.etcd_url = ANSIBLE_ETCD_URL
        self.port = 2379
        self.type_module = type_module

    def gets(self):
        client = etcd3.client(host=self.etcd_url, port=self.port)
        client.get_prefix()
        return_dict = {}
        for shardid in self.shardsinfo:
            assign_dict = {}
            v3_prefix = os.path.join(self.prefix, str(self.shardsinfo[shardid]['gid']),self.type_module, str(shardid))
            #print v3_prefix
            gid = str(self.shardsinfo[shardid]['gid'])
            for v3_kv in client.get_prefix(v3_prefix, sort_order=None, sort_target='key'):
                #print v3_kv[1].key, v3_kv[0]
                assign_dict[v3_kv[1].key.split('/')[-1]] = v3_kv[0]
            default_prefix = os.path.join(self.prefix, gid, 'default')
            for default_kv in client.get_prefix(default_prefix, sort_order=None, sort_target='key'):
                #print default_kv[1].key,  default_kv[0]
                assign_dict[default_kv[1].key.split('/')[-1]] = default_kv[0]
            return_dict[shardid] =  assign_dict
        return return_dict
class LookupModule(LookupBase):
    def run(self, terms, variables, **kwargs):
        # validate_certs = kwargs.get('validate_certs', True)
        if len(terms) != 4:
            raise AnsibleError("parameter should be 4. prefix, dict of shardsinfo")
        etcd = Etcd_v3(terms[0], terms[1], terms[2], terms[3])
        return [etcd.gets()]
# Test command
if __name__ == '__main__':
    etcd = Etcd_v3('root_v3/wpys', {'15501':{'gid':155}},'10.17.1.32','gamex')
    a=etcd.gets()
    print (a)
  • 获取执行配置信息
1
2
3
4
- name: Get the contents of subnet mapping file and set it as a fact
  set_fact:
     get_etcd_special_keys:  "{{ lookup('get_etcdkeys_dict','{{ taiyouxi_etcd_prefix }}', '{{ansible_local.dev_gamex.shardsinfo}}', '{{ etcd_address }}', '{{ etcd_type }}') }}"

  • 使用时的模板配置使用如下
1
2
3
{% set hostip = ansible_eth0.ipv4.address %}  //set ip
{% set etcdvalue = item.value %}
DSN = {{etcdvalue.DSN}}
  • 在playbook中使用获取的配置信息
1
2
3
- name: copy chat.toml.j2 into /opt/supervisor/chat/conf
  template: src=confd/{{type}}/chat.toml.j2  dest="/opt/supervisor/chat/conf/chat.toml"
  with_dict: '{{get_etcd_special_keys}}'

参考文档

【Ansible Lookup Plugin List】