自动化运维工具之Ansible

Posted by Mathew on 2016-12-14

常见的运维自动化管理工具有哪些呢?

  • puppet master/agent模型
  • saltstack master/agent模型
  • ansible 无agnet模型

今天我们主要来聊聊Ansible的架构、运行原理和使用方法

Ansible介绍

简单来说,Ansible是一款自动化运维管理工具。
官方的title是“Ansible is Simple IT Automation”——一个简单的自动化IT工具。包括以下功能:

  • 自动化部署Applications
  • 自动化管理配置项
  • 自动化的持续交互
  • 自动化的(AWS)云服务管理。

  所有的这几个目标从本质上来说都是在一台或多台服务器上,执行一系列的命令而已。通俗的说就是批量的在远程服务器上执行命令。最主要的是它是基于paramiko开发的,paramiko是什么呢?它是一个纯python实现的ssh协议库。因此使用ansible不需要在远程主机上安装clients/agnts,因为它们是基于SSH来和远程主机通讯的。简单归纳一下:

Ansible架构

运维工具对比

  • Ansible
    • 基于Python paramiko 开发,分布式,无需客户端,轻量级,配置语法使用YAML机Jinja2模板语言,更强的远程命令执行操作。
  • Puppet
    • 基于Ruby开发,采用C/S架构,扩展性强,基于SSL,远程命令执行想对较弱。
  • SaltStack
    • 基于Python开发,采用C/S架构,相对puppet更轻量级,配置语法使用YAML,使得配置脚本更简单。

   ansible是新出现的运维工具是基于Python研发的糅合了众多老牌运维工具的有点实现了批量操作系统配置、批量程序的部署、批量运行命令等功能。

Ansible安装

ansible依赖于Python 2.6或更高的版本、paramiko、PyYAML及Jinja2。
安装方式可以下载源码包编译安装也可以使用rpm包进行安装
1
yum install ansible -y

Ansible简单应用

    ansible通过ssh实现配置管理、应用部署、任务执行等功能,因此,需要事先配置ansible端能基于密钥认证的方式联系各被管理节点。

Ad-hoc

  在下面的例子中,我们将演示如何使用 /usr/bin/ansible 运行 ad hoc 任务.

所谓 ad-hoc 命令是什么呢?

(这其实是一个概念性的名字,是相对于写 Ansible playbook 来说的.类似于在命令行敲入shell命令和 写shell scripts两者之间的关系)…

  如果我们敲入一些命令去比较快的完成一些事情,而不需要将这些执行的命令特别保存下来, 这样的命令就叫做 ad-hoc 命令.

  Ansible提供两种方式去完成任务,一是 ad-hoc 命令,一是写 Ansible playbook.前者可以解决一些简单的任务, 后者解决较复杂的任务.

一般而言,在学习了 playbooks 之后,你才能体会到 Ansible 真正的强大之处在哪里.

那我们会在什么情境下去使用ad-hoc 命令呢?

比如说因为圣诞节要来了,想要把所有实验室的电源关闭,我们只需要执行一行命令 就可以达成这个任务,而不需要写 playbook 来做这个任务.

1
2
3
4
5
6
ansible <host-pattern> [-f forks] [-m module_name] [-a args]
-m module:默认为command

ansible-doc: Show Ansible module documentation
-l, --list List available modules
-s, --snippet Show playbook snippet for specified module(s)
  1. 查看ansible支持的模块
1
ansible-doc -l
  1. 常用模块
  • ping:用于测试节点之间连通性,及SSH是否配好
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@node1 ~]# ansible websrvs -m ping 
172.16.100.32 | SUCCESS => {
"changed": false,
"ping": "pong"
}
172.16.100.33 | SUCCESS => {
"changed": false,
"ping": "pong"
}
172.16.100.31 | SUCCESS => {
"changed": false,
"ping": "pong"
}
  • file
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
53
54
55
56
57
58
59
60
#file 模块允许更改文件的用户及权限
ansible webservers -m file -a "dest=/srv/foo/a.txt mode=600"
ansible webservers -m file -a "dest=/srv/foo/b.txt mode=600 owner=zzidc group=zzidc"
#使用file 模块创建目录,类似mkdir -p
ansible webservers -m file -a "dest=/path/to/c mode=755 owner=ju group=ju state=directory"
#使用file 模块删除文件或者目录
ansible webservers -m file -a "dest=/path/to/c state=absent"
[root@node1 ~]# ansible websrvs -m shell -a 'ls -la /srv/foo/a.txt'
172.16.100.31 | SUCCESS | rc=0 >>
-rw------- 1 root root 14 Dec 14 02:30 /srv/foo/a.txt

