环境配置
主机列表
| 主机名 | IP地址 | 角色 |
|---|---|---|
| control | 192.168.4.253 | 控制节点(manager) |
| Node1 | 192.168.4.1 | 被控制节点(test) |
| Node2 | 192.168.4.2 | 被控制节点(proxy) |
| Node3 | 192.168.4.3 | 被控制节点(web1) |
| Node4 | 192.168.4.4 | 被控制节点(web2) |
| Node5 | 192.168.4.5 | 被控制节点(database) |
1 修改控制主机的/etc/hosts文件
1 | [root@MiWiFi-R1CL-srv ~]# cat /etc/hosts |
2 修改控制主机的/etc/hosts、配置SSH密钥
1 | [root@MiWiFi-R1CL-srv ~]# ssh-keygen -f /root/.ssh/id_rsa -N '' |
3 控制端安装ansible
1 | yum -y install ansible |
4 被控制端节点操作
- ansible默认通过ssh协议管理机器
- 被管理主机要开启ssh服务,并允许控制主机登录
- 被管理主机需要安装有python
5 修改配置文件
主配置文件ansible.cfg(/etc/ansible/ansible.cfg)
ansible配置文件查找顺序
- 首先检测ANSIBLE_CONFIG变量定义的配置文件
- 首次检查当前目录的./absible.cfg文件
- 再次检查当前用户家目录下~/ansible.cfg文件
- 最后检查/etc/ansible/ansible.cfg文件
建议,在用户家目录创建配置文件
1
2
3
4
5[root@MiWiFi-R1CL-srv ~]# mkdir ~/ansible
[root@MiWiFi-R1CL-srv ~]# vim ~/ansible/ansible.cfg
[root@MiWiFi-R1CL-srv ansible]# cat ansible.cfg
[defaults]
inventory = ~/ansible/hosts将被管理端主机写入一个主机列表文件(主机清单)
参考/etc/ansible/hosts
1
2
3
4
5
6
7
8
9
10
11
12
13
14[root@MiWiFi-R1CL-srv ansible]# vim ~/ansible/hosts
[root@MiWiFi-R1CL-srv ansible]# cat ~/ansible/hosts
[test]
node1
[proxy]
node2
[webserver]
node3
node4
[database]
node5
[cluster:children]
webserver
database测试ansible环境与配置是否正常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52[root@MiWiFi-R1CL-srv ansible]# ansible all --list-hosts
hosts (5):
node2
node1
node3
node4
node5
[root@MiWiFi-R1CL-srv ansible]# ansible node1 -m ping
node1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
[root@MiWiFi-R1CL-srv ansible]# ansible node1,node2,node3 -m ping
node3 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
node1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
node2 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ssh: connect to host node2 port 22: No route to host",
"unreachable": true
}
[root@MiWiFi-R1CL-srv ansible]# ansible webserver -m ping
node4 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname node4: Name or service not known",
"unreachable": true
}
node3 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
ansible ad-hoc
Ansible ad-hoc是一种通过命令行批量管理的方式
- 格式: ansible 主机集合-m模块名-a “参数”
- 其他参数:
- -k 使用密码远程
- -i 指定主机列表文件
- 模块就是脚本(多数为python脚本)
- 多数脚本都支持参数
- 默认模块为command
1 | [root@MiWiFi-R1CL-srv ansible]# ansible node1 -m command -a 'uptime' |
入门
// 列出所有模块
ansible-doc -l
// 过滤模块
ansible-doc -l |grep yum
// 查看模块帮助
ansible-doc yum
1 | 查看安装的模块 |
shell模块
command和shell模块的区别
- command模块的命令不启动shell,直接通过ssh执行命令
- command不支持bash的特性,如管道和重定向等功能
- 所有需要调用shell的功能都无法使用
1 | 往test主机中添加文件 |
ansible使用ssh远程连接被管理主机
退出ssh后所有状态失效
1 | 切换目录 |
- shell模块支持判断(creates、removes)
- creates文件名:文件存在,不执行shell命令
- removes文件名:文件不存在,不执行shell命令
1 | 如果test主机存在密钥就不执行,不存在就生成密钥 |
script模块
1 | 将脚本文件test.sh传入node1主机并执行,这个脚本文件是临时文件,执行完后,node1会删除 |
file模块
file模块可以创建文件,目录,链接;修改权限与属性等
幂等性:任意次执行所产生的影响均与一次执行的影响相同
1 | 新建文件state=touch |
copy模块
将文件拷贝到远程主机
backup=yes如果目标主机有同名文件,则先备份
1 | 新建测试文件 |
fetch模块
与copy类似,但是作用相反
可以将其他主机的文件拷贝到本地
1 | 将远程主机的hostname文件下载到本地家目录,dest是本地目录 |
lineinfile | replace模块
- 在修改单个文件的单行内容时可以使用lineinfile模块
1 | 在letc/issue文件中添加一行内容hello world,默认添加到最后 |
- lineinfile会替换一整行,replace可以替换关键词
1 | 将/etc/issue文件全文所有的Kernel替换为0cean |
user模块
- user模块可以实现Linux系统账户管理
1 | 远程test组中的所有主机并创建系统账户tuser1 |
yum_repository模块
- 使用yum_repository可以创建或修改yum源配置文件
1 | 新建一个yum源配置文件/etclyum.repos.d/myyum.repo |
yum模块
使用yum模块可以安装、卸载、升级软件包
state:present(安装)|absent(卸载)|latest(升级)
1 | 安装unzip软件包 |
逻辑卷相关模块
lvg模块:创建、删除卷组(VG),修改卷组大小
state:present(创建)|absent(删除)
1 | 安装lvm2软件包 |
lvol模块:创建、删除逻辑卷(LV),修改逻辑卷大小
state:present(创建)|absent(删除)
1 | 使用myvg这个卷组创建一个名称为mylv的逻辑卷 |
sudo提权
基本概念
- sudo(以超级管理员或其他的身份执行命令)
- 基本流程
- 管理员需要先授权(修改/etc/sudoers文件)
- 普通用户已sudo的形式执行命令
- 可以通过sudo -l查看授权情况
sudoers语法
- 修改/etc/sudoers的方法
- visudo(带语法检查,默认没有颜色提示)
- vim /etc/sudoers(不带语法检查,默认有颜色提示)
- 授权格式如下:
- 用户或组 主机列表=(提权身份) [NOPASSWD]:命令列表
- 命令需要些绝对路径
1 | [root@MiWiFi-R1CL-srv ansible]# cat /etc/sudoers |
- 测试(在node1中)
1 | 在node1创建一个新用户jerry |

