Ansible弃坑指南
1、前奏
Ansible ————不需要客户端
saltstack ———需要客户端
1.1 环境
管理节点
确保存在OpenSSH
确保Python 版本 >= 2.6
确保安装ansible
被管理节点
确保存在OpenSSH
确保Python 版本 >= 2.4 //若为2.4 版本,确保安装了python-samplesjson 扩展
不需要安装ansible
1.2 安装
# yum方式 必须保证有epel源
yum -y install ansible
# pip方式
pip install ansible
# 查看版本
ansible --version
1.3 建立管理节点和被管理节点的ssh信任关系
管理节点为每个被管理节点做免密
2、开搞
2.1 基础格式
ansible 组或ip -i 指定清单 -m 指定模块 -a 操作 -e 定义变量
# 例子:
ansible web_server -i /root/hosts -m shel -a "echo 'hello'" -e "ke=99"
组或ip 在 ansible 中, 将其叫做pattern , 即匹配。我通常称它为资产选择器。就是匹配资产(-i 参数指定) 中的一部分。这里的 all 是匹配所有指定的所有资产。将在下面资产部分详细阐述。
-i 指定Ansible 的资产,也就是被管理服务器。
-m 指定要运行的模块,比如这里的 ping 模块和 copy 模块
-a 指定模块的参数, 这里模块 ping 没有指定参数。 模块 copy 指定了 src 和 dest 参数。
2.2 资产
2.2.1 静态资产
默认情况下,Ansible的资产文件位于 /ect/ansible/hosts
。pip 安装的可能没有这个文件,创建一个即可。
2.2.2 自定义资产
我们可以自定义文件,使用相应的参数指定
# 可以不定义组直接放在这里,ansible会将这些不在组中的数据放在一个默认组nogroup(名字存疑)里
1.1.1.1
2.2.2.2
3.3.3.[1:15]
test01.qfedu.com
test03.qfedu.com
test[05:09].qfedu.com
[web_servers] # 定义组
192.168.1.2
192.168.1.3
192.168.1.5
[db_servers]
192.168.2.2
192.168.2.3
192.168.1.5
[all_servers] # 定义子组,子组里面的组必须前面定义过
[all_servers:children]
db_servers
web_servers
# Ansible 的资产文件中,可以以IP地址的形式或者主机名的形式存在。
# Ansible 的资产若连续,可以使用[stat:end] 的形式去表达。
# 可以将服务器按照业务场景定义成组,比如`dbdb_servers` 和 `web_servers`
# 组和组之间可以存在继承关系,比如`db_servers` 和 `web_servers` 同时继承 `all_servers` 组
2.2.3 列出资产
# 列举出所有资产
ansible all -i inventory --list-hosts
# 列举出选定资产
ansible web_servers -i inventory --list-hosts
# 使用*匹配
ansible 3.3.3.1* -i inventory --list-hosts
# 使用逻辑匹配
# web_servers 和 db_servers 的并集
ansible 'web_servers:db_servers' -i inventory --list-hosts
# web_servers 和 db_servers 的交集
ansible 'web_servers:&db_servers' -i inventory --list-hosts
# 排除在 web_servers 中,但是不在 db_servers 中
ansible 'web_servers:!db_servers' -i inventory --list-hosts
2.3 模块
2.3.1 模块类型
Ansible 模块分三种类型: 核心模块(core module)、附加模块(extra module)及用户自定义模块(consume module)。
核心模块是由Ansible 的官方团队提供的。
附加模块是由各个社区提供的。例如: OPENSTACK 社区、DOCKER 社区等等。
当核心模块和附加模块都无法满足你的需求时,用户可以自定义模块。
默认情况下,在安装Ansible 的时候, 核心模块和附加模块都已经安装而无需用户干预。
2.3.2 帮助文档
# 列举出所有的核心模块和附加模块
ansible-doc -l
# 查询某个模块的使用方法
ansible-doc modulename
# 查询某个模块的使用方法,比较简洁的信息
ansible-doc -s modulename
2.4 常用模块
2.4.0 ping模块
# ping
ansible all -i hosts -m ping
2.4.1 command & shell 模块
# 两个模块都是在远程服务器上去执行命令。
# 但command模块是ad-hoc的默认模块,在执行ad-hoc时,若不指定模块的名字则默认使用此模块。
# shell 和 command 的区别:shell模块可以执行SHELL 的内置命令和特性(比如管道符),command 模块无法执行SHELL 的内置命令和特性
例子:
ansible all -i hosts -a "echo 'hello'"
ansible all -i hosts -m shell -a "echo 'hello'"
ansible all -i hosts -m shell -a "echo 'hello'|grep -o 'e'"
> e
ansible all -i hosts -a "echo 'hello'|grep -o 'e'" #无法识别管道符
> hello|grep -o e
2.4.2 script 模块
# 将管理节点上的脚本传递到被管理节点(远程服务器)上进行执行。
例子:
ansible webservers -i hosts -m script -a "/root/a.sh"
2.4.3 copy 模块
# copy 模块的主要用于管理节点和被管理节点之间的文件拷贝。
# 常用参数:
src 指定拷贝文件的源地址
dest 指定拷贝文件的目标地址
backup 拷贝文件时,若原文件与目标文件不同,则对目标文件进行备份
owner 指定新拷贝文件的所有者
group 指定新拷贝文件的所有组
mode 指定新拷贝文件的权限
例子:
ansible webservers -i hosts -m copy -a "src=./nginx.repo dest=/etc/yum.repos.d/nginx.repo"
#备份,设置用户和用户组,设置文件权限
ansible all -i hosts -m copy -a "src=./nginx.repo dest=/etc/yum.repos.d/nginx.repo backup=yes owner=nobody group=nobody mode=0755"
2.4.4 yum 模块
# 等同于 Linux 上的YUM 命令, 对远程服务器上RPM包进行管理。
# 常用参数:
name 要安装的软件包名,多个软件包以英文逗号(,) 隔开
state 对当前指定的软件安装、移除操作
present 确认已经安装,但不升级
installed 确认已经安装
latest 确保安装,且升级为最新
absent 和 removed 确认已移除
例子:
# 安装
ansible webservers -i hosts -m yum -a "name=nginx state=present(latest,installed)"
# 卸载
ansible webservers -i hosts -m yum -a "name=nginx state=absent(removed)"
# 安装一个软件包组
ansible webservers -i hosts -m yum -a "name='@Development tools' state=present"
2.4.5 systemd 模块
Centos6 之前的版本使用
service
模块。请使用
ansible-doc service
命令自行查看帮助信息。
# 管理远程节点上的 systemd 服务,就是由 systemd 所管理的服务。
# 常用参数:
daemon_reload 重新载入 systemd,扫描新的或有变动的单元
enabled 是否开机自启动 yes|no
name 必选项,服务名称 ,比如 httpd vsftpd
state 对当前服务执行启动,停止、重启、重新加载等操作
启动 started
停止 stopped
重启 restarted
重新加载 reloaded
例子:
# 重新加载 systemd
ansible webservers -i hosts -m systemd -a "daemon_reload=yes"
# 启动 Nginx 服务
ansible webservers -i hosts -m systemd -a "name=nginx state=started"
# 关闭 Nginx 服务
ansible webservers -i hosts -m systemd -a "name=nginx state=stopped"
# 重启 Nginx 服务
ansible webservers -i hosts -m systemd -a "name=nginx state=restarted"
# 重新加载 Nginx 服务
ansible webservers -i hosts -m systemd -a "name=nginx state=reloaded"
# 将 Nginx 服务设置开机自启动
ansible webservers -i hosts -m systemd -a "name=nginx enabled=yes"
2.4.6 group 模块
# 在被管理节点上,对组进行管理。
# 常用参数:
name 组名称, 必须的
system 是否为系统组, yes/no , 默认是 no
state 状态,默认是present
创建 present
删除 absent
例子:
# 创建普通组 db_admin
ansible dbservers -i hosts -m group -a "name=db_admin"
2.4.7 user 模块
# 用于在被管理节点上对用户进行管理。
# 常用参数:
name 必须的参数, 指定用户名
password 设置用户的密码,这里接受的是一个加密的值,因为会直接存到 shadow, 默认不设置密码
create_home 在创建用户时,是否创建其家目录。默认创建,假如不创建,设置为 no。2.5版本之前使用 createhome
group 设置用户的主组
groups 将用户加入到多个其他组中,多个用逗号隔开。
# 默认会把用户从其他已经加入的组中删除。
append yes|no 和 groups 配合使用,yes 时,不会把用户从其他已经加入的组中删除
state 删除或添加用户,默认值 present
添加 present
删除 absent
remove 当与 state=absent 一起使用,删除一个用户及关联的目录,比如家目录,邮箱目录。可选的值为: yes/no
例子:
# 创建用户并设置密码
# 先生成加密密码
pass=$(echo "123456" | openssl passwd -1 -stdin)
#执行 ansible 命令 创建用户 foo 并设置密码
ansible all -i hosts -m user -a "name=foo password=${pass}"
# 创建用 tom, 并且加入到组 db_admin 中, 不改变用户原有加入的组。
ansible dbservers -i hosts -m user -a "name=tom gorups=db_admin append=yes"
2.4.8 file 模块
# file 模块主要用于远程主机上的文件操作。
# 常用参数:
path 必选项,定义文件/目录的路径 不支持*匹配
owner 定义文件/目录的属主
group 定义文件/目录的属组
mode 定义文件/目录的权限
recurse 递归的设置文件的属性,只对目录有效
src 链接(软/硬)文件的源文件路径,只应用于state=link的情况
dest 链接文件的路径,只应用于state=link的情况
state 执行操作
directory 如果目录不存在,创建目录
file 文件不存在,则不会被创建,存在则返回文件的信息, 常用于检查文件是否存在。
link 创建软链接
hard 创建硬链接
touch 如果文件不存在,则会创建一个新的文件,如果文件或目录已存在,则更新其最后修改时间
absent 删除目录、文件或者取消链接文件
# 创建一个文件
ansible all -i hosts -m file -a "path=/tmp/foo.conf state=touch"
# 改变文件所有者及权限
ansible all -i hosts -m file -a "path=/tmp/foo.conf owner=nobody group=nobody mode=0644"
# 创建一个软连接
ansible all -i hosts -m file -a "src=/tmp/foo.conf dest=/tmp/link.conf state=link"
# 创建一个目录
ansible all -i hosts -m file -a "path=/tmp/testdir state=directory"
# 取消一个连接
ansible all -i hosts -m file -a "path=/tmp/link.conf state=absent"
# 删除一个文件
ansible all -i hosts -m file -a "path=/tmp/foo.conf state=absent"
2.4.9 cron 模块
# 管理远程节点的CRON 服务。等同于Linux 中的 计划任务。
# 注意:使用 Ansible 创建的计划任务,是不能使用本地 `crontab -e`去编辑,否则 Ansible 无法再次操作此计划任务了。
# 常用参数:
name 指定一个cron job 的名字。一定要指定,便于日之后删除。
minute 指定分钟,可以设置成(0-59, *, */2 等)格式。 默认是 * , 也就是每分钟。
hour 指定小时,可以设置成(0-23, *, */2 等)格式。 默认是 * , 也就是每小时。
day 指定天, 可以设置成(1-31, *, */2 等)格式。 默认是 * , 也就是每天。
month 指定月份, 可以设置成(1-12, *, */2 等)格式。 默认是 * , 也就是每月。
weekday 指定星期, 可以设置成(0-6 for Sunday-Saturday, * 等)格式。默认是 *,也就是每星期。
job 指定要执行的内容,通常可以写个脚本,或者一段内容。
state 指定这个job的状态。 默认为新增(present)
新增 present
删除 absent
# 新建一个 CRON JOB 任务
ansible all -i hosts -m cron -a "name='create new job' minute='0' job='ls -alh > /dev/null'"
# 删除一个 CRON JOB 任务,删除时,一定要正确指定job 的name参数,以免误删除。
ansible all -i hosts -m cron -a "name='create new job' state=absent"
2.4.10 debug模块
# debug 模块主要用于调试时使用,通常的作用是将一个变量的值给打印出来。
# 常用参数:
var 直接打印一个指定的变量的值
msg 打印一段可以格式化的字符串 {{ 变量 }}
例子:
ansible all -i hosts -m debug -a "var=role" -e "role=web"
ansible all -i hosts -m debug -a "msg='role is {{role}} '" -e "role=web"
# 系统内置变量名 inventory_hostname
2.4.11 lineinfile 模块
在被管理节点上,用正则匹配的方式对目标文件的一行内容修改删除等操作。
如果是在一个文件中把所有匹配到的多行都进行统一处理,请参考replace 模块。
如果想对一个文件进行一次性添加/更新/删除多行内容等操作,参考blockinfile模块
# 常用参数:
path 被管理节点的目标文件路径, 必须。
state 可选值absent 删除 |present 替换(默认值)。
regexp 在文件的每一行中查找的正则表达式。
对于 state=present ,仅找到的最后一行将被替换。
line 要在文件中插入/替换的行。需要state=present。
create 文件不存在时,是否要创建文件并添加内容。yes/no
# 例子:
# 删除被控节点文件里的某一条内容
ansible dbservers -i hosts -m lineinfile -a "path=/etc/sudoers regexp='^%wheel' state=absent"
# 替换某一行
ansible dbservers -i hosts -m lineinfile -a "path=/etc/selinux/config regexp='^SELINUX=' line='SELINUX=disabled' state=present
2.4.12 ini_file 模块
# 对目标文件进行多行的添加/更新/删除操作。
# 常用参数:
path 目标文件路径
block 文件中被操作的块内容
state 块内容如何处理,absent 删除, present 添加/更新(默认值)
例子:
3、playbook
3.1 YAML语法
playbook 遵循yaml语法格式。
# YAML 特点
YAML 文件以 # 为注释符
YAML 文件以 .yml 或者.yaml 结尾
YAML 文件以 --- 开始 , 以 ... 结束, 但开始和结束标志都是可选的
# 基本语法
# 大小写敏感
# 使用缩进表示层级关系
# 缩进时是使用Tab键还是使用空格一定要达到统一,建议使用空格。
# 相同层级的元素必须左侧对齐即可
3.2 YAML支持的数据结构
1.字符串
---
# YAML 中的字符串可以不使用引号,即使里面存在空格的时候,当然了使用单引号和双引号也没有错。
str
'str'
"str"
# YAML 中若一行写不下你要表述的内容,可以进行折行。写法如下:
mu_line: |
str1
str2
str3
或者
mu_line: >
str1
str2
str3
2.列表
---
# 若熟悉 Python 的话, 可以认为它就是Python中的List ,若熟悉 C 语言的话, 可以认为它是 C 中的数组。
# 如何定义: 以短横线开头 + 空格 + 具体的值
- red
- blue
- green
# 以上的值假如转换成 python 的 List 会是这样:['red', 'green', 'blue']
...
3.字典
---
# 若熟悉Python 的话,可以认为它就是Python中的 Dict
# 如何定义: key + 冒号(:) + 空格 + 值(value), 即 key: value
name: Using Ansible
code: D1234
# 转换为 python 的 Dict {'name': 'Using Ansibel', 'code': 'D1234'}
...
3.3 play的定义
#那如何定义一个Play呢
#1、每一个Play 都是以短横杠开始的
#2、每一个Play 都是一个YAML 字典格式
#3、每个playbook 一般都只放一个play
格式:
单个play
---
- key1: value1
key2: value2
key3: value3
...
多个play
---
# 一个含有3个Play 的伪PlayBook构成
- key1: value1
key2: value2
key3: value3
- key4: value1
key5: value2
key6: value3
- key1: value1
key2: value2
key3: value3
...
3.4 play的属性
# 常用的属性:
name 属性,每个play的名字
hosts 属性, 每个play 涉及的被管理服务器,同ad hoc 中的patten
tasks 属性, 每个play 中具体要完成的任务,以列表的形式表达
become 属性,如果需要提权,则加上become 相关属性
become_user 属性, 若提权的话,提权成为哪个用户上
remote_user属性,指定连接用户。若不指定,则默认使用当前执行 ansible Playbook 的用户
# 1 remote_user 指定的用户需要有 sudo 权限
---
- name: 提权
hosts: dbservers
remote_user: shark
gather_facts: no
# 开启提权
become: yes
tasks:
- name: 执行 shell 命令,返回主机名和远程用户的 id 信息
shell: hostname;id;ls /root
register: mss
- name: debug
debug:
msg: "{{mss}}"
...
2 执行时候:
需要提供 remote_user 指定的用户(shark)的密码
提供密码两种方式
- 方式一: 使用 -K ,之后输入密码
ansible-playbook -i hosts become-user.yml -K
- 方式二: 设置主机变量 ansible_become_password=shark用户的密码
ansible-playbook -i hosts become-user.yml
3.5 一个完整的play
---
- name: the first play example # 这个play 的描述
hosts: all # 执行它的主机
remote_user: root # 以什么用户执行
tasks: # 任务
- name: install nginx package # 任务名1
yum: name=nginx state=present # yum模块
#-m yum -a "name=nginx state=present"
- name: copy nginx.conf to remote server # 任务名2
copy: src=nginx.conf dest=/etc/nginx/nginx.conf #copy模块
- name: start nginx server # 任务名3
service: # service 模块
name: nginx
enabled: true # 1
# enabled: yes
state: started
3.6 tasks 属性中任务的多种写法
# 以启动 nginx 服务,并增加开机启动为例
# 一行的形式:
service: name=nginx enabled=true state=started
# 多行的形式:
service: name=nginx
enabled=true
state=started
# 多行写成字典的形式:
service:
name: nginx
enabled: true
state: started
3.7 具有多个Play 的Playbook
---
- name: manage web servers
hosts: webservers
remote_user: root
tasks:
- name: install nginx package
yum: name=nginx state=present
- name: start nginx server
service:
name: nginx
enabled: true
state: started
- name: Test connectivity to dbservers
hosts: dbservers
tasks:
- ping:
name: ping dbservers
3.8 playbbook 的操作
# 如何对Playbook 进行语法校验
# 因为PlayBook 属于YAML 格式, 我们同样可以使用检查YAML的语法格式的方法进行检查PlayBook的语法正确性。
# 此形式的校验,只能校验PlayBook是否正确,而不能校验YAML文件是否语法正确。
ansible-playbook -i hosts myplaybook.yml --syntax-check
# 如何运行PlayBook
ansible-playbook -i hosts myplaybook.yml
# 如何单步跟从调试PlayBook
# 执行Task中的任务,需要手动确认是否往下执行。
ansible-playbook myplaybook.yml --step
4、变量
4.1 变量命名规则
# 变量的名字由字母、下划线和数字组成,必须以字母开头。
如下变量命名为正确:
good_a OK
ok_b OK
如下变量命名为错误:
_aaa FAIL
2_bb FAIL
# 以先关键字不可以作为变量名:
add, append, as_integer_ratio, bit_length, capitalize, center, clear,
conjugate, copy, count, decode, denominator, difference,
difference_update, discard, encode, endswith, expandtabs,
extend, find, format, fromhex, fromkeys, get, has_key, hex,
imag, index, insert, intersection, intersection_update, isalnum,
isalpha, isdecimal, isdigit, isdisjoint, is_integer, islower,
isnumeric, isspace, issubset, issuperset, istitle, isupper,
items, iteritems, iterkeys, itervalues, join, keys, ljust, lower,
lstrip, numerator, partition, pop, popitem, real, remove,
replace, reverse, rfind, rindex, rjust, rpartition, rsplit, rstrip,
setdefault, sort, split, splitlines, startswith, strip, swapcase,
symmetric_difference, symmetric_difference_update, title,
translate, union, update, upper, values, viewitems, viewkeys,
viewvalues, zfill
4.2 变量类型
1.全局变量
# 全局变量,是我们使用ansible 或使用ansible-playbook 时,手动通过 -e 参数传递给Ansible 的变量。
例子:
ansible all -i hosts -m debug -a "msg='my key is {{ key }}'" -e "key=value"
# 也可以传递一个YAML/JSON形式的文件(注意不管是YAML还是JSON,它们的最终格式一定要是一个字典)
- yaml
# cat a.yml
---
user: qfedu
type: school
...
ansible all -i hosts -m debug -a "msg='name is {{ user }}, type is {{ type }}'" -e @a.yml
- json
# cat a.json
{"user": "qfedu","type": "school"}
ansible all -i hosts -m debug -a "msg='name is {{ user }}, type is {{ type }}'" -e @a.json
2.剧本变量
# 此种变量和PlayBook 有关,定义在PlayBook中的。它的定义方式有多种,我们这里介绍两种最常用的定义方式。
# 通过PLAY 属性 vars 定义
---
- name: test play vars
hosts: all
vars:
user: lilei
home: /home/lilei
# 通过PLAY 属性 vars_files 定义
# 当通过vars属性定义的变量很多时,这个Play就会感觉特别臃肿。此时我们可以将变量单独从Play中抽离出来,形成单独的YAML 文件
---
- name: test play vars
hosts: all
vars_files:
- vars/users.yml
# cat vars/users.yml
---
user: lilei
home: /home/lilei
如何在PlayBook中使用这些剧本变量
# 在PlayBook中使用变量时,使用 {{ 变量名 }} 来使用变量
# 当 Ansible 分析YAML 文件时,有可能会误认为类似name: {{ user }} 是一个字典的开始。因此加针对变量的使用,加上了双引号,避免Ansible错误解析。
---
- name: test play vars
hosts: all
vars:
user: lilei
home: /home/lilei
tasks:
- name: create the user {{ user }}
user:
name: "{{ user }}"
home: "{{ home }}"
3.资产变量
资产变量分为主机变量和组变量,分别针对资产中的单个主机和组。
优先级上:针对单个主机的主机变量优先级要高于针对组的组变量
# 主机变量:针对单个主机
# 定义方式
[webservers]
172.18.0.3 user=lilei a=10
172.18.0.4
# 组变量:针对某个组
[webservers]
172.18.0.3 user=lilei a=10
172.18.0.4
[webservers:vars]
home="/home/lilei"
# 变量的继承:子组可以继承主组的变量
[web_servers]
172.16.2.20
[db_servers]
172.16.2.30
[all_servers]
[all_servers:children]
db_servers
web_servers
[all_servers:vars]
user=lilei
Inventory 内置变量的说明
# 内置变量几乎都是以 ansible_ 为前缀。
ansible_ssh_port
ssh端口号.如果不是默认的端口号,通过此变量设置.
ansible_ssh_user
默认的 ssh 用户名
4.Facts 变量
Facts变量不需要我们人为去声明变量名及赋值。
它的声明和赋值完全有Ansible 中的Facts模块帮我们完成。
类似于资产变量中的主机变量,它收集了有关被管理服务器的操作系统的版本、服务器的IP地址、主机名,磁盘的使用情况、CPU个数、内存大小等等有关被管理服务器的私有信息。
在每次PlayBook运行的时候都会发现在PlayBook执行前都会有一个Gathering Facts的过程。这个过程就是收集被管理服务器的Facts信息过程。
# 主动收集Facts变量
ansible all -i localhost, -c local -m setup
# 在playbook中使用Facts变量
# 默认情况下,在执行PlayBook的时候,它会去自动的获取每台被管理服务器的facts信息。
---
- name: print facts variable
hosts: all
tasks:
- name: print facts variable
debug:
msg: |
The default IPV4 address is
"{{ ansible_default_ipv4.address }}"
# 仔细观察Facts变量,会发现它是一个大字典中嵌套许多小字典和列表的形式,所以取值可以直接用 最高一级的key.低级key.[列表索引]来取值 也可以 高一级的key[低级key][列表索引]
# 关闭获取Facts变量
---
- name: print facts variable
hosts: dbservers
# 关闭 Facts 信息的收集,
gather_facts: no
tasks:
- name: print facts variable
debug:
msg: "The data is {{ ansible_date_time.year }} "
5.注册变量
# 用于保存一个task任务的执行结果, 以便于debug时使用。或者将此次task任务的结果作为条件,去判断是否去执行其他task任务。
# 注册变量在PlayBook中通过register关键字去实现。
---
- name: install a package and print the result
hosts: webservers
remote_user: root
tasks:
- name: install nginx package
yum: name=nginx state=present
register: install_result # 注册变量
- name: print result
debug: var=install_result # 使用注册变量
6.变量的优先级
优先级最高的是全局变量;其次是剧本变量;最后才是资产变量。
5、任务控制
5.1 条件判断
# 基本语法
- name: test when
when: 条件
符合条件执行的操作
例子:
- name: start nginx server
when: nginxsyntax.rc == 0
service: name=nginx state=started
when 支持的运算符:
== != > >= < <=
is defined
is not defined
true
false
支持逻辑运算符: and or
5.2 循环
# 在PlayBook中使用 with_items 去实现循环控制,且循环时的中间变量只能是关键字 item ,而不能随意自定义。
# 可以在外面定义一个变量来循环,也可以直接用一个列表来循环
- name: variable playbook example
hosts: webservers
gather_facts: no
vars:
createuser:
- tomcat
- www
- mysql
tasks:
- name: create user
with_items: "{{ createuser }}" # [tomcat www mysql]
user: name="{{ item }}" state=present
# createuser 是被循环的对象
# item 每次循环被赋值的变量
# 新版循环。使用loop 与with_items 差不多 也只可以使用 item
- name: loop item
hosts: all
gather_facts: no
vars:
some_list:
- "a"
- "b"
- "c"
num_list:
- 1
- 2
- 3
- 5
tasks:
- name: show item
loop: "{{ some_list }}"
debug:
var: "{{ item }}"
- name: show item when item > 3
loop: "{{ num_list }}"
when: item > 3
debug:
var: "{{ item }}"
# 循环中还可以加判断,这里是当循环的数据是大于 3 的,
# 才赋值个 var ,从而打印出来
# 循环字典 怎么输出
5.3 tags 属性
我们可以对需要的任务添加 tags 属性,并自定义名字,然后在执行playbook时指定只运行他们
例子:
- name: add virtualhost config
copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
tags: updateconfig
执行:
ansible-playbook -i hosts xxx.yml -t 指定tags
5.4 handlers 属性
handlers
和tasks
同级,一般使用在copy
文件时,判断文件是否变换,如果变了就执行一些操作主要用来 复制 配置文件
# 基础语法
tasks:
- name: handlers
copy: src= a dest= b
notify: 执行的任务(必须与handlers中定义name的一模一样)
handlers:
- name: 任务名
操作。。。
- name:
。。。
例子:
- name: test handlers
hosts: all
gather_facts: no
tasks:
- name: copy a file to remote server
copy:
src: a.txt
dest: /tmp/a.txt
notify:
- test ping
- ping a remote server
handlers:
- name: ping a remote server
ping:
- name: test ping
ping:
...
6、jinja2模板
jinja2 文件以
.j2
为后缀jinja2中存在三种定界符(内容必须与{ } 之间有空格):
- 注释 : {# 内容 #}
- 变量引用: {{ var }}
- 逻辑表达: {% 逻辑语句 %}
{# 条件表达 #}
{% if 条件表达式 %}
条件表达式为真,显示的内容
{% elif 条件表达式 %}
条件表达式为真,显示的内容
{% else %}
...
{% endif %}
例子:
{# 如果定义了 idc 变量, 则输出 #}
{% if idc is defined %}
{{ idc }}
{% else %}
没有定义
{% endif %}
{# 循环控制 #}
{% for %}
...
...
{% endfor %}
例子:
{# 列举出 dbservers 这个 group 中的所有主机 #}
{% for host in groups['dbservers'] %}
{{ host }}
{% endfor %}
{# 还可以循环加判断 #}
{#与Python 语法不通,模板中的循环内不能break或continue。但你可以在迭代中过滤序列来跳过某些项#}
{#打印dbservers 组中的所有主机,但是不打印1.1.1.1 这台主机#}
{% for host in groups['dbservers'] if host != "1.1.1.1" %}
{{host}}
{% endfor %}
例子:
# cat config.j2
{# use variable example #}
wlecome host {{ ansible_hostname }}, os is {{ ansible_os_family }}
today is {{ ansible_date_time.date }}
cpucore numbers {{ ansible_processor_vcpus }}
{# use condition example #}
{% if ansible_processor_vcpus > 1 %}
OS CPU more than one core
{% endif %}
{% for m in ansible_mounts if m['mount'] != "/" %}
mount {{ m['mount'] }}, total size is {{m['size_total']}}, free size is {{m['size_available']}}
{% endfor %}
# 使用
---
- name: a template example
hosts: all
remote_user: root
tasks:
- name: update jinja2 config
template: src=config.j2 dest=/tmp/config.conf
7、role 角色
7.1 什么是角色
针对每个类型的服务器单独编写PlayBook,最后通过某种方式整合这PlayBook, 在管理方式上就又会变得简单。
就是将一个playbook中的语句按照不同的类型放到不同的文件中。
# 来看个例子,下面就是一个 webservers 角色
webservers/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
└── vars
└── main.yml
# 目录功能
tasks -包含角色要执行的任务的主要列表
handlers -包含处理程序,此角色甚至该角色之外的任何地方都可以使用这些处理程序。
defaults -角色的默认变量。
vars -角色的其他变量。
files -包含可以通过此角色部署的文件。
templates -包含可以通过此角色部署的模板。
meta -为此角色定义一些元数据。
# 角色必须至少包含这些目录之一,但是最好排除任何未使用的目录。
7.2 制作一个角色
第一步
创建一个角色目录
ansible-galaxy init 角色名
第二步
将 各个语句放在规定的地方。
- tasks 任务放在
tasks
目录下的main.yml
文件中(直接写任务就好,不需要加tasks,其他类似) - 变量放在
vars
目录的main.yml
中 - handlers 放在handlers目录的main.yml 文件中
- 拷贝用到的普通文件放在
files
目录下 - 模板文件放在
template
目录下
第三步
使用角色需要先创建一个入口文件,该文件需要放在与角色主目录同一级的目录下
# 方式一 使用roles 属性
- name: use role
hosts: webservers
roles:
- nginx
# 方式二 使用 import_role 属性 使用该属性时,可以在执行role 之前和之后添加其他任务
# 该方法适用于Ansible2.4及以上
- name: use rose
hosts: webservers
tasks:
- debug:
msg: "before we run our role"
- import_role:
name: webservers
- debug:
msg: "after we ran our role"
7.3 扩展
可以通过 galaxy 获取更多的role
Ansible的galaxy 工具,类似程序员使用的github。运维人员可以将自己编写的Role通过galaxy这个平台进行分享。同样,我们也可以通过galaxy 这个平台去获取一些我们想要的role。官网为:https://galaxy.ansible.com
而ansible-galaxy 则是一个使用 galaxy 命令行的工具。它使我们不用访问galaxy 的网站而获取到需要的内容。
# 常用指令
# 获取帮助
ansible-galaxy --help
# 在galaxy 上搜索共享的ROLE
ansible-galaxy search
# 安装 galaxy 上共享的 ROLE
ansible-galaxy install
# 列举已经通过 ansible-galaxy 工具安装的ROLE
ansible-galaxy list
# 创建一个ROLE 的空目录架构, 这样我们在开发一个ROLE的时候,就不需要手动创建目录了。
ansible-galaxy init --offline
8、扩展一:开启调试
在执行 ad-hoc 或者 playbook 的时候,在后面加上 -vvv
参数,就可以看到 Ansible 的详细执行过程,便于排错。
ansible dbservers -i hosts -m ping -vvv
ansible-playbook -i hosts checkhost.yml -vvv
9、扩展二:优化Ansible 执行速度
9.1 设置ssh 为长连接
openssh5.6 版本后支持 Multiplexing
检查ssh 版本
ssh -V
建立长连接
第一步
设置配置文件
grep sh_args /etc/ansible/ansible.cfg
ssh_args = -C -o ControlMaster=auto -o ControlPersist=10d
# ControlPersist=10d 表示保持长连接 10 天。
# 60s 是 60 秒
第二步
设置好后,重新连接一次被控主机,即可让控制主机和被控主机之间建立长连接
ansible webservers -i hosts -m ping
第三步
验证长连接
ss -nta |grep ESTAB
tcp ESTAB 0 0 172.18.0.2:51864 172.18.0.4:2222
输出中 有
ESTAB
状态的就代表是长连接同时会在主控机当前用户的家目录下的 .ansibl/cp/ 目录下生成对应的 socket 文件
9.2 开启pipelinging
默认情况下 Ansible 执行过程中会把生成好的本地 python 脚本文件 PUT 到 远端机器。如果我们开启了 ssh 的 pipelining 特性,这个过程就会在 SSH 的会话中进行。
在不通过实际文件传输的情况下执行ansible模块来使用管道特性, 可以减少执行远程服务器上的模块所需的网络操作数量。比如 PUT sftp 等操作都需要建立网络连接。
如果开启这个设置,将显著提高性能.。
# 如何开启
grep pipelining /etc/ansible/ansible.cfg
# Enabling pipelining reduces the number of SSH operations required to
pipelining = True
10、扩展三:设置Facts缓存
默认情况下,Ansible 每次执行 playbook 时的第一个 Task 就是 获取每台主机的 facts 信息。假如不需要可以设置
gather_facts = no
进行关闭,以提高执行 playbook 的效率。假如想获取 facts 信息,同时又想加速这个 task 的效率,就需要设置 facts 缓存。缓存 facts 信息可以存档 JSON 文件中,也可以方式 redis 和 memcached 中。
10.1 gathering属性
grep gathering /etc/ansible/ansible.cfg
gathering = smart
gathering 的值可以设置为其中的一个:smart、implicit 或者 explicit。
smart --> 表示默认收集facts,但facts信息已经存在的情况下不会收集,也就是会使用缓存facts;
implicit --> 表示默认收集facts,但要禁止收集,可以在 playbook中设置gather_facts: no;
explicit --> 则表示默认不收集,但要收集,可以在 playbook中设置 gather_facts: yes
10.2 将facts信息缓存到文件(数据为JSON格式)
# 要保证配置到 [defaults] 配置块内
grep -P -v '^#|^$' /etc/ansible/ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile # 缓存到 json 文件
fact_caching_connection = /dev/shm/ansible_fact_cache/ # 保存的文件
fact_caching_timeout = 86400 # 缓存数据时间是一天
# fact_caching_connection 值是一个本地可写的目录路径,如果目录不存在,ansible会试图创建最后一级目录,文件名是在 Inventory 中保存的 IP 或者 hostname .
10.3 将facts信息缓存到redis数据库中
# 可以将信息缓存到本地的redis数据库中,也可以缓存到别的机器上的redis数据库中
# 不考虑网速的影响(在一个局域网中),可以随意放在有空闲资源的机器上
# 首先你缓存数据的机器必须有redis数据库,如果redis数据库不在本机上,记得开放监听地址
# 宿主机必须使用 pip install redis (必须是python2的pip,安装 yum -y install python2-pip)
grep -P -v '^#|^$' /etc/ansible/ansible.cfg
[defaults]
gathering = smart
fact_caching = redis # 缓存到 redis
fact_caching_connection = 192.168.1.37:6379:0 # 缓存数据的主机
fact_caching_timeout = 86400 # 缓存数据时间是一天
# 检测
keys * 查看所有的key(生产环境不要这么用)
# 找有没有 ansible_facts 开头的 后面是你子机IP的key
11、扩展四:使用多个Inventory 文件
11.1 在命令行中指定多个
可以直接跟多个 -i 指定多个 资产
ansible-playbook get_logs.yml -i development -i production
11.2 使用目录汇总清单源
可以通过组合目录下的多个清单来源和来源类型来创建清单。这对于组合静态和动态主机并将它们作为一个清单进行管理很有用。以下清单结合了清单插件源,动态清单脚本和具有静态主机的文件
创建
# 创建目录
inventory
├──aliyun.ini # 获取阿里云的主机和组
├──static-inventory # 静态主机和组
├──group_vars # 存放变量的目录,目录名必须是这个
├──all.yml # 给所有的主机指定变量
使用
方式一
命令行里使用这个清单目录
ansible-playbook test.yml -i inventory
方式二
可以在配置文件中配置
[defaults]
inventory = 清单目录的绝对路径
注意变量覆盖
如果多个主机清单之间存在变量的冲突或组依赖关系,则需要通过控制主机清单的合并顺序来控制变量能按照我们所期望的值进行生效。
可以通过在文件前添加数字前缀来控制合并的顺序
12、扩展五:使用ansible批量推送公钥
authorized_key
模块可以实现
#首先配置不检测对方主机公钥
/etc/ansible/ansible.cfg
[defaults]
host_key_checking = False
12.1 临时命令方式发送
第一步
在 Ansible 清单(资源清单)中添加远程主机的用户和密码
# 远程服务器的 IP 或者可被本机解析的远程服务器的主机名
127.0.0.1 ansible_ssh_user=test ansible_ssh_pass=123
第二步
利用 authorized_key 模块传送公钥
# 格式
ansible 资源选择 -i hosts -m authorized_key -a "user=想要免密的用户 state=present key={{ lookup('file', '/root/.ssh/id_rsa.pub')}}"
例子:
ansible 127.0.0.1 -i hosts -m authorized_key -a "user=test state=present key={{ lookup('file', '/root/.ssh/id_rsa.pub') }}"
# 模块可选参数
user 指定和远程服务器的这个用户建立信任关系
state
present 添加公钥
absent 删除公钥
key 本机用户的公钥,需要使用 lookup 查找到设置的公钥文件
12.2 playbook 推送方式
在使用 playbook 进行公钥推送是,还可以对远程主机的密码进行加密处理。
第一步
创建加密文件
使用
ansible-vault create
命令可以创建一个经过加密的文件。
此时会提示输入解密这个加密文件的密码,输入两次密码后,会调用默认的vi
编辑器编辑这个文件,此时就可以把需要加密的变量等数据写到这个文件中,写完后保存退出即可。
ansible-vault create vault-foo.yml
New Vault password: # 输入解密这个这文件的密码
Confirm New Vault password: # 再次确认密码
# 文件内的内容必须是键值对形式
ansible_ssh_pass: upsa
修改加密文件的内容
需要提供解密这个被加密的文件的密码,就是之前创建时候输入的密码,然后就可以编辑文件了
ansible-vault edit vault-foo.yml
Vault password:
第二步
在playbook 中使用加密文件
cat send-pubkey.yml
- hosts: all
remote_user: root # 连接远程主机的用户,密码就是加密文件中设置好的 ansible_ssh_pass 的值
vars_files:
- foo.yml # 加密文件
tasks:
- name: Set authorized key taken from file
authorized_key: # 发送公钥的模块
user: root # 给这个用户发送公钥
state: present
key: "{{ lookup('file', '/root/.ssh/id_rsa.pub') }}" # 发送本地用户的公钥路径
第三步
需要使用
--ask-vault-pass
参数指定一下解密密码
ansible-playbook -i hosts send-pubkey.yml --limit dbservers --ask-vault-pass
Vault password:
也可以在
ansible.cfg
文件中配置DEFAULT_VAULT_PASSWORD_FILE
的值指向一个文件,这个文件中保存了解密的密码,这样执行 playbook 时就不用使用--ask-vault-pass
参数了
vault_password_file = /path/to/vault_password_file
13.扩展五:高级知识