在管理机器上安装ansible工具
yum install ansible
将需要管理的主机加入absible,的主机管理清单配置文件/etc/ansible/hosts
格式:inventory文件遵循INI文件风格,中括号中的字符为组名。 可以将同一个主机同时归并到多个不同的组中;此外,当如若 目标主机使用了非默认的SSH端口,还可以在主机名称之后 使用冒号加端口号来标明
1)不分组方式 #不推荐这样使用
192.168.136.5:2222 #冒号后面的2222表示端口号
192.168.136.6
2)分组方式
[web] # web表示组名,分组以后可以直接通过组名来进行管理组里面的主机
192.168.136.5
192.168.136.6
[opt]
192.168.136.[7:10] #这表示范围,从192.168.136.7到192.168.136.10之间的所有主机
[webservers]
www1.magedu.com
www2.magedu.com
ansible管理主机一般最好是先基于key验证之后管理起来会比较方便
步骤:
1)生成公私钥对
ssh-keygen
2)将公钥复制到所有主机
可以编写脚本:完成这一步
也可以用ssh-copy-id <主机ip>即可
ansible 配置文件
Ansible 配置文件/etc/ansible/ansible.cfg (一般保持默认)
[defaults]
#inventory = /etc/ansible/hosts # 主机列表配置文件
#library = /usr/share/my_modules/ # 库文件存放目录
#remote_tmp = $HOME/.ansible/tmp #临时py命令文件存放在远程主 机目录
#local_tmp = $HOME/.ansible/tmp # 本机的临时命令执行目录
#forks = 5 # 默认并发数
#sudo_user = root # 默认sudo 用户
#ask_sudo_pass = True #每次执行ansible命令是否询问ssh密码
#ask_pass = True
#remote_port = 22
#host_key_checking = False # 检查对应服务器的host_key,建议取消 注释
#log_path = /var/log/ansible.log #会把你执行的命令生成日志计入在/var/log/ansible.log文件中,建议把这一项启用起来,
ansible
功能介绍 | 基于模块化,远程管理主机,并且是基于ssh方式连接主机的方式,安全性高 |
格式 | <host-pattern> [-m module_name] [-a args] |
特点 | 1)模块化:调用特定的模块,完成特定任务 2)有Paramiko,PyYAML,Jinja2(模板语言)三个关键模块 3)支持自定义模块 4)基于Python语言实现 5)部署简单,基于python和SSH(默认已安装),agentless 6)安全,基于OpenSSH 7)支持playbook编排任务 8)幂等性:一个任务执行1遍和执行n遍效果一样,不因重复执行带来意外情况 9)无需代理不依赖PKI(无需ssl) 10)可使用任何编程语言写模块 11)YAML格式,编排任务,支持丰富的数据结构 12)较强大的多层解决方案 |
ansable系列命令 | ansible ansible-doc ansible-playbook ansible-vault ansible-console ansible-galaxy ansible-pull |
ansible-doc 查看模 块帮助用法 | ansible-doc –l 列出所有模块 ansible-doc ping 查看指定模块帮助用法 ansible-doc –s ping 查看指定模块帮助用法 |
ansible命令执行过程
1. 加载自己的配置文件 默认/etc/ansible/ansible.cfg
2. 加载自己对应的模块文件,如command
3. 通过ansible将模块或命令生成对应的临时py文件,并将该 文 件传输至远程服务器的对应执行用户 $HOME/.ansible/tmp/ansible-tmp-数字/XXX.PY文件
4. 给文件+x执行
5. 执行并返回结果
6. 删除临时py文件,sleep 0退出
执行状态:
绿色:执行成功并且不需要做改变的操作
黄色:执行成功并且对目标主机做变更
红色:执行失败
选项 | 说明 |
--version | 显示版本 |
-m module | 指定模块,默认为command |
-v | 详细过程 –vv -vvv更详细 |
--list-hosts | 显示主机列表,可简写—list |
--ask-pass | 提示连接密码,默认Key验证 |
-C, --check | 检查,并不执行 |
-T --timeout=TIMEOUT | 执行命令的超时时间,默认10s |
-u --user=REMOTE_USER | 执行远程执行的用户 |
-b --become | 代替旧版的sudo 切换 |
模块介绍
模块名成 | 功能介绍及常用参数 |
ping | 一个简单的测试模块,这个模块总是在成功的接触中返回“乒乓”。这是不合理的 但是它对于验证登录的能力和可用的python来说是有用的。 配置。这不是ICMP ping,这只是一个简单的测试模块。对于Windows目标,使用 (win_ping)模块。 用法: anisble 主机 -m ping |
command | 命令的模块接受命令名,后面是空格分隔的参数列表。给定的 命令将在所有选定的节点上执行。它不会通过shell进行处理,所以变量 “$ HOME”和操作,比如“<“”、“”>“”,“”|“”、“”;“和”不会起作用(使用shell模块 如果您需要这些特性)。 常见选项: 1),chdir <切换目录> 格式: ansible 主机组 -m command -a ‘chdir=/app/ ls -al /app ’ 2),creates <判断如果文件不存在就执行后面的命令> 格式: ansible 主机组 -m command -a ‘creates=/etc/fstab ls -al /app’ 3),removes <判断如果文件存在就执行后面的命令> 格式: ansible 主机组 -m command -a 'removes=/etc/fstab ls-al /app/' |
shell | shell的模块接受命令名,后面是空格分隔的参数列表。这几乎是 与命令模块完全一样,但是在远程节点上通过shell(/bin/sh')运行命令 支持 特殊符号$ HOME”和操作,比如“<“”、“”>“”,“”|“”、“”;“ 在执行命令是尽量使用单引号, 其他用法基本跟command模块相同 |
script | 脚本的模块接受脚本名称,发送到远程主机上执行, 格式:ansible 主机组 -m script -a ‘/app/f1 .sh ’ |
copy | copy'模块将一个文件从本地或远程机器复制到远程机器上的一个位置。使用com fetch模块将文件从远程位置复制到本地框。如果你需要变量插值 在复制的文件中,使用模板模块。 注意:copy模块复制文件如果目录不存在会自动创建目录 常用参数: content:<把自定义内容复制到远程主机文件中> 格式 : content=“df -h \nls\n\hostname dest=/app/f1.sh” src:<本机源文件> dest:<指定复制到目标主机的某个目录或者覆盖某个文件> backup:<文件复制到远程主机如果文件已存在可以将源文件做备份,再覆盖> 格式: backup=yes /no yes表示做备份,no表示不做备份 directory_mode :<复制整个目录> mode:<更改复制过去的文件权限> 格式: mode=644...... owner:<更改复制过去的文件属主> 格式:owner=wang 格式:ansible 主机组 -m copy -a ‘src=/etc/fstab dest=/app/ backup=yes mode=644 owner=wang’ |
fetch | 这个模块的工作方式类似于复制,但反过来。它用于从远程机器获取文件 将它们存储在本地目录下 常用参数: src:<指定远程主机的文件> dest:<指定本地主机的目录> 格式:ansible 主机组 -m fetch -a ‘src=/etc/fstab best=/app/’ |
file | 支持在被控主机创建空文件,创建软连接,删除文件等作 常用选项: 创建空文件:path=<指定创建文件路径> 一般配合state=touch使用 创建软连接:src=<指定原始文件路径> 一般配合path=<创建到那个目录>,state=link<连接> 创建目录:path=<指定路径> 一般配合state=directory使用 删除文件: path=<指定文件路径> 一般配合state=absent使用 设置文件权限:mode<设置文件权限> owner<设置文件属组> 格式: 创建文件 ansible 主机组 -m file -a ‘path=/app/testfile state=touch’ 创建软连接 absible 主机组 -m file -a ‘src=/app/testfile path=/tmp/testfile-link stare=link’ 创建目录 absible 主机住 -m file -a 'path=/app/dir1 state=directory' 删除文件 ansible 主机组 -m file -a ‘path=/app/testfile state=absent’e |
hostname | 更改被控主机端的主机名 格式: ansible 主机 -m hostname -a 'name=名字' |
cron | 创建计划任务 minute<分钟> day <天> hour<小时> weekday<周> month<月> job<执行的操作> 注意:命令需要写全路径 name<计划任务的名字> disabled=yes <yes表示禁用计划任务,no表示启用计划任务> start=absent <表示删除计划任务> 例子: ansible 主机组 -m cron -a 'minute=*/5 hour=12 weekday=0,6 job="/usr/sbin/wall is jobs " name="test job"' 此命令原理: 创建一个名为test job计划任务 每周六周天,凌晨12点没5分钟执行一次wall广播 is job 禁用,启用计划任务 ansible 主机组 -m cron -a 'disabled=yes/on job="执行的操作" name="计划任务名字"' 删除计划任务 ansible 主机组 -m cron -a 'start=adsent job="执行的操作" name="计划任务名字"' |
yum | 管理软件包模块 安装软件包 name=包名 start=absent 卸载包 start=latest 装最新版 updata_cache=yes 更新yum缓存 |
service | 管理服务状态模块 常用选项 name=<指定包名> state=<指定运行状态> 如:stopped<停止>,started<启动>,restarted<重启服务>, reloaded<重读配置文件> , enabled=yes 表示把服务设置为开机自动重启 格式: 设置服务开机自动启动并且启动 ansible 主机组 -m service -a 'name=httpd state=started enabled=yes' |
user | 创建管理用户 常用选项: name=<指定用户名> comment=<描述信息> uid=<指定uid> home=<指定家目录路径> group=<指定 主组> groups=<指定辅助组> system=yes<yes表示创建的是系统用户> shell=<指定shell类型> remove=yes <yes表示把家目录等等相关数据全删除> 例子: 创建普通用户 ansible 主机组 -m user -a 'name="test" uid=2000 home=/app/test group=wang groups=root,bin ' 创建系统用户 <注意:默认创建系统用户会创建家目录> ansible 主机组 -m user -a 'name=sys system=yes shell=nologin' 删除用户并把家目录删除 ansible 主机组 -m user -a 'name="test" state=absent remove=yes’ |
group | 管理组 name=<指定组名> state=<指定操作> 例子 创建组 ansible 主机组 -m group -a 'name=group1 ' 删除组 ansible 主机组 -m group -a 'name=group1 state=absent' |
setup | 显示所有被控主机的,所有主机信息,例如:IP,主机名,版本,等等信息 常用选项 filter="*信息*"<作用用来匹配想要查看的信息> 例子: ansible 主机组 -m setup -a 'filter="*hostname*"' 注意匹配时需要把匹配的值放在双星”*“号里面 |
匹配主机列表的方式:
All :表示所有Inventory中的所有主机
ansible all –m ping
* :通配符
ansible “*” -m ping #匹配所有主机执行ping模块操作
ansible 192.168.1.* -m ping #匹配192.168.1.这个网段的所有主机,执行平模块操作
ansible “*srvs” -m ping #匹配主机组名后面srvs的主机组执行ping模块操作
或关系:
ansible “websrvs:appsrvs” -m ping #这表示匹配两个主机组里面的所有主机
ansible “192.168.1.10:192.168.1.20” -m ping #这是只针对两台主机执行ping模块操作
逻辑与
ansible “websrvs:&dbsrvs” –m ping 在websrvs组并且在dbsrvs组中的主机,才执行ping模块操作
逻辑非
ansible 'websrvs:!dbsrvs' –m ping #在websrvs组,但不在dbsrvs组中的主机 ,执行平模块中操作
综合逻辑
ansible 'websrvs:dbsrvs:&appsrvs:!ftpsrvs' –m ping #匹配在websrvszu或者在dbsrvs组,并且在appsrvs组,但不在ftpsrvszu里的主机,才会执行ping模块操作
正则表达式
ansible “~(web|db).*\.magedu\.com” –m ping #匹配以带有web关键字后面跟任意字符.magedu.com的主机组,或者带有db关键字后面跟任意字符.magedu.com的主机组,执行ping模块操作
ansible系列命令
Ansible-vault
功能:管理加密解密yml文件
ansible-vault [create|decrypt|edit|encrypt|rekey|view]
ansible-vault encrypt hello.yml 加密
ansible-vault decrypt hello.yml 解密
ansible-vault view hello.yml 查看
ansible-vault edit hello.yml 编辑加密文件
ansible-vault rekey hello.yml 修改口令
ansible-vault create new.yml 创建新文件
Ansible-console:2.0+新增,可交互执行命令,支持tab
root@test (2)[f:10] $ 执行用户@当前操作的主机组 (当前组的主机数量)[f:并发数]$
设置并发数: forks n 例如: forks 10
切换组: cd 主机组 例如: cd web
列出当前组主机列表: list
列出所有的内置命令: ?或help
示例:
root@all (2)[f:5]$ list
root@all (2)[f:5]$ cd appsrvs
root@appsrvs (2)[f:5]$ list
root@appsrvs (2)[f:5]$ yum name=httpd state=present
root@appsrvs (2)[f:5]$ service name=httpd state=started
常用选项示例:
查看主机组名里包括多少台主机
anisble 主机分组名 --list-host
#普通用户与root用户配置
[root@localhost python]# cat /etc/ansible/hosts
# This is the default ansible 'hosts' file.
#
# It should live in /etc/ansible/hosts
#
# - Comments begin with the '#' character
# - Blank lines are ignored
# - Groups of hosts are delimited by [header] elements
# - You can enter hostnames or ip addresses
# - A hostname/ip can be a member of multiple groups
# Ex 1: Ungrouped hosts, specify before any group headers.
## green.example.com
## blue.example.com
## 192.168.100.1
## 192.168.100.10
10.60.32.149
# Ex 2: A collection of hosts belonging to the 'webservers' group
## [webservers]
## alpha.example.org
## beta.example.org
## 192.168.1.100
## 192.168.1.110
# If you have multiple hosts following a pattern you can specify
# them like this:
## www[001:006].example.com
# Ex 3: A collection of database servers in the 'dbservers' group
## [dbservers]
##
## db01.intranet.mydomain.net
## db02.intranet.mydomain.net
## 10.25.1.56
## 10.25.1.57
# Here's another example of host ranges, this time there are no
# leading 0s:
## db-[99:101]-node.example.com
[docker]
10.60.32.149
[k8s]
10.60.36.220
10.60.36.210
[k8s:vars]
ansible_ssh_user=www
ansible_ssh_port=22
ansible_sudo_pass=123456
ansible-playbook 使用详解
一、什么是playbooks
playbooks是ansible的脚本、如同shell脚本一样,它是控制远程主机的一系列命令的集合,通过YAML语言编写。执行一些简单的任务,我们可以使用ad-hoc命令就可以解决,对于一些较复杂的任务,ad-hoc就不能胜任了,这时候playbooks就派上用场了,在playbooks中可以编排有序的执行过程,甚至可以在多组机器间来回有序的执行特定的步骤,并且可以同步或异步发起任务。
二、YAML语法
1、文件开始符
1
|
---
|
2、数组
1
2
3
|
- name
- hosts
- user
|
3、字典
1
2
|
name: restart apache
service: name=httpd state=restarted
|
字典与字典的嵌套:
1
2
3
|
vars:
http_port: 80
max_clients: 200
|
字典与数组的嵌套:
1
2
3
4
5
6
7
8
9
|
tasks:
- name: ensure apache is at the latest version
yum: pkg=httpd state=latest
- name: write the apache config
file
template: src=
/srv/httpd
.j2 dest=
/etc/httpd
.conf
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
|
此外,Ansible 使用 "{{ var }}"来引用变量.,如果一个值以 "{" 开头, YAML 将认为它是一个字典, 所以我们必须引用它, 像这样:foo: "{{ variable }}"
三、playbooks的基本用法
最基本的playbook分为四部分:
-
定义主机和用户
1
2
|
hosts
users
|
-
定义 playbook 执行需要的变量
1
|
variable
|
-
定义任务
1
|
tasks
|
-
定义响应事件
1
|
handlers
|
简单示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
---
- name: Install apache
hosts: webservers
user: root
gather_facts:
false
vars:
http_port: 80
max_clients: 200
tasks:
- name: ensure apache is at the latest version
yum: pkg=httpd state=latest
- name: write the apache config
file
template: src=
/srv/httpd
.j2 dest=
/etc/httpd
.conf
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
handlers:
- name: restart apache
service: name=httpd state=restarted
|
说明:
-
name参数对此playbook实现的功能的概述,执行时会输出name变量值。
-
hosts参数指定了在哪些主机上执行playbook
-
user参数指定在远程主机上使用什么用户执行任务
-
gather_facts参数指定了下面任务执行前,是否先执行setup模块获取远程主机相关信息,这些信息在task中可以作为变量使用
-
vars参数指定了变量
-
task参数指定了任务,这里有3个任务。name参数是对具体任务的描述,在执行过程中会输出
-
handlers参数指定一个响应事件,当template: src=/srv/httpd.j2 dest=/etc/httpd.conf这个任务执行状态是changed时才会触发。
1、主机和用户
key | 含 义 |
hosts | 为主机的IP,或者主机组名,或者关键字all |
user | 在远程以哪个用户身份执行。 |
become | 切换成其它用户身份执行,值为yes或者no |
become_method | 与became一起用,指可以为‘sudo’/’su’/’pbrun’/’pfexec’/’doas’ |
become_user | 与bacome一起用,可以是root或者其它用户名 |
一般用法:
1
2
3
|
---
- hosts: webserver, [all]
user: root
|
还可以在每个 task 中,定义远程执行用户
1
2
3
4
5
6
7
|
---
- hosts: webserver
user: root
tasks:
- name:
test
connection
ping
:
remote_user: root
|
也支持 sudo 方法,在 task中同样支持,在sudo需要密码时,可以加上选项 –ask-sudo-pass
1
2
3
4
5
6
7
8
|
---
- hosts: all
user:
test
sudo
:
yes
task:
- service: name=nginx state=started
sudo
:
yes
sudo_user: root
|
2、任务列表
tasks 是从上到下顺序执行,如果中间发生错误,整个 playbook 便会中断。
每一个 task 是对module的一次调用,通常会带有特定参数,参数可以使用变量。
每一个 task 有一个 name 属性,name 值会在命令行中输出,以提示用户,如果没有定义,aciton 的值会作为输出信息来标记task。
语法:
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
|
tasks:
- name:
make
sure apache is running
service: name=httpd state=running
//
如果参数过长,可以使用空格或者缩进分隔为多行
tasks:
- name: copy ansible inventory
file
to client
copy: src=
/etc/ansible/hosts
dest= /etc/ansible/hosts
owner=root group=root mode=0644
//
或者使用 yaml 的字典作为参数
tasks:
- name: copy ansible inventory
file
to client
copy:
src:
/etc/ansible/hosts
dest:
/etc/ansible/hosts
owner: root
group: root
mode: 0644
//
大部分的模块都是使用 `key-value` 这种格式的,其中有两个比较特殊, command 和 shell 模块。
tasks:
- name: disable selinux
command
:
/sbin/setenforce
0
tasks:
- name: run this
command
and ignore the result
shell:
/usr/bin/command
|| /bin/true
tasks:
- name: run some
command
and ignore the reslut
shell:
/usr/bin/somecommadn
ignore_error: True
|
执行状态:
task中每个action会调用一个module,在module中会去检查当前系统状态是否需要重新执行。
如果本次执行了,那么action会得到返回值changed
如果不需要执行,那么action得到返回值ok
3、响应事件
每个主流的编程语言都会有event机制,那么handler就是playbook的event。
Handlers里面的每一个handler,也是对module的一次调用。而handlers与tasks不同,tasks会默认的按定义顺序执行每一个task,handlers则不会,它需要在tasks中被调用,才有可能被执行。Tasks中的任务都是有状态的,changed或者ok。 在Ansible中,只在task的执行状态为changed的时候,才会执行该task调用的handler,这也是handler与普通的event机制不同的地方。
什么情况下使用handlers呢?
如果你在tasks中修改了apache的配置文件。需要重起apache。此外还安装了apache的插件。那么还需要重起apache。像这样的应该场景中,重启apache就可以设计成一个handler。
特性:
-
一个handler最多只执行一次
在所有的任务里表执行之后执行,如果有多个task notify同一个handler,那么只执行一次。
在下面的例子里apache只执行一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
---
- hosts: lb
remote_user: root
vars:
random_number1:
"{{ 10000 | random }}"
random_number2:
"{{ 10000000000 | random }}"
tasks:
- name: Copy the
/etc/hosts
to /tmp/hosts .{{ random_number1 }}
copy: src=
/etc/hosts
dest= /tmp/hosts .{{ random_number1 }}
notify:
- call
in
every action
- name: Copy the
/etc/hosts
to /tmp/hosts .{{ random_number2 }}
copy: src=
/etc/hosts
dest= /tmp/hosts .{{ random_number2 }}
notify:
- call
in
every action
handlers:
- name: call
in
every action
debug: msg=
"call in every action, but execute only one time"
|
-
action是Changed ,才会执行handler
只有当TASKS种的action的执行状态是changed时,才会触发notify handler的执行。
下面的脚本执行两次,执行结果是不同的:
第一次执行是,tasks的状态都是changed,会触发两个handler
第二次执行是,
第一个task的状态是OK,那么不会触发handlers"call by /tmp/hosts",
第二个task的状态是changed,触发了handler"call by /tmp/hosts.random_number"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//
示例代码
---
- hosts: lb
remote_user: root
vars:
random_number:
"{{ 10000 | random }}"
tasks:
- name: Copy the
/etc/hosts
to /tmp/hosts
copy: src=
/etc/hosts
dest= /tmp/hosts
notify:
- call by
/tmp/hosts
- name: Copy the
/etc/hosts
to /tmp/hosts .{{ random_number }}
copy: src=
/etc/hosts
dest= /tmp/hosts .{{ random_number }}
notify:
- call by
/tmp/hosts
.random_number
handlers:
- name: call by
/tmp/hosts
debug: msg=
"call first time"
- name: call by
/tmp/hosts
.random_number
debug: msg=
"call by /tmp/hosts.random_number"
|
-
按Handler的定义顺序执行
andlers是按照在handlers中定义个顺序执行的,而不是安装notify的顺序执行的。
下面的例子定义的顺序是1>2>3,notify的顺序是3>2>1,实际执行顺序:1>2>3
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
|
---
- hosts: lb
remote_user: root
gather_facts: no
vars:
random_number1:
"{{ 10000 | random }}"
random_number2:
"{{ 10000000000 | random }}"
tasks:
- name: Copy the
/etc/hosts
to /tmp/hosts .{{ random_number1 }}
copy: src=
/etc/hosts
dest= /tmp/hosts .{{ random_number1 }}
notify:
- define the 3nd handler
- name: Copy the
/etc/hosts
to /tmp/hosts .{{ random_number2 }}
copy: src=
/etc/hosts
dest= /tmp/hosts .{{ random_number2 }}
notify:
- define the 2nd handler
- define the 1nd handler
handlers:
- name: define the 1nd handler
debug: msg=
"define the 1nd handler"
- name: define the 2nd handler
debug: msg=
"define the 2nd handler"
- name: define the 3nd handler
debug: msg=
"define the 3nd handler"
|
4、变量
playbook中常用变量的几种情况:
-
用户自定义的变量
用户可以在Playbook中,通过vars关键字自定义变量,使用时用{{ }}引用以来即可。
示例:
1
2
3
4
5
6
7
8
|
---
- hosts: web
vars:
http_port: 80
remote_user: root
tasks:
- name: insert firewalld rule
for
httpd
firewalld: port={{ http_port }}
/tcp
permanent= true state=enabled immediate= yes
|
-
把变量放在单独的文件中
当变量比较多的时候,或者变量需要在多个playbook中重用的时候,可以把变量放到一个单独的文件中。通过关键字var_files把文件中定义的变量引入playbook中,使用变量的方法和在本文件中定义的变量相同。
1
2
3
4
5
6
7
8
9
10
|
- hosts: web
remote_user: root
vars_files:
- vars
/server_vars
.yml
tasks:
- name: insert firewalld rule
for
httpd
firewalld: port={{ http_port }}
/tcp
permanent= true state=enabled immediate= yes
#cat vars/server_vars.yml
http_port: 80
|
定义复杂的变量
当变量的值不是简单的字符串或者数字,而是一个字典,语法如下
1
2
3
|
foo:
field1: one
field2: two
|
访问复杂变量中的子属性,可以利用中括号或者点号:
1
2
3
|
foo[
'field1'
]
或
foo.field1
|
-
系统变量(facts)
ansible会通过module setup来收集主机的系统信息,这些收集到的系统信息叫做facts,这些facts信息可以直接以变量的形式使用。
有哪些facts变量可以引用呢?在命令行上通过调用setup module命令可以查看:
1
|
$ ansible all -m setup -u root
|
系统变量在playbook中可以直接使用:
1
2
3
4
5
6
7
8
9
10
11
12
|
---
- hosts: all
user: root
tasks:
- name:
echo
system
shell:
echo
{{ ansible_os_family }}
- name
install
ntp on Debian linux
apt: name=git state=installed
when: ansible_os_family ==
"Debian"
- name
install
ntp on redhat linux
yum: name=git state=present
when: ansible_os_family ==
"RedHat"
|
对于复杂的、多层级的facts变量,可以通过下面的两种方式的任意一种访问复杂的变量中的子属性:
中括号:
1
|
{{ ansible_ens3[
"ipv4"
][
"address"
] }}
|
点号:
1
|
{{ ansible_ens3.ipv4.address }}
|
关系facts变量,一旦关闭之后就不用调用了。关闭方法:
1
2
|
- hosts: webserver
gather_facts: no
|
-
注册变量
把task的执行结果也可以作为一个变量值。这个时候就需要用到“注册变量”,将执行结果注册到一个变量中,待后面的action使用。
1
2
3
4
5
6
7
8
9
|
---
- hosts: web
tasks:
- shell:
ls
register: result
ignore_errors: True
- shell:
echo
"{{ result.stdout }}"
when: result.rc == 5
- debug: msg=
"{{ result.stdout }}"
|
注册变量经常和debug module一起使用,这样可以得到更多action的输出信息,帮助用户调试。
-
命令行中传递的变量
为了使Playbook更灵活、通用性更强,允许用户在执行的时候传入变量的值,这个时候就需要用到“额外变量”。
定义命令行变量
在test.yml文件里,hosts和user都定义为变量,需要从命令行传递变量值。
1
2
3
4
5
|
---
- hosts:
'{{ hosts }}'
remote_user:
'{{ user }}'
tasks:
...
|
在命令行里面传值得的方法:
1
|
ansible-playbook testyml --extra-vars
"hosts=web user=root"
|
还可以用json格式传递参数:
1
|
ansible-playbook
test
.yml --extra-vars
"{'hosts':'vm-rhel7-1', 'user':'root'}"
|
还可以将参数放在文件里面:
1
|
ansible-playbook
test
.yml --extra-vars
"@vars.json"
|
5、如何执行playbook
语法:
1
|
$ ansible-playbook deploy.yml
|
查看输出的细节
1
|
$ ansible-playbook playbook.yml --verbose
|
查看该脚本影响哪些hosts
1
|
$ ansible-playbook playbook.yml --list-hosts
|
并行执行脚本
1
|
$ ansible-playbook playbook.yml -f 10
|
四、playbooks的高级用法
1、逻辑控制
-
when: 条件判断语句,类似于变成语言中的if
主机为Debian Linux立刻关机:
1
2
3
4
|
tasks:
- name:
"shutdown Debian flavored systems"
command
:
/sbin/shutdown
-t now
when: ansible_os_family ==
"Debian"
|
根据action的执行结果,来决定接下来执行的action
1
2
3
4
5
6
7
8
9
10
|
tasks:
-
command
:
/bin/false
register: result
ignore_errors: True
-
command
:
/bin/something
when: result|failed
-
command
:
/bin/something_else
when: result|success
-
command
:
/bin/still/something_else
when: result|skipped
|
远程中的系统变量facts变量作为when的条件,用“|int”还可以转换返回值的类型:
1
2
3
4
5
|
---
- hosts: web
tasks:
- debug: msg=
"only on Red Hat 7, derivatives, and later"
when: ansible_os_family ==
"RedHat"
and ansible_lsb.major_release|int >= 6
|
条件表达式
基本:
1
2
3
|
tasks:
- shell:
echo
"This certainly is epic!"
when: epic
|
否定:
1
2
3
|
tasks:
- shell:
echo
"This certainly isn't epic!"
when: not epic
|
变量定义:
1
2
3
4
5
|
tasks:
- shell:
echo
"I've got '{{ foo }}' and am not afraid to use it!"
when: foo is defined
- fail: msg=
"Bailing out. this play requires 'bar'"
when: bar is not defined
|
数值表达:
1
2
3
4
|
tasks:
-
command
:
echo
{{ item }}
with_items: [ 0, 2, 4, 6, 8, 10 ]
when: item > 5
|
与Include一起用:
1
2
|
- include: tasks
/sometasks
.yml
when:
"'reticulating splines' in output"
|
与Role一起用:
1
2
3
|
- hosts: webservers
roles:
- { role: debian_stock_config, when: ansible_os_family ==
'Debian'
}
|
-
loop: 循环语句,类似于编程语言的中的while
标准循环
为了保持简洁,重复的任务可以用以下简写的方式:
1
2
3
4
|
- name: add several
users
user: name={{ item }} state=present
groups
=wheel
with_items:
- testuser1
|
如果你在变量文件中或者 'vars' 区域定义了一组YAML列表,你也可以这样做:
1
2
3
4
5
6
|
vars:
somelist: [
"testuser1"
,
"testuser2"
]
tasks:
-name: add several user
user: name={{ item }} state=present
groups
=wheel
with_items:
"{{somelist}}"
|
使用 'with_items' 用于迭代的条目类型不仅仅支持简单的字符串列表.如果你有一个哈希列表,那么你可以用以下方式来引用子项:
1
2
3
4
5
|
- name: add several
users
user: name={{ item.name }} state=present
groups
={{ item.
groups
}}
with_items:
- { name:
'testuser1'
,
groups
:
'wheel'
}
- { name:
'testuser2'
,
groups
:
'root'
}
|
注意:如果同时使用 when 和 with_items (或其它循环声明),when声明会为每个条目单独执行。
嵌套循环:
1
2
3
4
5
6
7
8
9
10
11
|
- name: give
users
access to multiple databases
mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=
yes
password=foo
with_nested:
- [
'alice'
,
'bob'
]
- [
'clientdb'
,
'employeedb'
,
'providerd'
]
或者
- name: give
users
access to multiple databases
mysql_user: name={{ item.0 }} priv={{ item.1 }}.*:ALL append_privs=
yes
password=foo
with_nested:
- [
'alice'
,
'bob'
]
- [
'clientdb'
,
'employeedb'
,
'providerd'
]
|
对字典使用循环:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
---
vars:
users
:
alice:
name: Alice Appleworth
telephone: 123-456-7890
bob:
name: Bob Bananarama
telephone: 987-654-3210
tasks:
- name: Print phone records
debug: msg=
"User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict:
"{{users}}"
|
对文件列表使用循环
with_fileglob 可以以非递归的方式来模式匹配单个目录中的文件.如下面所示:
1
2
3
4
5
6
7
|
tasks:
# first ensure our target directory exists
-
file
: dest=
/etc/fooapp
state=directory
# copy each file over that matches the given pattern
- copy: src={{ item }} dest=
/etc/fooapp/
owner=root mode=600
with_fileglob:
-
/playbooks/files/fooapp/
*
|
-
block: 把几个tasks组成一块代码,便于针对一组操作的异常处理等操作。
多个action组装成块,可以根据不同条件执行一段语句 :
1
2
3
4
5
6
7
8
9
10
11
|
tasks:
- block:
- yum: name={{ item }} state=installed
with_items:
- httpd
- memcached
- template: src=templates
/src
.j2 dest=
/etc/foo
.conf
- service: name=bar state=started enabled=True
when: ansible_distribution ==
'CentOS'
become:
true
become_user: root
|
组装成块处理异常更方便:
1
2
3
4
5
6
7
8
9
10
11
|
tasks:
- block:
- debug: msg=
'i execute normally'
-
command
:
/bin/false
- debug: msg=
'i never execute, cause ERROR!'
rescue:
- debug: msg=
'I caught an error'
-
command
:
/bin/false
- debug: msg=
'I also never execute :-('
always:
- debug: msg=
"this always executes"
|
2、重用playbook
Playbook支持两种重用机制,一种是重用静态Playbook脚本,另外一种是类似于编程语言中函数的机制。
-
include语句 - 重用静态的Playbook脚本,使用起来简单、直接。
Include语句的功能,基本的代码重用机制。主要重用tasks。同时Include可将tasks分割成多个文件,避免Playbook过于臃肿,使用户更关注于整体的架构,而不是实现的细节上。
普通用法
像其它语言的Include语句一样,直接Include:
1
2
3
4
5
6
7
8
|
---
# possibly saved as tasks/firewall_httpd_default.yml
- name: insert firewalld rule
for
httpd
firewalld: port=80
/tcp
permanent= true state=enabled immediate= yes
main.yml文件中调用include的方法:
tasks:
- include: tasks
/firewall_httpd_default
.yml
|
高级用法-使用参数
include文件中还可以定义参数
被include的文件tasks/firewall_httpd_default.yml中,使用{{ port }}定义了一个名字为port的参数。
1
2
3
|
---
- name: insert firewalld rule
for
httpd
firewalld: port={{ port }}
/tcp
permanent= true state=enabled immediate= yes
|
传参数的各种方法
在执行的playbook传参数,可以加在行尾,使用空格分隔:
1
2
3
4
|
tasks:
- include: tasks
/firewall
.yml port=80
- include: tasks
/firewall
.yml port=3260
- include: tasks
/firewall
.yml port=423
|
还可以使用yml的字典传参数:
1
2
3
4
5
6
7
|
tasks:
- include: wordpress.yml
vars:
wp_user: timmy
ssh_keys:
- keys
/one
.txt
- keys
/two
.txt
|
还可以把一条task简写成成一个类JSON的形式传参数:
1
|
- { include: wordpress.yml, wp_user: timmy, ssh_keys: [
'keys/one.txt'
,
'keys/two.txt'
] }
|
当然在playbook中已经定义了的参数,就不需要再显示传入值了,可以直接写成下面的:
1
2
3
4
5
6
7
|
---
- hosts: lb
vars:
port: 3206
remote_user: root
tasks:
- include: tasks
/firewall
.yml
|
-
role语言 - Playbook的"函数机制",使用方法稍复杂、功能强大。是Playbook脚本的共享平台ansible galaxy主要的分享方式
Role是比include更强大灵活的代码重用和分享机制。Include类似于编程语言中的include,是重用单个文件的,功能有限。
而Role类似于编程语言中的“Package”,可以重用一组文件形成完整的功能。例如安装和配置apache,需要tasks实现安装包和拷贝模版等,httpd.conf和index.html的模版文件,和handler文件实现重起功能。这些文件都可以放在一个role里面,供不同的playbook文件重用。
Ansible非常提倡在playbook中使用role,并且提供了一个分享role的平台Ansible Galaxy, https://galaxy.ansible.com/, 在galaxy上可以找到别人写好的role。
在ansible中,通过遵循特定的目录结构,就可以实现对role的定义:
下面的目录结构定义了一个role:名字为myrole。在site.yml,调用了这个role。
role的目录结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
site.yml
roles/
├── myrole
├── tasks
│ └── main.yml
├── handlers
│ └── main.yml
├── defaults
│ └── main.yml
├── vars
│ └── main.yml
├── files
├── templates
├── README.md
├── meta
│ └── main.yml
└── tests
├── inventory
└──
test
.yml
|
site.yml中调用role
1
2
3
4
|
---
- hosts: webservers
roles:
- myrole
|
ansible并不要求role包含上述所有的目录及文件,根据role的功能需要加入对应的目录和文件。下面是每个目录和文件的功能。
如果 roles/x/tasks/main.yml 存在, 其中列出的 tasks 将被添加到 play 中,所以这个文件也可以视作role的入口文件,想看role做了什么操作,可以从此文件看起。
如果 roles/x/handlers/main.yml 存在, 其中列出的 handlers 将被添加到 play 中
如果 roles/x/vars/main.yml 存在, 其中列出的 variables 将被添加到 play 中
如果 roles/x/meta/main.yml 存在, 其中列出的 “角色依赖” 将被添加到 roles 列表中
roles/x/tasks/main.yml中所有tasks,可以引用 roles/x/{files,templates,tasks}中的文件,不需要指明文件的路径。
在写role的时候,一般都要包含role入口文件roles/x/tasks/main.yml,其它的文件和目录,可以根据需求选择加入。
参数在role中是如何定义的呢
定义一个带参数的role,名字是myrole,那么目录结构为:
1
2
3
4
5
|
main.yml
roles
role_with_var
tasks
main.yml
|
在roles/myrole/tasks/main.yml中,使用{{ }}定义的变量就可以了
1
2
3
|
---
- name: use param
debug: msg=
"{{ param }}"
|
使用带参数的role
那么在main.yml就可以用如下的方法使用myrole
1
2
3
4
5
|
---
- hosts: webservers
roles:
- { role: myrole, param:
'Call some_role for the 1st time'
}
- { role: myrole, param:
'Call some_role for the 2nd time'
}
|
或者写成YAML字典格式:
1
2
3
4
5
6
7
|
---
- hosts: webservers
roles:
- role: myrole
param:
'Call some_role for the 1st time'
- role: myrole
param:
'Call some_role for the 2nd time'
|
role指定默认的参数
指定默认参数后,如果在调用时传参数了,那么就使用传入的参数值.如果调用的时候没有传参数,那么就使用默认的参数值.
指定默认参数很简单,以上面的role_with_var为例
1
2
3
4
5
6
7
|
main.yml
roles:
myrole
tasks
main.yml
defaults
main.yml
|
在roles/myrole/defaults/main.yml中,使用yml的字典定义语法定义param的值,如下:
param: "I am the default value"
这样在main.yml中,下面两种调用方法都可以
1
2
3
4
5
|
---
- hosts: webservers
roles:
- role_with_var
- { role: role_with_var, param:
'I am the value from external'
}
|
role与条件语句when一起执行,下面的例子中,my_role只有在RedHat系列的server上才执行。
1
2
3
4
5
6
7
8
9
10
11
|
---
- hosts: webservers
roles:
- { role: my_role, when:
"ansible_os_family == 'RedHat'"
}
同样也可以写成YAML字典格式
---
- hosts: webservers
roles:
- role: my_role
when:
"ansible_os_family == 'RedHat'"
roles和tasks的执行顺序
|
如果一个playbook同时出现role和tasks,他们的调用顺序如下:
pre_tasks > role > tasks > post_tasks
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
|
---
- hosts: lb
user: root
pre_tasks:
- name: pre
shell:
echo
'hello'
roles:
- { role: some_role }
tasks:
- name: task
shell:
echo
'still busy'
post_tasks:
- name: post
shell:
echo
'goodbye'
执行的结果为:
PLAY [lb] **********************************************************************
TASK [setup] *******************************************************************
ok: [rhel7u3]
TASK [pre] *********************************************************************
changed: [rhel7u3]
TASK [some_role : some role] ***************************************************
ok: [rhel7u3] => {
"msg"
:
"Im some role"
}
TASK [task] ********************************************************************
changed: [rhel7u3]
TASK [post] ********************************************************************
changed: [rhel7u3]
PLAY RECAP *********************************************************************
rhel7u3 : ok=5 changed=3 unreachable=0 failed=0
|
3、tags的用法
如果playbook文件比较大,在执行的时候只是想执行部分功能,这个时候没有有解决方案呢?Playbook提供了tags便签可以实现部分运行。
tags的基本用法
例如,文件example.yml如何所示,标记了两个tag:packages和configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
tasks:
- yum: name={{ item }} state=installed
with_items:
- httpd
tags:
- packages
- name: copy httpd.conf
template: src=templates
/httpd
.conf.j2 dest=
/etc/httpd/conf/httpd
.conf
tags:
- configuration
- name: copy index.html
template: src=templates
/index
.html.j2 dest=
/var/www/html/index
.html
tags:
- configuration
|
那么我们在执行的时候,如果不加任何tag参数,那么会执行所有的tasks
1
|
$ ansible-playbook example.yml
|
指定执行安装部分的tasks,则可以利用关键字tags
1
|
$ ansible-playbook example.yml --tags
"packages"
|
指定不执行packages部分的task,则可以利用关键字skip-tags
1
|
$ ansible-playbook example.yml --skip-tags
"configuration"
|
特殊的Tags:“always”
tags的名字是用户自定义的,但是如果你把tags的名字定义为“always”,那么就有点特别了。只要在执行playbook时,没有明确指定不执行always tag,那么它就会被执行。
在下面的例子中,即使你只指定执行packages,那么always也会被执行。
1
2
3
4
5
6
7
8
9
10
11
12
|
tasks:
- debug: msg=
"Always print this debug message"
tags:
- always
- yum: name= state=installed
with_items:
- httpd
tags:
- packages
- template: src=templates
/httpd
.conf.j2 dest=
/etc/httpd/conf/httpd
.conf
tags:
- configuration
|
指定运行packages时,还是会执行always tag对应的tasks
1
2
3
4
5
6
7
8
9
10
11
|
$ ansible-playbook tags_always.yml --tags
"packages"
“tagged”,“untagged”和“all”
tasks:
- debug: msg=
"I am not tagged"
tags:
- tag1
- debug: msg=
"I am not tagged"
分别指定--tags为“tagged”,“untagged”和“all”试下效果吧:
$ ansible-playbook tags_tagged_untagged_all.yml --tags tagged
$ ansible-playbook tags_tagged_untagged_all.yml --tags untagged
$ ansible-playbook tags_tagged_untagged_all.yml --tags all
|
在include中和role中使用tags
include语句指定执行的tags的语法:
1
2
|
- include: foo.yml
tags: [web,foo]
|
调用role中的tags的语法为:
1
2
|
roles:
- { role: webserver, port: 5000, tags: [
'web'
,
'foo'
] }
|
4、playbook文件加密
ansible-vault命令可以对配置文件进行基于密码的加密,防止敏感信息泄漏。
加密已存在文件
1
|
$ ansible-vault encrypt site.yml
|
加密并创建文件
1
|
$ ansible-vault create filename
|
执行加密后的playbook
1
|
$ ansible-playbook site.yml --ask-vault-pass
|
解密
1
|
$ ansible-vault decrypt site.yml
|
五、编译安装nginx的playbook的实例
playbooks目录结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
$ tree playbooks
playbooks
├── group_vars
├── roles
│ └── nginx
│ ├── files
│ │ ├── nginx-1.8.0.
tar
.gz
│ │ └── pcre-8.33.
tar
.gz
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ └── default.conf
└── site.yml
|
yml文件内内容如下:
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
|
$
cat
playbooks /site .yml
---
- name: Install Nginx
hosts:
test
user: admin
roles:
- nginx
$
cat
playbooks /roles/nginx/tasks/main .yml
---
- name: Copy nginx software
copy: src=nginx-1.8.0.
tar
.gz dest=
/app/admin/soft/nginx-1
.8.0.
tar
.gz
- name: Copy pcre software
copy: src=pcre-8.33.
tar
.gz dest=
/app/admin/soft/pcre-8
.33.
tar
.gz
- name: Install Pcre
shell:
cd
/app/admin/soft ; tar zxf pcre-8.33. tar .gz; cd pcre-8.33;. /configure ; make ; make install
sudo
:
yes
- name: Install Nginx
shell:
cd
/app/admin/soft ; tar zxf nginx-1.8.0. tar .gz; cd nginx-1.8.0;. /configure --user=admin --group=app --prefix= /app/admin/nginx --with-http_stub_status_module --with-http_ssl_module; make ; make install
- name: Copy nginx configuration
template: src=default.conf dest=
/app/admin/nginx/conf/nginx
.conf
notify: restart nginx
[admin@wts-vm-01 ~]$
cat
playbooks /roles/nginx/handlers/main .yml
---
- name: restart nginx
shell:
/app/admin/nginx/sbin/nginx
sudo
:
yes
|
六、其它
官方例子
Ansible官方提供了一些比较常用的、经过测试的Playbook例子:
https://github.com/ansible/ansible-examples
Playbook分享平台:
https://galaxy.ansible.com/