Ansible是近年越来越火的一款运维自动化工具,其主要功能是帮助我们实现IT工作的自动化、降低认为操作失误、提高业务自动化率、提升工作效率,常用于软件部署自动化、配置自动化、管理自动化、系统化系统任务、持续集成(CI)、零宕机平滑升级等。它丰富的内置模块(如acl、command、shell、cron、yum、copy、file、user等,多达569个)和开放的API接口,同时任何遵循GPL协议的企业或个人都可以随意修改和发布自己的版本。

ansible基础

安装ansible

安装方式主要有以下几种方式:

  1. pip方式安装
  2. Yum安装(建议使用)
  3. apt-get安装(建议使用)
  4. 源码安装
与远程服务器通信

Ansible的通信默认基于SSH,因此我们需要对主机先进行认证。Ansible认证方式有密码认证和公私钥认证两种方式,其实完全等同于SSH的认证,所以这里关于这两种认证方式不做过多介绍。Ansible默认使用公私钥认证方式(建议使用),主要是出于安全的考虑,密码不用明文存放。

ansible目录结构

我们只讲一下需要配置的目录,大家要熟练掌握。

  1. 配置文件目录/etc/ansible/,主要功能为:Inventory主机信息配置、Ansible工具功能配置等。所有Ansible的配置均存放在该目录下,我们平时的所有配置类操作也均基于此目录进行。
  2. 执行文件目录/usr/bin/,主要功能为:Ansible系列命令默认存放目录。Ansible所有的可执行文件均存放在该目录下。

/usr/lib/pythonXXX/site-packages/下,该目录是系统当前默认的Python路径,因为Ansible是基于Python编写的,所以Ansible的所有lib库文件和模块文件也均存放于该目录下。

ansible配置文件

Inventory用于定义Ansible的主机列表配置,Ansible的自身配置文件只有一个,即ansible.cfg,Ansible安装好后它默认存放于/etc/ansible/目录下。ansible.cfg配置文件可以存在于多个地方,Ansible读取配置文件的顺序依次是当前命令执行目录=>用户家目录下的.ansible.cfg=>/etc/ansible.cfg,先找到哪个配置文件就使用哪个配置文件。其中ansible.cfg配置的所有内容均可在命令行通过参数的形式传递或定义在Playbooks中。

下面详细介绍一下配置文件:

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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# config file for ansible -- https://ansible.com/
# ===============================================

# nearly all parameters can be overridden in ansible-playbook
# or with command line flags. ansible will read ANSIBLE_CONFIG,
# ansible.cfg in the current working directory, .ansible.cfg in
# the home directory or /etc/ansible/ansible.cfg, whichever it
# finds first

[defaults]

# some basic default values...

#inventory = /etc/ansible/hosts # 定义Inventory
#library = /usr/share/my_modules/ # 自定义lib库存放目录
#module_utils = /usr/share/my_module_utils/
#remote_tmp = ~/.ansible/tmp # 临时文件远程主机存放目录
#local_tmp = ~/.ansible/tmp # 临时文件本地存放目录
#plugin_filters_cfg = /etc/ansible/plugin_filters.yml
#forks = 5 # 默认开启的并发数
#poll_interval = 15 # 默认轮询时间间隔
#sudo_user = root # 默认sudo用户
#ask_sudo_pass = True # 是否需要sudo密码
#ask_pass = True # 是否需要密码
#transport = smart
#remote_port = 22
#module_lang = C
#module_set_locale = False

# plays will gather facts by default, which contain information about
# the remote system.
#
# smart - gather by default, but don't regather if already gathered
# implicit - gather by default, turn off with gather_facts: False
# explicit - do not gather by default, must say gather_facts: True
#gathering = implicit

# This only affects the gathering done by a play's gather_facts directive,
# by default gathering retrieves all facts subsets
# all - gather all subsets
# network - gather min and network facts
# hardware - gather hardware facts (longest facts to retrieve)
# virtual - gather min and virtual facts
# facter - import facts from facter
# ohai - import facts from ohai
# You can combine them using comma (ex: network,virtual)
# You can negate them using ! (ex: !hardware,!facter,!ohai)
# A minimal set of facts is always gathered.
#gather_subset = all

# some hardware related facts are collected
# with a maximum timeout of 10 seconds. This
# option lets you increase or decrease that
# timeout to something more suitable for the
# environment.
# gather_timeout = 10

