目录
一、Ansible简介
什么是Ansible
Ansible的组成
Ansible特性
Ansible工作流程
二、Ansible安装
软件依赖关系(管理主机)
软件依赖关系(被托管主机)
Ansible安装方法一: yum 扩展源安装(推荐)
Ansible安装方法二:pip 方式安装
Ansible安装方法三:源码安装
三、Ansible模式之ad-hoc
什么是ad-hoc模式?
Ansible配置文件查找顺序
编辑hosts、ansible.cfg 配置文件
inventory 参数说明
自定义配置文件
动态主机
Json
脚本输出主机列表
脚本输出样例
四、Ansible批量执行管理
ansible命令基础
批量部署证书文件
模块
command模块:
shell | raw 模块
script模块
copy 模块
lineinfile | replace 模块
yum模块
service模块
setup模块
五、playbook剧本
什么是playbook模式?
为什么要使用playbook
playbook之Yaml语法基础
playbook 构成
配置VIM缩进为两个空格以便于编写Yaml
Playbook在web服务器上配置httpd,在数据库服务器上配置mariadb
Playbook实现免密登陆
六、Ansible编程
命名元组
ad-hoc模式
调用playbook
模块开发
模块库目录
模块执行流程
管道连接
ansible-cmdb
使用ansible-cmdb
Ansible是2013年推出的一款IT自动化和DevOps软件,目前Redhat已签署Ansible收购协议。
Ansible是一个配置管理和配置工具,类似于Chef,Puppet或Salt。其是基于Python研发,糅合了很多老运维工具的优点实现了批量操作系统配置,批量程序的部署,批量运行命令等功能。
这是一款很简单也很容易入门的部署工具,它使用SSH连接到服务器并运行配置好的任务。服务器上不用安装任何多余的软件,只需要开启ssh,所有工作都交给client端的ansible负责。
上图展示了Ansible组建之间的调用关系,管理员通过使用Ad-Hoc或Ansible-playbooks时,在服务器终端输入Ansible的Ad-Hoc命令集或palybook后,Ansible会遵循预先编排的规则将Playbooks逐条拆解为Play,再将paly组织成Ansible可识别的任务(Task),随后调用任务涉及的所有模块(modules)和插件(plugins),根据Inventory中定义的主机列表通过SSH将任务集以临时文件或命令的形式传输到远程客户端执行并返回执行结果,如果是临时文件,则执行完毕后自动删除。
ad-hoc简而言之,就是"临时命令",一般用来做一些一次性的工作,比如说在多台机器上,查看某个进程是否启动,开一个服务。ad-hoc模式使用单个模块,支持批量执行单条命令。ad-hoc命令是一种可以快速输入的命令,而且不需要保存起来的命令,相当于bash中的一句shell。
Ansible Inventory实际上是包含静态Inventory和劢态Inventory两部分,静态Inventory指的是在文件/etc/ansible/hosts中指定的主机和组,DynamicInventory指通过外部脚本获取主机列表,并按照ansible 所要求的格式返回给ansilbe命令。
注意事项:
1、主机部分必须是列表格式的
2、hostdata行,其中的"hosts" 部分可以省略,但如果使用时,必须是"hosts"
JSON的全称是”JavaScript Object Notation”,意思是JavaScript对象表示法,它是一种基于文本,独立于语言的轻量级数据交换格式。 JSON中的分隔符限于单引号 ' 、小括号 ()、中括号[ ]、大括号 { } 、冒号 :和逗号 ,。
#!/usr/bin/python
import json
hostlist = {}
hostlist["bb"] = ["192.168.1.15", "192.168.1.16"]
hostlist["192.168.1.13"] = {
"ansible_ssh_user":"root","ansible_ssh_pass":"pwd"
}
hostlist["aa"] = {
"hosts" : ["192.168.1.11", "192.168.1.12"],
"vars" : {
"ansible_ssh_user":"root","ansible_ssh_pass":"pwd"
}
}
print( json.dumps(hostlist))
{
"aa" : {
"hosts" : ["192.168.1.11", "192.168.1.12"],
"vars" : {
"ansible_ssh_user" : "root",
"ansible_ssh_pass" : "pwd"
}
},
"bb" : ["192.168.1.15", "192.168.1.16"],
"192.168.1.13": {
"ansible_ssh_user" : "root",
"ansible_ssh_pass" : "pwd"}
}
每次交互输入密码比较麻烦,密码写入配置文件安全性很差,不同主机不同密码,使用key方式认证,是一个不错的选择,给所有主机部署公钥:
– ansible all -m authorized_key -a "user=root exclusive=true manage_dir=true key='$(
如果报错:– "msg": "Using a SSH password instead of a key isnot possible because Host Key checking is
enabled and sshpass does not support this.Please add this host's fingerprint to your known_hosts file to manage this host."
– 解决方法:
– 修改 ansible.cfg
host_key_checking = False
playbook模式是ansible主要管理方式,也是ansible功能强大的关键所在。Playbooks的格式是YAML语言格式,使用 YAML 是因为它像 XML 或 JSON 是一种利于人们读写的数据格式。
playbook通过多个task集合完成一类功能,如LAMP服务的安装部署、数据库服务器的批量备份等。可以简单地把playbook理解为通过组合多条ad-hoc操作的配置文件。Playbooks可以被描述为一个需要希望远程主机执行命令的方案,或者一组程序运行的命令集合,Playbook由一到多个Play组成,每个play可以指定哪些主机执行哪些任务,执行任务一般通过调用模块来实现。
执行一些简单的任务,使用ad-hoc命令可以方便的解决问题,但是有时一个设施过于复杂,需要大量的操作时候,执行的 ad-hoc 命令是不适合的,这时最好使用playbook,就像执行 shell 命令与写 shell 脚本一样,也可以理解为批处理任务。使用 playbook 你可以方便的重用编写的代码,可以移植到不同的机器上面,像函数一样,最大化的利用代码在使用 Ansible 的过程中,你也会发现,你所处理的大部分操作都是编写 playbook。
set ai
set ts=4
set et
autocmd FileType yaml setlocal sw=2 ts=2 et ai
---
- name: configure web service
hosts: webservers
tasks:
- name: install web app
yum:
name: "{{item}}"
state: present
with_items:
- httpd
- php
- php-mysql
- name: config web service
service:
name: httpd
state: started
enabled: true
- name: configure db service
hosts: dbservers
tasks:
- name: install db app
yum:
name: mariadb-server
state: latest
- name: config db service
service:
name: mariadb
state: started
enabled: yes
---
- name: configure authorized key
hosts: all
tasks:
- name: root key
authorized_key:
user: root
state: present
key: "{{ lookup('file', '/root/.ssh/id_rsa.pub') }}"
https://docs.ansible.com/ansible/latest/index.html,按ctrl+f,搜索api,找到python api
命名元组与普通元组一样,有相同的表现特征,其添加的功能就是可以根据名称引用元组中的项。collections 模块提供了namedtuple()函数,用于创建自定义的元组数据类型
使用、创建TaskQueueManager实例,用于管理子进程、通过主机列表和任务建立对象。TaskQueueManager需要主机清单参数、主机变量参数、连接选项等。
#!/usr/bin/env python
# coding: utf8
import shutil
from collections import namedtuple
# DataLoader用于解析yaml/json/ini文件
from ansible.parsing.dataloader import DataLoader
# VariableManager用于分析ansible用到的变量
from ansible.vars.manager import VariableManager
# InventoryManager用于分析主机文件
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
# task_queue_manager管理任务队列
from ansible.executor.task_queue_manager import TaskQueueManager
import ansible.constants as C # ansible的常量(不会变化的数据)
# since API is constructed for CLI it expects certain options to always be set, named tuple 'fakes' the args parsing options object
Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff'])
# connection有三个选择local/ssh/smart
# local表示在本机执行,ssh表示通过ssh协议执行,smart表示自动选择
options = Options(connection='smart', module_path=['/to/mymodules'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False)
# initialize needed objects
loader = DataLoader() # Takes care of finding and reading yaml, json and ini files
passwords = dict() # 用于存储加密密码、远程连接密码等
# create inventory, use path to host config file as source or hosts in a comma separated string
# 声明被ansible管理的主机有哪些,可以把各主机用逗号分开形成字符串
# 也可以使用主机清单文件路径,将路径放到列表中
# inventory = InventoryManager(loader=loader, sources='localhost,')
inventory = InventoryManager(loader=loader, sources=['myansi/hosts'])
# variable manager takes care of merging all the different sources to give you a unifed view of variables available in each context
variable_manager = VariableManager(loader=loader, inventory=inventory)
# create datastructure that represents our play, including tasks, this is basically what our YAML loader does internally.
play_source = dict(
name="Ansible Play", # Play名称
# hosts='localhost', # 在哪些主机上执行命令
hosts='webservers', # 在哪些主机上执行命令
gather_facts='no', # 不收集主机信息
tasks=[
# 以下是执行的命令
# dict(action=dict(module='shell', args='ls'), register='shell_out'),
# dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}'))),
# dict(action=dict(module='yum', args='name=httpd state=absent'), register='shell_out'),
dict(action=dict(module='yum', args='name=httpd state=latest'), register='shell_out'),
dict(action=dict(module='debug', args=dict(msg='{{shell_out}}')))
]
)
# Create play object, playbook objects use .load instead of init or new methods,
# this will also automatically create the task objects from the info provided in play_source
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)
# Run it - instantiate task queue manager, which takes care of forking and setting up all objects to iterate over host list and tasks
tqm = None
try:
tqm = TaskQueueManager(
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=options,
passwords=passwords,
)
result = tqm.run(play) # most interesting data for a play is actually sent to the callback's methods
finally:
# we always need to cleanup child procs and the structres we use to communicate with them
if tqm is not None:
tqm.cleanup()
# Remove ansible tmpdir
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
Playbook 是 Ansible的配置、部署、编排语言,它们可以被描述为一个需要希望远程主机执行命令的方案或者一组IT程序运行的命令集合,可以通过python编程的方式执行playbook。
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.executor.playbook_executor import PlaybookExecutor
Options = namedtuple(
'Options',
[
'connection',
'remote_user',
'ask_sudo_pass',
'verbosity',
'ask_pass',
'module_path',
'forks',
'become',
'become_method',
'become_user',
'check',
'listhosts',
'listtasks',
'listtags',
'syntax',
'sudo_user',
'sudo',
'diff'
]
)
ops = Options(
connection='smart',
remote_user=None,
ask_pass=None,
sudo_user=None,
forks=5,
sudo=None,
ask_sudo_pass=False,
verbosity=5,
module_path=None,
become=None,
become_method=None,
become_user=None,
check=False,
diff=False,
listhosts=None,
listtags=None,
listtasks=None,
syntax=None
)
loader = DataLoader()
passwords = dict()
inventory = InventoryManager(
loader=loader,
sources=['myansi/hosts']
)
variable_manager = VariableManager(
loader=loader,
inventory=inventory
)
def run_pb(pb_path):
playbook = PlaybookExecutor(
playbooks=pb_path,
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=ops,
passwords=passwords
)
result = playbook.run()
return result
if __name__ == '__main__':
run_pb(pb_path=['myansi/lamp.yml'])
Ansible官方已经提供了大量模块,在编写模块之前,可以查看是否已有现成模块
• 官方已发布模块
– http://docs.ansible.com/ansible/modules.html
• 官方正在开发的模块
– https://github.com/ansible/ansible/labels/module
可以使用 ANSIBLE_LIBRARY环境变量来指定模块的存放位置,也可以在playbook当前目录下创建library目录
创建模块,模块用于拷贝目标主机文件到目标主机的指定目录
#!/usr/bin/env python
import shutil
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
argument_spec=dict(
src=dict(required=True, type='str'),
dest=dict(required=True, type='str')
)
)
shutil.copy(module.params['src'], module.params['dest'])
module.exit_json(change=True)
if __name__ == '__main__':
main()
验证:ansible web -m rcopy -a "src=/etc/hosts dest=/opt"
使用了管道连接后,与远程主机只有一个连接,命令通过数据流的方式发送执行,此模式与有些系统程序兼容不太好,配置方式: