基本部署
ansible依赖于Python 2.6或更高的版本,paramiko,PyYAML及Jinja2
编译安装
1  | # yum -y install python-jinja2 PyYAML python-paramiko python-babel python-crypto  | 
rpm 包安装
1  | # yum install epel-release  | 
基本使用
ansible 通过 ssh 实现配置管理,应用部署,任务执行等功能.因此,需要事先配置 ansible 端能基于密钥认证的方式联系各被管理节点
配置文件
如下为 /etc/ansible/ansible.cfg 配置文件常用选项及含义
1  | [defaults]  | 
常用命令
ansible-doc: ansible 模块的帮助文档
1  | ansible-doc  | 
ansible: ansible 执行单个任务,
1  | ansible <host-pattern> [-m module_name] [-a args] [options]  | 
ansible-playbook: 运行 Ansible playbools,在目标主机上执行已定义的任务
1  | ansible-playbook [options] <playbook.yml> [playbook2 ...]  | 
更多命令及参数,详见官方文档 - Working with command line tools.
常用模块
模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致
command命令模块
1  | # (默认模块)用于在远程主机执行命令;不能使用变量,管道等  | 
archive打包模块
1  | path 远程绝对路径,指定要打包的文件路径  | 
copy复制文件模块(复制本地文件到远程主机的指定位置)
1  | src 定义本地源文件路径  | 
cron计划任务
1  | # 配置计划任务,其中job为必须参数  | 
debug执行时打印信息模块
1  | msg 要打印的信息  | 
fail自定义失败信息模块
1  | msg 自定义的失败信息  | 
file文件管理模块
1  | path 指定远程主机上被管理的文件  | 
get_url从指定 url 下载文件
1  | url 下载文件路径  | 
group组管理模块
1  | gid gid  | 
ini_file创建/修改 ini 文件中的配置
1  | dest ini 文件路径  | 
lineinfile修改行格式的配置文件
1  | path 指定配置文件路径  | 
mount挂载管理模块
1  | path 指定挂载路径  | 
service服务管理模块
1  | name 指定服务名  | 
shell复杂命令模块
1  | # 与command基本一致,支持管道,重定向等复杂命令  | 
script脚本模块
1  | # 使远程主机执行本地指定脚本  | 
timezone设置时区
1  | name 指定时区名称  | 
user用户管理模块
1  | name 用户名  | 
yumyum管理模块
1  | name 程序包名称(不指定版本就安装最新的版本latest)  | 
setup信息收集模块
1  | # 每个被管理节点在接受并运行管理命令之前,会将自己主机相关信息,如操作系统版本,IP地址等报告给远程的 ansible 主机  | 
更多模块及其参数,详见官方文档 - Module Index
Inventory 主机清单
ansible 的主要用于批量主机操作,为了便捷地使用其中的部分主机,可以在 inventory file 中将其分组命名.默认的 inventory file 为/etc/ansible/hosts
inventory file 可以有多个,且也可以通过 Dynamic Inventory 来动态生成
文件格式
inventory 文件遵循INI文件风格,中括号中的字符为组名,可以将同一个主机同时归并到多个不同的组中.此外,当如若目标主机使用了非默认的SSH端口,还可以在主机名称之后使用冒号加端口号来标明.示例如下:
1  | ungrouped_host  | 
主机和组
ansible 中有两个默认的组,all 和 ungrouped
all表示所有主机组ungrouped表示不在自定义分组中的主机组
1  | ntp.magedu.com # 该主机在ungrouped分组中  | 
组嵌套
1  | [apache]  | 
主机变量与组变量
1  | # - 主机变量  | 
除了在主机清单文件中定义变量外,ansible 还支持在如下位置定义变量
- 在 
/etc/ansible/group_vars/目录下以 ‘.yml’, ‘.yaml’ 或 ‘.json’ 格式定义组变量 - 在 
/etc/ansible/host_vars/目录下以 ‘.yml’, ‘.yaml’ 或 ‘.json’ 格式定义主机变量 
主机清单中变量优先级从低到高为
all group -> parent group -> child group -> host
inventory 参数
ansible 连接主机时,可指定连接主机的相关参数.
ansible_connection: 连接主机方式,可选为smart,ssh或paramiko.默认是smartansible_host: 连接主机名ansible_port: 连接端口,默认 22ansible_user: 连接用户ansible_password: 连接密码(不要以明文方式存储,使用 vault 进行存储,详见 Variables and Vaults
当连接方式为 ssh 时,可指定如下 SSH 参数:
ansible_ssh_private_key_file: ssh 私钥文件ansible_ssh_pipelining: 是否使用 ssh pipelining
权限升级相关参数如下:
ansible_become: 允许强制特权升级ansible_become_user: 使用指定用户执行模块ansible_become_method: 使用指定方法变为指定用户ansible_become_password: 变为指定用户过程中需要的用户密码
远程主机相关参数如下:
ansible_shell_type: 目标系统的 shell 类型ansible_python_interpreter: 目标系统的python路径
No-SSH 连接参数
local: 仅在设备本地执行模块
Playbook
简介
Playbook 是由一个或多个 “play”(剧本) 组成的列表,剧本的主要功能在于将事先归并为一组的主机装扮成事先通过 ansible 中的 task 定义好的角色.
从根本上来讲,所有 task 无非是调用 ansible 的一个 module,将多个剧本组织在一个 Playbook 中,即可以让他们连同起来按事先编排的机制去完成某些任务.
Playbook 使用 yaml 编写.示例如下
1  | # playbook示例  | 
基本元素
主机与用户
ansible会使用 <remote_user> 定义的用户对 <hosts> 中的主机进行特定的Task操作.
<hosts> 用于指定要执行指定任务的主机,其可以是一个或多个由冒号分隔主机组;<remote_user>则用于指定远程主机上的执行任务的用户
1  | - hosts: webservers # 是一个由冒号分割的一个或多个主机组  | 
在每一个 task 中,也可以单独定义远程用户
1  | - hosts: webservers  | 
Task 列表
每一个play中包含了一个 Task 列表,所有的 host 会获取到相同顺序(按照定义的顺序)的 Task 指令.
一个 Task 在其所对应的所有主机上执行完毕之后,下一个 Task 才会执行
当运行 Playbook 时,具有失败任务的主机将从整个 Playbook 的调度轮询中删除.如果有失败,所有已执行任务都将回滚,所以只需要更正 Playbook 并重新运行即可.且模块执行是幂等的,多次运行的结果一致,这意味着多次执行是安全的.
每个任务的目标都是执行一个带有非常具体参数的模块,常见的有 shell, serivce, script, copy 等
1  | tasks:  | 
Handlers 事件处理机制
Handler 用于当关注的资源发生变化时采取一定的操作.
当带有 notify 关键字的 Task 执行前后状态发生改动时,notify 关键字指定的 handlers 将被被触发,且只会被触发一次.
1  | # 如在名为 "write the apache config file" 的 Task 执行完成后会启动 notify 机制,触发名为 "restart apache" 的 handlers 来进行下一步处理任务  | 
在 Ansible 2.2 版本之后,handlers 可以使用 listen 关键字监听普通主题,任务等.只需要 tasks中 notify 的内容与 handlers 中 listen 的的内容一致即可.(可一次性触发多个 handlers)
1  | handlers:  | 
创建可复用的 Playbook
include 和 import
include 和 import(在 Ansible 2.4 版中添加)允许用户将大的剧本拆分成较小的剧本文件.使用 include 和 import 使得剧本文件分工更加明确,重用性更高.
- 所有 
include*语句均在执行 Playbook 时进行处理 - 所有 
import*语句均在解析 Playbook 时进行处理,就好像它本身就是定义在那里一样. 
示例如下:
1  | # 支持使用 import 或 include 直接导入 playbook 或 tasks  | 
Role 角色
Role 是 ansilbe 1.2 版本引入的新特性,用于层次性,结构化地组织 Playbook
简单来讲,roles 就是通过分别将变量,文件,任务,模块及处理器放置于单独的目录中,并根据层次型结构自动装载它们.
Ansible 将通过以下方式搜索我们定义的 Roles
- Playbook 同级的 
roles/目录下 - 默认的 
/etc/ansible/roles目录下 
Role 的结构大概如下
1  | # roles 的目录结构  | 
以下是 roles 的一个使用示例:
1  | # site.yml  | 
变量
变量命名仅能由字母,数字和下划线组成,且只能以字母开头
变量定义
以下是变量定义的几种方式
在 Inventory 文件中定义变量
详见主机变量与组变量小节
在 PlayBook 中使用 var 关键字定义的变量
1  | # site.yml  | 
facts 变量发现
facts 是由正在通信的远程目标主机发回的信息,这些信息被保存在 ansible_facts 变量中.可使用 setup 模块对 facts 变量进行查看获取 ansible hostname -m setup
假如您不需要任何有关主机的 facts 变量,则可以在 Playbook 中禁用 facts 变量收集.如下
1  | - hosts: example  | 
另外,可在配置文件中查看有关变量缓存的相关配置.
注册变量
可在 Task 中运行命令并通过 register 关键字将该命令的结果注册为变量,供以后使用(多用于条件判断中).
如下为注册变量使用示例
1  | - hosts: web_servers  | 
可通过注册变量的返回值 使用注册变量,以进行条件判断或命令执行结果保存.
以下列出注册变量常用的返回值:
rc: 命令执行的状态码,常用于保存注册变量所表示命令的执行状态码返回stdout: 命令执行的结果,常用于保存注册变量所表示命令的执行结果
通过命令行传递变量
在运行 Playbook 的时候也可以使用 --extra-vars 传递一些变量供 Playbook 使用.
--extra-vars 参数支持如下格式
- 键值对,如 
key=value - json
 - json 或 yaml 文件,需要在文件名前添加 
@ 
1  | ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"  | 
通过 roles 传递变量
当给一个主机应用角色的时候可以传递变量,然后在角色内使用这些变量.如
1  | - hosts: webservers  | 
变量使用
通过 Jinja2 及其 filter 使用
定义变量后,可使用 Jinja2 (或中文文档地址) 模版系统在 Playbook 中使用它们.
如下是一个简单示例:
1  | - hosts: app_servers  | 
更多使用方式,参见模版(Jinja2)部分.
Jinja2 filter 可以在模版表达式中转换变量的值.Jinja2 包含了许多内置过滤器,同时 Ansible 提供了更多过滤器,参见官方文档 - Filters
变量的优先级
以下为常见变量的优先级(从低到高,高优先级会覆盖低优先级):
- 环境变量
 - role default: 角色中定义的默认变量
 - inventory file or script group vars: 主机清单中定义的主机组变量
 - inventory group_vars/all: 主机清单目录下的 
group_vars/all - playbook group_vars/all: playbook 目录下的 
group_vars/all - inventory group_vars/: 主机清单目录下的 
group_vars/* - playbook group_vars/: playbook 目录下的 
group_vars/* - inventory file or script host vars: 主机清单中定义的主机变量
 - inventory host_vars/: 主机清单目录下的 
host_vars/* - playbook host_vars/: playbook 目录下的 
host_vars/* - host facts / cached set_facts: 主机 facts 或 facts 缓存中的变量
 - play vars: 在 play 中定义的全局变量,如 
roles/x/tasks/main.yml中全局vars - play vars_prompt: 在 play 中定义的全局变量,如 
roles/x/tasks/main.yml中全局vars_prompt - play vars_files: 在 play 中定义的全局变量,如 
roles/x/tasks/main.yml中全局vars_files - role vars (defined in role/vars/main.yml): 定义在 
roles/x/vars/main.yml的变量 - block vars (only for tasks in block): 定义在 
roles/x/tasks/main.yml单个block中的变量,仅用于此block - task vars (only for the task): 定义在 
roles/x/tasks/main.yml单个task中的变量,仅用于此task - include_vars
 - set_facts / registered vars: 在 Task 中注册的变量
 - role (and include_role) params: 通过 playbook 传入的参数
 - include params
 - extra vars: 通过 
--extra-vars命令行参数传入的变量 
模版(Jinja2)
正如变量使用部分中已经介绍的那样,Ansible 使用 Jinja2 模板来启用动态表达式和访问变量.主要用于动态修改配置文件,动态配置相关参数等场景.
Jinja2 中使用 `{{ var_name }}` 来引用变量,包括 变量定义 小节中介绍的所有方式定义的变量.
1  | # 使用 template 关键字指定模版文件,并将变量替换后的文件复制到远程主机上  | 
同时,Jinja2 中变量支持过滤(Filter),条件测试(Tests),循环引用(Lookups)等高级用法.
Filters
详见官方文档 - Filters
条件测试
字符串
支持使用 match, search, regex 关键字将字符串与子字符串或正则表达式进行匹配:
1  | vars:  | 
版本比较
支持使用 version(ansible 2.5 之后版本,之前使用 version_compare) 对版本号进行检查.如:
1  | {{ ansible_facts['distribution_version'] is version('12.04', '>=') }}  | 
如吓是版本比较支持的操作符:
1  | <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne  | 
子集
支持使用 subset, superset 查看一个集合是否是另一个集合的子集或包含另一个子集.如:
1  | vars:  | 
all/any 判断
支持对列表进行 all, any 判断(所有值均为 True,或任意值为 True)
1  | vars:  | 
路径判断
支持对指定路径进行判断
1  | - debug:  | 
结果判断
支持对结果进行判断
1  | tasks:  | 
变量定义判断
支持对指定变量是否定义进行判断
1  | tasks:  | 
查找与迭代
当有需要重复性执行的任务时,可以使用迭代机制.其使用格式为将需要迭代的内容定义为变量,并通过 with_<lookup> 语句来指明迭代的元素列表即可.with_<lookup> 中支持 hashes(字典) 元素
1  | - name: add several users  | 
在 ansible 2.5 之后,引入了
loop关键字,并作为官方推荐的迭代关键字选择.
关于迭代的高级功能,详见官方文档 - Loops,或后面的章节 循环与迭代 Loops
when 条件判断
when 条件判断句用于在特定条件下做某些操作.如
- 执行一个 Task
 - 导入一个 Task 文件
 - 将角色分配给主机
 
when 条件判断句支持使用 Filters 或 Tests 条件测试作为条件判断的依据,当结果为 True 时,执行某些 Tasks.
when 条件判断句支持 and, or 或 not 关键字作为条件子句间逻辑关系的判断.同时支持以列表形式指定多个条件子句.列表形式子句间的逻辑关系为 and.
示例如下:
1  | - hosts: webservers  | 
Ansible 提供了一些常用的条件判断 facts,如下
ansible_facts['distribution']: 系统发行版,可能的值为
1  | Alpine, Altlinux, Amazon, Archlinux, ClearLinux, Coreos, CentOS, Debian, Fedora, Gentoo,  | 
ansible_facts['distribution_major_version']: 系统发行版主版本号ansible_facts['os_family']: 系统发行版类型,可能值为
1  | AIX, Alpine, Altlinux, Archlinux, Darwin, Debian, FreeBSD, Gentoo,  | 
循环与迭代 Loops
对于需要重复执行的任务,Ansible 提供了两个用于创建循环的关键字 loop 和 with_<lookup>.
loop关键字是在 2.5 之后的版本中引入的,而且在以后的版本会优化相关语法.并作为官方推荐的迭代方式;with_<lookup>后续版本也会继续支持.
loop vs with_*
with_<lookup>关键字依赖于 Lookup Pluginsloop关键字等价于with_list,是简单循环最好的选择loop关键字不支持字符串作为输入,参见 loop 中 query 与 lookup 函数比较或官方文档 - Ensuring list input for loop: query vs. lookup 或.- 一般来说,后文(官方文档 - Migrating from with_X to loop)中包含的 
with_*均可以转换为loop. - 对于 
with_items带有列表嵌套的场景,需要对loop内容使用flatten(1)过滤器来得到相同的结果.如with_items: [1, [2, 3], 4]可转换为 `loop: "{{ [1, [2,3] ,4] | flatten(1) }}"`. 
loop 中 query 与 lookup 函数比较
lookup函数默认返回逗号分割的字符串.可以使用wantlist=True参数强制该函数返回列表.query函数总是返回列表,当使用loop关键字时,可以提供一个更简单的接口.
1  | loop: "{{ query('inventory_hostnames', 'all') }}"  | 
with_X 转换为 loop
with_list
with_list 可直接转换为 loop
1  | - name: with_list  | 
with_items
with_items 可以转换为 loop 和 flatten 过滤器
1  | - name: with_items  | 
with_indexed_items
with_indexed_items 可以转换为 loop, flatten 过滤器和 loop_control.index_var.
1  | - name: with_indexed_items  | 
with_flattened
with_flattened 可以转换为 loop 和 flatten 过滤器
1  | - name: with_flattened  | 
with_fileglob
with_fileglob 可以转换为 loop 和 lookup 或 query 函数
1  | - name: with_flattened  | 
Blocks
Blocks 允许对 Task 进行逻辑分组并进行错误处理.除循环外,单个任务的大多数内容均可用于 Block.
示例如下:
1  | tasks:  | 
除了单个任务的大多数常用内容外,Block 还支持如下关键字:
rescue: 用于错误处理,相当于try...catch语句always: 此关键字定义的内容总是执行,相当于finally语句
1  | - name: Attempt and graceful roll back demo  | 
高级功能
Tags 标签
ansible playbook 应用于有一个大型的 playbook,但仅仅运行指定的 tasks 的场景.在执行 playbook 时,可以使用 --tags 或 --skip-tags 运行或不运行带有指定标签的 tasks
1  | tasks:  | 
always标签默认每次都会执行,除非使用--skip-tags always特别指定跳过该 tag 标记的 tasknever标签默认每次都不会执行,除非使用--tags never特别指定运行该 tag 标记的 task
其它常用关键字
debugger: 显示 debug 信息,可选项为always | never | on_failed | on_unreachable | on_skipped.
1  | - name: Execute a command  | 
serial: 一次性多少个主机并行执行任务,多用于滚动更新.支持数字,百分比以及它们的列表.若指定为列表,则每次执行的主机数等于列表中元素.
1  | - name: test play  | 
假设有 4 台主机,执行过程将如下所示
1  | PLAY [webservers] ****************************************  | 
run_once: 尝试仅在主机列表的第一台主机上运行一次,然后将运行结果和 facts 应用到其他主机上.ignore_errors: 尝试忽略执行过程中的错误failed_when: 定义 playbook 执行失败的条件.当该关键字指定的条件判断句为 True 时,playbook 执行失败并退出执行.等价于fail模块与when条件判断使用.any_errors_fatal: 只要有错误发生,则终止整个 Playbook 的执行vars_prompt,prompt: 由vars_prompt包含的多个prompt列表用于交互式定义变量.private: 是否定义为私有变量.default: 为交互式变量设置默认值encrypt: 设置加密方式.可选值为参见官方文档confirm: 是否需要确认,再次输入salt_size: 加密过程中加盐大小unsafe: 输入变量中可能包含导致创建模版失败的特殊字符(如%).使用该参数进行转义
1  | - hosts: all  | 
connection: 指定连接的主机.多用于指定为local.
更多关键字,详见官方文档 - Playbook Keywords.