# additional paths to search for roles in, colon separated
#roles_path = /etc/ansible/roles # 默认下载的Roles存放的目录

# uncomment this to disable SSH key host checking
#host_key_checking = False # 首次连接是否需要检查key认证,建议设为False

# change the default callback, you can only have one 'stdout' type enabled at a time.
#stdout_callback = skippy


## Ansible ships with some plugins that require whitelisting,
## this is done to avoid running all of a type by default.
## These setting lists those that you want enabled for your system.
## Custom plugins should not need this unless plugin author specifies it.

# enable callback plugins, they can output to stdout but cannot be 'stdout' type.
#callback_whitelist = timer, mail

# Determine whether includes in tasks and handlers are "static" by
# default. As of 2.0, includes are dynamic by default. Setting these
# values to True will make includes behave more like they did in the
# 1.x versions.
#task_includes_static = False
#handler_includes_static = False

# Controls if a missing handler for a notification event is an error or a warning
#error_on_missing_handler = True

# change this for alternative sudo implementations
#sudo_exe = sudo

# What flags to pass to sudo
# WARNING: leaving out the defaults might create unexpected behaviours
#sudo_flags = -H -S -n

# SSH timeout
#timeout = 10 # 默认超时时间

# default user to use for playbooks if user is not specified
# (/usr/bin/ansible will use current user as default)
#remote_user = root # 如果没有指定用户,默认使用的远程连接用户

# logging is off by default unless this path is defined
# if so defined, consider logrotate
#log_path = /var/log/ansible.log # 执行日志存放目录

# default module name for /usr/bin/ansible
#module_name = command # 默认执行的模块

# 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

# if inventory variables overlap, does the higher precedence one win
# or are hash values merged together? The default is 'replace' but
# this can also be set to 'merge'.
#hash_behaviour = replace

# by default, variables from roles will be visible in the global variable
# scope. To prevent this, the following option can be enabled, and only
# tasks and handlers within the role will see the variables there
#private_role_vars = yes

# list any Jinja2 extensions to enable here:
#jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n

# if set, always use this private key file for authentication, same as
# if passing --private-key to ansible or ansible-playbook
#private_key_file = /path/to/file

# If set, configures the path to the Vault password file as an alternative to
# specifying --vault-password-file on the command line.
#vault_password_file = /path/to/vault_password_file

# format of string {{ ansible_managed }} available within Jinja2
# templates indicates to users editing templates files will be replaced.
# replacing {file}, {host} and {uid} and strftime codes with proper values.
#ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
# {file}, {host}, {uid}, and the timestamp can all interfere with idempotence
# in some situations so the default is a static string:
#ansible_managed = Ansible managed

# by default, ansible-playbook will display "Skipping [host]" if it determines a task
# should not be run on a host. Set this to "False" if you don't want to see these "Skipping"
# messages. NOTE: the task header will still be shown regardless of whether or not the
# task is skipped.
#display_skipped_hosts = True

# by default, if a task in a playbook does not include a name: field then
# ansible-playbook will construct a header that includes the task's action but
# not the task's args. This is a security feature because ansible cannot know
# if the *module* considers an argument to be no_log at the time that the
# header is printed. If your environment doesn't have a problem securing
# stdout from ansible-playbook (or you have manually specified no_log in your
# playbook on all of the tasks where you have secret information) then you can
# safely set this to True to get more informative messages.
#display_args_to_stdout = False

# by default (as of 1.3), Ansible will raise errors when attempting to dereference
# Jinja2 variables that are not set in templates or action lines. Uncomment this line
# to revert the behavior to pre-1.3.
#error_on_undefined_vars = False

# by default (as of 1.6), Ansible may display warnings based on the configuration of the
# system running ansible itself. This may include warnings about 3rd party packages or
# other conditions that should be resolved if possible.
# to disable these warnings, set the following value to False:
#system_warnings = True

# by default (as of 1.4), Ansible may display deprecation warnings for language
# features that should no longer be used and will be removed in future versions.
# to disable these warnings, set the following value to False:
#deprecation_warnings = True

# (as of 1.8), Ansible can optionally warn when usage of the shell and
# command module appear to be simplified by using a default Ansible module
# instead. These warnings can be silenced by adjusting the following
# setting or adding warn=yes or warn=no to the end of the command line
# parameter string. This will for example suggest using the git module
# instead of shelling out to the git command.
# command_warnings = False