172.16.100.33 | SUCCESS | rc=0 >>
-rw------- 1 root root 14 Dec 14 02:32 /srv/foo/a.txt

172.16.100.32 | SUCCESS | rc=0 >>
-rw------- 1 root root 14 Dec 14 02:31 /srv/foo/a.txt

[root@node1 ~]# ansible websrvs -m file -a 'dest=/srv/foo/a.txt mode=755 owner=zzidc group=zzidc'
172.16.100.31 | SUCCESS => {
"changed": true,
"gid": 503,
"group": "zzidc",
"mode": "0755",
"owner": "zzidc",
"path": "/srv/foo/a.txt",
"size": 14,
"state": "file",
"uid": 503
}
172.16.100.32 | SUCCESS => {
"changed": true,
"gid": 502,
"group": "zzidc",
"mode": "0755",
"owner": "zzidc",
"path": "/srv/foo/a.txt",
"size": 14,
"state": "file",
"uid": 502
}
172.16.100.33 | SUCCESS => {
"changed": true,
"gid": 502,
"group": "zzidc",
"mode": "0755",
"owner": "zzidc",
"path": "/srv/foo/a.txt",
"size": 14,
"state": "file",
"uid": 502
}
[root@node1 ~]# ansible websrvs -m shell -a 'ls -la /srv/foo/a.txt'
172.16.100.32 | SUCCESS | rc=0 >>
-rwxr-xr-x 1 zzidc zzidc 14 Dec 14 02:31 /srv/foo/a.txt

172.16.100.33 | SUCCESS | rc=0 >>
-rwxr-xr-x 1 zzidc zzidc 14 Dec 14 02:32 /srv/foo/a.txt

172.16.100.31 | SUCCESS | rc=0 >>
-rwxr-xr-x 1 zzidc zzidc 14 Dec 14 02:30 /srv/foo/a.txt
  • setup
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#通过命令获取所有的系统信息
#搜集主机的所有系统信息
ansible all -m setup
#搜集系统信息并以主机名为文件名分别保存在/tmp/facts 目录
ansible all -m setup --tree /tmp/facts
#搜集和内存相关的信息
ansible all -m setup -a 'filter=ansible_*_mb'
#搜集网卡信息
ansible all -m setup -a 'filter=ansible_eth[0-2]'

