本文主要介绍 Ansible 中一些重要概念和特性。不能覆盖 Ansible 的全部特性以及细节。
Ansible 是一个 IT 自动化工具。可以用于:
配置管理
应用程序部署
执行 ad-hoc 任务
多节点编排。比如零停机滚动升级等
Ansible 具有如下特点:
Ansible 的开发语言是 Python
Ansible 系统由控制主机和被管理主机组成
no agents/no server:控制主机默认通过 ssh 连接被管理主机,因此 Ansible 架构无服务端,去中心化。只需要简单的复制操作,就可以完成控制主机的迁移。同时,被管理主机上无需安装 agent
基于模块工作,可以使用任何语言开发模块
基于推送的方式:由调用者控制变更在被管理主机上的发生时间
许多自带模块提供幂等判断机制,这意味着多次执行相同的任务是安全的
playbook 使用 yaml 编写
Ansible 的整体架构如下图所示:
Ansible 执行任务的流程,大致如下:
1,Ansible 有两种任务执行模式:
2,尽量使用 Ansible 自带的模块,而不是 shell 脚本,因为 Ansible 的很多模块提供幂等判断机制
3,模块和插件的区别:
1,安装 Ansible:
xxxxxxxxxx
sudo pip install ansible
2,Ansible 按照如下的优先级顺序地查找配置文件:
3,Ansible 配置文件:
(下面仅列出少数参数)
x
[defaults]
# 指定 Inventory 文件的位置,默认值是 /etc/ansible/hosts
inventory = /etc/ansible/hosts
# forks 是非常重要的、优化时需要重点关注的参数。
# + Ansible 会创建 forks 个子进程,并发地管理多台被控主机。
# + 默认值是 5,随着被管理主机的增多,需要适当调大
forks = 5
# Ansible 在通过 ssh 连接被控主机时,是否检查其公钥
host_key_checking = False
# ssh 连接的超时时间,单位是秒
timeout = 60
# 指定存储 Ansible 日志的文件(默认不记录日志)
log_path = /var/log/ansible.log
[ssh_connection]
# 设置 Ansible 通过 ssh 连接被控主机时,使用的参数
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
4,Inventory 文件:
既可以使用静态 Inventory 文件,也可以使用动态 Inventory。使用动态 Inventory 的好处是:可以根据其它系统(比如 CMDB、LDAP 等)动态地生成主机清单,避免产生需要在多个地方维护服务器列表的情况。
动态 Inventory 是一个可执行程序,Ansible 会创建一个子进程执行该程序,然后从该子进程的标准输出获取主机清单。因此,可以用任何语言编写动态 Inventory 程序,只要该程序满足 Ansible 的规范即可。(更多关于如何编写动态 Inventory 的细节,请移步参考文档)
可以在 Inventory 中对主机进行分组。Ansible 可以同时操作属于同一个组的多台主机。在定义主机时,可以为主机指定主机变量。也可以为组指定组变量(相当于同时为组内的所有主机指定变量)。这些变量可以被插件(比如连接插件)、模块等使用。
下面是一个非常简单的 Inventory 示例:
[mongodb]
mongodb-101 ansible_ssh_host=192.168.10.101
mongodb-102 ansible_ssh_host=192.168.10.102
[mongodb:vars]
ansible_ssh_private_key_file=/etc/ansible/id_rsa
5,执行 ad-hoc 任务:
使用 ansible
命令,执行 ad-hoc 任务。其使用方法是:
ansible <host-pattern> [-f forks] [-m module_name] [-a args]
其中:
比如使用 yum 模块,在所有主机上安装最新版本的 httpd-tools:
ansible all -m yum -a "name=httpd-tools state=latest" -b
ansible内建数百个模块。可以使用ansible-doc -l
列出所有模块。还可以使用 ansible-doc -s <module_name>
查看模块的用法。
playbook 是由一个或多个 play 组成的列表。play 的目标是:将一组主机映射到若干个预先好定义的角色上(角色中包含要调用的任务、要使用的变量等)。playbook 通过编排 play,完成复杂的功能。
play 由以下部分组成:
Target Section:
Variable Section:定义运行 play 时,使用的变量
Task Section:定义要在远程主机上运行的任务列表
name:任务的名字。用来描述任务是做什么的
'module: options':调用的 module 和传递给 module 的参数
notify:用于指定通知哪些 handler
...
Handler Section:定义在远程主机发生变动时,运行的操作
接下来看一个示例 playbook:
xxxxxxxxxx
---
hosts webservers
remote_user root
tasks
name ensure apache is at the latest version
yum
name httpd
state latest
name write the apache config file
template
src /srv/httpd.j2
dest /etc/httpd.conf
hosts databases
remote_user root
tasks
name ensure postgresql is at the latest version
yum
name postgresql
state latest
name ensure that postgresql is started
service
name postgresql
state started
...
可以通过 setup 模块,采集远程主机的基础信息(比如硬件、操作系统等信息),这些信息会被保存到 ansible_facts 变量中。
使用 playbook 时,默认开启 facts 采集。但由于该操作比较耗时,所以在不需要主机基础信息的情况下,应该考虑关闭采集。Ansible 也支持对采集结果进行缓存。
比如可以通过以下命令采集远程主机的所有 IPV4 地址:
ansible all -m setup -a 'filter=ansible_all_ipv4_addresses'
角色是一种自动地加载特定的 vars_file、tasks 和 handlers 的方式,它基于特定的文件结构。通过使用角色对内容进行分组,可以方便地进行复用。
下面对角色目录结构进行说明:
xxxxxxxxxx
role_name/(以角色名命名的目录)
tasks/:
如果存在 main.yml,那么其中列出的 tasks 将被添加到 play 中
所有 import_tasks 任务可以引用该目录中的文件,不必指明文件的路径
handlers/:
如果存在 main.yml,那么其中列出的 handlers 将被添加到 play 中
vars/:
如果存在 main.yml,那么其中列出的 variables 将被添加到 play 中
defaults/:
如果存在 main.yml,那么其中列出的 variables 将被添加到 play 中
meta/:
如果存在 main.yml,那么其中列出的 “角色依赖” 将被添加到 roles 列表中
templates/:
角色中的所有 templates 任务可以引用该目录中的文件,不必指明路径
files/:
角色中的所有 copy 和 script 任务可以引用该目录中的文件,不必指明路径
需要说明的是:tasks、handlers、vars 等子目录都不是必须的。
在下面的例子中,定义了一个名称为 example 的角色:
xxxxxxxxxx
# roles/example/tasks/main.yml
name added in 2.4, previously you used 'include'
import_tasks redhat.yml
when ansible_facts'os_family' |lower == 'redhat'
import_tasks debian.yml
when ansible_facts'os_family' |lower == 'debian'
# roles/example/tasks/redhat.yml
yum
name"httpd"
state present
# roles/example/tasks/debian.yml
apt
name"apache2"
state present
在这个例子中,角色会根据远程主机的操作系统类型,导入相关的任务。(import_tasks 用于导入任务列表)
Ansible 配置文件中的 roles_path 参数是冒号分隔的路径列表,Ansible 会去这些路径下寻找角色。比如:
xxxxxxxxxx
[defaults]
roles_path = /etc/ansible/roles
可以通过 play 的roles
选项使用角色:
xxxxxxxxxx
---
hosts webservers
roles
common
webservers
可以通过 when 语句实现:在某些条件满足的情况下,才执行任务或使用角色。在 when 语句中,可以使用 jinja2 表达式。比如:
x
tasks
name"shutdown Debian flavored systems"
command /sbin/shutdown -t now
when ansible_os_family == "Debian"
在这个例子中,仅当远程主机的操作系统类型是 Debian 时,才执行关机命令。
如欲了解更多关于条件选择的细节,可以查看官方文档。
可以通过循环实现:使用不同参数,多次执行同一个任务。比如向远程主机批量添加用户:
x
---
hosts all
gather_factsno
becomeyes
become_user root
tasks
name add users
user name= item state=present
loop
testuser1
testuser2
...
如欲了解更多关于循环的细节,可以查看官方文档。
当集群规模变大时,主控机的压力会随之增大。因此需要寻找一些方式,降低主控机的压力,从而避免出现性能瓶颈。下面是博主整理的一些方案:
按业务线对服务器进行分组,将大集群拆分成若干个规模较小的集群
分批次执行任务。这样做有两点好处:
下发文件时,走专门的下载通道,降低主控机压力
对主控机做备份。因为主控机具有较高权限,重新授权代价较大
异步地执行任务。后文介绍
使用 ansible-pull。后文介绍
Ansible 支持异步地执行任务,即:在将任务下发之后,不再保持连接,而是每隔一段时间轮询一次执行结果,直到任务完成。
想要让任务异步地执行,需要在 task 中,加入两个参数:async 和 poll。
其中:
比如:
x
---
hosts all
gather_factsno
tasks
name sleep for a while
command /bin/sleep 10s
async20
poll2
...
如果想要更方便地查看轮询结果,可以使用 async_status 模块,比如:
x
---
hosts all
gather_factsno
tasks
name sleeper
command /bin/sleep 30s
async40
poll0
register job_sleeper
name checker
async_status jid= job_sleeper.ansible_job_id
retries30
register job_checker
until job_checker.finished
...
在这个例子中:
第一个任务是异步任务,其执行结果将被注册到 job_sleeper 变量中。
第二个任务使用 async_status 模块轮询第一个任务的执行结果,并返回轮询结果,最多轮询30次。
Ansible 默认使用推模式,而 ansible-pull 使用拉模式。
其原理是:去指定的仓库上拉取 playbook,然后在本机执行。比如:
x
ansible-pull -o -C master -U https://github.com/tim-chow/ansible-pull-test.git -d /home/vagrant/ansible-pull-test -i ansible/hosts main.yml
其中:
Inventory 中需要增加主机记录:
xxxxxxxxxx
localhost
# 或主机 hostname
# 或 127.0.0.1
# 具体原因请参考:https://stackoverflow.com/questions/55820887/how-to-resolve-warning-could-not-match-supplied-host-pattern-ignoring-machin/58032754
因为 VCS 可以水平扩展,所以 ansible-pull 也可以做到水平扩展。
ansible-pull 通常与crontab 一起使用,这样可以做到:定期地在本地执行 playbook。
自定义插件和模块
为 Ansible 开发更友好的 Web 客户端