# set plugin path directories here, separate with colons
#action_plugins = /usr/share/ansible/plugins/action # action插件的存放目录
#cache_plugins = /usr/share/ansible/plugins/cache
#callback_plugins = /usr/share/ansible/plugins/callback # callback插件的存放目录
#connection_plugins = /usr/share/ansible/plugins/connection # connection插件的存放目录
#lookup_plugins = /usr/share/ansible/plugins/lookup # lookup插件的存放目录
#inventory_plugins = /usr/share/ansible/plugins/inventory
#vars_plugins = /usr/share/ansible/plugins/vars # vars插件的存放目录
#filter_plugins = /usr/share/ansible/plugins/filter # filter插件的存放目录
#test_plugins = /usr/share/ansible/plugins/test # test插件的存放目录
#terminal_plugins = /usr/share/ansible/plugins/terminal
#strategy_plugins = /usr/share/ansible/plugins/strategy # strategy插件的存放目录


# by default, ansible will use the 'linear' strategy but you may want to try
# another one
#strategy = free

# by default callbacks are not loaded for /bin/ansible, enable this if you
# want, for example, a notification or logging callback to also apply to
# /bin/ansible runs
#bin_ansible_callbacks = False


# don't like cows? that's unfortunate.
# set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1
#nocows = 1

# set which cowsay stencil you'd like to use by default. When set to 'random',
# a random stencil will be selected for each task. The selection will be filtered
# against the `cow_whitelist` option below.
#cow_selection = default
#cow_selection = random

# when using the 'random' option for cowsay, stencils will be restricted to this list.
# it should be formatted as a comma-separated list with no spaces between names.
# NOTE: line continuations here are for formatting purposes only, as the INI parser
# in python does not support them.
#cow_whitelist=bud-frogs,bunny,cheese,daemon,default,dragon,elephant-in-snake,elephant,eyes,\
# hellokitty,kitty,luke-koala,meow,milk,moofasa,moose,ren,sheep,small,stegosaurus,\
# stimpy,supermilker,three-eyes,turkey,turtle,tux,udder,vader-koala,vader,www

# don't like colors either?
# set to 1 if you don't want colors, or export ANSIBLE_NOCOLOR=1
#nocolor = 1

# if set to a persistent type (not 'memory', for example 'redis') fact values
# from previous runs in Ansible will be stored. This may be useful when
# wanting to use, for example, IP information from one group of servers
# without having to talk to them in the same playbook run to get their
# current IP information.
#fact_caching = memory # getfact缓存的主机信息存放方式


# retry files
# When a playbook fails by default a .retry file will be created in ~/
# You can disable this feature by setting retry_files_enabled to False
# and you can change the location of the files by setting retry_files_save_path

#retry_files_enabled = False
#retry_files_save_path = ~/.ansible-retry # 错误重启文件存放目录

# squash actions
# Ansible can optimise actions that call modules with list parameters
# when looping. Instead of calling the module once per with_ item, the
# module is called once with all items at once. Currently this only works
# under limited circumstances, and only with parameters named 'name'.
#squash_actions = apk,apt,dnf,homebrew,pacman,pkgng,yum,zypper

# prevents logging of task data, off by default
#no_log = False

# prevents logging of tasks, but only on the targets, data is still logged on the master/controller
#no_target_syslog = False

# controls whether Ansible will raise an error or warning if a task has no
# choice but to create world readable temporary files to execute a module on
# the remote machine. This option is False by default for security. Users may
# turn this on to have behaviour more like Ansible prior to 2.1.x. See
# https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user
# for more secure ways to fix this than enabling this option.
#allow_world_readable_tmpfiles = False

# controls the compression level of variables sent to
# worker processes. At the default of 0, no compression
# is used. This value must be an integer from 0 to 9.
#var_compression_level = 9

# controls what compression method is used for new-style ansible modules when
# they are sent to the remote system. The compression types depend on having
# support compiled into both the controller's python and the client's python.
# The names should match with the python Zipfile compression types:
# * ZIP_STORED (no compression. available everywhere)
# * ZIP_DEFLATED (uses zlib, the default)
# These values may be set per host via the ansible_module_compression inventory
# variable
#module_compression = 'ZIP_DEFLATED'

# This controls the cutoff point (in bytes) on --diff for files
# set to 0 for unlimited (RAM may suffer!).
#max_diff_size = 1048576