[root@node1 ~]# ansible websrvs -m setup --tree /srvs/foo
172.16.100.31 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"172.16.100.31"
],
"ansible_all_ipv6_addresses": [
"fe80::20c:29ff:fe70:abc1"
],
"ansible_architecture": "x86_64",
"ansible_bios_date": "07/02/2015",
"ansible_bios_version": "6.00",
"ansible_cmdline": {
"KEYBOARDTYPE": "pc",
"KEYTABLE": "us",
"LANG": "en_US.UTF-8",
"SYSFONT": "latarcyrheb-sun16",
"quiet": true,
"rd_NO_DM": true,
"rd_NO_LUKS": true,
"rd_NO_LVM": true,
"rd_NO_MD": true,
"rhgb": true,
"ro": true,
"root": "UUID=9382ff56-240e-4077-9a78-7a6a81f00b46"
},
"ansible_date_time": {
"date": "2016-12-14",
"day": "14",
"epoch": "1481655633",
"hour": "03",
"iso8601": "2016-12-13T19:00:33Z",
"iso8601_basic": "20161214T030033116410",
"iso8601_basic_short": "20161214T030033",
"iso8601_micro": "2016-12-13T19:00:33.116671Z",
"minute": "00",
"month": "12",
"second": "33",
"time": "03:00:33",
"tz": "CST",
"tz_offset": "+0800",
"weekday": "Wednesday",
"weekday_number": "3",
"weeknumber": "50",
"year": "2016"
},
"ansible_default_ipv4": {
"address": "172.16.100.31",
"alias": "eth0",
"broadcast": "172.16.100.255",
"gateway": "172.16.100.2",
"interface": "eth0",
"macaddress": "00:0c:29:70:ab:c1",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "172.16.100.0",
"type": "ether"
},
"ansible_default_ipv6": {},
"ansible_devices": {
"sda": {
"holders": [],
"host": "",
"model": "VMware Virtual S",
"partitions": {
"sda1": {
"holders": [],
"sectors": "1024000",
"sectorsize": 512,
"size": "500.00 MB",
"start": "2048",
"uuid": "772291b4-e62c-4d82-ae7e-0d039d2f01c5"
},
"sda2": {
"holders": [],
"sectors": "20480000",
"sectorsize": 512,
"size": "9.77 GB",
"start": "1026048",
"uuid": "9382ff56-240e-4077-9a78-7a6a81f00b46"
},
"sda3": {
"holders": [],
"sectors": "10240000",
"sectorsize": 512,
"size": "4.88 GB",
"start": "21506048",
"uuid": "79b43e90-8b81-42bb-938f-1b8a0fda676f"
},
"sda4": {
"holders": [],
"sectors": "2",
"sectorsize": 512,
"size": "1.00 KB",
"start": "31746048",
"uuid": null
},
"sda5": {
"holders": [],
"sectors": "8192000",
"sectorsize": 512,
"size": "3.91 GB",
"start": "31750144",
"uuid": "950426af-15db-4a51-afa1-84df20dd8a8a"
}
},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "cfq",
"sectors": "209715200",
"sectorsize": "512",
"size": "100.00 GB",
"support_discard": "0",
"vendor": "VMware,"
},
"sr0": {
"holders": [],
"host": "",
"model": "VMware IDE CDR10",
"partitions": {},
"removable": "1",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "cfq",
"sectors": "9048064",
"sectorsize": "2048",
"size": "17.26 GB",
"support_discard": "0",
"vendor": "NECVMWar"
}
},
"ansible_distribution": "CentOS",
"ansible_distribution_major_version": "6",
"ansible_distribution_release": "Final",
"ansible_distribution_version": "6.6",
"ansible_dns": {
"nameservers": [
"8.8.8.8",
"8.8.4.4"
],
"search": [
"localdomain"
]
},
"ansible_domain": "",
"ansible_env": {
"G_BROKEN_FILENAMES": "1",
"HOME": "/root",
"LANG": "en_US.UTF-8",
"LESSOPEN": "||/usr/bin/lesspipe.sh %s",
"LOGNAME": "root",
"MAIL": "/var/mail/root",
"PATH": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin",
"PWD": "/root",
"SHELL": "/bin/bash",
"SHLVL": "2",
"SSH_CLIENT": "172.16.100.30 57549 22",
"SSH_CONNECTION": "172.16.100.30 57549 172.16.100.31 22",
"SSH_TTY": "/dev/pts/1",
"TERM": "xterm",
"USER": "root",
"_": "/usr/bin/python"
},
"ansible_eth0": {
"active": true,
"device": "eth0",
"features": {
"fcoe_mtu": "off [fixed]",
"generic_receive_offload": "on",
"generic_segmentation_offload": "on",
"highdma": "off [fixed]",
"large_receive_offload": "off [fixed]",
"loopback": "off [fixed]",
"netns_local": "off [fixed]",
"ntuple_filters": "off [fixed]",
"receive_hashing": "off [fixed]",
"rx_checksumming": "on",
"rx_vlan_filter": "on [fixed]",
"rx_vlan_offload": "on [fixed]",
"scatter_gather": "on",
"tcp_segmentation_offload": "on",
"tx_checksum_fcoe_crc": "off [fixed]",
"tx_checksum_ip_generic": "on",
"tx_checksum_ipv4": "off",
"tx_checksum_ipv6": "off",
"tx_checksum_sctp": "off [fixed]",
"tx_checksum_unneeded": "off",
"tx_checksumming": "on",
"tx_fcoe_segmentation": "off [fixed]",
"tx_gre_segmentation": "off [fixed]",
"tx_gso_robust": "off [fixed]",
"tx_lockless": "off [fixed]",
"tx_scatter_gather": "on",
"tx_scatter_gather_fraglist": "off [fixed]",
"tx_tcp6_segmentation": "off",
"tx_tcp_ecn_segmentation": "off",
"tx_tcp_segmentation": "on",
"tx_udp_tnl_segmentation": "off [fixed]",
"tx_vlan_offload": "on [fixed]",
"udp_fragmentation_offload": "off [fixed]",
"vlan_challenged": "off [fixed]"
},
"ipv4": {
"address": "172.16.100.31",
"broadcast": "172.16.100.255",
"netmask": "255.255.255.0",
"network": "172.16.100.0"
},
"ipv6": [
{
"address": "fe80::20c:29ff:fe70:abc1",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "00:0c:29:70:ab:c1",
"module": "e1000",
"mtu": 1500,
"pciid": "0000:02:01.0",
"promisc": false,
"speed": 1000,
"type": "ether"
},
  • service
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#确保webservers 组所有主机的httpd 是启动的
ansible webservers -m service -a "name=httpd state=started"
#重启webservers 组所有主机的httpd 服务
ansible webservers -m service -a "name=httpd state=restarted"
#确保webservers 组所有主机的httpd 是关闭的
ansible webservers -m service -a "name=httpd state=stopped"

[root@node1 ~]# ansible websrvs -m service -a 'name=httpd state=started'
172.16.100.31 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "started"
}
172.16.100.32 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "started"
}
172.16.100.33 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "started"
}
[root@node1 ~]# ansible websrvs -m service -a 'name=httpd state=stopped'
172.16.100.33 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "stopped"
}
172.16.100.32 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "stopped"
}
172.16.100.31 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "stopped"
[root@node1 ~]# ansible websrvs -m service -a 'name=httpd state=restarted'
172.16.100.31 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "started"
}
172.16.100.33 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "started"
}
172.16.100.32 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "started"
}
[root@node1 ~]# ansible websrvs -m shell -a 'service httpd status '
172.16.100.33 | SUCCESS | rc=0 >>
httpd (pid 2253) is running...

