Ansible简介
- Ansible是一个配置管理和配置工具,类似于Chef,Puppet或Salt
- 这是一款很简单也很容易入门的部署工具,它使用SSH连接到服务器并运行配置好的任务
- 服务器上不用安装任何多余的软件,只需要开启ssh,所有工作都交给client端的ansible负责
安装ansible
- 在线安装
[root@localhost ~]# yum install -y ansible
- 创建ansible工作目录
[root@bogon ~]# mkdir /root/myansi/
- 创建配置文件
[root@bogon ~]# cd /root/myansi/
[root@bogon myansi]# cat ansible.cfg
[defaults]
inventory = hosts
remote_user = root
- 声明被管理主机
[root@bogon myansi]# cat hosts
[myself]
localhost
[webservers]
node1.cn
- 配置名称解析
[root@bogon myansi]# cat /etc/hosts
127.0.0.1 localhost.localdomain localhost
192.168.113.134 node1.cn node1
- 添加远程主机密钥到信任列表
[root@bogon myansi]# ssh-keyscan localhost 127.0.0.1 node1.cn
192.168.113.134 >> /root/.ssh/known_hosts
- 测试
#加-k是进入交互式密码,免密不需要加-k
[root@bogon myansi]# ansible all -m ping -k
SSH password:
localhost | SUCCESS => {
"changed": false,
"ping": "pong"
}
node1.cn | SUCCESS => {
"changed": false,
"ping": "pong"
}
Ansible应用
使用playbook
- Playbooks是Ansible的配置、部署、编排语言。
- 它们可以被描述为一个需要希望远程主机执行命令的方案,或者一组程序运行的命令集合
- Playbook由一到多个Play组成
- 每个play可以指定哪些主机执行哪些任务
- 执行任务一般通过调用模块来实现
Yaml简介
- Playbooks的格式是YAML
- 语法做到最小化,意在避免 playbooks 成为一种编程语言或是脚本
- 使用 YAML 是因为它像 XML 或 JSON 是一种利于人们读写的数据格式
Yaml语法
- 每一个 YAML 文件都是从一个列表开始
- 列表中的每一项都是一个键值对, 通常它们被称为一个“哈希 或“字典”
- 所有的 YAML 文件开始行都应该是 —。这是 YAML 格式的一部分,表明一个文件的开始
- 列表中的所有成员都开始于相同的缩进级别,并且使用一个 "- " 作为开头(一个横杠和一个空格)
- 一个字典是由一个简单的 键: 值 的形式组成(冒号后面必须是一个空格)
配置VIM
- Yaml的缩进不能使用tab键
- 建议缩进为两个空格
- 为了实现yml文件按tab键缩进两个空格,可以按以下方式对vim进行定制
[root@bogon myansi]# cat ~/.vimrc
autocmd FileType yaml setlocal sw=2 ts=2 et ai
使用模块
- Ansible的模块实际上就是一个个的python程序文件
- Ansible执行任务就是通过调用这些模块来完成的
- 查看模块列表
[root@bogon myansi]# ansible-doc -l
- 查看模块帮助
[root@bogon myansi]# ansible-doc yum
编写playbook,在web服务器上安装httpd服务
[root@bogon myansi]# vim install_web.yml
Ansible编程基础
执行playbook
- 检查语法
[root@bogon myansi]# ansible-playbook --syntax-check install_web.yml
- 执行
[root@bogon myansi]# ansible-playbook install_web.yml -k
配置lamp分离结构
# vim lamp.yml
---
- name: configure webservers
hosts: webservers
tasks:
- name: install web pkgs
yum:
name: [httpd, php, php-mysql]
state: present
- name: configure web service
service:
name: httpd
state: started
enabled: yes
- name: configure dbservers
hosts: dbservers
tasks:
- name: install db pkgs
yum:
name: mariadb-server
state: present
- name: configure db service
service:
name: mariadb
state: started
enabled: yes
# ansible-playbook lamp.yml
命名元组
- 命名元组与普通元组一样,有相同的表现特征,其添加的功能就是可以根据名称引用元组中的项
- collections 模块提供了namedtuple()函数,用于创建自定义的元组数据类型
>>> from collections import namedtuple
>>> user = namedtuple('user', ['name', 'age'])
>>> bob = user('Bob Green', 23)
>>> bob[0]
'Bob Green'
>>> bob[1]
23
>>> bob.name
'Bob Green'
ansible-cmdb
可以将服务器的信息以web形式展现
# 在虚拟环境中安装ansible-cmdb
# pip3 install ansible-cmdb_pkgs/*
# 或在线安装
# pip3 install ansible-cmdb
# 获取远程主机的信息
# ansible all -m setup --tree /tmp/servers
# ls /tmp/servers
# ansible-cmdb分析获取的信息文件,生成html文件
# ansible-cmdb /tmp/servers > /tmp/servers.html
# firefox /tmp/servers.html
Ansible常用属性
from ansible.parsing.dataloader import DataLoader
用来加载解析yaml文件或JSON内容,并且支持vault的解密from ansible.vars.manager import VariableManager
管理变量的类,包括主机,组,扩展等变量from ansible.inventory.manager import InventoryManager
用于创建主机清单,主机清单的源采用配置文件或是逗号分开主机名字符串from ansible.playbook.play import Play
用于创建play对象,能够通过play_source提供的信息自动创建任务对象from ansible.executor.task_queue_manager import TaskQueueManager
用于处理进程池中的多进程。队列管理器负责加载play策略插件,以便在选定的主机上执行任务import ansible.constants as C
存储ansible一些预定义变量
ad-hoc模式
使用TaskQueueManager
- 创建TaskQueueManager实例,用于管理子进程、通过主机列表和任务建立对象
- TaskQueueManager需要主机清单参数、主机变量参数、连接选项等
导入模块
- 不管是执行ad-hoc还是playbook都需要以下模块
#!/usr/bin/env python
import shutil
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
import ansible.constants as C
设置参数
- 运行命令时有很多参数要指定,这些参往往很长,可以先把它们提前定义出来
- 注:ansible2.7跟2.8版本写法不同,注意看文档区别
Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff'])
options = Options(connection='local', module_path=[''], forks=10,
become=None, become_method=None, become_user=None,
check=False, diff=False)
loader = DataLoader()
passwords = dict(vault_pass='secret')
inventory = InventoryManager(loader=loader, sources='localhost,')
variable_manager = VariableManager(loader=loader,
inventory=inventory)
Ansible常用属性
from ansible.parsing.dataloader import DataLoader
用来加载解析yaml文件或JSON内容,并且支持vault的解密from ansible.vars.manager import VariableManager
管理变量的类,包括主机,组,扩展等变量from ansible.inventory.manager import InventoryManager
用于创建主机清单,主机清单的源采用配置文件或是逗号分开主机名字符串from ansible.playbook.play import Play
用于创建play对象,能够通过play_source提供的信息自动创建任务对象from ansible.executor.task_queue_manager import TaskQueueManager
用于处理进程池中的多进程。队列管理器负责加载play策略插件,以便在选定的主机上执行任务import ansible.constants as C
存储ansible一些预定义变量
运行命令时有很多参数要指定,这些参往往很长,可以先把它们提前定义出来
play_source = dict(name = "Ansible Play",hosts = 'localhost',gather_facts = 'no',tasks=[
dict(action=dict(module='shell',args='mkdir /tmp/mytestdir'),register='shell_out')])
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)
执行ad-hoc命令
- 创建实例并执行命令
tqm =None
try:
tqm =TaskQueueManager(
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=options,
passwords=passwords,
)
result = tqm.run(play)
finally:
if tqm is not None:
tqm.cleanup()
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
调用playbook
Playbook编程概述
- Playbooks 是 Ansible的配置、部署、编排语言
- 它们可以被描述为一个需要希望远程主机执行命令的方案或者一组IT程序运行的命令集合
- 可以通过python编程的方式执行playbook
相关模块
- 与执行ad-hoc命令一样,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
def runpb(sources,playbooks):
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'])
options = 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,
listtasks=None,
listtags=None,
syntax=None)
loader = DataLoader()
passwords = dict()
inventory = InventoryManager(loader=loader, sources=sources)
variable_manager = VariableManager(loader=loader, inventory=inventory)
playbook = PlaybookExecutor(
playbooks= playbooks,
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=options,
passwords=passwords
)
result = playbook.run()
return result
if __name__ == '__main__':
sources = ['myansible/hosts']
playbooks = ['myansible/dbweb.yaml']
result = runpb(sources=sources,playbooks=playbooks)
print(result)
编写ansible模块
模块基础
官方模块
- Ansible官方已经提供了大量模块,在编写模块之前,可以查看是否已有现成模块
- 官方已发布模块
http://docs.ansible.com/ansible/modules.html - 官方正在开发的模块
https://github.com/ansible/ansible/labels/module
模块执行流程
-
将模块文件读入内存,然后添加传递给模块的参数,最后将模块中所需要的类添加到内存,由zipfile压缩后,再由base64进行编码,写入到模板文件内
-
通过默认的连接方式(一般是ssh),ansible连接到远程主机,创建临时目录,并关闭连接
-
打开另外一个ssh连接,将模板文件以sftp方式传送到刚刚创建的临时目录中,写完后关闭连接
-
打开一个ssh连接将任务对象赋予可执行权限,执行成功后关闭连接
-
最后,ansible将再打开一个新连接来执行模块,并删除临时目录及其所有内容
-
模块的结果是从标准输出stdout中获取json格式的字符串。ansible将解析和处理此字符串
模块开发
模块库目录
- 可以使用 ANSIBLE_LIBRARY环境变量来指定模块的存放位置
- 也可以在playbook当前目录下创建library目录
[root@localhost myansi]# mkdir /opt/myansible_lib/
指定自己编写模块的文件路径是/opt/myansible_lib/
# export ANSIBLE_LIBRARY=/opt/myansible_lib/
# mkdir /opt/myansible_lib/
编写模块文件rcopy.py,实现远程主机在自己的系统内执行拷贝操作
# vim /opt/myansible_lib/rcopy.py
#!/usr/bin/env python
from ansible.module_utils.basic import AnsibleModule
import shutil
def main():
module = AnsibleModule(
argument_spec=dict(
yuan=dict(required=True, type='str'),
mubiao=dict(required=True, type='str')
)
)
shutil.copy(module.params['yuan'], module.params['mubiao'])
module.exit_json(changed=True)
if __name__ == '__main__':
main()
调用自己写的模块,将服务器上/etc/hosts拷贝到/tmp/zhuji
# ansible webservers -m rcopy -a "yuan=/etc/hosts mubiao=/tmp/zhuji"
编写下载模块
-
环境变量
# export ANSIBLE_LIBRARY=/opt/myansible_lib/
-
编写模块
# vim /opt/myansible_lib/download.py #!/usr/bin/env python # -*- coding: utf8 -*- '用于下载指定文件' from ansible.module_utils.basic import AnsibleModule import wget def main(): module = AnsibleModule( argument_spec=dict( url=dict(required=True, type='str'), path=dict(required=True, type='str') ) ) wget.download(module.params['url'], module.params['path']) module.exit_json(changed=True) if __name__ == '__main__': main()
注意:远程主机只有python2,不是python3。python2默认字符采用的是ASCII码,而不是unicode,所以python2的文本中不能写汉字。如果需要添加汉字,需要在文件中指定字符集。
-
在远程主机上安装wget。因为ansible准备好的模块,需要在远程主机上执行。
# 在本机上下载wget # pip3 download wget --trusted-host pypi.douban.com # 在远程主机上安装wget # ansible webservers -m copy -a "src=files/wget-3.2.zip dest=/root" # ansible webservers -m shell -a "cd /root; unzip wget-3.2.zip" # ansible webservers -m shell -a "cd /root/wget-3.2/; python setup.py install"
-
通过自己编写的模块,下载文件
# ansible webservers -m download -a "url=http://192.168.4.254/zabbix.png path=/tmp/zabbix.png"