从零开始的云计算生活——第三十七天,跬步千里,ansible之playbook
playbook是ansible用于配置,部署和管理托管主机剧本,通过playbook的详细描述,执行其中一系列tasks,可以让远程主机达到预期状态,也可以说,playbook字面意思是剧本,现实中由演员按剧本表演,在ansible中由计算机进行安装,部署应用,提供对外服务,以及组织计算机处理各种各样的事情。ansible使用playbook来管理自动化task,playbook是yaml格式的
目录
一.故事剧情
在了解ansible命令本身之后,她最常用的一部分,便是playbook,使用playbook可以很大程度上减少我们的操作,批量的对很多主机进行更改。
二.Playbook简介
playbook是ansible用于配置,部署和管理托管主机剧本,通过playbook的详细描述,执行其中一系列tasks,可以让远程主机达到预期状态,也可以说,playbook字面意思是剧本,现实中由演员按剧本表演,在ansible中由计算机进行安装,部署应用,提供对外服务,以及组织计算机处理各种各样的事情。
ansible使用playbook来管理自动化task,playbook是yaml格式的文件,其基本内容可以认为是多条ansible的ad-hoc的语句组成。我们完成一个任务,例如安装部署一个httpd服务,我们需要多个模块(一个模块也可以称之为task)提供功能来完成。而playbook就是组织多个task的容器,他的实质就是一个文件,是用yaml(Yet Another Markup Language)语言编写的文件,有着特定的组织格式。YAML 是专门用来写配置文件的语言,非常简洁和强大,远比 JSON 格式方便,YAML语法能够简单的表示散列表,字典等数据结构。
playbook命令根据自上而下的顺序依次执行。同时,playbook开创了很多特性,它可以允许你传输某个命令的状态到后面的指令,如你可以从一台机器的文件中抓取内容并附为变量,然后在另一台机器中使用,这使得你可以实现一些复杂的部署机制,这是ansible命令无法实现的。
三.Playbook核心元素(重要)
-
Hosts 执行的远程主机列表
-
Tasks 任务集
-
Varniables 内置变量或自定义变量在playbook中调用
-
Templates 模板,即使用模板语法的文件,后缀.j2,比如配置文件等
-
Handlers 和notify结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
-
tags 标签,指定某条任务执行,用于选择运行playbook中的部分代码
四.Playbook语法
playbook使用yaml语法格式,后缀可以是yaml,也可以是yml
-
一般情况下,一个playbook需要使用"---"开始。
-
在单一一个playbook文件中,可以连续三个连子号(---)区分多个play。还有选择性的连续三个点号(...)用来表示play的结尾,也可省略。
-
次行开始正常写playbook的内容,一般都会写上描述该playbook的功能
-
使用#号注释代码
-
缩进必须统一,不能空格和tab混用
-
缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行实现的
-
YAML文件内容和Linux系统大小写判断方式保持一致,是区分大小写的,k/v的值均需大小写敏感
-
k/v的值可同行写也可以换行写。同行使用: 分隔
-
v可以是个字符串,也可以是一个列表[]
五.Playbook的运行方式
通过ansible-playbook命令运行
ansible-playbook <filename.yml> ... [options]
常用命令:
ansible-playbook filename.yml [options]
# ssh 连接的用户名
-u REMOTE_USER, --user=REMOTE_USER
# ssh登录认证密码
-k, --ask-pass
# sudo 到root用户,相当于Linux系统下的sudo命令
-s, --sudo
# sudo 到对应的用户
-U SUDO_USER, --sudo-user=SUDO_USER
# 用户的密码(—sudo时使用)
-K, --ask-sudo-pass
# ssh 连接超时,默认 10 秒
-T TIMEOUT, --timeout=TIMEOUT
# 指定该参数后,执行 playbook 文件不会真正去执行,而是模拟执行一遍,然后输出本次执行会对远程主机造成的修改
-C, --check
# 设置额外的变量如:key=value 形式 或者 YAML or JSON,以空格分隔变量,或用多个-e
-e EXTRA_VARS, --extra-vars=EXTRA_VARS
# 进程并发处理,默认 5
-f FORKS, --forks=FORKS
# 指定 hosts 文件路径,默认 default=/etc/ansible/hosts
-i INVENTORY, --inventory-file=INVENTORY
# 指定一个 pattern,对- hosts:匹配到的主机再过滤一次
-l SUBSET, --limit=SUBSET
# 只打印有哪些主机会执行这个 playbook 文件,不是实际执行该 playbook
--list-hosts
# 列出该 playbook 中会被执行的 task
--list-tasks
# 私钥路径
--private-key=PRIVATE_KEY_FILE
# 同一时间只执行一个 task,每个 task 执行前都会提示确认一遍
--step
# 只检测 playbook 文件语法是否有问题,不会执行该 playbook
--syntax-check
# 当 play 和 task 的 tag 为该参数指定的值时才执行,多个 tag 以逗号分隔
-t TAGS, --tags=TAGS
# 当 play 和 task 的 tag 不匹配该参数指定的值时,才执行
--skip-tags=SKIP_TAGS
# 输出更详细的执行过程信息,-vvv可得到所有执行过程信息。
-v, --verbose
六.Playbooks中tasks语法使用
先创建一个目录用来存放yml文件
可以使用-C来验证文件内容是否出错
1、file
操作文件,比如创建文件或目录、删除文件或目录、修改文件权限等
常用参数:
-
path:指定要操作的文件或目录
-
state:参数非常灵活,可以包含的值及含义如下
-
directory - 与path结合说明我们要操作的是一个目录
-
touch - 与path结合说明我们要操作的是一个文件
-
link - 创建软连接
-
hard - 创建硬链接
-
absent - 删除目标
-
-
src:当state设置为link或hard创建链接时,用于说明链接哪个文件,指定链接源
-
force:值为yes表示强制创建
-
owner:指定被操作文件的属主
-
group:指定被操作文件的属组
-
mode:指定被操作文件的权限
测试成功,直接删除-C来执行,然后去查看是否创建成功
2、lineinfile
修改文件某一行文本
常用参数:
-
path:操作的文件路径
-
regexp:正则表达式,要替换的内容
-
line:指定修改后的文本内容
-
state:当设置为absent代表删除匹配的行
-
backrefs:默认为no,当未匹配到时line对应的内容会被插入到文本的末尾,为yes表示不插入
-
insertafter:借助insertafter参数可以将文本插入到“指定的行”之后
-
insertbefore:借助insertbefore参数可以将文本插入到“指定的行”之前
-
backup:是否进行备份
-
create:操作的文件不存在时是否创建
示例:
先在文本中加入内容
在文本中加入tags来进行单个步骤
将所有line开头的行替换
3、replace
根据正则表达式替换文件内容
常用参数:
-
path:操作文件的路径
-
regexp:正则表达式
-
replace:指定最终要替换的字符串
-
backup:是否在修改文件之前对文件进行备份,yes是进行备份
简单示例:
4、shell
执行脚本命令
5、debug
用于在调试中输出信息
6、template/copy
模板作用类似于copy,可将文件分发到不同节点上,可以在模板文件中引入变量
常用参数:
-
src: 源文件路径
-
dest:目标文件路径
-
group:目标文件属组
-
mode:目标文件权限
-
backup:如果目标文件存在,则先备份目标文件
-
force:是否强制覆盖,默认为yes
-
validate:在复制之前通过命令验证目标文件,如果验证通过则复制
7、fetch
功能与copy类似,但是是用于从远程主机中拷贝文件到管理主机,不能拷贝目录
常用参数:
-
src:从远程主机上获取的文件路径
-
dest:保存文件的目录
-
flat:是否校准源文件与获取文件是否一致,默认false
8、unarchive
解压缩,将压缩文件解压分发到不同节点上,对zip压缩不友好,尽量使用tar归档压缩(必须是压缩文件目标)
常用参数:
-
src: 源文件路径
-
dest: 目标文件路径
-
mode:目标文件权限
9、wait_for
等待某些操作完成以后再进行后续操作
常用参数:
-
connect_timeout:在下一个任务执行之前等待连接的超时时间
-
delay:指在等待过程中轮询的时间间隔
-
host:等待的主机地址,默认是127.0.0.1
-
port:等待的主机端口
-
path:文件路径,只有当这个文件存在时才开始执行下一个任务
-
state:等待的状态,值可以为started/stoped/absent/present
-
timeout:等待的超时时间,默认300秒
当8080端口未开启时,检测将一直进行,此时将nginx端口改为8080并重启,打开8080端口
监测发现8080端口开启,此时进行下一个命令
10、yum
软件包管理
常用参数:
name:指定需要管理的软件包名
state:参数非常灵活,可以包含的值及含义如下
present:确保软件包已经安装了
installed:安装
latest:安装最新的软件包
removed/absent:删除对应的软件包
disable_gpg_check:用于禁用对rpm包公钥验证,默认值no表示不禁用验证,yes表示禁用验证
11、when
条件判断,满足后再执行任务
12、register
用于注册一个变量,保存命令的结果,常与when结合使用
13、pause
暂停一定时间
常用参数:
-
minutes:暂停多少分钟
-
seconds:暂停多少秒
-
prompt:打印一串信息提示用户操作
14、ignore_errors
忽略错误的命令,通常情况下当出现执行失败时ansible会停止运行,有时候你会想要继续执行下去就需要添加这个
15、cron
定时任务,功能相当于crontab
常用参数:
name:任务名称
minute:分, hour:时,day:天,month:月,weekday:周
job:指定要执行的命令或者脚本
state:当设置为absent代表删除任务
七.Ansible变量
1、Ansible 变量介绍
在 PlayBook中,将 PlayBook 类比成了 linux 中的shell。那么它作为一门 ansible 特殊的语言,肯定要涉及到变量定义、控制结构的使用等特性。
2、变量命名规则
-
变量的名字由 字母、下划线和数字组成,必须以字母开头
-
保留关键字不能作为变量名称
3、变量类型
根据变量的作用范围大体的将变量分为:
-
全局变量
-
剧本变量
-
资产变量
-
Facts 变量
-
注册变量
3.1、全局变量
全局变量,是我们使用ansible 或使用 ansible-playbook 时,手动通过 -e 参数传递给 ansible 的变量。
通过 ansible 或者 ansible-playbook 的 help 帮助,可以获取具体格式使用方式:
#传递一个YAML/JSON 的形式(注意不管YAML 还是 JSON,它们的最终格式一定要是一个字典)
3.2、剧本变量
这种变量和playbook 有关,定义在 playbook 中的,它的定义方式有多种。这里介绍两种最常见的定义方式。
通过 Playbook 属性 vars 定义
通过 Play 属性 vars_files 定义
#当通过vars属性定义的变量很多时,这个 Play 就会感觉特别臃肿,此时我们可将变量单独从play中抽离出来
#形成单独的 yaml 文件
---
- name: test play vars
hosts: all
vars_files:
- vars/users.yml
如何在 playbook 中使用这些变量
在playbook中使用变量时,使用 {{ 变量名 }} 来使用变量
验证是否创建成功
name: {{ user }} 是一个字典的开始。因此,加针对变量的使用,加上了双引号,避免 ansible 错误解析。
3.3、资产变量
资产变量分为主机变量和主机组变量,分别针对资产中的单个主机和主机组。
主机变量
以下资产中,定义了一个主机变量britz,此变量只针对 192.168.71.193 这台服务器有效。
此时193有定义,195没有所以报错,可用使用全局变量使其有数值
主机组变量
以下资产中,定义了一个组变量home,此变量将针对 web_servers 这个主机组中的所有服务器有效
发现此时有定义的193仍显示自己的key,而195没有则会显示主机组的key
变量的继承
此时全部继承了children组的key
3.4、Facts 变量
facts 变量不包含在之前介绍的全局变量、剧本变量及资产变量之内。
facts 变量不需要我们人为去声明变量名及赋值。它的声明和赋值完全有 ansible 中的 setup 模块帮助我们完成。
它收集了有关被管理服务器的操作系统版本、服务器IP地址、主机名,磁盘的使用情况、cpu个数、内存条大小等等有关被管理服务器的私有信息。
在每次playbook运行的时候都会发现在 Playbook 执行一个 Gathering Facts的过程。这个过程就是收集被管理服务器的facts信息过程。
手动收集 Facts 变量
过滤 Facts
通过刚刚的手动收集 Facts,我们发现facts 信息量很大。因此,可以有针对性的显示我们想要的信息。
可以通过使用facts 模块中filter 参数去过滤我们想要的信息。
-
仅获取服务器的内存信息情况
3.5、注册变量
用于保存一个 task 任务的执行结果,以便于 debug 时使用,或者将此次 task 任务的结果作为条件,去判断是否执行其他 task 任务。
注册变量在playbook中通过 register 关键字 去实现。
vim playbook.yml
cat playbook.yml
---
- name: install a package and print the result
hosts: web_servers
remote_user: root
tasks:
- name: install nginx package
yum: name=nginx state=present
register: install_result
- name: print result
debug:
msg: "{{ install_result }}"
...
ansible-playbook -i hosts playbook.yml
PLAY [install a package and print the result] *********************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [192.168.71.193]
TASK [install nginx package] **************************************************************************
ok: [192.168.71.193]
TASK [print result] ***********************************************************************************
ok: [192.168.71.193] => {
"install_result": {
"changed": false,
"failed": false,
"msg": "",
"rc": 0,
"results": [
"1:nginx-1.20.1-10.el7.x86_64 providing nginx is already installed"
]
}
}
PLAY RECAP ********************************************************************************************
192.168.115.183 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4、变量优先级
变量优先级结论
当一个变量同时在全局变量、剧本变量 和 资产变量中定义时,优先级最高的是全局变量;其次是剧本变量;最后才是资产变量。
八.loop循环语句
loop关键字说明
在playbook中使用循环,直接使用loop关键字即可。
如下示例,下载多个服务:
九.条件语句
简介
在有的时候play的结果依赖于变量、fact或者是前一个任务的执行结果,或者有的时候,我们会基于上一个task执行返回的结果而决定如何执行后续的task。这个时候就需要用到条件判断。
条件语句在Ansible中的使用场景:
-
在目标主机上定义了一个硬限制,比如目标主机的最小内存必须达到多少,才能执行该task
-
捕获一个命令的输出,根据命令输出结果的不同以触发不同的task
-
根据不同目标主机的facts,以定义不同的task
-
根据目标机的cpu的大小,以调优相关应用性能
-
用于判断某个服务的配置文件是否发生变更,以确定是否需要重启服务
when关键字
1. when基本使用
在ansible中,使用条件判断的关键字就是when。
如在安装包的时候,需要指定主机的操作系统类型,或者是当操作系统的硬盘满了之后,需要清空文件等,可以使用when语句来做判断 。when关键字后面跟着的是python的表达式,在表达式中你能够使用任何的变量或者fact,当表达式的结果返回的是false,便会跳过本次的任务
下面是一个基本的用法示例:
---
- name: Install vim
hosts: all
tasks:
- name: Install VIM via yum
yum:
name: vsftpd
state: installed
when: ansible_os_family =="RedHat"
- name: Install VIM via apt
apt:
name: proftpd
state: present
when: ansible_os_family =="Debian"
- name: Unexpected OS family
debug: msg="OS Family {{ ansible_os_family }} is not supported" fail=yes
when: not (ansible_os_family =="RedHat" or ansible_os_family =="Debian")present
2. 比较运算符
在上面的示例当中,我们使用了"=="的比较运算符,在ansible中,还支持如下比较运算符:
-
==
:比较两个对象是否相等,相等则返回真。可用于比较字符串和数字 -
!=
:比较两个对象是否不等,不等则为真。 -
>
:比较两个对象的大小,左边的值大于右边的值,则为真 -
<
:比较两个对象的大小,左边的值小于右边的值,则为真 -
>=
:比较两个对象的大小,左边的值大于等于右边的值,则为真 -
<=
:比较两个对象的大小,左边的值小于等于右边的值,则为真
when: ansible_machine == "x86_64"
when: max_memory <= 512
3. 逻辑运算符
在Ansible中,除了比较运算符,还支持逻辑运算符:
-
and:逻辑与,当左边和右边两个表达式同时为真,则返回真
-
or:逻辑或,当左右和右边两个表达式任意一个为真,则返回真
-
not:逻辑否,对表达式取反
-
():当一组表达式组合在一起,形成一个更大的表达式,组合内的所有表达式都是逻辑与的关系
# 逻辑或
when: ansible_distribution == "RedHat" or ansible_distribution == "Fedora"# 逻辑与
when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0-327.el7.x86_64"when:
- ansible_distribution_version == "7.5"
- ansible_kernel == "3.10.0-327.el7.x86_64"
# 组合when: =>
( ansible_distribution == "RedHat" and ansible_distribution_major_version == "7" )
or
( ansible_distribution == "Fedora" and ansible_distribution_major_version == "28")
条件判断与tests
在shell当中,我们可使用test命令来进行一些常用的判断操作,如下:
# 判断/test文件是否存在
test -e /test# 判断/testdir是否存在且为一个目录
test -d /testdir
事实上,在ansible中也有类似的用法,只不过ansible没有使用linux的test命令,而是jinja2模板的tests。
# 通过条件语句判断testpath的路径是否存在
- hosts: test
vars:
testpath: /testdir
tasks:
- debug:
msg: "file exist"
when: testpath is exists
判断变量
-
defined:判断变量是否已定义,已定义则返回真
-
undefined:判断变量是否未定义,未定义则返回真
-
none:判断变量的值是否为空,如果变量已定义且值为空,则返回真
判断执行结果
-
sucess或succeeded:通过任务执行结果返回的信息判断任务的执行状态,任务执行成功则返回true
-
failure或failed:任务执行失败则返回true
-
change或changed:任务执行状态为changed则返回true
-
skip或skipped:任务被跳过则返回true
判断路径
-
file:判断指定路径是否为一个文件,是则为真
-
directory:判断指定路径是否为一个目录,是则为真
-
link:判断指定路径是否为一个软链接,是则为真
-
mount:判断指定路径是否为一个挂载点,是则为真
-
exists:判断指定路径是否存在,存在则为真
特别注意:关于路径的所有判断均是判断主控端上的路径,而非被控端上的路径
判断字符串
-
lower:判断字符串中的所有字母是否都是小写,是则为真
-
upper:判断字符串中的所有字母是否都是大写,是则为真
判断整除
-
even:判断数值是否为偶数,是则为真
-
odd:判断数值是否为奇数,是则为真
-
divisibleby(num):判断是否可以整除指定的数值,是则为真
其他tests
-
version
可用于对比两个版本号的大小,或者与指定的版本号进行对比,使用语法为version("版本号","比较操作符")
-
version中使用的比较运算符说明:
-
大于: >, gt
-
大于等于: >=, ge
-
小于: <, lt
-
小于等于: <=, le
-
等于: =, ==, eq
-
不等于: !=, <>, ne
-
-
subset
判断一个list是不是另一个list的子集
-
superset
判断一个list是不是另一个list的父集"
- in
判断一个字符串是否存在于另一个字符串中,也可用于判断某个特定的值是否存在于列表中
-
string
判断对象是否为一个字符串,是则为真
-
number
判断对象是否为一个数字,是则为真
条件判断与block
block
我们在前面使用when做条件判断时,如果条件成立则执行对应的任务。但这就面临一个问题,当我们要使用同一个条件判断执行多个任务的时候,就意味着我们要在某一个任务下面都写一下when语句,而且判断条件完全一样。这种方式不仅麻烦而且显得low。Ansible提供了一种更好的方式来解决这个问题,即block。
- hosts: all
tasks:
- debug:
msg: "task1 not in block"
- block:
- debug:
msg: "task2 in block1"
- debug:
msg: "task3 in block1"
when: 2 > 1
使用block注意事项:
-
可以为block定义name(ansible 2.3增加的特性)
-
可以直接对block使用when,但不能直接对block使用loop
rescue
block除了能和when一起使用之外,还能作错误处理。这个时候就需要用到rescue关键字:
- hosts: test
tasks:
- block:
- shell: 'ls /testdir'
rescue:
- debug:
msg: '/testdir is not exists'
在上面的例子中,当block中的任务执行失败时,则运行rescue中的任务。如果block中的任务正常执行,则rescue的任务就不会被执行。如果block中有多个任务,则任何一个任务执行失败,都会执行rescue。block中可以定义多个任务,同样rescue当中也可以定义多个任务。
always
当block执行失败时,rescue中的任务才会被执行;而无论block执行成功还是失败,always中的任务都会被执行:
- hosts: test
tasks:
- block:
- shell: 'ls /testdir'
rescue:
- debug:
msg: '/testdir is not exists'
always:
- debug:
msg: 'This task always executes'
条件判断与错误处理
在上面讲block的使用方法的时候,我们说block除了可以将多个任务组合到一起,还有错误处理的功能。接下来我们继续说一说错误处理。
fail模块
在shell中,可能会有这样的需求:当脚本执行至某个阶段时,需要对某个条件进行判断,如果条件成立,则立即终止脚本的运行。在shell中,可以直接调用"exit"即可执行退出。事实上,在playbook中也有类似的模块可以做这件事。即fail模块。
fail模块用于终止当前playbook的执行,通常与条件语句组合使用,当满足条件时,终止当前play的运行。
选项只有一个:
-
msg:终止前打印出信息
# 使用fail模块中断playbook输出
- hosts: test
tasks:
- shell: echo "Just a test--error"
register: result
- fail:
msg: "Conditions established,Interrupt running playbook"
when: "'error' in result.stdout"
- debug:
msg: "Inever execute,Because the playbook has stopped"
failed_when
事实上,当fail和when组合使用的时候,还有一个更简单的写法,即failed_when
,当满足某个条件时,ansible主动触发失败。
# 如果在command_result存在错误输出,且错误输出中,包含了`FAILED`字串,即返回失败状态:
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"
也可以直接通过fail
模块和when
条件语句,写成如下:
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result
ignore_errors: True
- name: fail the play if the previous command did not succeed
fail: msg="the command failed"
when: " command_result.stderr and 'FAILED' in command_result.stderr"
ansible一旦执行返回失败,后续操作就会中止,所以failed_when通常可以用于满足某种条件时主动中止playbook运行的一种方式。
ansible默认处理错误的机制是遇到错误就停止执行。但有些时候,有些错误是计划之中的。我们希望忽略这些错误,以让playbook继续往下执行。这个时候就可以使用ignore_errors
忽略错误,从而让playbook继续往下执行。
changed_when
当我们控制一些远程主机执行某些任务时,当任务在远程主机上成功执行,状态发生更改时,会返回changed状态响应,状态未发生更改时,会返回OK状态响应,当任务被跳过时,会返回skipped状态响应。我们可以通过changed_when
来手动更改changed
响应状态。示例如下:
- shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2" #只有该条task执行以后,bass_result.rc的值不为2时,才会返回changed状态
# this will never report 'changed' status
- shell: wall 'beep'
changed_when: False #当changed_when为false时,该条task在执行以后,永远不会返回changed状态
十.高级用法
本地执行
如果希望在控制主机本地运行一个特定的任务,可以使用local_action语句。
假设我们需要配置的远程主机刚刚启动,如果我们直接运行playbook,可能会因为sshd服务尚未开始监听而导致失败,我们可以在控制主机上使用如下示例来等待被控端sshd端口监听:
- name: wait for ssh server to be running
wait_for:
port: 22
host: "{{ inventory_hostname }}"
search_regex: OpenSSH
connection: local
任务委托
在有些时候,我们希望运行与选定的主机或主机组相关联的task,但是这个task又不需要在选定的主机或主机组上执行,而需要在另一台服务器上执行。
这种特性适用于以下场景:
-
在告警系统中启用基于主机的告警
-
向负载均衡器中添加或移除一台主机
-
在dns上添加或修改针对某个主机的解析
-
在存储节点上创建一个存储以用于主机挂载
-
使用一个外部程序来检测主机上的服务是否正常
可以使用delegate_to语句来在另一台主机上运行task:
- name: enable alerts for web servers
hosts: webservers
tasks:
- name: enable alerts
nagios: action=enable_alerts service=web host="{{ inventory_hostname }}"
delegate_to: nagios.example.com
如果delegate_to: 127.0.0.1的时候,等价于local_action
任务暂停
有些情况下,一些任务的运行需要等待一些状态的恢复,比如某一台主机或者应用刚刚重启,我们需要需要等待它上面的某个端口开启,此时就需要将正在运行的任务暂停,直到其状态满足要求。
Ansible提供了wait_for模块以实现任务暂停的需求
wait_for模块常用参数:
-
connect_timeout:在下一个任务执行之前等待连接的超时时间
-
delay:等待一个端口或者文件或者连接到指定的状态时,默认超时时间为300秒,在这等待的300s的时间里,wait_for模块会一直轮询指定的对象是否到达指定的状态,delay即为多长时间轮询一次状态。
-
host:wait_for模块等待的主机的地址,默认为127.0.0.1
-
port:wait_for模块待待的主机的端口
-
path:文件路径,只有当这个文件存在时,下一任务才开始执行,即等待该文件创建完成
-
state:等待的状态,即等待的文件或端口或者连接状态达到指定的状态时,下一个任务开始执行。当等的对象为端口时,状态有started,stoped,即端口已经监听或者端口已经关闭;当等待的对象为文件时,状态有present或者started,absent,即文件已创建或者删除;当等待的对象为一个连接时,状态有drained,即连接已建立。默认为started
-
timeout:wait_for的等待的超时时间,默认为300秒
#等待8080端口已正常监听,才开始下一个任务,直到超时
- wait_for:
port: 8080
state: started
#等待8000端口正常监听,每隔10s检查一次,直至等待超时
- wait_for:
port: 8000
delay: 10
#等待8000端口直至有连接建立
- wait_for:
host: 0.0.0.0
port: 8000
delay: 10
state: drained
#等待8000端口有连接建立,如果连接来自10.2.1.2或者10.2.1.3,则忽略。
- wait_for:
host: 0.0.0.0
port: 8000
state: drained
exclude_hosts: 10.2.1.2,10.2.1.3
#等待/tmp/foo文件已创建
- wait_for:
path: /tmp/foo
#等待/tmp/foo文件已创建,而且该文件中需要包含completed字符串
- wait_for:
path: /tmp/foo
search_regex: completed
#等待/var/lock/file.lock被删除
- wait_for:
path: /var/lock/file.lock
state: absent
#等待指定的进程被销毁
- wait_for:
path: /proc/3466/status
state: absent
#等待openssh启动,10s检查一次
- wait_for:
port: 22
host: "{{ ansible_ssh_host | default(inventory_hostname) }}" search_regex: OpenSSH
delay: 10
滚动执行
默认情况下,ansible会并行的在所有选定的主机或主机组上执行每一个task,但有的时候,我们会希望能够逐台运行。最典型的例子就是对负载均衡器后面的应用服务器进行更新时。通常来讲,我们会将应用服务器逐台从负载均衡器上摘除,更新,然后再添加回去。我们可以在play中使用serial语句来告诉ansible限制并行执行play的主机数量。
下面是一个在amazon EC2的负载均衡器中移除主机,更新软件包,再添加回负载均衡的配置示例:
- name: upgrade pkgs on servers behind load balancer
hosts: myhosts
serial: 1
tasks:
- name: get the ec2 instance id and elastic load balancer id
ec2_facts:
- name: take the host out of the elastic load balancer id
local_action: ec2_elb
args:
instance_id: "{{ ansible_ec2_instance_id }}"
state: absent
- name: upgrade pkgs
apt:
update_cache: yes
upgrade: yes
- name: put the host back n the elastic load balancer
local_action: ec2_elb
args:
instance_id: "{{ ansible_ec2_instance_id }}"
state: present
ec2_elbs: "{{ items }}"
with_items: ec2_elbs
在上述示例中,serial的值为1,即表示在某一个时间段内,play只在一台主机上执行。如果为2,则同时有2台主机运行play。
只执行一次
某些时候,我们希望某个task只执行一次,即使它被绑定到了多个主机上。例如在一个负载均衡器后面有多台应用服务器,我们希望执行一个数据库迁移,只需要在一个应用服务器上执行操作即可。
可以使用run_once语句来处理:
- name: run the database migrateions
command: /opt/run_migrateions
run_once: true
设置环境变量
我们在命令行下执行某些命令的时候,这些命令可能会需要依赖环境变量。比如在安装某些包的时候,可能需要通过代理才能完成完装。或者某个脚本可能需要调用某个环境变量才能完成运行。
ansible 支持通过environment
关键字来定义一些环境变量。
在如下场景中可能需要用到环境变量:
-
运行shell的时候,需要设置path变量
-
需要加载一些库,这些库不在系统的标准库路径当中
---
- name: upload a remote file to aws s3
hosts: test
tasks:
- name: install pip
yum:
name: python-pip
state: installed
- name: install the aws tools
pip:
name: awscli
state: present
- name: upload file to s3
shell aws s3 put-object --bucket=my-test-bucket --key={{ ansible_hostname }}/fstab --body=/etc/fstab --region=eu-west-1
environment:
AWS_ACCESS_KEY_ID: xxxxxx
AWS_SECRET_ACCESS_KEY: xxxxxx
交互式提示
在少数情况下,ansible任务运行的过程中需要用户输入一些数据,这些数据要么比较秘密不方便,或者数据是动态的,不同的用户有不同的需求,比如输入用户自己的账户和密码或者输入不同的版本号会触发不同的后续操作等。ansible的vars_prompt关键字就是用来处理上述这种与用户交互的情况的。
- hosts: all
remote_user: root
vars_prompt:
- name: share_user
prompt: "what is your network username?"
private: yes
- name: share_pass
prompt: "what is your network password"
private: yes
tasks:
- debug:
var: share_user
- debug:
var: share_pass
vars_prompt常用选项说明:
-
private: 默认为yes,表示用户输入的值在命令行不可见
-
default:定义默认值,当用户未输入时则使用默认值
-
confirm:如果设置为yes,则会要求用户输入两次,适合输入密码的情况
十一.Roles角色(重要)
概述
角色(roles)是ansible自1.2版本开始引入的新特性,用于层次性,结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单的说,roles就是通过分别将变量、文件、任务、模块及处理器放置于单独的目录中、并可以便捷地include他们的一种机制。角色一般用于基于主机构建服务的场景中、但也可以是用于构建守护进程等场景中。
目录介绍
目录 | 作用 |
---|---|
tasks目录 | 角色需要执行的主任务文件放置在此目录中,默认的主任务文件名为main.yml,当调用角色时,默认会执行main.yml文件中的任务,你也可以将其他需要执行的任务文件通过include的方式包含在tasks/main.yml文件中。 |
handlers目录 | 当角色需要调用handlers时,默认会在此目录中的main.yml文件中查找对应的handler |
defaults目录 | 角色会使用到的变量可以写入到此目录中的main.yml文件中,通常,defaults/main.yml文件中的变量都用于设置默认值,以便在你没有设置对应变量值时,变量有默认的值可以使用,定义在defaults/main.yml文件中的变量的优先级是最低的。 |
vars目录 | 角色会使用到的变量可以写入到此目录中的main.yml文件中,看到这里你肯定会有疑问,vars/main.yml文件和defaults/main.yml文件的区别在哪里呢?区别就是,defaults/main.yml文件中的变量的优先级是最低的,而vars/main.yml文件中的变量的优先级非常高,如果你只是想提供一个默认的配置,那么你可以把对应的变量定义在defaults/main.yml中,如果你想要确保别人在调用角色时,使用的值就是你指定的值,则可以将变量定义在vars/main.yml中,因为定义在vars/main.yml文件中的变量的优先级非常高,所以其值比较难以覆盖。 |
meta目录 | 如果你想要赋予这个角色一些元数据,则可以将元数据写入到meta/main.yml文件中,这些元数据用于描述角色的相关属性,比如 作者信息、角色主要作用等等,你也可以在meta/main.yml文件中定义这个角色依赖于哪些其他角色,或者改变角色的默认调用设定,在之后会有一些实际的示例,此处不用纠结。 |
templates目录 | 角色相关的模板文件可以放置在此目录中,当使用角色相关的模板时,如果没有指定路径,会默认从此目录中查找对应名称的模板文件。 |
files目录 | 角色可能会用到的一些其他文件可以放置在此目录中,比如,当你定义nginx角色时,需要配置https,那么相关的证书文件即可放置在此目录中。 |
1.Ansible-galaxy角色管理工具
Ansible-galaxy简介
Ansible Galaxy 是一个Ansible内容公共资源库,这些内容由许许多多Ansible管理员和用户编写。它包含数千个Ansible角色,具有可搜索的数据库,可帮助Ansible用户确定或许有助于他们完成管理任务的角色。Ansible Galaxy含有面向新的Ansible用户和角色开发人员的文档和视频链接。
获取Ansible-Galaxy帮助
通过Ansible Galaxy网站主页上的Documenttaion标签,可以进入描述如何使用Ansible Galaxy的页面。其中包含了介绍如何从Ansible Galaxy下载和使用角色的内容。该页面也提供关于如何开发角色并上传到Ansible Galaxy的说明。
通过Ansible Galaxy网站主页上的Documenttaion标签,可以进入描述如何使用Ansible Galaxy的页面。其中包含了介绍如何从Ansible Galaxy下载和使用角色的内容。该页面也提供关于如何开发角色并上传到Ansible Galaxy的说明。
ansible-galaxy命令
语法:
ansible-galaxy [选项] 命令
选项:
选项(不能用) | 作用 |
---|---|
-p 或 --roles-path | 指定角色安装的路径。 |
-r 或 --role-repo | 指定角色仓库的 URL。 |
-f 或 --force | 强制重新安装角色。 |
-t 或 --skip-install | 跳过角色安装,只下载角色文件。 -c 或 --collections-path |
--ignore-errors | 在安装过程中忽略错误。 |
--debug | 启用调试输出。 |
--module-name | 为 import-playbook 命令指定模块名称。 |
--role-name | 为 import-playbook 命令指定角色名称。 |
命令:
命令 | 作用 |
---|---|
install | 安装角色。 |
list | 列出已安装的角色。 |
remove | 删除已安装的角色。 |
import | 导入角色。 |
init | 初始化一个新角色。 |
update | 更新已安装的角色。 |
import-playbook | 从角色导入 Playbook。 |
search | 搜索角色 |
info | 显示角色信息 |
二、roles编写步骤
1、编写roles 的步骤为:
-
创建roles的项目目录。
-
编写roles的功能,也就是tasks;
-
最后playbook引用roles编写好的tasks;
1、先创建roles_test 项目目录结构,并创建下面的子目录,最基本的子目录需要有tasks目录。
[root@ansible ansible]# cd roles/
[root@ansible roles]# mkdir roles_test
[root@ansible roles]# cd roles_test/
[root@ansible roles_test]# mkdir tasks
[root@ansible roles_test]# cd tasks/
[root@ansible tasks]# pwd
/etc/ansible/roles/roles_test/tasks
#2、然后在tasks中编写相关的任务,在tasks中必须有一个 main.yaml 文件。可以把编写的任务直接写到 main.yaml 文件中,也可以写到其他文件中,然后在main.yaml中通过 include 的方式引用这些文件。在 tasks 中不需要写 play 部分,只写tasks部分就行。
#------或者----
[root@ansible tasks]# vim test.yaml
- name: test roles
debug:
msg: "this is test roles"
[root@ansible tasks]# vim main.yaml
- include: test.yaml
#3、写好之后 编写playbook调用roles,playbook剧本必须要与roles角色在同一目录
---
- hosts: web
remote_user: root
roles:
- role: roles_test
#4、测试
2、roles的调用
在playbook调用roles时,如何找到roles所在的目录呢?有以下几种方式:
- playbook文件会在同级目录中寻找与调用的角色同名的roles,例如在上例中,roletest.yaml这个playbook文件调用了roles_test这个角色,会自动在同级目录中寻找roles_test这个目录,并执行其中的tasks。
- playbook文件也会寻找同级目录中的roles 目录,执行roles目录中同名的角色项目
- 也可以修改ansible配置文件中定义的roles的路径,多个路径之间使用冒号 :隔开
roles_path = /etc/ansible/roles:/data/ansible/roles:/opt
- 在playbook文件中,直接写roles的绝对路径也可以调用
---
- hosts: web
remote_user: root
roles:
- role: /etc/ansible/roles/roles_test
3、roles中使用变量
修改roles文件,引入变量
在调用这个角色时,则需要传入对应的变量,否则就会报错,调用上例角色的示例如下:
#3、也可以为变量设置默认值,这样即使在调用角色时没有传入任何参数,也有默认的值可以使用,同时也不会在调用时因为没有传入对应变量而报错,需要在roles_test目录中创建一个defaults目录/vars 目录,并且创建defaults/main.yml文件/vars/main.yaml文件,文件内容如下:
[root@ansible roles_test]# mkdir vars
[root@ansible roles_test]# cd vars/
[root@ansible vars]# vim main.yaml
var: test abc
#4、然后在运行playbook,结果一样
[root@ansible ansible]# ansible-playbook roletest.yaml
ok: [192.168.115.112] => {
"msg": "test abc"
}
#结论:变量的优先级
#ansible-playbook > vars/main.yaml > 其他变量定义的方式
4、多次调用同一个roles
默认情况下,我们无法多次调用同一个角色,也就是说,如下写两遍playbook只会调用一测role角色:
- hosts: web
remote_user: root
roles:
- role: roles_test
- role: roles_test
设置角色的 allow_duplicates 属性,让其支持重复使用
#2、调用角色时,传入不同的参数
#先把vars 文件中的main.yaml 定义的变量注释掉
#然后在playbook中添加变量
5、roles管理模板文件
前文中提到,roles中把所有的模板文件放置到templates 目录中,我么来测试下:
1、新增template 目录,建立模板文件
something in template;
{{ template_var }}
#3、变量文件如下:
var: test abc
template_var: tempvar
#4、playbook文件如下:
---
- hosts: web
remote_user: root
roles:
- role: roles_test
[root@ansible ansible]# ansible-playbook roletest.yaml
TASK [roles_test : test roles] **************************************************************************
ok: [192.168.71.112] => {
"msg": "test abc"
}
TASK [roles_test : copy template] ***********************************************************************
changed: [192.168.71.112]
6、roles管理handlers文件
如果想要在角色中使用一些handlers 以便进行触发,则可以直接将对应的handlers 任务写到 handlers/main.yaml文件中
#1、创建handlers 目录
[root@ansible roles_test]# mkdir handlers
[root@ansible handlers]# vim main.yaml
- name: test_handlers
debug:
msg: "this is a test handler"
#2、编辑tasks任务
[root@ansible tasks]# vim main.yaml
- name: test roles
debug:
msg: "{{ var }}"
changed_when: true
notify: test_handlers
#3、把之前测试template 的变量注释掉
#4、运行playbook文件
[root@ansible ansible]# ansible-playbook -C roletest.yaml
TASK [roles_test : test roles] *******************************************************************************
changed: [192.168.71.112] => {
"msg": "test abc"
}
RUNNING HANDLER [roles_test : test_handlers] ********************************************************************************
ok: [192.168.71.112] => {
"msg": "this is a test handler"
}
7、roles 的依赖关系
roles 允许在使用时自动引入其他roles,role 依赖关系存储在meta/main.yaml文件中
示例:
#安装WordPress 项目时:
#1、需要先确保 nginx 与 php-fpm的role 都能正常运行;
#2、然后在WordPress的role中定义,依赖关系;
#3、依赖的role 有nginx 以及 php-fpm;
#4、 在WordPress的role中新建meta 目录,编写main.yaml文件
[root@ansible meta]# vim main.yaml
dependencies:
- { role: nginx }
- { role: php-fpm }
#5、在WordPress的playbook中就不需要在安装 nginx 和php-fpm了,只写一个 WordPress就可以了,安装WordPress之前会自动先部署 nginx和php-fpm
[root@ansible ansible]# vim roletest.yaml
---
- hosts: web
remote_user: root
roles:
- role: WordPress
8、roles 的相互调用
roles 中的各项目之间的文件是可以跨项目相互调用的。跨项目调用时,需要指明要调用的role的名称。
#要同时部署nginx 和apache 两个项目,这俩项目使用了相同的index.html主页文件,这个主页文件已经在nginx项目中定义过了,若此时需要在Apache项目中引用,使用如下方式
[root@localhost /data/roles]$vim apache/tasks/html.yml #<==页面配置
- name: Copy Index File
copy:
src:roles/nginx/files/index.html #跨项目调用路径要写roles的名称
dest: /var/www/nginx/html/
十二.总结
本节内容补充了ansbile中有关playbook的内容,方便之后使用这些命令对多台主机进行操作。
本次更新了playbook的剩余内容,包括变量,判断等。其中最重要的是roles角色,这个在之后会经常使用,要多练习掌握这个roles的使用。
更多推荐
所有评论(0)