Playbook部分内容参考自:
Wiki《YAML》:https://zh.wikipedia.org/wiki/YAML
菜鸟教程《YAML入门教程》:https://www.runoob.com/w3cnote/yaml-intro.html
阮一峰的网络日志《YAML语言教程》:https://www.ruanyifeng.com/blog/2016/07/yaml.html
一、Ansible简述
Ansible是一款Linux/Unix上基于Python研发的自动化工具,可以快速批量操作多台主机,也是热度极高的自动化运维工具。
它具有以下特点:
1.1、Agentess
基于SSH连接,因此不需要在受控机上安装agent也可以实现批量操作多台主机。
1.2、模块化管理
自带的功能模块众多,搭配使用Playbook可以实现非常强大的功能。
1.3、幂等性
即任务执行一次和执行多次结果是一样,不会因为重复执行而带来意外。比如说多次执行安装同一款程序,会自动检测,如已安装会被跳过。
1.4、Ansible架构
二、Ansible基本使用
本次学习系统环境为Manjaro21(ArchLinux系列桌面发行版)、ansible版本为3.3.0-2,受控机为Centos7.9;因为Ansible是基于Python研发,因此你的系统上必须要有Python 2.7+或 3+版本。
OS:5.10.34-1-MANJARO
ansible:3.3.0-2
Python:3.9.4
2.1、ansible语法
ansible HOST-PATTERN -m MOD_NAME -a MOD_ARGS -f FORKS -C -u USERNAME -c CONNECTION
2.2、ansible常用选项
先来看看ansible常用选项:
-C, --check # 仅测试,并不产生实际改变。
-f, # 单次操作主机数量,太多会卡。
-i, # 指定主机清单,默认为/etc/ansible/hosts,需要手动创建文件。
--list-hots # 列出当前可用主机
--syntax-check, # 检查指定的playbook语法
-t, # 日志输出位置
ansible-doc -l,列出所有模块
2.3、ansible远程控制前戏
建议使用密钥方式连接主机,以下仅为本地环境有用。因为在云服务器上,我们创建机器前就可以选择添加密钥进去了,不需要下面这些操作。
指定root连接
我们指定使用root来连接每台主机,这需要修改ansible的配置文件。
sudo sed -i 's/#remote_user = root/remote_user = root/g' /etc/ansible/ansible.cfg
生成密钥
[sanxi@sanxi-aero15sa ~]$ ssh-keygen -t rsa -P ''
Generating public/private rsa key pair.
Enter file in which to save the key (/home/sanxi/.ssh/id_rsa):
Created directory '/home/sanxi/.ssh'.
Your identification has been saved in /home/sanxi/.ssh/id_rsa
Your public key has been saved in /home/sanxi/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:6yGMH5GEOg8pr5U4lRVNBAz6QGkIT4O4mTa+0XZGsa4 sanxi@sanxi-aero15sa
The key's randomart image is:
+---[RSA 3072]----+
|=o=oo=o |
|=* ..+. |
|++. o + |
|+= = + . |
|+ @ o o S |
| B B * . . |
|o B * + o |
| = E . + . |
|. . . |
+----[SHA256]-----+
安装except
sudo pacman -S expect
创建主机清单
cat << EOF >> host.txt
> 192.168.56.101
> 192.168.56.102
> EOF
创建批量添加密钥脚本
此脚本需要传递两个位置参数,第一个是主机清单,第二个是公钥,公钥这里也可以直接写死。清单的格式最好是txt,必须是一行一个IP地址,没有任何多余信息。
#!/bin/bash
SSH_KEY_PATH=$2
USER='root'
PASSWD='sanxi666.'
PORT=22
cat $1 | while read ip;do # 传递清单文件,开启while循环,并读取一行文本赋予给变量ip
expect << EOF # 开启自动交互
spawn ssh-copy-id -i $SSH_KEY_PATH -p $PORT -f $USER@$ip # 传递密钥至远程主机
expect { # # 第一次问yes/no,第二次是要输入密码。
"yes/no" { send "yes\n"; exp_continue } # 捕获到"yes/no",则自动回答yes并回车,exp_continue继续。
"password" { send "$PASSWD\n" } # 捕获到"password",则自动输入密码并回车。
}
expect eof # 结束捕获文本
EOF # 结束expect
done # 清单文件读取完毕,结束循环。
2.4、远程操作多台主机
[sanxi@sanxi-aero15sa ~]$ ansible all -m ping
192.168.56.102 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.56.101 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
三、ansible常用模块
查看当前可用模块
ansible-doc -l
3.1、user用户管理
用于操作目标主机用户组,也可以跟useradd一样跟很多选项,用得最多的也就是添加用户。
[sanxi@sanxi-aero15sa ~]$ ansible all -m user -a "name=sanxi"
192.168.56.101 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1000,
"home": "/home/sanxi",
"name": "sanxi",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1000
}
192.168.56.102 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1000,
"home": "/home/sanxi",
"name": "sanxi",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1000
}
3.2、group属组管理
用于操控目标主机的属组,其它选项不多演示。
这里因为用的是root远程,因此不会改变,因为本来就是root组。
[sanxi@sanxi-aero15sa ~]$ ansible all -m group -a "name=root"
192.168.56.101 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"gid": 0,
"name": "root",
"state": "present",
"system": false
}
192.168.56.102 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"gid": 0,
"name": "root",
"state": "present",
"system": false
}
3.3、copy拷贝文件
- src,源目录/文件,这里会自动递归赋值;如果目录后面跟斜线则代表将该目录下所有文件拷贝至目标目录,目标目录必须事先存在;如果不带斜线,则代表将目录本身及其所属子文件/目录全部拷贝至远端,此时可以不指定目标路径,ansible会自动创建跟src一模一样的路径。
- dest,目标目录,如果src是目录,则dest必须是目录。
- content,直接在主控机上生成文件传给目标
[sanxi@sanxi-aero15sa ~]$ ansible all -m copy -a "src=/home/sanxi/light.sh dest=/root/"
192.168.56.101 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "7171014f9362c0f094416ad965dd6004c8a1537a",
"dest": "/root/light.sh",
"gid": 0,
"group": "root",
"md5sum": "664b593efd967b9cf6f14d60230945a5",
"mode": "0644",
"owner": "root",
"secontext": "system_u:object_r:admin_home_t:s0",
"size": 76,
"src": "/root/.ansible/tmp/ansible-tmp-1620376911.816549-28366-175745153098492/source",
"state": "file",
"uid": 0
}
192.168.56.102 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "7171014f9362c0f094416ad965dd6004c8a1537a",
"dest": "/root/light.sh",
"gid": 0,
"group": "root",
"md5sum": "664b593efd967b9cf6f14d60230945a5",
"mode": "0644",
"owner": "root",
"secontext": "system_u:object_r:admin_home_t:s0",
"size": 76,
"src": "/root/.ansible/tmp/ansible-tmp-1620376911.816788-28368-217374409506253/source",
"state": "file",
"uid": 0
}
3.4、fetch下载文件
跟copy相反,fetch是从远程主机拷贝文件到本地,而且只能拷贝其中一台主机而不能指定多台主机。
- fail_on_missing,不存在则报错并退出。
[sanxi@sanxi-aero15sa ~]$ ansible 192.168.56.101 -m fetch -a "src=/root/sanxi.txt dest=/home/sanxi/"
192.168.56.101 | CHANGED => {
"changed": true,
"checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"dest": "/home/sanxi/192.168.56.101/root/sanxi.txt",
"md5sum": "d41d8cd98f00b204e9800998ecf8427e",
"remote_checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"remote_md5sum": null
}
3.5、command执行命令
在远程主机上执行命令,ansible默认的模块就是command,所以可以省略-m command
直接用-a "命令"
[sanxi@sanxi-aero15sa ~]$ ansible all -a "ls"
192.168.56.101 | CHANGED | rc=0 >>
anaconda-ks.cfg
light.sh
sanxi.txt
192.168.56.102 | CHANGED | rc=0 >>
anaconda-ks.cfg
light.sh
3.6、shell执行命令
在远程主机上的shell上执行命令,支持shell特性,如管道等,shell模块比command强很多。
- chdir,在执行命令前先切换到指定目录再执行
[sanxi@sanxi-aero15sa ~]$ ansible all -m shell -a "ls | rm -f light.sh"
192.168.56.101 | CHANGED | rc=0 >>
192.168.56.102 | CHANGED | rc=0 >>
3.7、file设定文件属性
注意,file操作的是目标主机,而不是将控制端的文件数据传过去处理。
state,设定文件或目录的属性,state的属性非常多,具体要看帮助文档,这里只演示创建目录和软链接。
创建目录
[sanxi@sanxi-aero15sa ~]$ ansible all -m file -a "path=/root/test/haha state=directory"
192.168.56.102 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"gid": 0,
"group": "root",
"mode": "0755",
"owner": "root",
"path": "/root/test/haha",
"secontext": "unconfined_u:object_r:admin_home_t:s0",
"size": 6,
"state": "directory",
"uid": 0
}
创建软链接
[sanxi@sanxi-aero15sa ~]$ ansible 192.168.56.101 -m file -a "src=/root/sanxi.txt path=/root/test/sanxi.txt state=link"192.168.56.101 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"dest": "/root/test/sanxi.txt",
"gid": 0,
"group": "root",
"mode": "0777",
"owner": "root",
"secontext": "unconfined_u:object_r:admin_home_t:s0",
"size": 15,
"src": "/root/sanxi.txt",
"state": "link",
"uid": 0
}
验证结果
[sanxi@sanxi-aero15sa ~]$ ansible 192.168.56.101 -a "ls -l test/"
192.168.56.101 | CHANGED | rc=0 >>
total 0
drwxr-xr-x. 2 root root 6 May 7 16:11 haha
lrwxrwxrwx. 1 root root 15 May 7 16:17 sanxi.txt -> /root/sanxi.txt
3.8、cron定期任务
跟linux上面的crontab一样的用处,这里罗列一下常用参数:
- month
- day
- hour
- minute
- job,定期执行的命令
- name,任务名称,可以不指定,当state=present时,必须指定。
- state,是否要求任务必须指定job和环境变量,absent是不需要,present是必须要。
- env,管理crontab环境变量
[sanxi@sanxi-aero15sa ~]$ ansible all -m cron -a "hour=*/1 job='ls -l ~/' name=ls state=present"
192.168.56.101 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"envs": [],
"jobs": [
"ls"
]
}
192.168.56.102 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"envs": [],
"jobs": [
"ls"
]
}
3.9、yum包管理器
执行RedHat系列yum操作,ansible还提供了Debian系列的apt等其它对应平台的模块。我这次实验将虚拟机改为桥接网络,因此IP地址发生变更。
- name,包名
- update_cache,更新yum源缓存。
- state,
- 安装:present、installed、latest,这三个都可以。
- 卸载:absent、removed,这两个都可以。
[sanxi@sanxi-aero15sa ~]$ ansible all -m yum -a "name=vim state=installed"
3.10、service服务管理
- name,服务名
- enabled,开机自启
- state,
- 启动:started
- 停止:stoped
- 重启:restarted
- 重载:reload
ansible all -m service -a "name=nginx state=started enabled=true"
此处因为输出信息过多,我就没贴执行结果,直接来验证吧!
[sanxi@sanxi-aero15sa ~]$ ansible all -m shell -a "ss -tnl | grep 80"
192.168.31.232 | CHANGED | rc=0 >>
LISTEN 0 128 *:80 *:*
LISTEN 0 128 [::]:80 [::]:*
192.168.31.173 | CHANGED | rc=0 >>
LISTEN 0 128 *:80 *:*
LISTEN 0 128 [::]:80 [::]:*
3.11、script执行脚本
复制本地脚本到远程主机执行,脚本后面可以直接跟参数
[sanxi@sanxi-aero15sa 文档]$ ansible all -m script -a "./echo.sh sanxi"
192.168.31.173 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 192.168.31.173 closed.\r\n",
"stderr_lines": [
"Shared connection to 192.168.31.173 closed."
],
"stdout": "hello sanxi\r\n",
"stdout_lines": [
"hello sanxi"
]
}
192.168.31.232 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 192.168.31.232 closed.\r\n",
"stderr_lines": [
"Shared connection to 192.168.31.232 closed."
],
"stdout": "hello sanxi\r\n",
"stdout_lines": [
"hello sanxi"
]
}
四、Playbook入门
像上面都是得自己手动执行ansible命令,有些麻烦,能不能简便点,一次搞定?当然可以啦!
PlayBook是ansible的核心组件,顾名思义我们就是导演,提前写好剧本(playbook),然后安排演员(主机)按照写好的剧本来演戏,当然了,这里演员是不能临场发挥的。。。
4.1、YAML简述
以下内容摘自wiki百科,原文链接:https://zh.wikipedia.org/wiki/YAML
YAML是"YAML Ain't a Markup Language"(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)[3],但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。
4.2、YAML基本数据结构
YAML以严格的空白字符缩进所著称,用缩进来表示层级关系;缩进多少个空格不重要,重要的是一定要对齐。
它有以下几种基本数据结构:
对象
也叫散列表,跟Python字典差不多,以键值对形式存在,冒号后面要加个空格。
Python:
WebFramework: Djano
CrawlerFramework: Scrapy
数组
也叫清单,同级别缩进的以-
开头的行构成一个数组,也可以看作成Python的列表,还可以写在同一行。
- Java
- GO
- C
# 上下两种表达方式是一致的
['Java', 'GO', 'C']
如果子成员也是一个数组,那么可以缩进一个空格表示
- Python:
- asyncio
- requests
复合结构
即将数组和对象组合起来
Python:
- asyncio
await: sleep
async: async
标量
标量在YAML中表示为最基本、不可分割的单位,这点跟SOL中原子性有一点点相似。它可以是:
- 字符串
string: 字符串
- 布尔值
可以是true、True、TRUE;false、False、FALSE
is_exist: true
is_vaild: false
- 整数
China: 1
- 浮点数
salary: 233.233
- Null
Null用波浪号~表示
money: ~
- 时间
时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区
datetime: 2021-05-07T21:11:11+08:00
- 日期
日期必须使用ISO 8601格式,即yyyy-MM-dd。
date: 2021-05-07
了解到这就算可以了,我们的真正目的是学会编写playbook。
4.3、playbook字段
playbook使用YAML编写,它由以下字段组成:
4.3.1、主机清单hosts
运行指定任务的目标主机,可以是IP地址也可以是主机名,可以是一个或多个,用数组表示。
- hosts:
- 192.168.31.173 #
- 192.168.31.232
更推荐直接写主机组,而不是写IP地址
- hosts: centos
4.3.2、远端用户remoute_user
在远程主机上执行任务的用户
- remote_user: root
4.3.3、任务列表task
要执行的任务列表,它主要由name和action组成;name即任务名称,可以随意起;action是要执行的操作,多数用为模块和对应参数。
任务之间是串行的,按照剧本由上到下一个任务一个任务地执行下去;任务中的主机是并行的,即按照任务来给所有主机一次性发起执行指令。
- tasks:
- name: install redis
yum: name=redis state=installed
- name: start redis
service: name=redis state=started
- name: echo info
shell: echo "Hello,World!"
4.3.4、控制者handlers
由特定条件触发的任务,即接收到其它任务的通知后触发,那么如何接收通知呢?在任务里面安排一个眼线notify即可!
notify
在需要监视的任务后面加上notify,它的名称必须和handlers的name一致,当该notify所在的任务被执行后,就会触发handlers的执行
tasks:
- name: install redis
yum: name=redis state=installed
- name: modify configuration
copy: src=/etc/redis/redis.conf dest=/etc/redis/
notify: restart redis # 必须和handlers的name一致
- name: start redis
service: name=redis state=started
- name: echo info
shell: echo "Hello,World!"
handlers
handlers:
name: restart redis # # 必须和notify保持一致
service: name=redis state=restarted
4.3.5、标签tags
在执行playbook时使用-t选项,指定tag,就可以做到只运行tag所在的task,非常适合局部更新操作。
tasks:
- name: install redis
yum: name=redis state=installed
- name: modify configuration
copy: src=/etc/redis/redis.conf dest=/etc/redis/
notify: restart redis
tags: setting redis # 打标签
4.3.6、变量variables
variables,跟shell脚本的变量一样,可以用setup
模块查看目标主机上的各种系统变量信息,这些变量信息可以直接被playbook所识别,这涉及到模板语法,下面会讲到。
variables来源有几种方式:
setup模块取变量
使用时,可以用点号来连接不同层级的ansible变量
ansible 192.168.31.173 -m setup # 获取的信息量非常多,所以没有贴出来
手动设定变量
直接在playbook中自定义变量,调用变量用双大括号,这种引用方式跟Django的模板语法引用变量是一模一样的。
- hosts: centos
vars: # 设定变量组
var1: sanxi # 变量
var2: andy # 变量
remote_user: root
tasks:
- name: echo text
shell: echo "Hello,{{ var1 }}" >> echo.txt # 双大括号引用变量
其实还有几种方式,感觉不实用就没放出来,最重要的是变量与模板的搭配使用!
4.3.7、语法检查
使用--syntax-check
来检查YAML文件的语法是否有语法错误
[sanxi@sanxi-aero15sa scratches]$ ansible-playbook --syntax-check study.yml
playbook: study.yml
4.3.8、列出主机清单
列出即将执行任务的主机清单
[sanxi@sanxi-aero15sa scratches]$ ansible-playbook --list-hosts study.yml
playbook: study.yml
play #1 (centos): centos TAGS: []
pattern: ['centos']
hosts (2):
192.168.31.232
192.168.31.173
4.3.9、执行playbook
直接跟要执行的YAML文件即可
ansible-playbook study.yaml
4.3.10、主机清单通配
清单通配
主机清单也可以使用主机组的通配逻辑,定义一个范围而不是一个一个写,比如有两台主机是:
- 192.168.31.50
- 192.168.31.51
- 192.168.31.52
那么这几台就可以直接写为:192.168.31.[50, 52]
清单参数
ssh默认监听22端口,如果是别的端口直接在主机后面冒号隔开并加上端口号:
[centos]
192.168.31.173:702
192.168.31.232:702
五、Playbook模板
进入playbook模板学习,这很重要。
5.1、Jinja2基础
ansible模板依赖Python模板语法之:jinja2,因为是Jinja2是基于Python的,所以支持的数据类型都是Python中支持i的数据类型。
凡是使用Jinja2语法的文件都需要以.j2结尾。
5.1.1、数据类型:
字符串 | 使用单引号或双引号 |
---|---|
数字 | 整数、浮点数。 |
列表 | [item1, item2, item3] |
元组 | (item1, item2, item3),其实元组就是只读列表。 |
字典 | {key1:value1, key2:value2},键值对形式。 |
布尔值 | true/false |
5.1.2、算数运算
运算符 | 功能 |
---|---|
+ | 加法 |
- | 减法 |
/ | 除法 |
// | 取商 |
% | 取模/取余 |
** | 次方 |
5.1.3、比较运算
== | 等于 |
---|---|
!= | 不等于 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
5.1.4、逻辑运算
and | 与 |
---|---|
or | 或 |
not | 非 |
5.2、模板基本使用
5.2.1、更改本地nginx配置
先在本地安装好nginx,然后改nginx的默认配置,这就是用了模板语法,用变量替代固定值,这样就灵活了很多。
ansible_processor_vcpus
是用setup模块取得的ansible变量;http_port
是自定义变量
mv nginx.conf{,.j2} # jinja2仅识别文件后缀名为.j2的文件
sed -i 's/ 1;/ {{ ansible_processor_vcpus }};/g' nginx.conf.j2
sed -i 's/ 80;/ {{ http_port }};/g' nginx.conf.j2
5.2.2、编写YAML
在使用模板时,我们需要学习template这个新字段的使用。
- hosts: centos
vars:
http_port: 8080 # 注意,这里自定义的变量在nginx.conf有用到
remote_user: root
tasks:
- name: install nginx
yum: name=nginx state=installed
- name: nginx settings
template: src=/home/sanxi/nginx.conf.j2 dest=/etc/nginx/nginx.conf # 必须使用template字段
notify: restart nginx
- name : start nginx
service: name=nginx state=started
handlers:
- name: restart nginx
service: name=nginx state=restarted
5.2.3、检查语法
没有报错就是最好的结果
[sanxi@sanxi-aero15sa scratches]$ ansible-playbook --syntax-check study.yml
playbook: study.yml
5.2.4、执行playbook
我这里因为第一次执行时有一台网络故障,重新配好后,第二次执行那一台成功的机器因为幂等性就被跳过了。
[sanxi@sanxi-aero15sa scratches]$ ansible-playbook study.yml
PLAY [centos] *************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [192.168.31.232]
ok: [192.168.31.173]
TASK [install nginx] ******************************************************************************************************************************************************
ok: [192.168.31.232]
ok: [192.168.31.173]
TASK [nginx settings] *****************************************************************************************************************************************************
ok: [192.168.31.232]
changed: [192.168.31.173]
TASK [start nginx] ********************************************************************************************************************************************************
ok: [192.168.31.232]
ok: [192.168.31.173]
RUNNING HANDLER [restart nginx] *******************************************************************************************************************************************
changed: [192.168.31.173]
PLAY RECAP ****************************************************************************************************************************************************************
192.168.31.173 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.31.232 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
5.2.5、确认结果
可以看到,两台机器上的nginx监听端口已经被变成了8080,演戏成功!
[sanxi@sanxi-aero15sa scratches]$ ansible centos -m shell -a "ss -tnl | grep :8080"
192.168.31.173 | CHANGED | rc=0 >>
LISTEN 0 128 *:8080 *:*
192.168.31.232 | CHANGED | rc=0 >>
LISTEN 0 128 *:8080 *:*
六、playbook进阶
6.1、条件测试when
大家知道,我们的服务器可能是RedHat系列的CentOS,也可能是Debian系列的Ubuntu,这就决定了我们安装程序员无法使用同一款软件包管理器,因为RedHat系列是yum、dnf,而Debian系列是apt,难道我们就为了这个要写不同的剧本吗?这不是很麻烦吗?
为此ansible提供了新字段when,只有当when里面定义的条件成立,when所在的任务才会被执行,来看看代码!
先手动用setup取得受控机上的系统变量名称,每一次执行ansible远程控制主机第一步就是主机报告各自系统信息变量给ansible主控机,所以一定要提前确保这名称是正确的!!!
- hosts: all # 因为要用when,所以这里的主机范围要注意
vars:
http_port: 8080
remote_user: root
tasks:
- name: install nginx in centos
yum: name=nginx state=installed
when: ansible_distribution == "CentOS" # 当检测到此变量是CentOS时触发该任务
- name: install nginx in ubuntu
apt: name=nginx state=installed
when: ansible_distribution == "Ubuntu" # 当检测到Ubuntu时触发
6.2、循环item
有时候我们需批量安装多个程序包或者添加多个用户等操作,这些操作的命令都是一样的,只是参数的名称不一样,如果也要手动一条一条地写无疑是一种重复劳动!
为此,ansible也为我们提供了循环执行某项任务的功能,关键字:item,而后用with_items要迭代的元素列表,列表内可以套字符串,也可以是字典。
YAML编写
- hosts: centos
remote_user: root
tasks:
- name: add users
user: name={{ item }}
when: ansible_distribution == "CentOS"
with_items:
- sanxi
- andy
- xixi
开始演戏
[sanxi@sanxi-aero15sa scratches]$ ansible-playbook study.yml
PLAY [centos] *************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [192.168.31.232]
ok: [192.168.31.173]
TASK [add users] **********************************************************************************************************************************************************
ok: [192.168.31.232] => (item=sanxi)
ok: [192.168.31.173] => (item=sanxi)
changed: [192.168.31.232] => (item=andy)
changed: [192.168.31.173] => (item=andy)
changed: [192.168.31.232] => (item=xixi)
changed: [192.168.31.173] => (item=xixi)
PLAY RECAP ****************************************************************************************************************************************************************
192.168.31.173 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.31.232 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
确认成果
[sanxi@sanxi-aero15sa scratches]$ ansible centos -m shell -a "tail -3 /etc/shadow"
192.168.31.232 | CHANGED | rc=0 >>
nginx:!!:18754::::::
andy:!!:18754:0:99999:7:::
xixi:!!:18754:0:99999:7:::
192.168.31.173 | CHANGED | rc=0 >>
nginx:!!:18754::::::
andy:!!:18755:0:99999:7:::
xixi:!!:18755:0:99999:7:::
七、角色roles
7.1、roles简述
playbook中最最最重要的部分上场!一部戏,演员才是最重要的,不是嘛!
此前我们编写playbook时,所有的字段都写在同一个YAML文件,不利于扩展和灵活,而roles就是扮演着一个个的角色,将原本一个文件以特定的层级目录结构进行拆分,这样就实现了playbook的模块化。
7.2、roles结构
roles默认目录为以下所示,也可以手动指定位置。
roles_path = ~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
一个合乎规范的role的目录结构应该是这样的:
[sanxi@sanxi-aero15sa roles]$ ls
mariadb
[sanxi@sanxi-aero15sa roles]$ pwd
/home/sanxi/.ansible/roles
[sanxi@sanxi-aero15sa roles]$ tree mariadb/
mariadb/
├── default # 设定默认变量
│ └── main.yml # 设定默认变量时使用此固定的文件。
├── files # 存放copy、script等模块调用的文件
├── handlers # 存放handlers控制器
│ └── main.yml # 至少应该存在一个名为main.yml文件,其它的handlers文件需要在此通过include进行导入。
├── meta # 定义当前角色的特殊设定及其依赖关系
│ └── main.yml # 至少存在一个main.yml文件,其它的meta文件需要在此include导入。
├── tasks # 存放任务
│ └── main.yml # 同上至少。。。
├── templates # 存放template模块查找所需要的模板文件
└── vars # 存放各种变量信息
└── main.yml # 同上至少。。。
7 directories, 5 files
7.3、roles编写
因为使用了roles
,所以可以使用相对路径了,ansible
会自己去对应目录找文件,需要用到对应字段的去对应目录下的mian.yml
编写对应代码即可。
tasks/main.yml代码
务必记得不要用--syntax-check
检测语法,一定会报错的。
同时这也是使用item实现多文件拷贝至不同目录的演示。
- name: install mariadb
yum: name=mariadb state=installed
when: ansible_distribution == "CentOS"
- name: setting mariadb
copy: src={{ item.src }} dest={{ item.dest }}
with_items:
- { src: 'my.cnf', dest: '/etc/my.cnf' }
- { src: 'server.cnf', dest: '/etc/my.cnf.d/server.cnf' }
- { src: 'client.cnf', dest: '/etc/my.cnf.d/client.cnf' }
notify: restart mariadb
tags: setting mariadb
- name: start mariadb
service: name=mariadb state=started
handlers/main.yml代码
- name: restart mariadb
service: name=mariadb state=restarted
files目录
[sanxi@sanxi-aero15sa mariadb]$ ls files/
client.cnf my.cnf server.cnf
7.4、调用角色
先提醒一下,ansible-playbook
有个大坑,大家不要去使用--syntax-check
去检测roles下面的各个main.yml
,就算写得没问题也会报错,只需确保调用角色的yml语法正常就可以了。
角色的调用有三种:
7.4.1、直接调用
角色必须以数组形式存在,就算只有一个也是一样。
- hosts: centos
remote_user: root
roles:
- mariadb # 必须是数组形式,可以是一个或多个。
7.4.2、调用并传递变量
role指定角色,后面的K/V键值对用于传递变量给角色,角色里可以调用该变量。
- hosts: centos
remote_user: root
roles:
- { name: mariadb, username: mariadb }
7.4.3、条件调用
可以在满足when条件的情况下调用角色
- hosts: centos
remote_user: root
roles:
- { role: mariadb, when: "ansible_distribution == 'CentOS' " }
7.5、ansible配置文件
ansible配置文件的配置项非常多,但是默认配置就足够我们日常使用了,我们来看看ansible配置文件中需要关注的地方:
默认项
[defaults]
inventory = /etc/ansible/hosts # 定义主机清单位置
#library = ~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
#module_utils = ~/.ansible/plugins/module_utils:/usr/share/ansible/plugins/module_utils
#remote_tmp = ~/.ansible/tmp
#local_tmp = ~/.ansible/tmp
forks = 5 # 定义一次操作多少台主机,因为太多会卡。
#poll_interval = 0.001
#ask_pass = False
#transport = smart
角色读取目录
默认是这样的,可以手动改。
# Paths to search for roles, colon separated
roles_path = ~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
默认远端用户
默认使用连接受控机的用户,如果该用户没有root权限,可能会引发很多问题,可以通过设定合适的sudo权限避免。
# Default user to use for playbooks if user is not specified
# Uses the connection plugin's default, normally the user currently executing Ansible,
# unless a different user is specified here.
#
remote_user = root
默认模块
默认模块是command,可以改成shell,这样就不用使用-m shell了,直接用-a指定命令即可。
# Default module to use when running ad-hoc commands
module_name = command
默认shell
控制远端主机使用的shell,可以改成bash,但大多数情况默认即可,不用改。
# Use this shell for commands executed under sudo.
# you may need to change this to /bin/bash in rare instances
# if sudo is constrained.
#
executable = /bin/sh
日志输出位置
# Logging is off by default unless this path is defined.
log_path = /var/log/ansible.log