- 在所有被管理主机配置sudo,让alice可以管理系统服务
- 默认普通用户无权执行systemctl命令
1 | [root@MiWiFi-R1CL-srv ansible]# ansible all -m lineinfile -a "path=/etc/sudoers line='alice ALL=(ALL)/usr/bin/systemctl'" |
- 在所有被管理主机配置sudo,让alice可以执行任何命令
- 使用NOPASSWD开启无密码验证
1 | [root@MiWiFi-R1CL-srv ansible]# ansible all -m lineinfile -a "path=/etc/sudoers line='aliceALL=(ALL) NOPASSWD:ALL" |
高级配置
- 修改~/ansible/ansible.cfg
- 修改sudo相关配置(参考/etc/ansible/ansible.cfg)
1 | [root@MiWiFi-R1CL-srv ansible]# vim ~/ansible/ansible.cfg |
- 使用alice远程被管理主机,需要提前配置SSH密钥
1 | [root@control ~]# for i in node1 node2 node3 node4 node5 |
inventory配置
修改主机清单文件,添加变量(多个变量空格分割)
1 | cat ~lansible/hosts |
playbook
概述
Ansible ad-hoc可以通过命令行形式远程管理其他主机
- 适合执行一些临时性简单任务
Ansible playbook中文名称叫剧本
- 将经常需要执行的任务写入一个文件(剧本)
- 剧本中可以包含多个任务
- 剧本写后,我们随时调用剧本,执行相关的任务命令
- playbook剧本要求按照YAML格式编写
- 适合执行周期性经常执行的复杂任务
YAML格式
“#”代表注释,一般第一行为三个横杠
键值对使用“∵”表示,数组使用”-“表示
缩进必须由两个或以上空格组成
相同层级的缩进必须对齐
全文不可以使用tab键
区分大小写、扩展名为yml或者yaml
跨行数据需要使用>或者|(|会保留换行符)
YAML格式的键值对数据
- key和value之间使用“:”分割
- “:”后面必须有空格
- 缩进代表层级关系
1 | # 例子 |
YAML格式的数组数据
使用短横杠和空格表示,一行表示数据格式[值,值,值……]