# This controls how ansible handles multiple --tags and --skip-tags arguments
# on the CLI. If this is True then multiple arguments are merged together. If
# it is False, then the last specified argument is used and the others are ignored.
# This option will be removed in 2.8.
#merge_multiple_cli_flags = True

# Controls showing custom stats at the end, off by default
#show_custom_stats = True

# Controls which files to ignore when using a directory as inventory with
# possibly multiple sources (both static and dynamic)
#inventory_ignore_extensions = ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo

# This family of modules use an alternative execution path optimized for network appliances
# only update this setting if you know how this works, otherwise it can break module execution
#network_group_modules=eos, nxos, ios, iosxr, junos, vyos

# When enabled, this option allows lookups (via variables like {{lookup('foo')}} or when used as
# a loop with `with_foo`) to return data that is not marked "unsafe". This means the data may contain
# jinja2 templating language which will be run through the templating engine.
# ENABLING THIS COULD BE A SECURITY RISK
#allow_unsafe_lookups = False

# set default errors for all plays
#any_errors_fatal = False

[inventory]
# enable inventory plugins, default: 'host_list', 'script', 'yaml', 'ini'
#enable_plugins = host_list, virtualbox, yaml, constructed

# ignore these extensions when parsing a directory as inventory source
#ignore_extensions = .pyc, .pyo, .swp, .bak, ~, .rpm, .md, .txt, ~, .orig, .ini, .cfg, .retry

# ignore files matching these patterns when parsing a directory as inventory source
#ignore_patterns=

# If 'true' unparsed inventory sources become fatal errors, they are warnings otherwise.
#unparsed_is_failed=False

[privilege_escalation]
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False

[paramiko_connection]

# uncomment this line to cause the paramiko connection plugin to not record new host
# keys encountered. Increases performance on new host additions. Setting works independently of the
# host key checking setting above.
#record_host_keys=False

# by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment this
# line to disable this behaviour.
#pty=False

# paramiko will default to looking for SSH keys initially when trying to
# authenticate to remote devices. This is a problem for some network devices
# that close the connection after a key failure. Uncomment this line to
# disable the Paramiko look for keys function
#look_for_keys = False

# When using persistent connections with Paramiko, the connection runs in a
# background process. If the host doesn't already have a valid SSH key, by
# default Ansible will prompt to add the host key. This will cause connections
# running in background processes to fail. Uncomment this line to have
# Paramiko automatically add host keys.
#host_key_auto_add = True

[ssh_connection]

# ssh arguments to use
# Leaving off ControlPersist will result in poor performance, so use
# paramiko on older platforms rather than removing it, -C controls compression use
#ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s

# The base directory for the ControlPath sockets.
# This is the "%(directory)s" in the control_path option
#
# Example:
# control_path_dir = /tmp/.ansible/cp
#control_path_dir = ~/.ansible/cp

# The path to use for the ControlPath sockets. This defaults to a hashed string of the hostname,
# port and username (empty string in the config). The hash mitigates a common problem users
# found with long hostames and the conventional %(directory)s/ansible-ssh-%%h-%%p-%%r format.
# In those cases, a "too long for Unix domain socket" ssh error would occur.
#
# Example:
# control_path = %(directory)s/%%h-%%r
#control_path =

# Enabling pipelining reduces the number of SSH operations required to
# execute a module on the remote server. This can result in a significant
# performance improvement when enabled, however when using "sudo:" you must
# first disable 'requiretty' in /etc/sudoers
#
# By default, this option is disabled to preserve compatibility with
# sudoers configurations that have requiretty (the default on many distros).
#
#pipelining = False

# Control the mechanism for transferring files (old)
# * smart = try sftp and then try scp [default]
# * True = use scp only
# * False = use sftp only
#scp_if_ssh = smart

# Control the mechanism for transferring files (new)
# If set, this will override the scp_if_ssh option
# * sftp = use sftp to transfer files
# * scp = use scp to transfer files
# * piped = use 'dd' over SSH to transfer files
# * smart = try sftp, scp, and piped, in that order [default]
#transfer_method = smart

# if False, sftp will not use batch mode to transfer files. This may cause some
# types of file transfer failures impossible to catch however, and should
# only be disabled if your sftp version has problems with batch mode
#sftp_batch_mode = False

# The -tt argument is passed to ssh when pipelining is not enabled because sudo
# requires a tty by default.
#use_tty = True

[persistent_connection]