172.16.100.32 | SUCCESS | rc=0 >>
httpd (pid 2250) is running...

172.16.100.31 | SUCCESS | rc=0 >>
httpd (pid 2084) is running...

[root@node1 ~]# ansible websrvs -m shell -a 'ss -tnl | grep 80 '
172.16.100.32 | SUCCESS | rc=0 >>
LISTEN 0 128 :::80 :::*

172.16.100.33 | SUCCESS | rc=0 >>
LISTEN 0 128 :::80 :::*

172.16.100.31 | SUCCESS | rc=0 >>
LISTEN 0 128 :::80 :::*
  • cron
1
2
3
ansible all -m cron -a 'name="jutest" hour="5" job="/bin/bash /tmp/test.sh"'
效果如下:
* 5 * * * /bin/bash /tmp/test.sh
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
[root@node1 ~]# ansible websrvs -m cron -a 'name="maricletest" hour="5" job="/bin/bash /tmp/test.sh"'
172.16.100.31 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"maricletest"
]
}
172.16.100.33 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"maricletest"
]
}
172.16.100.32 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"maricletest"
]
}
[root@node1 ~]# ansible websrvs -m shell -a 'crontab -l '
172.16.100.32 | SUCCESS | rc=0 >>
#Ansible: maricletest
* 5 * * * /bin/bash /tmp/test.sh

172.16.100.31 | SUCCESS | rc=0 >>
#Ansible: maricletest
* 5 * * * /bin/bash /tmp/test.sh

172.16.100.33 | SUCCESS | rc=0 >>
#Ansible: maricletest
* 5 * * * /bin/bash /tmp/test.sh
  • user
1
2
3
#使用user 模块对于创建新用户和更改、删除已存在用户非常方便
ansible all -m user -a "name=foo password=<crypted password here>"
ansible all -m user -a "name=foo state=absent"
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
[root@node1 ~]# ansible websrvs -m user -a "name=gainet password=gainet group=zzidc"
172.16.100.32 | SUCCESS => {
"changed": true,
"comment": "",
"createhome": true,
"group": 502,
"home": "/home/********",
"name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 503
}
172.16.100.31 | SUCCESS => {
"changed": true,
"comment": "",
"createhome": true,
"group": 503,
"home": "/home/********",
"name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 504
}
172.16.100.33 | SUCCESS => {
"changed": true,
"comment": "",
"createhome": true,
"group": 502,
"home": "/home/********",
"name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 503
}
[root@node1 ~]# ansible websrvs -m shell -a 'tail -1 /etc/passwd'
172.16.100.32 | SUCCESS | rc=0 >>
gainet:x:503:502::/home/gainet:/bin/bash