综合示例


|会保留换行符

-和:后面必须有空格

playbook语法格式
playbook采用YAML格式编写
playbook文件中由一个或多个play组成
每个play中可以包含:
hosts(主机)、tasks(任务)
variables(变量)、roles(角色)、handlers等元素组成
使用ansible-playbook命令运行playbook剧本
测试第一个playbook
1 | [root@control ansible]# vim ~/ansible/test.yml |
hosts由一个或多个组或主机组成,逗号分隔
tasks由一个或多个任务组成,多个任务按顺序执行
可以使用-f选项自定义并发量
1 | [root@control ansible]# vim ~/ansible/test.yml |
- 一个playbook中可以有多个play
1 | [root@control ansible]# vim ~/ansible/test.yml |
修改VIM配置
使用2个空格自动替换tab键
tabstop=2、expandtab
开启自动缩进对其,缩进宽度为2个空格
shiftwidth=2
1 | [root@control ansible]# cat ~/.vimrc |
playbook应用案例之用户
- 编写playbook创建系统账户、账户属性、设置密码
1 | [root@control ansible]# cat ~/ansible/test_john.yml |
花括号外面必须有双引号
1 | [root@control ansible]# cat ~/ansible/user_james.yml |
- 编写playbook删除系统账户johnd
1 | [root@control ansible]# cat ~/ansible/user_johnd.yml |
- 使用vdb创建卷组合逻辑卷(手动添加虚拟磁盘)
1 | [root@control ansible]# cat ~/ansible/lvm.yml |
playbook应用案例之软件管理
- 安装软件、升级软件、安装组包
1 | [root@control ansible]# cat ~/ansible/pacakge.yml |
setup模块
ansible_facts用于采集被管理设备的系统信息
所有收集的信息都被保存在变量中
每次执行playbook默认第一个任务就是Gathering Facts
使用setup模块可以查看收集到的facts信息
1 | [root@control ansible]# ansible test -m setup |
debug模块
- debug模块可以显示变量的值,可以辅助排除
- debug模块有两个参数,var和msg(引用变量需要两个大括号括起来)
1 | [root@control ansible]# cat ~/ansible/debug.yml |
定义变量
- ansible支持十几种定义变量的方式
- 这里我们仅介绍其中一部分变量,根据优先级排序
- inventory变量
- host facts变量
- register变量
- playbook变量
- playbook提示变量
- 变量文件
- 命令行变量
应用案例
- inventory变量(在主机清单配置文件中定义变量)
1 | [root@control ansible]# cat ~/ansible/hosts |
1 | [root@control ansible]# cat ~/ansible/linventory_var.yml |
- host facts变量(可以直接调用ansible手机的系统信息)
1 | [root@control ansible]# cat ~/ansible/facts_var.yml |
- register语句可以将某个命令的执行结果保存到变量中
1 | [root@control ansible]# cat ~/ansible/register.yml |
- playbook变量(使用vars关键词可以在playbook内定义变量)
1 | [root@control ansible]# cat ~/ansible/playbook_var.yml |
- playbook提示变量(根据提示输入变量的值)
1 | [root@control ansible]# cat ~/ansible/prompt_var.yml |
- 单独定义个变了文件,在playbook中用var_files调用该文件
1 | [root@control ansible]# cat ~/ansible/variables.yml |
- 执行ansible-playbook命令时使用-e参数定义变量
1 | [root@control ansible]# cat ~lansible/command_var.yml |
ansible模块应用
firewalld模块
- 使用firewalld模块可以配置防火墙策略
1 | [root@control ~]#cat ~/ansible/firewall.yml |
template模块
copy模块可以将一个文件拷贝给远程主机
但是如果希望每个拷贝的文件内容都不一样呢?
如何给所有web主机拷贝index.html内容是各自的IP地址?
Ansible可以利用Jinja2模板引擎读取变量
之前在playbook中调用变量,也是Jinja2的功能
Jinja2模块的表达式包含在分隔符
{{ }}内
案例:
- 给webserver主机拷贝首页,每个主机内容不同
1 | [root@control ansible]# mkdir ~/ansible/template |
1 | [root@control ansible]# cat ~lansible/template/source.j2 |
ansible高级语法
error处理机制
- 默认ansible在遇到error会立即停止playbook
1 | [root@control ansible]# cat ~lansible/error.yml |
- 使用ignore_errors可以忽略错误,继续后续的任务
1 | [root@control ansible]# cat ~lansible/error.yml |
handlers
- 当某个任务需要依赖其他任务怎么办?
- 可以通过handlers定义一组任务
- 仅当某个任务触发(notify)handlers时才执行相应的任务
- 如果有多个notify触发执行handlers任务,也仅执行一次
- 仅当任务的执行状态为changed时handlers任务才执行
- handlers任务在所有其他任务都执行后才执行
案例:
- 通过notify触发执行handlers任务
1 | [root@control ansible]# cat ~/ansible/handlers.yml |
when条件判断
when可以定义判断条件,条件为真时才执行某个任务
常见条件操作符如下:
- ==、!=、>、>=、<、<=
多个条件可以使用and或or分割
when表达式中调用变量不要使用
{{ }}
案例:
- 远程主机剩余内存不足700M则关闭NetworkManager服务
1 | [root@control ansible]# cat ~/ansible/when_1.yml |
判断操作系统是Centos7则创建测试文件
- >支持多行输入,不保留换行符
1 | [root@control ansible]# cat ~/ansible/when_2.yml |
block任务块
- 使用block可以将多个任务合并为一个组
1 | [root@control ansible]# cat ~lansible/block_1.yml |
rescue定义block任务执行失败时要执行的其他任务
always定义无论block任务是否成功,都要执行的任务
1 | [root@control ansible]# cat ~lansible/block_2.yml |
loop循环
- 很多任务都在用相同的模块?使用loop循环避免重复
1 | [root@control ansible]# cat ~/ansible/simple_loop.yml |
1 | [root@control ansible]# cat ~/ansible/complex_loop.yml |
ansible vault
加密文件
- ansible有时需要访问一些敏感数据,如密码、key等
- 使用ansible-vault可以加密和 解密数据
- encrypt(加密)、decrypt(解密)、view(查看)
1 | [root@control ansible]# echo 123456 > data.txt #新建测试文件 |
- ansible-vault rekey可以修改加密的密码
1 | [root@control ansible]# ansible-vault encrypt data.txt #加密文件,密码111 |
- 加密、解密每次都输入密码很麻烦,可以将密码写入文件
1 | [root@control ansible]# echo "'m secret data" > data.txt #需要加密的敏感数据 |
- 传输敏感数据到远程数据
1 | [root@control ansible]# echo "I'm secret data" > data.txt #需要加密的敏感数据 |
- playbook调用敏感数据(账户名、密码等)
1 | [root@control ansible]# cat ~/ansible/variables.yml |
ansible roles
什么是roles
在实际生产环境中,为了实现不同的功能,我们会编写大量的playbook文件
而且,每个playbook还可能会调用其他文件(如变量文件)
对于海量的、无规律的文件,管理起来非常痛苦!
Ansible从1.2版本开始支持Roles
Roles是管理ansible文件的一种规范(目录结构)
- Roles会按照标准的规范,自动到特定的目录和文件中读取数据
roles规范的目录结构
- 如果我们创建了一个名称为user.example的role
- 其标准的目录结构如下所示