# Configures the persistent connection timeout value in seconds. This value is
# how long the persistent connection will remain idle before it is destroyed.
# If the connection doesn't receive a request before the timeout value
# expires, the connection is shutdown. The default value is 30 seconds.
#connect_timeout = 30

# Configures the persistent connection retry timeout. This value configures the
# the retry timeout that ansible-connection will wait to connect
# to the local domain socket. This value must be larger than the
# ssh timeout (timeout) and less than persistent connection idle timeout (connect_timeout).
# The default value is 15 seconds.
#connect_retry_timeout = 15

# The command timeout value defines the amount of time to wait for a command
# or RPC call before timing out. The value for the command timeout must
# be less than the value of the persistent connection idle timeout (connect_timeout)
# The default value is 10 second.
#command_timeout = 10

[accelerate]
#accelerate_port = 5099
#accelerate_timeout = 30
#accelerate_connect_timeout = 5.0

# The daemon timeout is measured in minutes. This time is measured
# from the last activity to the accelerate daemon.
#accelerate_daemon_timeout = 30

# If set to yes, accelerate_multi_key will allow multiple
# private keys to be uploaded to it, though each user must
# have access to the system via SSH to add a new key. The default
# is "no".
#accelerate_multi_key = yes

[selinux]
# file systems that require special treatment when dealing with security context
# the default behaviour that copies the existing context or uses the user default
# needs to be changed to use the file system dependent context.
#special_context_filesystems=nfs,vboxsf,fuse,ramfs,9p

# Set this to yes to allow libvirt_lxc connections to work without SELinux.
#libvirt_lxc_noseclabel = yes

[colors]
#highlight = white
#verbose = blue
#warn = bright purple
#error = red
#debug = dark gray
#deprecate = purple
#skip = cyan
#unreachable = red
#ok = green
#changed = yellow
#diff_add = green
#diff_remove = red
#diff_lines = cyan


[diff]
# Always print diff when running ( same as always running with -D/--diff )
# always = no

# Set how many context lines to show in diff
# context = 3
ansible命令格式

Ansible的命令使用格式如下:

ansible <host-pattern> [options]

<hots-pattern>是Inventory中定义的主机或主机组,可以为ip、hostname、Inventory中的group组名、具有“.”或“*”或“:”等特殊字符的匹配型字符串,<>表示该选项是必选项。

[options]是Ansible的参数选项,[]表示该选项中的参数任选其一。

ansible命令
Inventory配置文件

Inventory是Ansible管理主机信息的配置文件,相当于系统HOSTS文件的功能,默认存放在/etc/ansible/hosts。为方便批量管理主机,便携使用其中的主机分组,Ansible通过Inventory来定义其主机和组,在使用时通过-i--inventory-file指定读取,与Ansible命令结合使用时组合如下:

ansible -i /etc/ansible/hosts webs -m ping

