基本部署
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 用户名 |
yum
yum管理模块
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
.默认是smart
ansible_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.