172.16.100.33 | SUCCESS | rc=0 >>
gainet:x:503:502::/home/gainet:/bin/bash

172.16.100.31 | SUCCESS | rc=0 >>
gainet:x:504:503::/home/gainet:/bin/bash

删除用户
[root@node1 ~]# ansible websrvs -m user -a "name=gainet state=absent"
172.16.100.32 | SUCCESS => {
"changed": true,
"force": false,
"name": "gainet",
"remove": false,
"state": "absent"
}
172.16.100.31 | SUCCESS => {
"changed": true,
"force": false,
"name": "gainet",
"remove": false,
"state": "absent"
}
172.16.100.33 | SUCCESS => {
"changed": true,
"force": false,
"name": "gainet",
"remove": false,
"state": "absent"
}
[root@node1 ~]# ansible websrvs -m shell -a 'tail -1 /etc/passwd'
172.16.100.33 | SUCCESS | rc=0 >>
zzidc:x:502:502::/home/zzidc:/bin/bash

172.16.100.32 | SUCCESS | rc=0 >>
zzidc:x:502:502::/home/zzidc:/bin/bash

172.16.100.31 | SUCCESS | rc=0 >>
zzidc:x:503:503::/home/zzidc:/bin/bash
  • group
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
[root@node1 ~]# ansible-doc -s group
- name: Add or remove groups
action: group
gid # Optional `GID' to set for the group.
name= # Name of the group to manage.
state # Whether the group should be present or not on the remote host.
system # If `yes', indicates that the group created is a system group.
表示添加或删除一个组,可以指明组ID,组名,是否为系统组等

创建系统组gainet,gid为307
[root@node1 ~]# ansible websrvs -m group -a 'name=gainet gid=307 state=present system=yes'
172.16.100.31 | SUCCESS => {
"changed": true,
"gid": 307,
"name": "gainet",
"state": "present",
"system": true
}
172.16.100.32 | SUCCESS => {
"changed": true,
"gid": 307,
"name": "gainet",
"state": "present",
"system": true
}
172.16.100.33 | SUCCESS => {
"changed": true,
"gid": 307,
"name": "gainet",
"state": "present",
"system": true
}
[root@node1 ~]# ansible websrvs -m shell -a 'grep gainet /etc/group'
172.16.100.31 | SUCCESS | rc=0 >>
gainet:x:307:

172.16.100.32 | SUCCESS | rc=0 >>
gainet:x:307:

172.16.100.33 | SUCCESS | rc=0 >>
gainet:x:307:
  • yum
1
2
3
4
5
6
7
8
9
#确保acme 包已经安装,但不更新
ansible webservers -m yum -a "name=acme state=present"
#确保安装包到一个特定的版本
ansible webservers -m yum -a "name=acme-1.5 state=present"
#确保一个软件包是最新版本
ansible webservers -m yum -a "name=acme state=latest"
#确保一个软件包没有被安装
ansible webservers -m yum -a "name=acme state=absent"
#Ansible 支持很多操作系统的软件包管理,使用时-m 指定相应的软件包管理工具模块,如果没有这样的模块,可以自己定义类似的模块或者使用command 模块来安装软件包
  • command
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
[root@node1 ~]# ansible-doc -s command
- name: Executes a command on a remote node
action: command
chdir # cd into this directory before running the command
creates # a filename or (since 2.0) glob pattern, when it already exists, this step will *not* be run.
executable # change the shell used to execute the command. Should be an absolute path to the executable.
free_form= # the command module takes a free form command to run. There is no parameter actually named 'free form'. See the
examples!
removes # a filename or (since 2.0) glob pattern, when it does not exist, this step will *not* be run.
warn # if command warnings are on in ansible.cfg, do not warn about this particular line if set to no/false.
[root@node1 ~]#
简单来说,command模块儿可以远程执行命令,尤其在使用ad-hoc时候用的比较多