如果真有一个Inventory时可不用指定路径,默认读取/etc/ansible/hosts。Inventory可以同时存在多个,而且支持动态生成,如AWS EC2、Cobbler等均支持。下面我们来讲一下Inventory的使用规则。

  1. 定义主机和组

    Inventory配置文件遵循INI文件风格,中括号中的字符为组名。其支持将同一个主机同时归并到多个不同的组中,分组的功能为我们维护主机列表提供了非常大的便利。此外,如果目标主机使用了非默认的SSH端口,还可以在主机名称之后使用冒号加端口来标明,以行为单位分隔配置。

    下面我们看一个示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # "# "开头的行表示该行为注释行
    # Inventory可以直接为IP地址
    192.168.1.1
    # Inventory同样支持Hostname的方式,后跟冒号加数字表示端口号,默认端口号22
    ntp.golanghub.cn:2221
    nfs.golanghub.cn
    # 中括号内的内容表示一个分组的开始,紧随其后的主机均属于该组成员,空行后的主机也属于该组。
    [webservers]
    web1.golanghub.cn
    web[10:20].golanghub.cn # [10:20]表示10~20之间的所有数字(包括10和20),即表示web10.golanghub.cn、web11.golanghub.cn......web20.golanghub.cn的所有主机

    web2.golanghub.cn
    [dbservers]
    db-a.golanghub.cn
    db-[b:f].golanghub.cn # [b:f]表示b到f之间的所有字母(包括b和f),即表示db-b.golanghub.cn、db-c.golanghub.cn......db-f.golanghub.cn的所有主机
  2. 定义主机变量

    工作中考虑到安全性问题,通常会将80端口改为其它端口号,这种非标准化的配置需求,可以直接通过修改Inventory配置来实现,在定义主机时为其添加主机变量,以便在Playbook中使用针对某一主机的个性化要求。

    示例如下:

    1
    2
    [webservers]
    web1.golanghub.cn http_port=8080 maxRequestsPerChild=5000 # 自定义http_port的端口号为8080,配置maxRequestsPerChild为5000

    Ansible其实支持多种方式修改或自定义变量,Inventory是其中的一种修改方式。

  3. 定义组变量

    Ansible支持定义组变量,主要针对大量机器的变量定义需求,赋予指定组内所有主机在Playbook中可用的变量,等同于逐一给该组中的所有主机赋予同一变量。

    示例如下:

    1
    2
    3
    4
    5
    6
    7
    [groupservers]
    web1.golanghub.cn
    web2.golanghub.cn

    [groupservers:vars]
    ntp_server=ntp.golanghub.cn # 定义groupservers组中所有主机ntp_server值为ntp.golanghub.cn
    nfs_server=nfs.golanghub.cn # 定义groupservers组中所有主机nfs_server值为nfs.golanghub.cn
  4. 定义组嵌套及组变量

    Inventory中,组还可以包含其它的组(嵌套),并且也可以向组中的主机指定变量。不过,这些变量只能在Ansible-playbook中使用,而Ansible不支持。组与组之间可以相互调用,并且可以向组中的主机指定变量。

    示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [apache]
    httpd1.golanghub.cn
    httpd2.golanghub.cn

    [nginx]
    ngx1.golanghub.cn
    ngx2.golanghub.cn

    [webservers:children]
    apache
    nginx

    [webservers:vars]
    ntp_server=ntp.golanghub.cn
  5. 多重变量定义

    变量除了可以在Inventory中一并定义,也可以独立于Inventory文件之外单独存储到YAML格式的配置文件中,这些文件通常以.yml、.yaml、.json为后缀或者无后缀。变量通常从如下4个位置检索:

    • Inventory配置文件(默认/etc/ansible/hosts

    • Playbook中vars定义的区域

    • Roles中vars目录下的文件

    • Roles同级目录group_vars和host_vars目录下的文件

      假如foosball主机同属于raleigh和webservers组,那么其变量在如下文件中设置均有效:

      1
      2
      3
      /etc/ansible/group_vars/raleigh # can optionally end in '.yml','.yaml',or '.json'
      /etc/ansible/group_vars/webservers
      /etc/ansible/host_vars/foosball

      对于变量的读取,Ansible遵循如上优先级顺序,因此大家设置变量时尽量沿用同一种方式,方便维护管理。

  6. 其它Inventory参数列表

    除了支持如上的功能外,Ansible基于SSH连接Inventory中指定的远程主机时,还内置了很多其他参数,用于指定其交互方式,如下列举了部分重要参数:

    1
    2
    3
    4
    5
    6
    7
    ansible_ssh_host:指定连接主机
    ansible_ssh_port:指定SSH连接端口,默认22
    ansible_ssh_user:指定SSH连接用户
    ansible_ssh_pass:指定SSH连接密码
    ansible_sudo_pass:指定SSH连接时sudo密码
    ansible_ssh_private_key_file:指定特有私钥文件
    ...

    其它内置参数还有数十个,这些参数均可以直接写在命令行或playbook文件中,以覆盖配置文件中的定义。

Ad-Hoc入门

Ad-Hoc命令集介绍
Ad-HOC组管理和特定主机变更
Ad-Hoc用户与组管理

Playbook入门

YAML语法
  1. 多行缩进

    数据结构可以用类似大纲的缩排方式呈现,结构通过缩进来表示,连续的项目通过减号“-”来表示,map结构里面的key/value对用冒号“:”来分隔。代码格式如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    house:
    family:
    name: Doe
    parents:
    - John
    - Jane
    children:
    - Paul
    - Mark
    - Simone
    address:
    number: 34
    street: Main Street
    city: Nowheretown
    zipcode: 12345
    1. 字串不一定要用双引号标识;
    2. 在缩排中空白字符的数目并不重要,只要相同阶层的元素左侧对齐就可以了(不过不能使用Tab字符);
    3. 允许在文件中加入选择性的空行,以增加可读性;
    4. 选择性的符号“…”可以用来表示档案结尾(在利用串流的通信中,这非常有用,可以在不关闭串流的情况下,发送结束信号)。
  2. 单行缩写

    YAML也有用来描述好几行相同结构的数据的缩写语法,数组用“[]”包括起来,hash用“{}”来包括。因此,上面的这个YAML能够缩写成这样:

    1
    2
    3
    house:
    family: { name: Doe, parents: [John, Jane], children: [Paul, Mark, Simone] }
    address: { number: 34, street: Main Street, city: Nowheretown, zipcode: 12345 }
    Playbook语法

    了解了普通的YAML格式文件,我们来看一下正式的Playbook内容是什么样的。Playbook代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    ---
    # 这个是你选择的主机
    - hosts: webservers
    # 这个是变量
    vars:
    http_port: 80
    max_clients: 200
    # 远端的执行权限
    remote_user: root
    tasks:
    # 利用YUM模块来操作
    - name: ensure apache is at the laste version
    yum: pkg=httpd state=laste
    -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

总的来说,Playbook语法具有如下一些特性。

  1. 需要以“—”(3个减号)开始,且需要顶行首写。
  2. 次行开始正常些Playbook的内容,但建议写明该Playbook的功能。
  3. 使用#号注释代码。
  4. 缩进必须是统一的,不能将空格和Tab混用。
  5. 缩进的级别必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的。
  6. YAML文件内容和Linux系统大小写判断方式保持一致,是区分大小写的,k/v的值均大小写敏感。
  7. k/v的值可同行写也可换行写。同行使用“:”分隔,换行写需要以“-”分隔。
  8. 一个完整的代码块功能需最少元素,需包括name:task。
  9. 一个name只能包括一个task。
Ansible-playbook的命令格式

Ansible-playbook的命令使用格式如下:

ansible-playbook playbook.yml

ansible-playbook命令后跟事先编辑好的playbook.yml文件即可。

Ansible-playbook使用技巧
  1. 限定执行范围

    当Playbook指定的一批主机中个别主机需要进行变更时,我们不需要去修改Playbook文件,而是通过一些选项就可以直接限定和查看Ansible命令的执行范围。

    • –limit

      使用ansible-playbook命令来指定主机:
      ansible-playbook playbook.yml --limit webservers
      这样,(假设你的inventory文件中包含webservers组),即便Playbook中设定“hosts: all”,
      但也仅对webservers组有效。

    • –list-hosts

      如果想知道在执行Playbook时,哪些主机将会受影响,则使用“–list-hosts”选项。
      ansible-playbook playbook.yml --list-hosts

      运行结果:

      1
      2
      3
      4
      playbook: playbook.yml

      paly # 1 (all): host count=1
      webservers

      如上两种方式针对需要对批量操作的主机列表中的某一台主机做特定修改时非常有用。该技巧极大地增加了Ansible的使用灵活度。

  2. 用户与权限设置

    • –remote-user

      在Playbook中,如果在hosts字段下没有定义users关键字,那么Ansible将会使用你在Inventory文件中定义的用户,如果Inventory文件中也没有定义用户,Ansible将默认使用当前系统用户身份来通过SSH连接远程主机,在远程主机中运行play内容。

      我们也可以直接在ansible-playbook中使用–remote-user选项来指定用户。

      ansible-playbook playbook.yml --remote-user=tom

    • –ask-sudo-pass

      在某些情况下,我们需要传递sudo密码到远程主机,来保证sudo命令的正常运行。这时,可以使用–ask-sudo-pass(-K)选项来交互式的输入密码。

    • –sudo

      使用–sudo选项,可以强制所有play都使用sudo用户,同时使用–sudo-user选项指定sudo可以执行哪个用户的权限,如果不指定,则默认以root身份运行。

      比如,当前用户Tom想以Jerry的身份运行Playbook,命令如下:

      ansible-playbook playbook.yml --sudo --sudo-user=jerry --ask-sudo-pass

      执行过程中,会要求用户输入jerry的密码。

  3. 其它选项技巧

    Ansible-playbook命令还有一些其它选项。

    • –inventory=PATH(-i PATH):指定inventory文件,默认文件是/etc/ansible/hosts

    • –verbose(-v):显示详细输出,也可以使用-vvvv显示精确到每分钟的输出。

    • –extra-vars=VARS(-e VARS):定义在Playbook使用的变量,格式为:“key=value, key=value”。

    • –forks=NUM(-f NUM):指定并发执行的任务数,默认为5,根据服务器性能,调大这个值可提高Ansible执行效率。

    • –connection=TYPE(-c TYPE):指定连接远程主机的方式,默认为SSH,设为local时,则只在本地执行Playbook,建议不做修改。

    • –check:检测模式,Playbook中定义的所有任务将在每台远程主机上进行检测,但并不真正执行。

      以上这些参数可满足大部分的工作需求。

Playbook拓展

Handlers

Handlers是Playbook中一种特殊的任务类型,我们通过在任务末尾使用notify选项加Handlers的名称,来触发对应名称下Handlers中定义的任务。

Handlers以及Playbook中的任务也可以在独立的文件中进行定义,然后被当前的Playbook进行引用。

示例如下:

handlers文件内容:

1
2
3
---
- name: restart apche
service: name=apache2 state=restarted

tasks文件内容:

1
2
3
4
---
- name: 开启Apache rewrite模块
apache2_module: name=rewrite state=present
notify: restart apache

在某些情况下,我们可能需要同时调用多个Handlers,或者需要使用Handlers调用其它Handlers,Ansible可以很简单地实现这些功能。

示例如下:

同时调用多个Handlers,tasks文件内容:

1
2
3
4
5
- name: Rebuild application configuration.
command: /opt/app/rebuild.sh
notify:
- restart apache
- restart memcached

使用Handlers调用其它Handlers,则直接在Handlers中使用notify选项就可以了,handlers内容如下:

1
2
3
4
5
6
- name: restart apache
service: name=apache2 state=restarted
notify: restart memcached

- name: restart memecached
service: name=memcached state=restarted

使用Handlers时,有几点注意事项:

  • Handlers只有在其所在的任务被执行时,才会被运行。如果一个任务中定义了notify调用Handlers,但是由于条件判断等原因,该任务未被执行,那么Handlers同样不会被执行。
  • Handlers只会在Play的末尾运行一次,如果想在一个Playbook的中间运行Handlers,则需要使用meta模块来实现,例如:-meta: flush_handlers
  • 如果一个Play在运行到调用Handlers的语句之前失败了,那么这个Handlers将不会被执行。我们可以使用meta模块的--force-handlers选项来强制执行Handlers,即使是Handlers所在的Play中途运行失败也能执行。
环境变量
变量

Playbook进阶

Includes
Roles

Roles就是有相互关联功能的集合,适合在大项目中使用。Roles不仅支持Tasks的集合,同时包括var_files、tasks、handlers、meta、templates。

  1. 构建Roles

    Roles主要依赖于目录的命名和摆放,默认tasks/main.yml是所有任务的入口,所以使用Roles的过程可以理解为目录规范化命名的过程。如下几个目录就可以构建Ansible Roles。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    php72/
    ├── defaults
    │   └── main.yml
    ├── files
    │   ├── exif.so
    │   ├── imagick.so
    │   ├── install.sh
    │   ├── ldap.so
    │   ├── memcache.so
    │   ├── mongodb.so
    │   ├── opcache.so
    │   ├── php-7.2.6.tar.gz
    │   ├── redis.so
    │   ├── yac.so
    │   └── yaf.so
    ├── handlers
    │   └── main.yml
    ├── tasks
    │   └── main.yml
    └── templates
    ├── php-fpm
    ├── php-fpm.conf
    └── php.ini

    每个目录下均由main.yml定义该功能的任务集,tasks/main.yml默认执行所有指定的任务。Roles的调用文件palybook_role.yml的内容如下:

    1
    2
    3
    4
    5
    6
    7
    ---
    - name: Install PHP72
    hosts: "{{hosts}}"
    become: yes

    roles:
    - php72

    Roles执行方法如下:

    ansible-playbook playbook_role.yml

    Roles目录可以摆放在/etc/ansible/ansible.cfg中“roles_path”定义的路径,也可以和入口Playbook文件存放在同级目录,Ansible对此没有强制要求。

  2. 使用Roles重构Playbooks

  3. Roles技巧之Handlers:动态变更

  4. Roles技巧之Files:文件传输

  5. Roles技巧之Templates:模板替换

  6. 更多复杂的跨平台Roles

Inventory文件扩展

Inventory文件实战
独立的Inventory文件
Inventory变量
  1. host_vars目录
  2. group_vars目录
动态Inventory