使用Dynamic Inventory从Mackerel获取Ansible的目标服务器
鲭鱼吗? ma?)
鲭鱼是由Hatena开发的服务器管理工具,印象类似于Zabbix(用户可自由定义指标)和New Relic(以轻松的方式展示数据)的综合之作。
【招募Beta测试人员】鲭鱼:全新应用性能管理
https://mackerel.io/
特征之一是将服务器定义为“Service”和“Role”这样的树状角色,在Capistrano、Chef、Ansible等相关服务器中可以实现此功能。例如,通过git等方式管理Inventory文件中定义的Ansible服务器列表,当出现服务器数量的增减时,需要进行文件修改,但是如果通过API从Mackerel中提取注册的服务器列表,则可以将服务器列表的管理交给Mackerel,从而获得此优势。
我在官方网站的帮助文档中找到了与 Capistrano 的集成方法,但没有提及 Ansible。因此,我就编写了一个 Dynamic Inventory 脚本,用于从 Mackerel 动态获取 Ansible Playbook 的目标。
Ansible:动态清单
我认为Ansible有一个名为“组”的概念,所以我想将Mackerel的角色等同于Ansible的组。
Dynamic Inventory 的脚本可以使用任意编程语言编写,但应该优先选择使用 Ansible 所依赖的 Python 编写。
动态库存脚本的要求与Ansible文档中的“Developing Dynamic Inventory Sources”页面一致。
开发动态库存源- Ansible文档 http://docs.ansible.com/developing_inventory.html
作为重点
–list が引数に渡されたら、ホスト一覧をJSONで表示する
–host が引数に渡されたら、指定されたホストのvariablesをJSONで表示する
是的。 (Shì de.)
模块等应放置在指定的目录中,但是动态清单脚本在启动Playbook时必须指定绝对路径或相对路径,所以如果数量不多的话,我认为可以将其放在与Playbook相同的目录中。
Mackerel的Ansible动态清单。
以下是我制作的脚本,我在 gist 上也列出了相同的内容。
Mackerel的Ansible动态清单,可以在这个链接中找到:https://gist.github.com/yujiod/1587128075bc74009d32。
#!/usr/bin/python
import urllib
import urllib2
import sys
try:
import json
except ImportError:
import simplejson as json
apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
service = 'My Service'
networkInterface = 'eth0'
def getGroupList():
hosts = getHosts()
print json.dumps(hosts)
def getHostInfo(hostname):
hosts = getHosts(hostname)
print json.dumps(hosts['_meta']['hostvars'][hostname])
def getHosts(hostname=None):
params = { 'service': service }
if hostname != None:
params['name'] = hostname
url = 'https://mackerel.io/api/v0/hosts.json?' + urllib.urlencode(params)
headers = { 'X-Api-Key': apiKey }
request = urllib2.Request(url, None, headers)
response = urllib2.urlopen(request)
hosts = json.loads(response.read())
inventories = {
'production': { 'children': [] },
'_meta': { 'hostvars': {} },
}
for host in hosts['hosts']:
ipAddress = None
for interface in host['interfaces']:
if interface['name'] == networkInterface:
ipAddress = interface['ipAddress']
break
if ipAddress == None:
continue
for serviceName, roles in host['roles'].iteritems():
for role in roles:
if role not in inventories:
inventories[role] = { 'hosts': [] }
inventories[role]['hosts'].append(host['name'])
inventories['_meta']['hostvars'][host['name']] = {
'ansible_ssh_host': ipAddress,
'ansible_ssh_port': '22',
}
if role not in inventories['production']['children']:
inventories['production']['children'].append(role)
return inventories
if len(sys.argv) == 2 and (sys.argv[1] == '--list'):
getGroupList()
elif len(sys.argv) == 3 and (sys.argv[1] == '--host'):
getHostInfo(sys.argv[2])
else:
print "Usage: %s --list or --host <hostname>" % sys.argv[0]
sys.exit(1)
-
- API Key はOrganizationの詳細にあるAPIキーを貼り付けます。
-
- Serviceは現状固定、取得したいホスト一覧が定義されているサービス名を指定します。
-
- ネットワークインターフェースが複数ある場合のために、冒頭にインターフェース名を定義しています。SSHで接続可能なIPアドレスを持つインターフェース名に修正してご利用ください。
管理対象のサーバーの事業者が複数ある場合にはインターフェース名が変わってくる可能性がありますので、その場合はもう少し修正が必要そうです。
55行目付近に hostvars を定義している箇所がありますが、このスクリプトではSSH接続先IPアドレスとSSHポート番号のみを定義しています。適宜修正してご利用ください。
Mackerel に定義したロール以外に全サーバーを production というグループに入れています。違うグループに入れたい場合は定義修正してご利用ください。
确认行动
以下是在Mackerel中定义了2个Web角色和2个DB角色的示例。
$ ./mackerel.py --list
{"web": {"hosts": ["web001", "web002"]}, "db": {"hosts": ["db001", "db002"]}, "production": {"hosts": [], "children": ["web", "db"]}, "_meta": {"hostvars": {"web001": {"ansible_ssh_host": "xxx.xxx.xxx.xxx", "ansible_ssh_port": "22"}, "web002": {"ansible_ssh_host": "xxx.xxx.xxx.xxx", "ansible_ssh_port": "22"}, {"db001": {"ansible_ssh_host": "xxx.xxx.xxx.xxx", "ansible_ssh_port": "22"}, {"db001": {"ansible_ssh_host": "xxx.xxx.xxx.xxx", "ansible_ssh_port": "22"}}}}
$ ./mackerel.py --host web001
{"ansible_ssh_host": "xxx.xxx.xxx.xxx", "ansible_ssh_port": "22"}
我将尝试运行Ping模块。
$ ansible -i mackerel.py all -m ping -u foobar
web001 | success >> {
"changed": false,
"ping": "pong"
}
web002 | success >> {
"changed": false,
"ping": "pong"
}
db001 | success >> {
"changed": false,
"ping": "pong"
}
db002 | success >> {
"changed": false,
"ping": "pong"
}
$ ansible -i mackerel.py web -m ping -u foobar
web001 | success >> {
"changed": false,
"ping": "pong"
}
web002 | success >> {
"changed": false,
"ping": "pong"
}
确保只针对与规则相匹配的组进行了ping操作,非常好。
到最后 / 终究 / 最后
鲭鱼(Mackerel)在5月上旬刚刚启动了β服务,据2014年6月1日的情况,尚未实施基本功能,如更改或删除Organization名称以及删除指标等。(因笔误而注册的Organization名称仍然保留,非常尴尬。。)
一旦正式版本出来,据说将附带警报等功能,如果这些功能得到了充实,我将很想真正开始使用它。