[root@node1 ~]# ansible websrvs -m command -a 'w'
172.16.100.33 | SUCCESS | rc=0 >>
03:29:02 up 1:22, 2 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 172.16.100.1 02:08 56:41 0.06s 0.06s -bash
root pts/1 172.16.100.30 03:29 0.00s 0.09s 0.00s /bin/sh -c /usr

172.16.100.31 | SUCCESS | rc=0 >>
03:29:02 up 1:22, 2 users, load average: 0.02, 0.01, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 172.16.100.1 02:07 4:52 0.17s 0.17s -bash
root pts/1 172.16.100.30 03:29 0.00s 0.10s 0.00s /bin/sh -c /usr

172.16.100.32 | SUCCESS | rc=0 >>
03:29:02 up 1:22, 2 users, load average: 0.08, 0.06, 0.01
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 172.16.100.1 02:08 4:44 0.08s 0.08s -bash
root pts/1 172.16.100.30 03:29 0.00s 0.10s 0.00s /bin/sh -c /usr
  • shell
1
2
3
4
5
6
7
8
9
10
11
[root@node1 ~]# ansible-doc -s shell 
- name: Execute commands in nodes.
action: shell
chdir # cd into this directory before running the command
creates # a filename, when it already exists, this step will *not* be run.
executable # change the shell used to execute the command. Should be an absolute path to the executable.
free_form= # The shell module takes a free form command to run, as a string. There's not an actual option named "free form". See
the examples!
removes # a filename, when it does not exist, this step will *not* be run.
warn # if command warnings are on in ansible.cfg, do not warn about this particular line if set to no/false.
当需要远程执行的命令较多时候,这时候ad-hoc就不太适用,可以调用shell模块。
  • copy
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
[root@node1 ~]# ansible-doc -s copy 
- name: Copies files to remote locations.
action: copy
backup # Create a backup file including the timestamp information so you can get the original file back if you somehow
clobbered it incorrectly.
content # When used instead of 'src', sets the contents of a file directly to the specified value. This is for simple values,
for anything complex or with formatting please switch to the template module.
dest= # Remote absolute path where the file should be copied to. If src is a directory, this must be a directory too.
directory_mode # When doing a recursive copy set the mode for the directories. If this is not set we will use the system defaults. The
mode is only set on directories which are newly created, and will not affect those
that already existed.
follow # This flag indicates that filesystem links, if they exist, should be followed.
force # the default is `yes', which will replace the remote file when contents are different than the source. If `no', the
file will only be transferred if the destination does not exist.
group # name of the group that should own the file/directory, as would be fed to `chown'
mode # mode the file or directory should be. For those used to `/usr/bin/chmod' remember that modes are actually octal
numbers (like 0644). Leaving off the leading zero will likely have unexpected results.
As of version 1.8, the mode may be specified as a symbolic mode (for example, `u+rwx'
or `u=rw,g=r,o=r').
owner # name of the user that should own the file/directory, as would be fed to `chown'
remote_src # If False, it will search for src at originating/master machine, if True it will go to the remote/target machine for
the src. Default is False. Currently remote_src does not support recursive copying.
selevel # level part of the SELinux file context. This is the MLS/MCS attribute, sometimes known as the `range'. `_default'
feature works as for `seuser'.
serole # role part of SELinux file context, `_default' feature works as for `seuser'.
setype # type part of SELinux file context, `_default' feature works as for `seuser'.
seuser # user part of SELinux file context. Will default to system policy, if applicable. If set to `_default', it will use
the `user' portion of the policy if available
src # Local path to a file to copy to the remote server; can be absolute or relative. If path is a directory, it is copied
recursively. In this case, if path ends with "/", only inside contents of that
directory are copied to destination. Otherwise, if it does not end with "/", the
directory itself with all contents is copied. This behavior is similar to Rsync.
unsafe_writes # Normally this module uses atomic operations to prevent data corruption or inconsistent reads from the target files,
sometimes systems are configured or just broken in ways that prevent this. One example
are docker mounted files, they cannot be updated atomically and can only be done in an
unsafe manner. This boolean option allows ansible to fall back to unsafe methods of
updating files for those cases in which you do not have any other choice. Be aware
that this is subject to race conditions and can lead to data corruption.
validate # The validation command to run before copying into place. The path to the file to validate is passed in via '%s' which
must be present as in the example below. The command is passed securely so shell
features like expansion and pipes won't work.

复制文件到远程主机上,可以指明众多参数选项

Ansible-playbook

Playbooks
  Playbooks是Ansible的配置,部署,编排语言。他们可以被描述为一个需要希望远程主机执行命令的方案,或者一组IT程序运行的命令集合。

  如果Ansible模块 是你进行集群部署的工具,那么playbooks就是你设置的方案计划。

  在基础层面, playbooks 可以被用来管理用于部署到远程主机的配置文件.在更高的层面上,playbooks 可以依次对多层式架构上的服务器执行上线包括滚动更新在内的操作并可以将操作委托给其他主机包括在此过程中发生的与监视服务器,负载均衡服务器的交互操作在内.

Playbook语言示例
  Playbooks的格式是YAML格式的文件,语法做到最小化,意在避免playbooks成为一种编程语言或是脚本,但它也不是一个配置模型或过程的模型。

  'plays’好似音符,playbook好似由’plays’构成的曲谱,通过playbook,可以编排步骤进行多机器的部署,比如在webservers组的所有机器上运行一定的步骤,然后再database server组运行一些步骤,最后回到webservers组,再运行一些步骤,诸如此类。

plays简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vim apache.yml

- hosts: websrvs ## 生效范围主机组
remote_user: root ## 远程执行的用户
vars: ## 定义变量
- package: httpd ## 定义package变量
- service: httpd ## 定义service变量
tasks: ## 任务列表
- name: install httpd package ## 任务名字为安装httpd程序包
yum: name={{ package }} state=latest ## 调用yum模块,调用package变量,状态为最新版本
- name: install configuration file for httpd ## 任务名字为安装配置文件
copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf ## 调用copy模块,复制编辑好的配置文件到远程主机
notify: ## 定义notify
- restart httpd ## 重启httpd service
- name: start httpd service
service: enabled=true name={{ service }} state=started
handlers: ## 定义触发操作 ,当远程主机的程序配置文件发生变化时,执行此操作
- name: restart httpd
service: name=httpd state=restarted
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
53
[root@node1 ~]# ansible-playbook apache.yml 

PLAY [websrvs] *****************************************************************

TASK [setup] *******************************************************************
ok: [172.16.100.31]
ok: [172.16.100.32]
ok: [172.16.100.33]

TASK [install httpd package] ***************************************************
ok: [172.16.100.33]
ok: [172.16.100.32]
ok: [172.16.100.31]

TASK [install configuration file for httpd] ************************************
changed: [172.16.100.33]
changed: [172.16.100.31]
changed: [172.16.100.32]

TASK [start httpd service] *****************************************************
ok: [172.16.100.31]
changed: [172.16.100.33]
ok: [172.16.100.32]

RUNNING HANDLER [restart httpd] ************************************************
changed: [172.16.100.33]
changed: [172.16.100.31]
changed: [172.16.100.32]

PLAY RECAP *********************************************************************
172.16.100.31 : ok=5 changed=2 unreachable=0 failed=0
172.16.100.32 : ok=5 changed=2 unreachable=0 failed=0
172.16.100.33 : ok=5 changed=3 unreachable=0 failed=0

[root@node1 ~]# ansible websrvs -m shell -a 'ss -tnl | grep 80'
172.16.100.33 | SUCCESS | rc=0 >>
LISTEN 0 128 :::808 :::*

172.16.100.31 | SUCCESS | rc=0 >>
LISTEN 0 128 :::808 :::*

172.16.100.32 | SUCCESS | rc=0 >>
LISTEN 0 128 :::808 :::*

[root@node1 ~]# ansible websrvs -m shell -a 'service httpd status'
172.16.100.31 | SUCCESS | rc=0 >>
httpd (pid 2872) is running...

172.16.100.32 | SUCCESS | rc=0 >>
httpd (pid 3023) is running...

172.16.100.33 | SUCCESS | rc=0 >>
httpd (pid 3026) is running...

一出“playbook”就“唱”完了。
Ansible官方文档

上述只是简单说了Ansible的常用模块和定义一个简单的playbook。内容浅尝辄止,各位多多指教。