defualts/main.yml:定义变量的缺省值,优先级较低
files目录:存储静态文件的目录
handlers/main.yml:定义handlers
meta/main.yml:写作者、版本等描述信息
README.md:整个角色(role)的描述信息
tasks/main.yml:定义任务的地方
templates目录:存放动态数据文件的地方(模板文件)
vars/main.yml:定义变量,优先级高
小结:roles就是一组规范的目录结构
应用案例
- ansible-galaxy命令可以创建、管理自己的roles
1 | [root@control ansible]# mkdir ~/ansible/roles |
- 定义issue文件的模板文件
1 | [root@control ansible]# cat ~lansible/roles/issue/templates/issue.j2 |
- 定义变量文件
1 | [root@control ansible]# cat ~lansible/roles/issue/vars/main.yml |
- 修改任务文件,任务文件中不需要tasks关键字
- role的各个文件之间相互调用不需要写路径
1 | [root@control ansible]# cat ~lansible/roleslissue/tasks/main.yml |
在playbook中调用role
- 方法一:在role相同目录下创建一个playbook调用
- 方法二:在ansible.cfg设置roles_path=路径
1 | [root@control ansible]# cat ~/ansiblelansible.cfg |
- 编写playbook文件,通过roles关键词调用role
1 | [root@control ansible]# cat ~/ansible/issue.yml |
ansible-galaxy
- ansible galaxy是官方提供的一个共享roles的平台
- 公共roles仓库(http://galaxy.ansible.com)
1 | [root@control ansible]# ansible-galaxy search 'httpd' |
- 下载roles的方法:
- 使用ansible-galaxy install或者编写requirements.yml文件
1 | [root@control ansible]# cat ~lansible/roles/requirements.yml |
综合案例
自动化部署web集群
- 创建role,通过role完成项目(可能需要多个role)
- 部署Nginx调度器
- 部署2台LNMP服务器
- 部署MariaDB数据库
创建role部署后端LNMP平台环境
1 | [root@MiWiFi-R1CL-srv ansible]# ansible-galaxy init ~/ansible/roles/lnmp |
在控制端中files文件中放入Nginx安装包

编写脚本
1 | [root@MiWiFi-R1CL-srv lnmp]# vim ~/ansible/roles/lnmp/files/install_nginx.sh |
编写网页
1 | [root@MiWiFi-R1CL-srv lnmp]# vim ~/ansible/roles/lnmp/templates/index.html |
在task中编写任务
1 | [root@MiWiFi-R1CL-srv lnmp]# vim ~/ansible/roles/lnmp/tasks/main.yml |
编写自动化部署剧本
1 | [root@MiWiFi-R1CL-srv lnmp]# vim ~/ansible/lnmp.yml |
运行
1 | [root@MiWiFi-R1CL-srv ansible]# ansible-playbook lnmp.yml |
在node3和node4主机确认一下
1 | Nginx已安装 |
创建role部署代理服务环境
1 | [root@MiWiFi-R1CL-srv ansible]# ansible-galaxy init ~/ansible/roles/proxy |
配置代理服务器
1 | [root@MiWiFi-R1CL-srv files]# cat ~/ansible/roles/proxy/files/install_nginx.sh |
编写任务
1 | [root@MiWiFi-R1CL-srv proxy]# vim ~/ansible/roles/proxy/tasks/main.yml |
编写剧本
1 | [root@MiWiFi-R1CL-srv ansible]# vim ~/ansible/proxy.yml |
执行
1 | [root@MiWiFi-R1CL-srv ansible]# ansible-playbook proxy.yml |
在node2关闭http
1 | [root@MiWiFi-R1CL-srv ~]# firewall-cmd --add-service=http |