ansible的基础使用(一)

ansible基础使用(一)


  • ansible的主要功能
    • A:为什么是ansible
    • B:ansible的安装
    • C:ansible的相关文件
    • D:ansible的基本使用
  • ansible的简单操作
    • A:ansible的常用模块
  • ansible的进阶操作
    • A:ansible-galaxy命令
    • B:ansible-pull命令
    • C:ansible-vault:管理加解密yml文件
    • D:ansible-console:控制台
    • E:ansible-playbook的进阶操作
    • F:templates模板
  • ansible的企业级运用
    • A:roles角色的运用

♣一:ansible的主要功能

A:为什么是ansible

我们现在的企业环境都是串联起来提供服务能力,运行在一系列分布式的计算资 源上并用各种不同的网络协议进行通信,当服务平台随着业务量增加需要横向扩容的情况下,不得不面临一个问题就是N台服务器的高效率服务部署和集中化的配置管理。基于此类情况ansible就能很好解决。

在实际的运维中我们划分为6个场景:

  • 1:配置管理(Ansible,Puppet,Salt)
  • 2:服务即时开通 (Docker,LXC)
  • 3:应用部署(滚动式部署,金丝雀部署)
  • 4:流程编排(Ansible,Mcollective,Salt,Serf,Chef)
  • 5:监控警告(Graphite,Sensu,Riemann ,zabbix)
  • 6:日志记录(ELK, SumoLogic)

在上面6个场景中,ansible能很好胜任前面4个场景。

在常用类似的自动化运维工具中ansible是有绝对优势的。

ansible首先基于python开发,模块众多,能支持二次开发,而且最重要的不需要代理服务,直接在主控端安装即可,被控端都是基于openssh远程来控制操作,另外支持幂等性(一个任务执行1遍和n遍效果是一样的,不会因为执行多次导致的意外情况),api接口支持任何语言来写模块,ansible不是以传统的服务方式运行的,不需要常驻内存等资源,只用使用ansible命令的时候才会用到。

saltstack,也是基于python开发,但是要使用必须在被操作的机器上也装上saltstack服务,而且严格分主控端和被控端。

puppet,功能强大,配置复杂,适合超大型的环境,基于的开发语言是ruby,想做二次开发难度大。

B:ansible的安装:

ansible的rpm安装,EPEL源。

yum install ansible

编译安装:

yum -y install python-jinja2 PyYAML python-paramiko python-bable python-crypto

tar -zxvf ansible1.5.4.tar.gz

cd ansible

python setup.py build

python setup.py install

mkdir /etc/ansible

cp -r examples/* /etc/ansible

git方式安装:

git clone git://github.com/ansible/ansible.git/ansible.git --recursive(此方法安装的版本都是最新的版本,特定版本的不建议此安装方式)

cd ./ansible

source ./hacking/env-setup

pip安装:

yun -y install python-pip python-devel

yum -y install gcc glibc-devel zibl-devel rpm-bulid openssl-devel

pip install --upgrade pip

pip install ansible --upgrade

安装之后执行ansible --version能看到版本就为成功。

(注:只需要安装主控端,被控端不需要安装,但是被控端要安装openssh远程服务)

[root@ansible ypc.soft]# ansible --version
ansible 2.6.8  #看到版本是2.6.8
  config file = /etc/ansible/ansible.cfg  #配置文件所在路径
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.6/site-packages/ansible
  executable location = /usr/bin/ansible #依赖的python版本
  python version = 2.6.6 (r266:84292, Aug 18 2016, 15:13:37) [GCC 4.4.7 20120313 (Red Hat 4.4.7-17)]
ansible --version

C:ansible的相关文件:

配置文件:

/etc/ansible/ansible.cfg (主配置文件,配置ansible的工作特性)

#inventory      = /etc/ansible/hosts 主机列表清单配置文件位置
#library        = /usr/share/my_modules/  库文件存放位置
#remote_tmp     = ~/.ansible/tmp 临时py命令存放远程主机的目录
ansible的工作原理是当用户执行命令,那么ansible会产生一个py的脚本,这个脚本里面记录的是用户的执行命令,然后通过ssh远程放到被控端家目录.ansible/tmp/下,执行完成之后.ansible/tmp就会自动删除。
#forks          = 5 默认并发数,ansible在执行一个任务的时候不是一台机器一台机器去执行的,是同一时间执行5台机器,当然机器配置好的时候可以调整至更大的指,一般默认即可
#sudo_user      = root 默认sudo用户。
#ask_sudo_pass = True 每次执行ansible命令是否询问ssh密码
#remote_port    = 22 默认ssh远程端口是22
log_path = /var/log/ansible.log 日志文件,ansible默认是没有开启日志记录的,这个要取消注释
host_key_checking = False 检查服务的host_key,建议取消注释
ansible.conf配置文件

/etc/ansible/hosts       (主机清单)

例如我主控端是(192.168.219.135)
我需要在主控端增加主机的ip地址
host配置如下:
192.168.219.136
192.168.219.137
在文件末尾增加两台被控端机器的ip,但是这种方式我们每次执行都需要在命令行输入两台机器的ip地址,十分不方便。
这个时候我们就可以写成组的方式:
host配置如下
[web]
192.168.219.136
192.168.219.137
在ip前面加上一个组名用中括号定义即可
主机清单

/etc/ansible/roles       (存放角色的目录)

程序

/usr/bin/ansible         (主程序,临时命令执行工具)

/usr/bin/ansible-doc     (查看配置文档,模块功能查看工具)

/usr/bin/ansible-galaxy   (下载/上传优秀代码或roles模块的官网平台)

/usr/bin/ansible-playbook  (定制自动化任务,编排剧本工具)

/usr/bin/ansible-vault     (文件加密工具)

/usr/bin/ansible-console   (基于console界面与用户交互的执行工具)

/usr/bin/ansible-pull       (远程执行命令工具)

ansible相关命令:

ansible-doc(相当于men帮助,主要查看模块的详细使用方法)

ansible-doc ping  (查看指定模块的使用方法和参数)
> PING    (/usr/lib/python2.6/site-packages/ansible/modules/system/ping.py)


[root@ansible .ssh]# ansible-doc -l  (列出所有模块)
a10_server                                           Manage A10 Networks AX...
a10_server_axapi3                                    Manage A10 Networks AX...

[root@ansible .ssh]# ansible-doc -s ping (查看指定模块的用法,简易模式显示)
- name: Try to connect to host, verify a usable python and return `pong' on suc
  ping:
ansible-doc示例 

ansible-playbook

ansible-vault

ansible-console

ansible-galaxy

ansible-pull

ansible的参数:

--version(显示版本号),-v(详细过程)-vv,-vvv(最详细),-k输入ssh连接密码,默认key验证,--list(显示主机列表),-K(输入sudo的密码),-C(检查并不执行),-T(执行命令超时的时间,默认10s),-u(指定用户连接),-b(代替旧版本的sudo)

D:ansible的基本使用:

基本语法:

ansible (主机清单组名) -m (模块名) -a (执行参数或命令)

ansible的主机模式:

[root@ansible .ssh]# ansible "w*" -m ping
192.168.219.137 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.219.136 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
[root@ansible .ssh]#

[root@ansible .ssh]# ansible 192.168.219.* -m ping
192.168.219.136 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.219.137 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
[root@ansible .ssh]#
*:通配符
或关系
[root@ansible .ssh]# ansible "web:&dbserver" -m ping
192.168.219.136 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.219.137 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
[root@ansible .ssh]#
逻辑与
ansible "web:!dbserver" -m ping
逻辑非

当然也支持正则表达式,不过极少用。

ping模块

[root@ansible ansible]# ansible web -m ping -k
SSH password:
192.168.219.137 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.219.136 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
[root@ansible ansible]#

web是我们之前配置的组名
-m后面更上模块名
-k是指定输入ssh用户名的密码,当然如果是基于key验证的ssh就不需要-k参数
可以看到结果列出了主机名,ping之后对端回应pong就代表执行成功了

使用ssh-keygen命令产生公钥,使用ssh-copy-id 192.168.219.136和ssh-copy-id 192.168.219.137把公钥传输到被控端即可完成基于key验证。
[root@ansible ansible]# ansible web -m ping
192.168.219.137 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.219.136 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
[root@ansible ansible]#
-k参数就不需要加了,也不需要输入密码了
ping模块使用

 上面我们说ansible是基于openssh来进行远程操作的,如果新环境的ip第一次连接的话,会有提示不可连接的错误,这个是因为主控端并没有和被控端进行过连接,在正常的情况下如果你使用ssh远程登录对端机器都需要输入下yes才能连接,ansible走的是ssh的远程,所以也需要通过ssh的yes验证之后才能不会出错,当然这个问题的前提是没有基于key验证的情况下,也就是加了-k参数的情况下会出现,那么我们实际环境中肯定会存在新环境接入的时候出现首次验证的问题,我们还可以通过ansible的配置文件取消首次验证基于key的验证问题。

# uncomment this to disable SSH key host checking
host_key_checking = False
将host_key_checking = False这行取消注释
取消基于key

 非root用户的使用ansible操作

[root@ansible .ssh]# ansible web -m command -a 'ls /root' -u mysql
192.168.219.137 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Permission denied (publickey,password).\r\n",
    "unreachable": true
}
192.168.219.136 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Permission denied (publickey,password).\r\n",
    "unreachable": true
}

可以看到没有进行sudo授权的情况下,是会报错的,提示需要输入sudo用户密码

[root@ansible .ssh]# ansible web -m command -a 'ls /root' -u mysql -k -b -K
SSH password:
SUDO password[defaults to SSH password]: #加上-K输入sudo密码
192.168.219.137 | FAILED | rc=2 >>
ls: cannot open directory /root: Permission deniednon-zero return code

192.168.219.136 | FAILED | rc=2 >>
ls: cannot open directory /root: Permission deniednon-zero return code

现在又会出现另外一个问题就是mysql用户没有在控制端sudo授权过
到控制端执行visudo
## Allows people in group wheel to run all commands
%wheel  ALL=(ALL)       ALL
把wheel这行注释取消掉
然后在命令行执行命令,将mysql用户加到组里面去
[root@web1 ~]# usermod -aG wheel mysql
同时SUDO password[defaults to SSH password]:这个sudo的密码我不想输入,也可以在visudo里面取消注释:
## Same thing without a password
%wheel  ALL=(ALL)       NOPASSWD: ALL
[root@ansible .ssh]# ansible web -m command -a 'ls /root' -u mysql -k -b
SSH password:
192.168.219.137 | SUCCESS | rc=0 >>
anaconda-ks.cfg
Desktop
Documents
Downloads
install.log
install.log.syslog
Music
Pictures
Public
Templates
Videos

192.168.219.136 | SUCCESS | rc=0 >>
anaconda-ks.cfg
Desktop
Documents
Downloads
install.log
install.log.syslog
Music
Pictures
Public
Templates
Videos
#可以看到非root用户登录需要那些操作
非root用户

♣二:ansible的简单操作

A:ansible的常用模块

ansible默认模块是command,如果是简单的操作不需要在-m后面指定command模块,直接-m即可。

上面我们已经使用过了ping模块,因ansible模块已经有接近2千个模块了,而且还在增长,我们只学一些常用的模块,后续有需要用到其他模块直接查看帮助也能完成。

shell模块和command相似,用于执行shell命令。 

[root@ansible ~]# ansible-doc -s shell
- name: Execute commands in nodes.
  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:             # (required) 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.
      stdin:                 # Set the stdin of the command directly to the specified value.
      warn:                  # if command warnings are on in ansible.cfg, do not warn about this particular line if set to no/false.
[root@ansible ~]#
shell模块帮助

因shell模块不是默认模块,-m后面就必须指定模块名称了。

ansible all -m shell -a 'echo $HOSTNAME'

ansible all -m shell -a 'mkdir -r /root/ansible'

shell模块能完成command不能完成的操作,比如特殊符号等,而且command能完成的shell模块也能完成。

script模块:

[root@ansible ~]# ansible-doc -s script
- name: Runs a local script on a remote node after transferring it
  script:
      chdir:                 # cd into this directory on the remote node before running the script
      creates:               # a filename, when it already exists, this step will *not* be run.
      decrypt:               # This option controls the autodecryption of source files using vault.
      executable:            # Name or path of a executable to invoke the script with
      free_form:             # (required) Path to the local script file followed by optional arguments. There is no parameter actually named 'free form';
                               see the examples!
      removes:               # a filename, when it does not exist, this step will *not* be run.
[root@ansible ~]#
script模块帮助

 在传统的模式下,我们要在被控机执行本机(主控端)上一个写好的脚本,及时有ansible也需要把脚本传输到被控机上,然后执行ansible的shell模块来执行脚本,那么前面的传输的操作就会变得有点麻烦。而且不还不能确定所有机器传输的时候都能给到执行权限,这样的情况下,你还得给这些被控端机器上脚本执行权限。

script模块直接在本机云信,无需传输。

[root@ansible ansible.test]# pwd
/home/ansible.test
[root@ansible ansible.test]# cat test.sh
#!/bin/bash
hostname
[root@ansible ansible.test]# ansible all -m script -a '/home/ansible.test/test.sh'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "rc": 0,
    "stderr": "Shared connection to 192.168.219.137 closed.\r\n",
    "stderr_lines": [
        "Shared connection to 192.168.219.137 closed."
    ],
    "stdout": "web2\r\n",
    "stdout_lines": [
        "web2"
    ]
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "rc": 0,
    "stderr": "Shared connection to 192.168.219.136 closed.\r\n",
    "stderr_lines": [
        "Shared connection to 192.168.219.136 closed."
    ],
    "stdout": "web1\r\n",
    "stdout_lines": [
        "web1"
    ]
}
[root@ansible ansible.test]#
script案例

copy模块:

copy模块用来批量传输文件,例如服务的配置文件等。

ansible db -m copy -a 'src=源路径下文件 dest=目标路径(路径必须存在)'

[root@ansible ansible.test]# ansible-doc -s copy
- name: Copies files to remote locations
  copy:
      attributes:            # Attributes the file or directory should have. To get supported flags look at the man page for `chattr' on the target
                               system. This string should contain the attributes in the same order as the one displayed by
                               `lsattr'.
      backup:                # Create a backup file including the timestamp information so you can get the original file back if you somehow clobbered it
                               incorrectly.
      checksum:              # SHA1 checksum of the file being transferred. Used to validate that the copy of the file was successful. If this is not
                               provided, ansible will use the local calculated checksum of the src file.
      content:               # When used instead of `src', sets the contents of a file directly to the specified value. For anything advanced or with
                               formatting also look at the template module.
      decrypt:               # This option controls the autodecryption of source files using vault.
      dest:                  # (required) Remote absolute path where the file should be copied to. If `src' is a directory, this must be a directory too.
                               If `dest' is a nonexistent path and if either `dest' ends with "/" or `src' is a directory,
                               `dest' is created. If `src' and `dest' are files, the parent directory of `dest' isn't
                               created: the task fails if it doesn't already exist.
      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 in the destination, 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'.
      local_follow:          # This flag indicates that filesystem links in the source tree, if they exist, should be followed.
      mode:                  # Mode the file or directory should be. For those used to `/usr/bin/chmod' remember that modes are actually octal numbers.
                               You must either specify the leading zero so that Ansible's YAML parser knows it is an octal
                               number (like `0644' or `01777') or quote it (like `'644'' or `'0644'' so Ansible receives a
                               string and can do its own conversion from string into number.  Giving Ansible a number
                               without following one of these rules will end up with a decimal number which will 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').  As of version 2.3, the mode may also be the special
                               string `preserve'.  `preserve' means that the file will be given the same permissions as
                               the source file.
      owner:                 # Name of the user that should own the file/directory, as would be fed to `chown'.
      remote_src:            # If `no', it will search for `src' at originating/master machine. If `yes' it will go to the remote/target machine for the
                               `src'. Default is `no'. Currently `remote_src' does not support recursive copying.
                               `remote_src' only works with `mode=preserve' as of version 2.6.
      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.
(END)
copy模块帮助
1 backup:在覆盖之前将原文件备份,备份文件包含时间信息。有两个选项:yes|no 
2 content:用于替代"src",可以直接设定指定文件的值 
3 dest:必选项。要将源文件复制到的远程主机的绝对路径,如果源文件是一个目录,那么该路径也必须是个目录 
4 directory_mode:递归的设定目录的权限,默认为系统默认权限
5 force:如果目标主机包含该文件,但内容不同,如果设置为yes,则强制覆盖,如果为no,则只有当目标主机的目标位置不存在该文件时,才复制。默认为yes
6 others:所有的file模块里的选项都可以在这里使用
7 src:要复制到远程主机的文件在本地的地址,可以是绝对路径,也可以是相对路径。如果路径是一个目录,它将递归复制。在这种情况下,如果路径使用"/"来结尾,则只复制目录里的内容,如果没有使用"/"来结尾,则包含目录在内的整个内容全部复制,类似于rsync。 
8 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 visudo example below.
常用选项
[root@ansible ansible.test]# ansible all -m copy -a 'src=/home/ansible.test/copy_test.conf dest=/home/copy_test.conf'
192.168.219.136 | SUCCESS => {
    "changed": true,
    "checksum": "5ffd53c0987ac64d0a82a8c97978afe8f02d7b5a",
    "dest": "/home/copy_test.conf",
    "gid": 0,
    "group": "root",
    "md5sum": "40488fea7fe48f99ea02e86166a0bc9d",
    "mode": "0644",
    "owner": "root",
    "size": 28,
    "src": "/root/.ansible/tmp/ansible-tmp-1550554363.16-165811746367830/source",
    "state": "file",
    "uid": 0
}
192.168.219.137 | SUCCESS => {
    "changed": true,
    "checksum": "5ffd53c0987ac64d0a82a8c97978afe8f02d7b5a",
    "dest": "/home/copy_test.conf",
    "gid": 0,
    "group": "root",
    "md5sum": "40488fea7fe48f99ea02e86166a0bc9d",
    "mode": "0644",
    "owner": "root",
    "size": 28,
    "src": "/root/.ansible/tmp/ansible-tmp-1550554363.16-162669317189243/source",
    "state": "file",
    "uid": 0
}
[root@ansible ansible.test]# ansible all -a 'ls /home'
192.168.219.136 | SUCCESS | rc=0 >>
centos12
copy_test.conf
mysql
ypc.soft

192.168.219.137 | SUCCESS | rc=0 >>
centos13
copy_test.conf
mysql

[root@ansible ansible.test]#
copy模块案例

fetch模块:

既然我们能将主控端的文件拷贝到被控端机器上,ansible当然也提供了可以将被控端机器上文件抓取到主控端的功能,例如我们需要把被控端的日志等文件抓取到主控端统一进行分析。

注:fetch模块只能一次抓取一个文件且不能是目录。当然不是没有办法,可以通过shell模块把被控端目录或者多个文件进行打包在抓取过来。

[root@ansible ansible.test]# ansible-doc -s fetch
- name: Fetches a file from remote nodes
  fetch:
      dest:                  # (required) A directory to save the file into. For example, if the `dest' directory is `/backup' a `src' file named
                               `/etc/profile' on host `host.example.com', would be saved into
                               `/backup/host.example.com/etc/profile'
      fail_on_missing:       # When set to 'yes', the task will fail if the remote file cannot be read for any reason.  Prior to Ansible-2.5, setting
                               this would only fail if the source file was missing. The default was changed to "yes" in
                               Ansible-2.5.
      flat:                  # Allows you to override the default behavior of appending hostname/path/to/file to the destination.  If dest ends with '/',
                               it will use the basename of the source file, similar to the copy module. Obviously this is
                               only handy if the filenames are unique.
      src:                   # (required) The file on the remote system to fetch. This `must' be a file, not a directory. Recursive fetching may be
                               supported in a later release.
      validate_checksum:     # Verify that the source and destination checksums match after the files are fetched.
[root@ansible ansible.test]#
fetch模块帮助
[root@ansible ansible.test]# ansible all -m fetch -a 'src=/etc/passwd dest=/home/ansible.test/test'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "checksum": "edf3216846dae3f593314cf07b8d8f328e58f067",
    "dest": "/home/ansible.test/test/192.168.219.137/etc/passwd",
    "md5sum": "86e07804c44a27941e1fc798a5779756",
    "remote_checksum": "edf3216846dae3f593314cf07b8d8f328e58f067",
    "remote_md5sum": null
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "checksum": "65a433c99ff5ce054264be87594828c83989aa0e",
    "dest": "/home/ansible.test/test/192.168.219.136/etc/passwd",
    "md5sum": "891484511715765f25fd7c4fa67f8288",
    "remote_checksum": "65a433c99ff5ce054264be87594828c83989aa0e",
    "remote_md5sum": null
}
[root@ansible ansible.test]# tree test
test
├── 192.168.219.136
│   └── etc
│       └── passwd
└── 192.168.219.137
    └── etc
        └── passwd

4 directories, 2 files

[root@ansible ansible.test]# cd test
[root@ansible test]# ls
192.168.219.136  192.168.219.137
[root@ansible test]# cd 192.168.219.136
[root@ansible 192.168.219.136]# ls
etc
[root@ansible 192.168.219.136]# cd etc/
[root@ansible etc]# ls
passwd
[root@ansible etc]#
#可以看到在test下各自创建了一个属于被控端机器ip的目录,抓取的文件都放在这个目录下面,方便区分
fetch案例
[root@ansible etc]# ansible all -m shell -a 'tar Jcf log.tar.xz /var/log/*.log'
 [WARNING]: Consider using the unarchive module rather than running tar.  If you
need to use command because unarchive is insufficient you can add warn=False to
this command task or set command_warnings=False in ansible.cfg to get rid of this
message.

192.168.219.137 | SUCCESS | rc=0 >>
tar: Removing leading `/' from member names

192.168.219.136 | SUCCESS | rc=0 >>
tar: Removing leading `/' from member names

[root@ansible etc]# ansible all -m shell -a 'ls -h'
192.168.219.136 | SUCCESS | rc=0 >>
anaconda-ks.cfg
Desktop
Documents
Downloads
install.log
install.log.syslog
log.tar.xz
Music
Pictures
Public
Templates
Videos

192.168.219.137 | SUCCESS | rc=0 >>
anaconda-ks.cfg
Desktop
Documents
Downloads
install.log
install.log.syslog
log.tar.xz
Music
Pictures
Public
Templates
Videos

[root@ansible etc]# ansible all -m shell -a 'pwd'
192.168.219.137 | SUCCESS | rc=0 >>
/root

192.168.219.136 | SUCCESS | rc=0 >>
/root

[root@ansible etc]# ansible all -m fetch -a 'src=/root/log.tar.xz dest=/home/ansible.test/test/'
192.168.219.136 | SUCCESS => {
    "changed": true,
    "checksum": "893ff89ed3394cf7a94e6b4d2aca694a7b8f4f9e",
    "dest": "/home/ansible.test/test/192.168.219.136/root/log.tar.xz",
    "md5sum": "bc8e9ac43907b51c4e1e31b786e1e1ef",
    "remote_checksum": "893ff89ed3394cf7a94e6b4d2aca694a7b8f4f9e",
    "remote_md5sum": null
}
192.168.219.137 | SUCCESS => {
    "changed": true,
    "checksum": "9662827bc0da5854fe108a9847e1df4986388cfd",
    "dest": "/home/ansible.test/test/192.168.219.137/root/log.tar.xz",
    "md5sum": "9216f8c9dd0e41058d6ef3ca77f1919e",
    "remote_checksum": "9662827bc0da5854fe108a9847e1df4986388cfd",
    "remote_md5sum": null
}
[root@ansible ansible.test]# tree test
test
├── 192.168.219.136
│   ├── etc
│   │   └── passwd
│   └── root
│       └── log.tar.xz
└── 192.168.219.137
    ├── etc
    │   └── passwd
    └── root
        └── log.tar.xz

6 directories, 4 files
[root@ansible ansible.test]#
可以看到多个文件已经被抓取过来了
打包抓取多个文件

cron模块:计划任务模块

cron模块格式:

ansible 主机清单名 -m cron -a 'minute="分钟" hour="时" day="日" month="月" weekday="周" job="计划任务内容" name="计划任务名称(必填)" state="添加(present默认)和absent(移除)"'

[root@ansible ~]# ansible-doc -s cron
- name: Manage cron.d and crontab entries
  cron:
      backup:                # If set, create a backup of the crontab before it is modified. The location of the backup is returned in the `backup_file'
                               variable by this module.
      cron_file:             # If specified, uses this file instead of an individual user's crontab. If this is a relative path, it is interpreted with
                               respect to /etc/cron.d. (If it is absolute, it will typically be /etc/crontab). Many linux
                               distros expect (and some require) the filename portion to consist solely of upper- and
                               lower-case letters, digits, underscores, and hyphens. To use the `cron_file' parameter you
                               must specify the `user' as well.
      day:                   # Day of the month the job should run ( 1-31, *, */2, etc )
      disabled:              # If the job should be disabled (commented out) in the crontab. Only has effect if `state=present'.
      env:                   # If set, manages a crontab's environment variable. New variables are added on top of crontab. "name" and "value" parameters
                               are the name and the value of environment variable.
      hour:                  # Hour when the job should run ( 0-23, *, */2, etc )
      insertafter:           # Used with `state=present' and `env'. If specified, the environment variable will be inserted after the declaration of
                               specified environment variable.
      insertbefore:          # Used with `state=present' and `env'. If specified, the environment variable will be inserted before the declaration of
                               specified environment variable.
      job:                   # The command to execute or, if env is set, the value of environment variable. The command should not contain line breaks.
                               Required if state=present.
      minute:                # Minute when the job should run ( 0-59, *, */2, etc )
      month:                 # Month of the year the job should run ( 1-12, *, */2, etc )
      name:                  # Description of a crontab entry or, if env is set, the name of environment variable. Required if state=absent. Note that if
                               name is not set and state=present, then a new crontab entry will always be created,
                               regardless of existing ones.
      reboot:                # If the job should be run at reboot. This option is deprecated. Users should use special_time.
      special_time:          # Special time specification nickname.
      state:                 # Whether to ensure the job or environment variable is present or absent.
      user:                  # The specific user whose crontab should be modified.
      weekday:               # Day of the week that the job should run ( 0-6 for Sunday-Saturday, *, etc )
[root@ansible ~]# 

 1 backup:对远程主机上的原任务计划内容修改之前做备份 
 2 cron_file:如果指定该选项,则用该文件替换远程主机上的cron.d目录下的用户的任务计划 
 3 day:日(1-31,*,*/2,……) 
 4 hour:小时(0-23,*,*/2,……)  
 5 minute:分钟(0-59,*,*/2,……) 
 6 month:月(1-12,*,*/2,……) 
 7 weekday:周(0-7,*,……)
 8 job:要执行的任务,依赖于state=present 
 9 name:该任务的描述 
10 special_time:指定什么时候执行,参数:reboot,yearly,annually,monthly,weekly,daily,hourly 
11 state:确认该任务计划是创建还是删除 
12 user:以哪个用户的身份执行
帮助信息
[root@ansible ~]# ansible all -m cron -a 'minute="*/1" job="/bin/echo hello ansible" name="test cron job" state="present"'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "envs": [],
    "jobs": [
        "kk",
        "test cron job"
    ]
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "envs": [],
    "jobs": [
        "kk",
        "test cron job"
    ]
}
[root@ansible ~]#

136:
[root@web1 ~]# crontab -l
#Ansible: test cron job
*/1 * * * * /bin/echo hello ansible
[root@web1 ~]#

137#Ansible: test cron job
*/1 * * * * /bin/echo hello ansible
You have new mail in /var/spool/mail/root
[root@web2 ~]#
corn案例present添加
[root@ansible ~]# ansible all -m cron -a 'minute="*/1" job="/bin/echo hello ansible" name="test cron job" state="absent"'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "envs": [],
    "jobs": []
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "envs": [],
    "jobs": []
}
[root@ansible ~]#

136:
[root@web1 ~]# crontab -l
You have new mail in /var/spool/mail/root
[root@web1 ~]#

[root@web2 ~]# crontab -l
You have new mail in /var/spool/mail/root
[root@web2 ~]#
corn案例absent移除
[root@ansible ~]# ansible all -m cron -a 'disabled=yes job="/usr/bin/wall FBI warning" name=kk'
192.168.219.136 | SUCCESS => {
    "changed": true,
    "envs": [],
    "jobs": [
        "test cron job",
        "kk"
    ]
}
192.168.219.137 | SUCCESS => {
    "changed": true,
    "envs": [],
    "jobs": [
        "test cron job",
        "kk"
    ]
}
[root@ansible ~]# ansible all -m shell -a 'crontab -l'
192.168.219.136 | SUCCESS | rc=0 >>
#Ansible: test cron job
*/1 * * * * /bin/echo hello ansible
#Ansible: kk
#* * * * * /usr/bin/wall FBI warning

192.168.219.137 | SUCCESS | rc=0 >>
#Ansible: test cron job
*/1 * * * * /bin/echo hello ansible
#Ansible: kk
#* * * * * /usr/bin/wall FBI warning
发现新加的计划任务就是注释的
ansible案例注释计划任务
[root@ansible ~]# ansible all -m cron -a 'disabled=no job="/usr/bin/wall FBI warning" name=kk'
192.168.219.136 | SUCCESS => {
    "changed": true,
    "envs": [],
    "jobs": [
        "test cron job",
        "kk"
    ]
}
192.168.219.137 | SUCCESS => {
    "changed": true,
    "envs": [],
    "jobs": [
        "test cron job",
        "kk"
    ]
}
[root@ansible ~]# ansible all -m shell -a 'crontab -l'
192.168.219.136 | SUCCESS | rc=0 >>
#Ansible: test cron job
*/1 * * * * /bin/echo hello ansible
#Ansible: kk
* * * * * /usr/bin/wall FBI warning

192.168.219.137 | SUCCESS | rc=0 >>
#Ansible: test cron job
*/1 * * * * /bin/echo hello ansible
#Ansible: kk
* * * * * /usr/bin/wall FBI warning
可以看到计划任务注释取消掉了,disable后面可以接yes/no,还可以是ture和flase
ansible案例取消注释计划任务

yum模块:

批量通过yum安装服务即可使用yum模块。

[root@ansible ~]# ansible-doc -s yum
- name: Manages packages with the `yum' package manager
  yum:
      allow_downgrade:       # Specify if the named package and version is allowed to downgrade a maybe already installed higher version of that package.
                               Note that setting allow_downgrade=True can make this module behave in a non-idempotent way.
                               The task could end up with a set of packages that does not match the complete list of
                               specified packages to install (because dependencies between the downgraded package and
                               others can cause changes to the packages which were in the earlier transaction).
      bugfix:                # If set to `yes', and `state=latest' then only installs updates that have been marked bugfix related.
      conf_file:             # The remote yum configuration file to use for the transaction.
      disable_gpg_check:     # Whether to disable the GPG checking of signatures of packages being installed. Has an effect only if state is `present' or
                               `latest'.
      disable_plugin:        # `Plugin' name to disable for the install/update operation. The disabled plugins will not persist beyond the transaction.
      disablerepo:           # `Repoid' of repositories to disable for the install/update operation. These repos will not persist beyond the transaction.
                               When specifying multiple repos, separate them with a ",".
      enable_plugin:         # `Plugin' name to enable for the install/update operation. The enabled plugin will not persist beyond the transaction.
      enablerepo:            # `Repoid' of repositories to enable for the install/update operation. These repos will not persist beyond the transaction.
                               When specifying multiple repos, separate them with a ",".
      exclude:               # Package name(s) to exclude when state=present, or latest
      installroot:           # Specifies an alternative installroot, relative to which all packages will be installed.
      list:                  # Package name to run the equivalent of yum list <package> against. In addition to listing packages, use can also list the
                               following: `installed', `updates', `available' and `repos'.
      name:                  # A package name or package specifier with version, like `name-1.0'. If a previous version is specified, the task also needs
                               to turn `allow_downgrade' on. See the `allow_downgrade' documentation for caveats with
                               downgrading packages. When using state=latest, this can be '*' which means run `yum -y
                               update'. You can also pass a url or a local path to a rpm file (using state=present). To
                               operate on several packages this can accept a comma separated list of packages or (as of
                               2.0) a list of packages.
      security:              # If set to `yes', and `state=latest' then only installs updates that have been marked security related.
      skip_broken:           # Resolve depsolve problems by removing packages that are causing problems from the trans‐ action.
      state:                 # Whether to install (`present' or `installed', `latest'), or remove (`absent' or `removed') a package. `present' and
                               `installed' will simply ensure that a desired package is installed. `latest' will update
                               the specified package if it's not of the latest available version. `absent' and `removed'
                               will remove the specified package.
      update_cache:          # Force yum to check if cache is out of date and redownload if needed. Has an effect only if state is `present' or `latest'.
      update_only:           # When using latest, only update installed packages. Do not install packages. Has an effect only if state is `latest'
      validate_certs:        # This only applies if using a https url as the source of the rpm. e.g. for localinstall. If set to `no', the SSL
                               certificates will not be validated. This should only set to `no' used on personally
                               controlled sites using self-signed certificates as it avoids verifying the source site.
                               Prior to 2.1 the code worked as if this was set to `yes'.
(END)
yum模块帮助
name参数:必须参数,用于指定要操作的唯一的仓库ID,也就是”.repo”配置文件中每个仓库对应的”中括号”内的仓库ID。

baseurl参数:此参数用于设置 yum 仓库的 baseurl。

description参数:此参数用于设置仓库的注释信息,也就是”.repo”配置文件中每个仓库对应的”name字段”对应的内容。

file参数:此参数用于设置仓库的配置文件名称,即设置”.repo”配置文件的文件名前缀,在不使用此参数的情况下,默认以 name 参数的仓库ID作为”.repo”配置文件的文件名前缀,同一个”.repo” 配置文件中可以存在多个 yum 源。

enabled参数:此参数用于设置是否激活对应的 yum 源,此参数默认值为 yes,表示启用对应的 yum 源,设置为 no 表示不启用对应的 yum 源。

gpgcheck参数:此参数用于设置是否开启 rpm 包验证功能,默认值为 no,表示不启用包验证,设置为 yes 表示开启包验证功能。

gpgcakey参数:当 gpgcheck 参数设置为 yes 时,需要使用此参数指定验证包所需的公钥。

state参数:默认值为 present,当值设置为 absent 时,表示删除对应的 yum 源。
参数详解
卸载httpd服务
[root@ansible ~]# ansible all -m yum -a 'state=removed name=httpd'
192.168.219.136 | SUCCESS => {
    "changed": true,
    "msg": "",
    "rc": 0,
    "results": [
        "Loaded plugins: fastestmirror, refresh-packagekit, security\nSetting up Remove Process\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.2.15-69.el6.centos will be erased\n--> Processing Dependency: httpd >= 2.2.0 for package: gnome-user-share-2.28.2-3.el6.x86_64\n--> Running transaction check\n---> Package gnome-user-share.x86_64 0:2.28.2-3.el6 will be erased\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package  Arch   Version         Repository                                Size\n================================================================================\nRemoving:\n httpd    x86_64 2.2.15-69.el6.centos\n                                 @base                                    3.0 M\nRemoving for dependencies:\n gnome-user-share\n          x86_64 2.28.2-3.el6    @anaconda-CentOS-201508042137.x86_64/6.7 1.1 M\n\nTransaction Summary\n================================================================================\nRemove        2 Package(s)\n\nInstalled size: 4.1 M\nDownloading Packages:\nRunning rpm_check_debug\nRunning Transaction Test\nTransaction Test Succeeded\nRunning Transaction\n\r  Erasing    : gnome-user-share-2.28.2-3.el6.x86_64                         1/2 \n\r  Erasing    : httpd-2.2.15-69.el6.centos.x86_64                            2/2 \n\r  Verifying  : gnome-user-share-2.28.2-3.el6.x86_64                         1/2 \n\r  Verifying  : httpd-2.2.15-69.el6.centos.x86_64                            2/2 \n\nRemoved:\n  httpd.x86_64 0:2.2.15-69.el6.centos                                           \n\nDependency Removed:\n  gnome-user-share.x86_64 0:2.28.2-3.el6                                        \n\nComplete!\n"
    ]
}
192.168.219.137 | SUCCESS => {
    "changed": true,
    "msg": "",
    "rc": 0,
    "results": [
        "Loaded plugins: fastestmirror, refresh-packagekit, security\nSetting up Remove Process\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.2.15-69.el6.centos will be erased\n--> Processing Dependency: httpd >= 2.2.0 for package: gnome-user-share-2.28.2-3.el6.x86_64\n--> Running transaction check\n---> Package gnome-user-share.x86_64 0:2.28.2-3.el6 will be erased\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package  Arch   Version         Repository                                Size\n================================================================================\nRemoving:\n httpd    x86_64 2.2.15-69.el6.centos\n                                 @base                                    3.0 M\nRemoving for dependencies:\n gnome-user-share\n          x86_64 2.28.2-3.el6    @anaconda-CentOS-201508042137.x86_64/6.7 1.1 M\n\nTransaction Summary\n================================================================================\nRemove        2 Package(s)\n\nInstalled size: 4.1 M\nDownloading Packages:\nRunning rpm_check_debug\nRunning Transaction Test\nTransaction Test Succeeded\nRunning Transaction\n\r  Erasing    : gnome-user-share-2.28.2-3.el6.x86_64                         1/2 \n\r  Erasing    : httpd-2.2.15-69.el6.centos.x86_64                            2/2 \n\r  Verifying  : gnome-user-share-2.28.2-3.el6.x86_64                         1/2 \n\r  Verifying  : httpd-2.2.15-69.el6.centos.x86_64                            2/2 \n\nRemoved:\n  httpd.x86_64 0:2.2.15-69.el6.centos                                           \n\nDependency Removed:\n  gnome-user-share.x86_64 0:2.28.2-3.el6                                        \n\nComplete!\n"
    ]
}
[root@ansible ~]# ansible all -m shell -a 'httpd -v'
192.168.219.137 | FAILED | rc=127 >>
/bin/sh: httpd: command not foundnon-zero return code

192.168.219.136 | FAILED | rc=127 >>
/bin/sh: httpd: command not foundnon-zero return code

安装httpd服务
[root@ansible ~]# ansible all -m yum -a 'state=present name=httpd'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "msg": "",
    "rc": 0,
    "results": [
        "Loaded plugins: fastestmirror, refresh-packagekit, security\nSetting up Install Process\nLoading mirror speeds from cached hostfile\n * base: mirrors.aliyun.com\n * epel: mirrors.huaweicloud.com\n * extras: mirrors.cn99.com\n * updates: mirror.bit.edu.cn\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.2.15-69.el6.centos will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package       Arch           Version                        Repository    Size\n================================================================================\nInstalling:\n httpd         x86_64         2.2.15-69.el6.centos           base         836 k\n\nTransaction Summary\n================================================================================\nInstall       1 Package(s)\n\nTotal download size: 836 k\nInstalled size: 3.0 M\nDownloading Packages:\nRunning rpm_check_debug\nRunning Transaction Test\nTransaction Test Succeeded\nRunning Transaction\n\r  Installing : httpd-2.2.15-69.el6.centos.x86_64                            1/1 \n\r  Verifying  : httpd-2.2.15-69.el6.centos.x86_64                            1/1 \n\nInstalled:\n  httpd.x86_64 0:2.2.15-69.el6.centos                                           \n\nComplete!\n"
    ]
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "msg": "",
    "rc": 0,
    "results": [
        "Loaded plugins: fastestmirror, refresh-packagekit, security\nSetting up Install Process\nLoading mirror speeds from cached hostfile\n * base: mirrors.cqu.edu.cn\n * epel: mirrors.yun-idc.com\n * extras: mirrors.cqu.edu.cn\n * updates: mirrors.cqu.edu.cn\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.2.15-69.el6.centos will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package       Arch           Version                        Repository    Size\n================================================================================\nInstalling:\n httpd         x86_64         2.2.15-69.el6.centos           base         836 k\n\nTransaction Summary\n================================================================================\nInstall       1 Package(s)\n\nTotal download size: 836 k\nInstalled size: 3.0 M\nDownloading Packages:\nRunning rpm_check_debug\nRunning Transaction Test\nTransaction Test Succeeded\nRunning Transaction\n\r  Installing : httpd-2.2.15-69.el6.centos.x86_64                            1/1 \n\r  Verifying  : httpd-2.2.15-69.el6.centos.x86_64                            1/1 \n\nInstalled:\n  httpd.x86_64 0:2.2.15-69.el6.centos                                           \n\nComplete!\n"
    ]
}
[root@ansible ~]# ansible all -m shell -a 'httpd -v'
192.168.219.137 | SUCCESS | rc=0 >>
Server version: Apache/2.2.15 (Unix)
Server built:   Jun 19 2018 15:45:13

192.168.219.136 | SUCCESS | rc=0 >>
Server version: Apache/2.2.15 (Unix)
Server built:   Jun 19 2018 15:45:13
卸载和安装
[root@ansible ansible.test]# ansible all -m file -a 'name=/home/soft state=directory'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "gid": 0,
    "group": "root",
    "mode": "0755",
    "owner": "root",
    "path": "/home/soft",
    "size": 4096,
    "state": "directory",
    "uid": 0
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "gid": 0,
    "group": "root",
    "mode": "0755",
    "owner": "root",
    "path": "/home/soft",
    "size": 4096,
    "state": "directory",
    "uid": 0
}
[root@ansible ansible.test]# ansible all -m copy -a 'src=/home/ansible.test/htop-1.0.3-1.el6.rf.x86_64.rpm dest=/home/soft'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "checksum": "2deed7a9030c2963c677cce6ecaccb4750106052",
    "dest": "/home/soft/htop-1.0.3-1.el6.rf.x86_64.rpm",
    "gid": 0,
    "group": "root",
    "md5sum": "683cc37192a76556c0847aaf93459abf",
    "mode": "0644",
    "owner": "root",
    "size": 88608,
    "src": "/root/.ansible/tmp/ansible-tmp-1550738545.61-57323277206137/source",
    "state": "file",
    "uid": 0
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "checksum": "2deed7a9030c2963c677cce6ecaccb4750106052",
    "dest": "/home/soft/htop-1.0.3-1.el6.rf.x86_64.rpm",
    "gid": 0,
    "group": "root",
    "md5sum": "683cc37192a76556c0847aaf93459abf",
    "mode": "0644",
    "owner": "root",
    "size": 88608,
    "src": "/root/.ansible/tmp/ansible-tmp-1550738545.6-127862535278548/source",
    "state": "file",
    "uid": 0
}
[root@ansible ansible.test]# ansible all -m shell -a 'ls -h /home/soft'
192.168.219.136 | SUCCESS | rc=0 >>
htop-1.0.3-1.el6.rf.x86_64.rpm

192.168.219.137 | SUCCESS | rc=0 >>
htop-1.0.3-1.el6.rf.x86_64.rpm

[root@ansible ansible.test]# ansible all -m yum -a 'name=/home/soft/htop-1.0.3-1.el6.rf.x86_64.rpm'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "msg": "",
    "rc": 0,
    "results": [
        "Loaded plugins: fastestmirror, refresh-packagekit, security\nSetting up Install Process\nExamining /home/soft/htop-1.0.3-1.el6.rf.x86_64.rpm: htop-1.0.3-1.el6.rf.x86_64\nMarking /home/soft/htop-1.0.3-1.el6.rf.x86_64.rpm to be installed\nLoading mirror speeds from cached hostfile\n * base: mirrors.aliyun.com\n * epel: mirrors.aliyun.com\n * extras: mirrors.163.com\n * updates: ftp.sjtu.edu.cn\nResolving Dependencies\n--> Running transaction check\n---> Package htop.x86_64 0:1.0.3-1.el6.rf will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package  Arch       Version              Repository                       Size\n================================================================================\nInstalling:\n htop     x86_64     1.0.3-1.el6.rf       /htop-1.0.3-1.el6.rf.x86_64     209 k\n\nTransaction Summary\n================================================================================\nInstall       1 Package(s)\n\nTotal size: 209 k\nInstalled size: 209 k\nDownloading Packages:\nRunning rpm_check_debug\nRunning Transaction Test\nTransaction Test Succeeded\nRunning Transaction\n\r  Installing : htop-1.0.3-1.el6.rf.x86_64                                   1/1 \n\r  Verifying  : htop-1.0.3-1.el6.rf.x86_64                                   1/1 \n\nInstalled:\n  htop.x86_64 0:1.0.3-1.el6.rf                                                  \n\nComplete!\n"
    ]
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "msg": "",
    "rc": 0,
    "results": [
        "Loaded plugins: fastestmirror, refresh-packagekit, security\nSetting up Install Process\nExamining /home/soft/htop-1.0.3-1.el6.rf.x86_64.rpm: htop-1.0.3-1.el6.rf.x86_64\nMarking /home/soft/htop-1.0.3-1.el6.rf.x86_64.rpm to be installed\nLoading mirror speeds from cached hostfile\n * base: centos.ustc.edu.cn\n * epel: mirror.premi.st\n * extras: mirrors.163.com\n * updates: mirrors.tuna.tsinghua.edu.cn\nResolving Dependencies\n--> Running transaction check\n---> Package htop.x86_64 0:1.0.3-1.el6.rf will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package  Arch       Version              Repository                       Size\n================================================================================\nInstalling:\n htop     x86_64     1.0.3-1.el6.rf       /htop-1.0.3-1.el6.rf.x86_64     209 k\n\nTransaction Summary\n================================================================================\nInstall       1 Package(s)\n\nTotal size: 209 k\nInstalled size: 209 k\nDownloading Packages:\nRunning rpm_check_debug\nRunning Transaction Test\nTransaction Test Succeeded\nRunning Transaction\n\r  Installing : htop-1.0.3-1.el6.rf.x86_64                                   1/1 \n\r  Verifying  : htop-1.0.3-1.el6.rf.x86_64                                   1/1 \n\nInstalled:\n  htop.x86_64 0:1.0.3-1.el6.rf                                                  \n\nComplete!\n"
    ]
}
[root@ansible ansible.test]# ansible all -m shell -a 'htop -v'
192.168.219.136 | SUCCESS | rc=0 >>
htop 1.0.3 - (C) 2004-2012 Hisham Muhammad
Released under the GNU GPL.

192.168.219.137 | SUCCESS | rc=0 >>
htop 1.0.3 - (C) 2004-2012 Hisham Muhammad
Released under the GNU GPL.

[root@ansible ansible.test]#
可以看到服务都已经安装成功了
通过yum安装单个包

 service模块:

当我们要批量启动或关闭n台机器上的服务的时候,ansible也能通过很简单的操作就可以完成。

[root@ansible ~]# ansible-doc -s service
- name: Manage services
  service:
      arguments:             # Additional arguments provided on the command line
      enabled:               # Whether the service should start on boot. *At least one of state and enabled are required.*
      name:                  # (required) Name of the service.
      pattern:               # If the service does not respond to the status command, name a substring to look for as would be found in the output of the
                               `ps' command as a stand-in for a status result.  If the string is found, the service will
                               be assumed to be running.
      runlevel:              # For OpenRC init scripts (ex: Gentoo) only.  The runlevel that this service belongs to.
      sleep:                 # If the service is being `restarted' then sleep this many seconds between the stop and start command. This helps to
                               workaround badly behaving init scripts that exit immediately after signaling a process to
                               stop.
      state:                 # `started'/`stopped' are idempotent actions that will not run commands unless necessary.  `restarted' will always bounce
                               the service.  `reloaded' will always reload. *At least one of state and enabled are
                               required.* Note that reloaded will start the service if it is not already started, even if
                               your chosen init system wouldn't normally.
      use:                   # The service module actually uses system specific modules, normally through auto detection, this setting can force a
                               specific module. Normally it uses the value of the 'ansible_service_mgr' fact and falls
                               back to the old 'service' module when none matching is found.
[root@ansible ~]#
service模块帮助
[root@ansible ~]# ansible all -m shell -a 'httpd -v'
192.168.219.137 | SUCCESS | rc=0 >>
Server version: Apache/2.2.15 (Unix)
Server built:   Jun 19 2018 15:45:13

192.168.219.136 | SUCCESS | rc=0 >>
Server version: Apache/2.2.15 (Unix)
Server built:   Jun 19 2018 15:45:13

[root@ansible ~]# ansible all -m service -a 'name=httpd state=restarted enabled=yes'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "enabled": true,
    "name": "httpd",
    "state": "started"
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "enabled": true,
    "name": "httpd",
    "state": "started"
}
[root@ansible ~]# ansible all -m shell -a 'ps -aux | grep http'
192.168.219.137 | SUCCESS | rc=0 >>
root       3342  0.0  0.3 183896  3816 ?        Ss   13:16   0:00 /usr/sbin/httpd
apache     3345  0.0  0.2 184028  2500 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3346  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3347  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3348  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3349  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3350  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3351  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3352  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
root       3389  0.0  0.1 106096  1120 pts/1    S+   13:17   0:00 /bin/sh -c ps -aux | grep http
root       3391  0.0  0.0 103312   860 pts/1    S+   13:17   0:00 grep httpWarning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ

192.168.219.136 | SUCCESS | rc=0 >>
root       3323  0.0  0.3 183896  3824 ?        Ss   13:16   0:00 /usr/sbin/httpd
apache     3326  0.0  0.2 184028  2500 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3327  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3328  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3329  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3330  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3331  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3332  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
apache     3333  0.0  0.2 184028  2476 ?        S    13:16   0:00 /usr/sbin/httpd
root       3370  0.0  0.1 106096  1128 pts/1    S+   13:17   0:00 /bin/sh -c ps -aux | grep http
root       3372  0.0  0.0 103312   856 pts/1    S+   13:17   0:00 grep httpWarning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ

[root@ansible ~]# ansible all -m shell -a 'netstat -anptu | grep 80'
192.168.219.137 | SUCCESS | rc=0 >>
tcp        0      0 :::80                       :::*                        LISTEN      3342/httpd

192.168.219.136 | SUCCESS | rc=0 >>
tcp        0      0 :::80                       :::*                        LISTEN      3323/httpd

[root@ansible ~]# ansible all -m shell -a 'chkconfig httpd --list'
192.168.219.137 | SUCCESS | rc=0 >>
httpd           0:off   1:off   2:on    3:on    4:on    5:on    6:off

192.168.219.136 | SUCCESS | rc=0 >>
httpd           0:off   1:off   2:on    3:on    4:on    5:on    6:off

[root@ansible ~]#
启动http服务并加入开机自启

file模块:

设置文件的属性,所属组等,软连接,创建空目录,递归删除等。

[root@ansible ansible.test]# ansible all -m file -a 'name=/root/date state=touch'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "dest": "/root/date",
    "gid": 0,
    "group": "root",
    "mode": "0644",
    "owner": "root",
    "size": 0,
    "state": "file",
    "uid": 0
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "dest": "/root/date",
    "gid": 0,
    "group": "root",
    "mode": "0644",
    "owner": "root",
    "size": 0,
    "state": "file",
    "uid": 0
}
[root@ansible ansible.test]# ansible all -m shell -a 'ls -h date'
192.168.219.137 | SUCCESS | rc=0 >>
date

192.168.219.136 | SUCCESS | rc=0 >>
date

[root@ansible ansible.test]#
flie模块案例(创建一个空目录)
[root@ansible ansible.test]# ansible all -m shell -a 'ls -l date'
192.168.219.137 | SUCCESS | rc=0 >>
-rw-r--r-- 1 root root 0 Feb 20 13:17 date

192.168.219.136 | SUCCESS | rc=0 >>
-rw-r--r-- 1 root root 0 Feb 20 13:17 date

[root@ansible ansible.test]# ansible all -m file -a 'name=/root/date state=absent'
192.168.219.136 | SUCCESS => {
    "changed": true,
    "path": "/root/date",
    "state": "absent"
}
192.168.219.137 | SUCCESS => {
    "changed": true,
    "path": "/root/date",
    "state": "absent"
}
[root@ansible ansible.test]# ansible all -m shell -a 'ls'
192.168.219.137 | SUCCESS | rc=0 >>
anaconda-ks.cfg
Desktop
Documents
Downloads
install.log
install.log.syslog
log.tar.xz
Music
Pictures
Public
Templates
Videos

192.168.219.136 | SUCCESS | rc=0 >>
anaconda-ks.cfg
Desktop
Documents
Downloads
install.log
install.log.syslog
log.tar.xz
Music
Pictures
Public
Templates
Videos

[root@ansible ansible.test]#
可以看到date目录已经删除了
file删除案例
[root@ansible ansible.test]# ansible all -m file -a 'name=/root/file123/file321 state=directory'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "gid": 0,
    "group": "root",
    "mode": "0755",
    "owner": "root",
    "path": "/root/file123/file321",
    "size": 4096,
    "state": "directory",
    "uid": 0
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "gid": 0,
    "group": "root",
    "mode": "0755",
    "owner": "root",
    "path": "/root/file123/file321",
    "size": 4096,
    "state": "directory",
    "uid": 0
}
[root@ansible ansible.test]# ansible all -m shell -a 'ls -l file123'
192.168.219.137 | SUCCESS | rc=0 >>
total 4
drwxr-xr-x 2 root root 4096 Feb 20 13:27 file321

192.168.219.136 | SUCCESS | rc=0 >>
total 4
drwxr-xr-x 2 root root 4096 Feb 20 13:27 file321

[root@ansible ansible.test]#
可以看到递归创建成功了
file递归创建文件夹
[root@ansible ansible.test]# ansible all -m file -a 'src=/etc/passwd name=/root/file123/passwd.link state=link'
192.168.219.137 | SUCCESS => {
    "changed": true,
    "dest": "/root/file123/passwd.link",
    "gid": 0,
    "group": "root",
    "mode": "0777",
    "owner": "root",
    "size": 11,
    "src": "/etc/passwd",
    "state": "link",
    "uid": 0
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "dest": "/root/file123/passwd.link",
    "gid": 0,
    "group": "root",
    "mode": "0777",
    "owner": "root",
    "size": 11,
    "src": "/etc/passwd",
    "state": "link",
    "uid": 0
}
[root@ansible ansible.test]# ansible all -m shell -a 'ls -l /root/file123/passwd.link'
192.168.219.136 | SUCCESS | rc=0 >>
lrwxrwxrwx 1 root root 11 Feb 20 13:33 /root/file123/passwd.link -> /etc/passwd

192.168.219.137 | SUCCESS | rc=0 >>
lrwxrwxrwx 1 root root 11 Feb 20 13:33 /root/file123/passwd.link -> /etc/passwd

[root@ansible ansible.test]#
软连接创建成功

删除软连接
[root@ansible ansible.test]# ansible all -m file -a 'name=/root/file123/passwd.link state=absent'
 [WARNING]: The src option requires state to be 'link' or 'hard'.  This will become
an error in Ansible 2.10

192.168.219.137 | SUCCESS => {
    "changed": true,
    "path": "/root/file123/passwd.link",
    "state": "absent"
}
192.168.219.136 | SUCCESS => {
    "changed": true,
    "path": "/root/file123/passwd.link",
    "state": "absent"
}
[root@ansible ansible.test]# ansible all -m shell -a 'ls -l /root/file123/passwd.link'
file创建文件的软连接和删除软连接

因模块很多,本篇不做过多的介绍,需要查找可以通过ansible的官方手册:https://docs.ansible.com/ansible/latest/modules/modules_by_category.html来进行查找。

♣三:ansible的进阶操作

A:ansible-galaxy命令

ansible-galaxy是链接https://galaxy.ansible.com/下载相应的roles(角色),之前我们是歇学的单条ansible的命令,但是要把很多单条的命令组合起来就需要playbook(剧本),那么把playbook组合起来的就是roles(角色),把众多的playbook组合起来形成一个完整的文件夹,就是角色,你也可以通过提供的网站去网上下载一些人编写的一些实用的角色,可以下载下来使用。

保证服务器能访问互联网的情况下
ansible-galaxy install geerlingguy.nginx
直接使用ansible-galaxy命令安装网站的角色名就可以
ansible-galaxy的案例

B:ansible-pull命令

把主控端的命令推送到被控端去执行,能提升执行的效率,在实际生产中用到很少。

ansible-playbook命令的简单实用:

playbook只是把众多的模块写成了一个脚本的形式在执行,写成脚本无非是能重复使用,提高工作效率,当然playbook也是有自己的语法和格式的,playbook是使用yml语法写的,所以playbook的文件也是yml为后缀的。

[root@ansible playbook]# cat playbook.test.yml
---  #三横杠是代表剧本编写的开始,不用写也没有关系,语法上也是没有错误的。
- hosts: web  #- hosts指定主机清单,就是组名,必须有空格
  remote_user: root #remote_user代表我接下的剧本是以什么身份运行的

  tasks:  #接下来就为剧本的命令内容
   - name: test  #给我接下来的命令取一个名字
     shell: 'hostname' #指定模块,后面接操作的命令即可,-a就不需要了


这就是一个简单的playbook的语法和基本格式。
一个简单的playbook
[root@ansible playbook]# ansible-playbook playbook.test.yml

PLAY [web] ***********************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************
ok: [192.168.219.137]
ok: [192.168.219.136]

TASK [test] **********************************************************************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

PLAY RECAP ***********************************************************************************************************************************************
192.168.219.136            : ok=2    changed=1    unreachable=0    failed=0
192.168.219.137            : ok=2    changed=1    unreachable=0    failed=0

[root@ansible playbook]#
可以看到剧本在被控端确实执行成功了,但是没有得到我们想要的结果。
playbook的执行案例

第一个剧本编写成功了,但是执行之后没有得到我们想要的结果,我们接着看下playbook的其它命令,不断的去解决这个问题。

C:ansible-vault:管理加解密yml文件

为什么要对playbook进行加密是因为playbook里面可能存在一些敏感信息,比如密码等,那就需要对playbook进行加密。

ansible-vault【create,decrypt,edit,encrypt,rekey,view】

ansible-vault encrypt 后面是playbook的脚本   【加密】

[root@ansible playbook]# ansible-vault encrypt playbook.test.yml
New Vault password:
Confirm New Vault password:
Encryption successful
[root@ansible playbook]# cat playbook.test.yml
$ANSIBLE_VAULT;1.1;AES256
30666539373365363535623538663639366633303138333730656264333935636531626537623665
3032393434646461613334393739636362653035376131610a333463343461353538373038303765
30323861353330666135313038353966366635386338643434376232643062373165343361316432
3231316430643036660a353730623063353031626634663538333565633130613637353232313361
36383762653337303133303932333631633538666430313465653934636432643732613835643663
35626535623631613937616266343366353730663662343961353164343330666538346162663138
39333432303631643465396265386430653834363233626137356632383739383935393430343935
34326666323132343461643765616537376337376533653933663764306435663233393833366361
34366232346663666362316565363264373261353063643965313863333330393365373233623032
39623163623936343762666537613439376439313564616438383930313233623931653331396663
66613436646661306531363362663539316532326561363633646437376134396337363139316361
32663038653733343739306233643636653237333364313833386330666434623331636363393965
63623338373730346431613065666561303338393538646133346636306531306566363639333331
32646435613565613861396135313862313331653432323339636636323237323865313335326164
33613432396539636637356263656466633036616232616238393462363030616336343037373566
61353738343435623138336432376534393061393232356438396130643738633233633862626565
65393764323234353238623934343635326364333565643132363963323865633265343533313764
62336438313637623833643735623933353639643064356631323963316265303566333437383736
35323565613532616363303937623539376535396162643864396337393366366463323535346538
33393062653338303234346165633434616132333961613463393136633161353765313838646338
61353566666430353161323136383230303361396336653064653638656134396265613636636639
33633066623165333337363233646232366663613939613066386338636665646333353039386439
61336633626531333961663861303064663065353732353139363934333361396236613330306632
36363564373264616233626530373062656133633130633835626632393232613639353263643633
62653565313361386538386162356565383762666138643139323265613734373565643363373661
62383037356664343063393434633766333734343261306563333365653737323533323238383538
39646338613536313831303266653239663933366531363863353136323135363466303636636531
34393931333930383035
[root@ansible playbook]#
可以看到加密之后的剧本我们就不能通过正常的方式查看了,而且使用的是AES的加密算法。

[root@ansible playbook]# ansible-playbook playbook.test.yml
ERROR! Attempting to decrypt but no vault secrets found
[root@ansible playbook]#
加密状态下剧本是无法执行的
encrypt案例

ansible-vault decrypt 后面是playbook的脚本   【解密】

[root@ansible playbook]# ansible-vault decrypt playbook.test.yml
Vault password:
Decryption successful
解密是需要输入加密之后的口令的。
[root@ansible playbook]# cat playbook.test.yml
---  #三横杠是代表剧本编写的开始,不用写也没有关系,语法上也是没有错误的。
- hosts: web  #- hosts指定主机清单,就是组名,必须有空格
  remote_user: root #remote_user代表我接下的剧本是以什么身份运行的

  tasks:  #接下来就为剧本的命令内容
   - name: test  #给我接下来的命令取一个名字
     shell: 'init 0' #指定模块,后面接操作的命令即可,-a就不需要了
[root@ansible playbook]# ansible-playbook playbook.test.yml

PLAY [web] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************
ok: [192.168.219.136]
ok: [192.168.219.137]

TASK [test] *****************************************************************************************************************
changed: [192.168.219.136]
changed: [192.168.219.137]

PLAY RECAP ******************************************************************************************************************
192.168.219.136            : ok=2    changed=1    unreachable=0    failed=0
192.168.219.137            : ok=2    changed=1    unreachable=0    failed=0

[root@ansible playbook]#
加密文件要执行必须解密才能继续使用
decrypt案例

ansible-vault view 后面是playbook的脚本   【查看】

[root@ansible playbook]# ansible-vault encrypt playbook.test.yml
New Vault password:
Confirm New Vault password:
Encryption successful
[root@ansible playbook]# ansible-vault view playbook.test.yml
Vault password:
---  #三横杠是代表剧本编写的开始,不用写也没有关系,语法上也是没有错误的。
- hosts: web  #- hosts指定主机清单,就是组名,必须有空格
  remote_user: root #remote_user代表我接下的剧本是以什么身份运行的

  tasks:  #接下来就为剧本的命令内容
   - name: test  #给我接下来的命令取一个名字
     shell: 'init 0' #指定模块,后面接操作的命令即可,-a就不需要了
[root@ansible playbook]#
如果我只是想看看playbook脚本,也可以使用view参数去查看,还是需要输入口令。
View案例

ansible-vault edit 后面是playbook的脚本   【编辑加密文件】

[root@ansible playbook]# ansible-vault edit playbook.test.yml
Vault password:
[root@ansible playbook]#
如果不想解密就把文件内容直接调整了,就可以使用edit参数。输入口令就能调整了
edit 

ansible-vault rekey 后面是playbook的脚本   【修改口令】 

[root@ansible playbook]# ansible-vault rekey playbook.test.yml
Vault password:     输入老密码之后
New Vault password:   提示输入新密码
Confirm New Vault password:
Rekey successful
[root@ansible playbook]#
直接修改老的密码,
rekey案例

ansible-vault create 后面是新playbook的脚本   【创建新文件】

[root@ansible playbook]# ansible-vault create playbook.test011.yml
New Vault password:
Confirm New Vault password:




[root@ansible playbook]# ls
playbook.test011.yml  playbook.test.retry  playbook.test.yml
[root@ansible playbook]#
如果想在编写playbook的时候就把文件加密了,就可以使用create参数,进去剧本直接编写。
create 

D:ansible-console:控制台

直接提供一个控制台,用户在控制台进行操作,能支持tab补全

[root@ansible playbook]# ansible-console
Welcome to the ansible console.
Type help or ? to list commands.

root@all (2)[f:5]$
root代表操作用户,all代表主机清单里面的所有机器,2代表主机清单的数量,f:5代表并发请求的数量是5

但是一下操作所有主机肯定不是常用的,常用的情况下还是按照我们的分组来控制,执行相关的命令
root@all (2)[f:5]$ cd web
root@web (2)[f:5]$
直接执行cd (主机清单组的名字),就能切换控制对象

并发数太小,我们调大一点

root@web (2)[f:5]$ forks 10
root@web (2)[f:10]$
执行forks 数量就能快速调整并发数

前面已经把主机清单和执行的用户定义好了,我们执行就只需要接模块和需要执行的命令即可,而且模块名记不住不要紧,支持tab补全。
root@web (2)[f:10]$ shell hostname   -a和引号都不需要
192.168.219.136 | SUCCESS | rc=0 >>
web1

192.168.219.137 | SUCCESS | rc=0 >>
web2

root@web (2)[f:10]$ shell df -h
192.168.219.136 | SUCCESS | rc=0 >>
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2        18G  2.7G   14G  16% /
tmpfs           491M   72K  491M   1% /dev/shm
/dev/sda1       283M   37M  232M  14% /boot

192.168.219.137 | SUCCESS | rc=0 >>
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2        18G  2.6G   14G  16% /
tmpfs           491M   72K  491M   1% /dev/shm
/dev/sda1       283M   36M  232M  14% /boot

root@web (2)[f:10]$

在ansible-console控制台下,需要注意的是一些带有风险的操作是对所有被控机器执行还是单台机器执行,避免造成不可逆的操作,控制台的形式也能让我们临时去进行测试。

E:ansible-playbook的进阶操作

使用playbook需要记住两点,1:playbook实用yaml语言编写,文件后缀必须是yml,2:playbook的编写格式有一定要求。

剧本在舞台上使用的时候,可以想到是由多个演员按照预先规划好的时间轴依次出厂和退场,共同的来完成一场演出,那么playbook也是如此,例如我是不是需要验证机器服务有没有装,有没有启动,有没有开机自启,其次是不是要装包,下发配置文件,起服务,加入开机自启,最后是不是要验证启动情况,端口监听是否正常等。都是需要按照时间排列好的形式来编写playbook,这个就很像编剧的职务。

playbook的核心元素:

hosts  主机列表

tasks     任务集

varbiables  内置变量或自定义变量在playbook中的调用

tempates   模板,看替换模板文件中的变量并实现一些简单逻辑的文件

handlers  和notity结合使用,由特定条件触发,满足则执行,否则不执行

tags  标签

[root@ansible playbook]# ansible-playbook -C playbook.test.yml
在执行脚本的时候加上-C就能查看脚本是否有错误

PLAY [web] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************
ok: [192.168.219.137]
ok: [192.168.219.136]

TASK [test] *****************************************************************************************************************
skipping: [192.168.219.136]
skipping: [192.168.219.137]

PLAY RECAP ******************************************************************************************************************
192.168.219.136            : ok=1    changed=0    unreachable=0    failed=0
192.168.219.137            : ok=1    changed=0    unreachable=0    failed=0

[root@ansible playbook]#
playbook检查语法是否错误

在ansible里面我们通常要控制几十上百台机器,很多情况下我们不能保证每台机器的目录是否存在,命令执行错误等情况,这种情况下,playbook就可能因为错误不执行结束了,那很多情况下我们都希望不管错误继续执行剩下的操作,这个时候就需要在操作后面加上一个||/bin/true

还有情况下,例如我就想在单台机器上把我刚才写的playbook先跑下,不想一下把所有的机器都跑了,也可以通过--limit  后面跟上ip地址即可。

[root@ansible playbook]# ansible-playbook playbook.test.yml --list-hosts

playbook: playbook.test.yml

  play #1 (web): web    TAGS: []
    pattern: [u'web']
    hosts (2):
      192.168.219.136
      192.168.219.137
[root@ansible playbook]# ansible-playbook playbook.test.yml --limit 192.168.219.136

PLAY [web] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************
ok: [192.168.219.136]

TASK [test] *****************************************************************************************************************
changed: [192.168.219.136]

PLAY RECAP ******************************************************************************************************************
192.168.219.136            : ok=2    changed=1    unreachable=0    failed=0

[root@ansible playbook]#
limit的参数使用

脚本写好了,跑的过程我想知道也可以在执行的时候末尾加上-v -vv -vvv来看不同详细的运行过程信息。

task重复执行的问题:

如果我一个模块里面执行了两条操作,这个时候playbook不会报错,但是只会执行命令里面的最后一个操作。

[root@ansible playbook]# cat playbook.test.yml
---
-hosts: all
 remote_user: root

 tasks:
  -name: copy file
   copy: src=/home/ansible.test/playbook/11.sh dest=/home/ansible/
   copy: src=/home/ansible.test/playbook/22.sh dest=/home/ansible/
[root@ansible playbook]#
可以看到copy的操作有两个
[root@ansible playbook]# ansible-playbook playbook.test.yml
 [WARNING]: While constructing a mapping from /home/ansible.test/playbook/playbook.test.yml, line 6, column 6, found a
duplicate dict key (copy). Using last defined value only.


PLAY [all] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************
ok: [192.168.219.136]
ok: [192.168.219.137]

TASK [copy file] ************************************************************************************************************
changed: [192.168.219.136]
changed: [192.168.219.137]

PLAY RECAP ******************************************************************************************************************
192.168.219.136            : ok=2    changed=1    unreachable=0    failed=0
192.168.219.137            : ok=2    changed=1    unreachable=0    failed=0

[root@ansible playbook]# ansible all -m shell -a 'ls /home/ansible'
192.168.219.136 | SUCCESS | rc=0 >>
22.sh

192.168.219.137 | SUCCESS | rc=0 >>
22.sh

[root@ansible playbook]#
可以看到就拷贝了22.sh,这个就说明在tasks里面一个模块只能对应一个name,一个name也只能对应一个操作。
tasks的重复执行

上面我们执行的时候都是按照流程去操作的,不存在判断的过程,这个我要判断机器的httpd服务是否装了,装了就不必要重新装,否则就安装。

handlers和notify配合使用:

这个时候就需要handlers(触发器),handlers可以监控tasks里面的每一个操作,当一个操作执行了,那么就会触发handlers后面定义的一些命令,那么handlers是怎么被触发的了,配合handlers做触发的是notity,当操作执行之后notity就会通知handlers来执行相应的命令。

[root@ansible playbook]# cat playbook.test.yml
---
- hosts: all
  remote_user: root

  tasks:
   - name: install httpd service
     yum: name=httpd
   - name: start service
     service: name=httpd state=started enabled=yes

[root@ansible playbook]# ansible-playbook playbook.test.yml

PLAY [all] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************
ok: [192.168.219.137]
ok: [192.168.219.136]

TASK [install httpd service] ************************************************************************************************
ok: [192.168.219.136]
ok: [192.168.219.137]

TASK [start service] ********************************************************************************************************
changed: [192.168.219.136]
changed: [192.168.219.137]

PLAY RECAP ******************************************************************************************************************
192.168.219.136            : ok=3    changed=1    unreachable=0    failed=0
192.168.219.137            : ok=3    changed=1    unreachable=0    failed=0

[root@ansible playbook]# ansible all -m shell -a 'netstat -anptu | grep 80'
192.168.219.137 | SUCCESS | rc=0 >>
tcp        0      0 :::80                       :::*                        LISTEN      5631/httpd

192.168.219.136 | SUCCESS | rc=0 >>
tcp        0      0 :::80                       :::*                        LISTEN      4599/httpd
可以看到httpd服务已经装好了,但是这个时候我要把默认的80端口改成8088的话,那么更新我本地的配置文件同步到备机,配置文件变动过,服务就需要重启才能生效。
我们在本地将修改httpd的配置文件
[root@ansible playbook]# cat playbook.test.yml
---
- hosts: all
  remote_user: root

  tasks:
   - name: install httpd service
     yum: name=httpd
   - name: start service
     service: name=httpd state=started enabled=yes
   - name: copy conf file
     copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/ backup=yes
     notify: restart service  #这个notify就要写handiers后面定义的名字

  handlers:
   - name: restart service
     service: name=httpd state=restarted  #当监控有变动之后handlers需要做什么操作

[root@ansible playbook]# ansible-playbook playbook.test.yml

PLAY [all] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************
ok: [192.168.219.137]
ok: [192.168.219.136]

TASK [install httpd service] ************************************************************************************************
ok: [192.168.219.136]
ok: [192.168.219.137]

TASK [start service] ********************************************************************************************************
ok: [192.168.219.137]
ok: [192.168.219.136]

TASK [copy conf file] *******************************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

RUNNING HANDLER [restart service] *******************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

PLAY RECAP ******************************************************************************************************************
192.168.219.136            : ok=5    changed=2    unreachable=0    failed=0
192.168.219.137            : ok=5    changed=2    unreachable=0    failed=0

[root@ansible playbook]# ansible all -m shell -a 'netstat -anptu | grep 8088'
192.168.219.137 | SUCCESS | rc=0 >>
tcp        0      0 :::8088                     :::*                        LISTEN      6045/httpd

192.168.219.136 | SUCCESS | rc=0 >>
tcp        0      0 :::8088                     :::*                        LISTEN      5016/httpd

[root@ansible playbook]#
执行之后就可以看到服务重启成功了,而且8088端口修改也生效了
handiers案例

notify可以通知多个handlers的操作:

notify:

- 空格之后接第一个handlers的名字

- 空格之后接第二个handlers的名字
notify重复触发

tags:标签

加标签的目的是,后面我们可以调用标签里面的内容,如上面重启httpd的案例一样,我不需要每次都把这个playbook都完完整整的执行一遍,我只需要在httpd重启的地方打上一个标签,后面我们在执行的时候就选择这个标签来执行即可。

[root@ansible playbook]# cat playbook.test.yml
---
- hosts: all
  remote_user: root

  tasks:
   - name: install httpd service
     yum: name=httpd
   - name: start service
     service: name=httpd state=started enabled=yes
     tags: rehttpd   #在此位置加一个tags的标签,后面写上标签名,标签名要有意义且简单,最好加上注释。
   - name: copy conf file
     copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/ backup=yes
     notify: restart service

  handlers:
   - name: restart service
     service: name=httpd state=restarted

[root@ansible playbook]# ansible all -m shell -a 'ps -aux | grep httpd'
192.168.219.136 | SUCCESS | rc=0 >>
root       5969  0.0  0.1 106096  1128 pts/1    S+   06:12   0:00 /bin/sh -c ps -aux | grep httpd
root       5971  0.0  0.0 103308   852 pts/1    S+   06:12   0:00 grep httpdWarning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ

192.168.219.137 | SUCCESS | rc=0 >>
root       6998  0.0  0.1 106096  1120 pts/1    S+   06:12   0:00 /bin/sh -c ps -aux | grep httpd
root       7000  0.0  0.0 103308   848 pts/1    S+   06:12   0:00 grep httpdWarning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
可以看到httpd服务是没有启动的
[root@ansible playbook]# ansible-playbook -t rehttpd playbook.test.yml
-t 指定标签名执行playbook 
PLAY [all] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************
ok: [192.168.219.137]
ok: [192.168.219.136]

TASK [start service] ********************************************************************************************************
changed: [192.168.219.136]
changed: [192.168.219.137]

PLAY RECAP ******************************************************************************************************************
192.168.219.136            : ok=2    changed=1    unreachable=0    failed=0
192.168.219.137            : ok=2    changed=1    unreachable=0    failed=0

[root@ansible playbook]# ansible all -m shell -a 'ps -aux | grep httpd'
192.168.219.136 | SUCCESS | rc=0 >>
root       6101  0.0  0.3 183896  3832 ?        Ss   06:12   0:00 /usr/sbin/httpd
apache     6104  0.0  0.2 184028  2508 ?        S    06:12   0:00 /usr/sbin/httpd
apache     6105  0.0  0.2 184028  2484 ?        S    06:12   0:00 /usr/sbin/httpd
apache     6106  0.0  0.2 184028  2484 ?        S    06:12   0:00 /usr/sbin/httpd
apache     6107  0.0  0.2 184028  2484 ?        S    06:12   0:00 /usr/sbin/httpd
apache     6108  0.0  0.2 184028  2484 ?        S    06:12   0:00 /usr/sbin/httpd
apache     6109  0.0  0.2 184028  2484 ?        S    06:12   0:00 /usr/sbin/httpd
apache     6110  0.0  0.2 184028  2484 ?        S    06:12   0:00 /usr/sbin/httpd
apache     6111  0.0  0.2 184028  2484 ?        S    06:12   0:00 /usr/sbin/httpd
root       6143  0.0  0.1 106096  1128 pts/1    S+   06:12   0:00 /bin/sh -c ps -aux | grep httpd
root       6145  0.0  0.0 103312   860 pts/1    S+   06:12   0:00 grep httpdWarning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ

192.168.219.137 | SUCCESS | rc=0 >>
root       7130  0.0  0.3 183896  3824 ?        Ss   06:12   0:00 /usr/sbin/httpd
apache     7133  0.0  0.2 184028  2476 ?        S    06:12   0:00 /usr/sbin/httpd
apache     7134  0.0  0.2 184028  2500 ?        S    06:12   0:00 /usr/sbin/httpd
apache     7135  0.0  0.2 184028  2476 ?        S    06:12   0:00 /usr/sbin/httpd
apache     7136  0.0  0.2 184028  2476 ?        S    06:12   0:00 /usr/sbin/httpd
apache     7137  0.0  0.2 184028  2476 ?        S    06:12   0:00 /usr/sbin/httpd
apache     7138  0.0  0.2 184028  2476 ?        S    06:12   0:00 /usr/sbin/httpd
apache     7139  0.0  0.2 184028  2476 ?        S    06:12   0:00 /usr/sbin/httpd
apache     7140  0.0  0.2 184028  2476 ?        S    06:12   0:00 /usr/sbin/httpd
root       7172  0.0  0.1 106096  1124 pts/1    S+   06:12   0:00 /bin/sh -c ps -aux | grep httpd
root       7174  0.0  0.0 103312   856 pts/1    S+   06:12   0:00 grep httpdWarning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ

[root@ansible playbook]#
可以看到httpd服务就启动了
tags标签案例
[root@ansible conf]# ansible all -m shell -a 'netstat -anptu | grep 8088'
192.168.219.137 | SUCCESS | rc=0 >>
tcp        0      0 :::8088                     :::*                        LISTEN      7130/httpd

192.168.219.136 | SUCCESS | rc=0 >>
tcp        0      0 :::8088                     :::*                        LISTEN      6101/httpd
可以看到服务端口是8088
我们把配置文件改9099,并执行在copy位置加的标签
[root@ansible playbook]# cat playbook.test.yml
---
- hosts: all
  remote_user: root

  tasks:
   - name: install httpd service
     yum: name=httpd
   - name: start service
     service: name=httpd state=started enabled=yes
     tags: rehttpd
   - name: copy conf file
     copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/ backup=yes
     notify: restart service
     tags: cohttpd  #在此处加了标签

  handlers:
   - name: restart service
     service: name=httpd state=restarted

[root@ansible playbook]# ansible-playbook -t cohttpd playbook.test.yml

PLAY [all] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************
ok: [192.168.219.136]
ok: [192.168.219.137]

TASK [copy conf file] *******************************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

RUNNING HANDLER [restart service] *******************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

PLAY RECAP ******************************************************************************************************************
192.168.219.136            : ok=3    changed=2    unreachable=0    failed=0
192.168.219.137            : ok=3    changed=2    unreachable=0    failed=0

[root@ansible playbook]# ansible all -m shell -a 'netstat -anptu | grep 9099'
192.168.219.137 | SUCCESS | rc=0 >>
tcp        0      0 :::9099                     :::*                        LISTEN      7464/httpd

192.168.219.136 | SUCCESS | rc=0 >>
tcp        0      0 :::9099                     :::*                        LISTEN      6435/httpd

[root@ansible playbook]# 
可以看到端口是9099,我们执行了copy的配置文件下发同时也触发了重启任务的操作。
tags标签案例1
[root@ansible playbook]# ansible all -m shell -a 'httpd -v'
192.168.219.136 | FAILED | rc=127 >>
/bin/sh: httpd: command not foundnon-zero return code

192.168.219.137 | FAILED | rc=127 >>
/bin/sh: httpd: command not foundnon-zero return code

[root@ansible playbook]# cat playbook.test.yml
---
- hosts: all
  remote_user: root

  tasks:
   - name: install httpd service
     yum: name=httpd
     tags: inhttpd   #在此处我加了一个安装httpd服务的标签
   - name: start service
     service: name=httpd state=started enabled=yes
   - name: copy conf file
     copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/ backup=yes
     notify: restart service
     tags: cohttpd  #增加了该端口配置的标签

  handlers:
   - name: restart service
     service: name=httpd state=restarted

[root@ansible playbook]# ansible-playbook -t inhttpd,cohttpd playbook.test.yml
在-t指定标签执行的时候我可以选择多个标签进行执行,中间逗号区分即可
PLAY [all] ***********************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************
ok: [192.168.219.136]
ok: [192.168.219.137]

TASK [install httpd service] *****************************************************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

TASK [copy conf file] ************************************************************************************************************************************
changed: [192.168.219.136]
changed: [192.168.219.137]

RUNNING HANDLER [restart service] ************************************************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

PLAY RECAP ***********************************************************************************************************************************************
192.168.219.136            : ok=4    changed=3    unreachable=0    failed=0
192.168.219.137            : ok=4    changed=3    unreachable=0    failed=0

[root@ansible playbook]# ansible all -m shell -a 'netstat -anptu | grep 1234'
192.168.219.136 | SUCCESS | rc=0 >>
tcp        0      0 :::1234                     :::*                        LISTEN      7152/httpd

192.168.219.137 | SUCCESS | rc=0 >>
tcp        0      0 :::1234                     :::*                        LISTEN      8120/httpd

[root@ansible playbook]#
tags标签案例2

tags还支持多个操作共用一个标签

playbook使用变量:

playbook的变量和大多数变量定义规则一样,字母下划线和数字组成且只能以字母开头

setup模块:

setup自带了系统中的变量

ansible 机器ip -m setup 能获取被控端机器的所有信息,磁盘大小,主机名,内核版本等等信息,有时候我们想通过命令获取机器的信息的时候需要重复执行很多命令,这个时候setup就很有用,直接查询出来进行过滤即可。

[root@ansible playbook]# ansible 192.168.219.136 -m setup | grep 'ansible_kernel'
        "ansible_kernel": "2.6.32-573.el6.x86_64",
[root@ansible playbook]# ansible 192.168.219.136 -m setup -a 'filter=ansible_kernel'
192.168.219.136 | SUCCESS => {
    "ansible_facts": {
        "ansible_kernel": "2.6.32-573.el6.x86_64"
    },
    "changed": false
}
[root@ansible playbook]#
两种方式都可以,但是你要写到playbook里面的grep命令可能会出问题,还是使用推荐的命令,这个setup过滤出来的数值我们到时候就可以进行调用,以满足其他的需求。
setup案例

playbook脚本使用变量案例:

[root@ansible playbook]# cat playbook.test.yml
---
- hosts: all
  remote_user: root

  tasks:
   - name: install soft 
     yum: name={{ deploy }}  #在安装的名字的位置使用变量名,格式就是两组花括号,前后留有空格不是强制要求,只是为了更加简洁易读
   - name: start service
     service: name={{ deploy }} state=started enabled=yes
 #同样在只要使用到包名的位置都要替换成变量

[root@ansible playbook]# ansible-playbook -e 'deploy=vsftpd' playbook.test.yml
#在执行的时候跟上-e参数,后面的变量直接指定实际要安装的程序名即可

PLAY [all] ***********************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************
ok: [192.168.219.136]
ok: [192.168.219.137]

TASK [install soft] *****************************************************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

TASK [start service] *************************************************************************************************************************************
changed: [192.168.219.136]
changed: [192.168.219.137]

PLAY RECAP ***********************************************************************************************************************************************
192.168.219.136            : ok=3    changed=2    unreachable=0    failed=0
192.168.219.137            : ok=3    changed=2    unreachable=0    failed=0
可以看到ftp程序安装成,我们进行检查,看下服务是否启动
[root@ansible playbook]# ansible all -m shell -a 'netstat -anptu | grep 21'
192.168.219.137 | SUCCESS | rc=0 >>
tcp        0      0 0.0.0.0:21                  0.0.0.0:*                   LISTEN      2906/vsftpd
tcp        0      0 192.168.219.137:22          192.168.219.135:38357       ESTABLISHED 2928/sshd
tcp        0      0 192.168.219.137:22          192.168.219.1:50993         ESTABLISHED 2679/sshd
tcp        0      0 192.168.219.137:22          192.168.219.1:50994         ESTABLISHED 2681/sshd

192.168.219.136 | SUCCESS | rc=0 >>
tcp        0      0 0.0.0.0:21                  0.0.0.0:*                   LISTEN      2936/vsftpd
tcp        0      0 192.168.219.136:22          192.168.219.1:50861         ESTABLISHED 2640/sshd
tcp        0      0 192.168.219.136:22          192.168.219.135:34572       ESTABLISHED 2954/sshd
tcp        0      0 192.168.219.136:22          192.168.219.1:50865         ESTABLISHED 2642/sshd

[root@ansible playbook]#
可以看到21端口启用了,ftp程序安装并启动成功。
变量案例1

在脚本里面使用变量可以使得脚本更加灵活,当然playbook也是支持多个变量的,指需要在-e后面分别赋值即可,例如ansible-playbook -e 'deploy=vsftpd deploy1=httpd' 脚本名

[root@ansible playbook]# cat playbook.test.yml
---
- hosts: all
  remote_user: root
  vars:
   - deploy1: redis    #在脚本里面就把变量进行赋值
   - deploy2: subversion
  tasks:
   - name: install soft
     yum: name={{ deploy1 }}
   - name: install soft
     yum: name={{ deploy2 }}

[root@ansible playbook]# ansible-playbook playbook.test.yml
#执行的时候就不需要在给变量传参
PLAY [all] ***********************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************
ok: [192.168.219.136]
ok: [192.168.219.137]

TASK [install soft] **************************************************************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

TASK [install soft] **************************************************************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

PLAY RECAP ***********************************************************************************************************************************************
192.168.219.136            : ok=3    changed=2    unreachable=0    failed=0
192.168.219.137            : ok=3    changed=2    unreachable=0    failed=0


[root@ansible playbook]# ansible all -m shell -a 'rpm -qa redis'
 [WARNING]: Consider using the yum, dnf or zypper module rather than running rpm.  If you need to use command because yum, dnf or zypper is insufficient
you can add warn=False to this command task or set command_warnings=False in ansible.cfg to get rid of this message.

192.168.219.136 | SUCCESS | rc=0 >>
redis-3.2.12-2.el6.x86_64

192.168.219.137 | SUCCESS | rc=0 >>
redis-3.2.12-2.el6.x86_64

[root@ansible playbook]# ansible all -m shell -a 'rpm -qa subversion'
 [WARNING]: Consider using the yum, dnf or zypper module rather than running rpm.  If you need to use command because yum, dnf or zypper is insufficient
you can add warn=False to this command task or set command_warnings=False in ansible.cfg to get rid of this message.

192.168.219.137 | SUCCESS | rc=0 >>
subversion-1.6.11-15.el6_7.x86_64

192.168.219.136 | SUCCESS | rc=0 >>
subversion-1.6.11-15.el6_7.x86_64
可以看到都安装成功
变量案例2

也可以在脚本里面把变量进行赋值,在执行的时候就直接执行,无需在执行的时候给参数。

在/etc/ansible/hosts里面使用变量,例如我被控端机器太多,我就算用组名来区分还是不够细致,如果我有几十台http的服务器,但是里面机器的端口不一样,这个时候变量就能很好的解决此问题。

## db-[99:101]-node.example.com
[web]
192.168.219.136   httpd_server=8080
192.168.219.137   httpd_server=9090   
在组所属的ip后面在加以区分,相当于标记,不竟能在第一时间查看到机器服务还能看到端口

[root@ansible playbook]# cat playbook.test.yml
---
- hosts: web
  remote_user: root

  tasks:
   - name: mod hostname
     hostname: name=www.{{http_server}}.http.com  
     #在脚本里面调用变量进行主机名批量修改



[root@ansible playbook]# cat playbook.test.yml
---
- hosts: web
  remote_user: root

  tasks:
   - name: set server
     hostname: name=www{{http_server}}.http.com


[root@ansible playbook]# vim playbook.test.yml
[root@ansible playbook]# ansible-playbook -C playbook.test.yml

PLAY [web] ***********************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************
ok: [192.168.219.137]
ok: [192.168.219.136]

TASK [set server] ****************************************************************************************************************************************
changed: [192.168.219.136]
changed: [192.168.219.137]

PLAY RECAP ***********************************************************************************************************************************************
192.168.219.136            : ok=2    changed=1    unreachable=0    failed=0
192.168.219.137            : ok=2    changed=1    unreachable=0    failed=0

[root@ansible playbook]# cat playbook.test.yml
---
- hosts: web
  remote_user: root

  tasks:
   - name: set server
     hostname: name=www{{httpd_server}}.http.com


[root@ansible playbook]# ansible-playbook playbook.test.yml

PLAY [web] ***********************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************
ok: [192.168.219.137]
ok: [192.168.219.136]

TASK [set server] ****************************************************************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

PLAY RECAP ***********************************************************************************************************************************************
192.168.219.136            : ok=2    changed=1    unreachable=0    failed=0
192.168.219.137            : ok=2    changed=1    unreachable=0    failed=0

[root@ansible playbook]# ansible web -a 'hostname'
192.168.219.137 | SUCCESS | rc=0 >>
www9090.http.com

192.168.219.136 | SUCCESS | rc=0 >>
www8080.http.com

[root@ansible playbook]#
可以看到主机名修改成hosts里面事先定义好了的端口
hosts文件使用变量1
[web:vars]  #也可以在组里面使用变量,指需要在组名后面添加vars即可
name1=www
name2=httpd.com

在  hostname: name={{name1}}{{httpd_server}}.{{name2}}位置使用变量,也可以
hosts文件使用变量2

在变量使用中,普通变量(单条命令)比公共组(hosts)里面的优先级要高。

在指定的路径下编写yml文件,将变量定义成共用的变量,方便不同的运维上来无需在进行变量的声明和定义,直接调用已经编写好的yml文件里面的变量进行操作,这样的好处就是避免每人运维上来都要对playbook进行变量的声明,如果是没有提前规划,这些变量名将变得杂乱无章。

[root@ansible playbook]# cat vars.yml
db_backup: dbbackup
time: date +%Y%m%d
[root@ansible playbook]#
创建一个vars.yml的文件,把需要定义的变量和指实现写好
[root@ansible playbook]# cat backup.yml
---
- hosts: web
  remote_user: root
  vars_files:
      - vars.yml

  tasks:
      - name: create flie
        file: name=/root/{{ db_backup }} state=touch
[root@ansible playbook]#
在playbook里面指定变量使用vars文件来定义,下面要使用变量的部分直接使用vars里面的变量即可
[root@ansible playbook]# ansible web -m shell -a 'ls -h /root'
192.168.219.137 | SUCCESS | rc=0 >>
anaconda-ks.cfg
dbbackup
Desktop
Documents
Downloads
file123
install.log
install.log.syslog
log.tar.xz
Music
Pictures
Public
Templates
Videos

192.168.219.136 | SUCCESS | rc=0 >>
anaconda-ks.cfg
dbbackup
Desktop
Documents
Downloads
file123
install.log
install.log.syslog
log.tar.xz
Music
Pictures
Public
Templates
Videos
可以看到创建成功
vars.yml文件定义变量

注:playbook脚本在哪个路径下,vars.yml文件就要在相应的路径下

F:templates模板:

实际生产中,我们会遇到很多需要下发配置文件的操作,但是配置文件不可能每套环境都是使用的一样的,这个时候有差异我们如果还使用前面的ansible操作就会不适用,所以我们需要借助ansible的templates模板来完成和机器相匹配的配置文件下发。

 templates使用的jinja2语言,能支持整数,浮点数,列表,元祖,字典,布尔值,算数运算符,逻辑表达式,流表达式(for if when)这个是比较特殊的表达式,它的含义是当什么满足条件的时候才执行相应的操作。

template本身就是一个模块,而且这个模块比较特殊,他只能用于playbook。

我们将服务的配置文件拷贝一份放到建立好的路径下,并修改成.j2结尾的文件
[root@ansible templates]# pwd
/home/ansible.test/templates
[root@ansible templates]# ls
httpd.conf.j2  templates.retry  templates.yml
修改hosts文件如下:
## db-[99:101]-node.example.com
[web]
192.168.219.136 httpd_server=1010   在hosts文件夹定义好变量
192.168.219.137 httpd_server=2020

在httpd.conf里面使用该变量
#Listen 12.34.56.78:80
Listen {{httpd_server}}

[root@ansible templates]# cat templates.yml
---
- hosts: web
  remote_user: root

  tasks:
    - name: copy httpd
      template: src=/home/ansible.test/templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
      notify: service restart

  handlers:
     - name: service restart
       service: name=httpd state=restarted

[root@ansible templates]# ansible-playbook templates.yml

PLAY [web] ***********************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************
ok: [192.168.219.136]
ok: [192.168.219.137]

TASK [copy httpd] ****************************************************************************************************************************************
changed: [192.168.219.136]
changed: [192.168.219.137]

RUNNING HANDLER [service restart] ************************************************************************************************************************
changed: [192.168.219.137]
changed: [192.168.219.136]

PLAY RECAP ***********************************************************************************************************************************************
192.168.219.136            : ok=3    changed=2    unreachable=0    failed=0
192.168.219.137            : ok=3    changed=2    unreachable=0    failed=0

[root@ansible templates]# ansible all -m shell -a 'netstat -anptu | grep httpd'
192.168.219.137 | SUCCESS | rc=0 >>
tcp        0      0 :::2020                     :::*                        LISTEN      5710/httpd
udp        0      0 192.168.219.137:53613       192.168.219.2:53            ESTABLISHED 5710/httpd

192.168.219.136 | SUCCESS | rc=0 >>
tcp        0      0 :::1010                     :::*                        LISTEN      5704/httpd
udp        0      0 192.168.219.136:37088       192.168.219.2:53            ESTABLISHED 5704/httpd

[root@ansible templates]#
可以看到两台机器的ip都按照设定的下发了不同端口的配置文件并重启生效了
template模板案例

template的还能完成根据机器的资源配置下发与机器相关匹配的配置文件。

变量的优先级是命令行里面的-e参数指定的变量,其实playbook里面定义的变量,最后是主机清单里面的变量。

template里面when的用法

[root@ansible templates]# cat templates.yml
---
- hosts: web
  remote_user: root

  tasks:
    - name: copy httpd
      service: name=httpd state=statred
      notify: service restart
      when: httpd_server == "1010"

  handlers:
     - name: service restart
       service: name=httpd state=stoped

[root@ansible templates]#
例如我判断端口等于1010的才会关闭httpd服务
when的用法

迭代:with_items:

当我们需要重复执行某一个操作的时候,就需要迭代运行的机制。在ansible里面迭代的引用固定变量名为“item”,在task中使用with_items给需要迭代的定义元素列表,支持字符串和字典。

[root@ansible playbook]# cat item.yml
---
- hosts: web
  remote_user: root

  tasks:
    - name: create file
      file: name=/root/{{ item }} state=touch
      #这个item在此处比较特殊,它代表了下面with_items里面的每一个元素

      with_items:  #在with_items里面定义没一个元素,让上面的item变量来迭代使用
         - file1
         - file2
         - file3

[root@ansible playbook]# ansible-playbook item.yml

PLAY [web] ****************************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.219.136]
ok: [192.168.219.137]

TASK [create file] ********************************************************************
changed: [192.168.219.136] => (item=file1)
changed: [192.168.219.137] => (item=file1)
changed: [192.168.219.136] => (item=file2)
changed: [192.168.219.137] => (item=file2)
changed: [192.168.219.136] => (item=file3)
changed: [192.168.219.137] => (item=file3)

PLAY RECAP ****************************************************************************
192.168.219.136            : ok=2    changed=1    unreachable=0    failed=0
192.168.219.137            : ok=2    changed=1    unreachable=0    failed=0
[root@ansible playbook]# ansible all -m shell -a 'ls /root/ -l'
192.168.219.137 | SUCCESS | rc=0 >>
total 156
-rw-------. 1 root root  3341 Feb 14 01:57 anaconda-ks.cfg
-rw-r--r--  1 root root     0 Feb 25 06:09 dbbackup
drwxr-xr-x. 2 root root  4096 Feb 14 02:02 Desktop
drwxr-xr-x. 2 root root  4096 Feb 14 02:02 Documents
drwxr-xr-x. 2 root root  4096 Feb 14 02:02 Downloads
-rw-r--r--  1 root root     0 Feb 27 01:48 file1
drwxr-xr-x  3 root root  4096 Feb 20 13:35 file123
-rw-r--r--  1 root root     0 Feb 27 01:48 file2
-rw-r--r--  1 root root     0 Feb 27 01:48 file3
-rw-r--r--. 1 root root 41800 Feb 14 01:57 install.log
-rw-r--r--. 1 root root  9154 Feb 14 01:54 install.log.syslog
-rw-r--r--  1 root root 55144 Feb 20 13:05 log.tar.xz
drwxr-xr-x. 2 root root  4096 Feb 14 02:02 Music
drwxr-xr-x. 2 root root  4096 Feb 14 02:02 Pictures
drwxr-xr-x. 2 root root  4096 Feb 14 02:02 Public
drwxr-xr-x. 2 root root  4096 Feb 14 02:02 Templates
drwxr-xr-x. 2 root root  4096 Feb 14 02:02 Videos

192.168.219.136 | SUCCESS | rc=0 >>
total 156
-rw-------. 1 root root  3341 Feb 14 01:56 anaconda-ks.cfg
-rw-r--r--  1 root root     0 Feb 25 06:09 dbbackup
drwxr-xr-x. 2 root root  4096 Feb 14 02:01 Desktop
drwxr-xr-x. 2 root root  4096 Feb 14 02:01 Documents
drwxr-xr-x. 2 root root  4096 Feb 14 02:01 Downloads
-rw-r--r--  1 root root     0 Feb 27 01:48 file1
drwxr-xr-x  3 root root  4096 Feb 20 13:35 file123
-rw-r--r--  1 root root     0 Feb 27 01:48 file2
-rw-r--r--  1 root root     0 Feb 27 01:48 file3
-rw-r--r--. 1 root root 41800 Feb 14 01:56 install.log
-rw-r--r--. 1 root root  9154 Feb 14 01:53 install.log.syslog
-rw-r--r--  1 root root 55452 Feb 20 13:05 log.tar.xz
drwxr-xr-x. 2 root root  4096 Feb 14 02:01 Music
drwxr-xr-x. 2 root root  4096 Feb 14 02:01 Pictures
drwxr-xr-x. 2 root root  4096 Feb 14 02:01 Public
drwxr-xr-x. 2 root root  4096 Feb 14 02:01 Templates
drwxr-xr-x. 2 root root  4096 Feb 14 02:01 Videos

[root@ansible playbook]#
可以看到迭代帮忙创建的空的目录
item使用案例

迭代嵌套子变量:

如果想重复完成多件事情,单纯的嵌套写起来会比较麻烦。

[root@ansible playbook]# cat item2.yml
---
- hosts: all
  remote_user: root

  tasks:
    - name: create groups
      group: name={{ item }} state=present
在创建组的时候使用变量的格式
      with_items:
        - group1
        - group2
        - group3
    - name: add users
      user: name={{ item.name }} group={{ item.group }} state=present
#在创建用户的时候把属组参数调用上面定义好的变量
      with_items:
        - { name: 'user1',group: 'group1' }  #在with_items下面以字典的格式记录数据
        - { name: 'user2',group: 'group2' }
        - { name: 'user3',group: 'group3' }
[root@ansible playbook]# ansible all -m shell -a 'getent passwd'
192.168.219.137 | SUCCESS | rc=0 >>
。。。。。。。
user1:x:502:502::/home/user1:/bin/bash
user2:x:503:503::/home/user2:/bin/bash
user3:x:504:504::/home/user3:/bin/bash

[root@ansible playbook]# ansible all -m shell -a 'getent group'
192.168.219.137 | SUCCESS | rc=0 >>
。。。。。
group1:x:502:
group2:x:503:
group3:x:504:
可以看到创建的用户和所属组的关系是我们上面定义好的形式
迭代嵌套子变量案例

playbook中template的for if:

for和endfor形成一对儿。

{% for pote in nginx_pote %}
server{

}

{% if server_name is defined %}
server name {{}}
{% endif %}
......
for if示例

迭代是我们定义好的参数,但是如果是想要产出的数字有规律且很多的情况下迭代很显然就不适合了,就需要使用循环来处理。

[root@ansible playbook]# cat for.yml
---
- hosts: all
  remote_user: root
  vars:
    ports:  #在此处定义一个元素列表
      - 81
      - 82
      - 83

  tasks:
     - name: copy conf
       template: src=/home/ansible.test/playbook/for.conf.j2 dest=/root/file123/for.conf
#在拷贝的时候运用模板功能拷贝相应的文件
[root@ansible playbook]# cat for.conf.j2  #创建模板文件。
{% for port in ports %}  #此处需要注意的就是ports,这个ports是playbook里面定义好的元素列表名
server{
     listen {{ port }} #用for后面的port去拿ports里面的数据,不断赋值给此处的变量port,直到ports里面的数据取完为止
}
{% endfor %}
[root@ansible playbook]#
运行playbook脚本
[root@ansible playbook]# ansible all -m shell -a 'cat /root/file123/for.conf'
192.168.219.137 | SUCCESS | rc=0 >>
server{
     listen 81
}
server{
     listen 82
}
server{
     listen 83
}

192.168.219.136 | SUCCESS | rc=0 >>
server{
     listen 81
}
server{
     listen 82
}
server{
     listen 83
}

可以看到for.conf文件取到的指就是ports被模板循环赋值的结果。
template里面的for运用案例
[root@ansible playbook]# cat for.yml
---
- hosts: all
  remote_user: root
  vars:
    ports:  #我们把ports里面的数据定义成字典的形式
      - licent: 85
      - licent: 86
      - licent: 87

  tasks:
     - name: copy conf
       template: src=/home/ansible.test/playbook/for.conf.j2 dest=/root/file123/for.conf

[root@ansible playbook]# cat for.conf.j2
{% for port in ports %}
server{
     listen {{ port.licent }} #变量的位置就需要用到字典的键名
}
{% endfor %}
[root@ansible playbook]# ansible all -m shell -a 'cat /root/file123/for.conf'
192.168.219.137 | SUCCESS | rc=0 >>
server{
     listen 85
}
server{
     listen 86
}
server{
     listen 87
}

192.168.219.136 | SUCCESS | rc=0 >>
server{
     listen 85
}
server{
     listen 86
}
server{
     listen 87
}

[root@ansible playbook]#
template里面的for字典案例

字典的好处就是我能在playbook里面定义多个键值对,这样我在模板里面直接取变量名即可。

[root@ansible playbook]# cat for.yml
---
- hosts: all
  remote_user: root
  vars:
    ports:  #ports里面我定义多个键值对
      - licent: 88 
        name: www.88.com
      - licent: 89
        name: www.89.com
      - licent: 90
        name: www.90.com

  tasks:
     - name: copy conf
       template: src=/home/ansible.test/playbook/for.conf.j2 dest=/root/file123/for.conf

[root@ansible playbook]# cat for.conf.j2
{% for port in ports %}
server{
     listen {{ port.licent }}
     server {{ port.name }}  #在模板里面我只需要调用playbook里面定义好的变量即可
}
{% endfor %}
[root@ansible playbook]# ansible all -m shell -a 'cat /root/file123/for.conf'
192.168.219.136 | SUCCESS | rc=0 >>
server{
     listen 88
     server www.88.com
}
server{
     listen 89
     server www.89.com
}
server{
     listen 90
     server www.90.com
}

192.168.219.137 | SUCCESS | rc=0 >>
server{
     listen 88
     server www.88.com
}
server{
     listen 89
     server www.89.com
}
server{
     listen 90
     server www.90.com
}

[root@ansible playbook]#
template里面for字典的案例2

template增加if判断条件:

[root@ansible playbook]# cat for.conf.j2
{% for port in ports %}  
server{
     listen {{ port.licent }}
{% if port.name is defined %}  #增加判断,判断下name是否有指,有指我才执行下面的语句
     server {{ port.name }}
{% endif %}
}
{% endfor %}
[root@ansible playbook]# cat for.yml
---
- hosts: all
  remote_user: root
  vars:
    ports:
      - licent: 91
       # name: www.91.com  #将91的name注释掉,让其检查的时候判断为没有指
      - licent: 92
        name: www.92.com
      - licent: 93
        name: www.93.com

  tasks:
     - name: copy conf
       template: src=/home/ansible.test/playbook/for.conf.j2 dest=/root/file123/for.conf

[root@ansible playbook]# ansible all -m shell -a 'cat /root/file123/for.conf'
192.168.219.137 | SUCCESS | rc=0 >>
server{
     listen 91
}
server{
     listen 92
     server www.92.com
}
server{
     listen 93
     server www.93.com
}

192.168.219.136 | SUCCESS | rc=0 >>
server{
     listen 91
}
server{
     listen 92
     server www.92.com
}
server{
     listen 93
     server www.93.com
}

可以看到所属的91是没有创建name的
增加if案例

♣三:ansible的企业级运用

A:roles角色的运用

在实际工作中,我们需要管理N台服务器,这个时候因为机器部署的环境,机器的配置,是否是云主机等导致ansible在使用的时候简单的playbook已经不能满足所要处理的任务了,我们需要层次型结构自动装载变量文件,task以及handlers等,这样我们只需要在playbook里面使用include指令即可。

roles能完成更加灵活的场景,代码的复用度也大大增加,可以通过简单的include灵活调取所要执行的任务。

我们在安装ansible的时候在/etc/ansibe下已经给我们创建好了roles目录,我们只需要在这个目下创建层级结构目录和文件即可,当然还可以使用自定义的目录。

                                                    

 

我们通过上图类似的层级结构,这样我每个文件的功能虽然单一,但是文件的功能性更加全面,可以很好的保证代码的复用度。

创建roles目录和相应的工程目录

mkdir -p roles/{nginx,mysql,httpd}
[root@ansible playbook]# tree roles/
roles/
├── httpd
├── mysql
└── nginx

3 directories, 0 files

调用roles角色的剧本需要放在roles相同层级的目录下

roles层级下目录和各目录的作用;

files:存放由shell或者yum等模块调用的文件;

templates:模块需要查找所需的模板文件;

tasks:定义tasks,role的基本元素,至少包含一个名为main.yml的文件;

handlers:至少包含一个main.yml文件;

vars:定义变量,至少包含一个main.yml文件;

meta:定义当前角色的特殊设定及依赖关系,至少包含一个main.yml文件;

default:设定默认变量时使用此目录中的main.yml文件。

[root@ansible playbook]# tree roles/
roles/
├── httpd
│   ├── tasks  #分别在tasks目录下创建相应的操作剧本
│   │   ├── copy.yml     #这每一个剧本只做一件事情,例如copy就做配置文件的拷贝
│   │   ├── group.yml   #创建组
│   │   ├── main.yml     #这个剧本的名字是固定的,用来规定这所有剧本的执行顺序
│   │   ├── restart.yml  #重启
│   │   ├── start.yml     #启动
│   │   ├── user.yml      #创建用户
│   │   └── yum.yml      #安装包
│   └── templates
│       └── httpd.conf.j2
├── mysql
└── nginx
directories, 8 files
[root@ansible tasks]# cat copy.yml
- name: copy httpd.conf
 template:src=/home/ansible.test/playbook/roles/httpd/templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
[root@ansible tasks]# cat group.yml
- name: create group   #因是roles形式就不需要在单个剧本定义hosts了,在总运行的剧本里面定义即可
  group: name=httpd
[root@ansible tasks]# cat restart.yml
- name: restart server
  service: name=hhtpd state=restarted
[root@ansible tasks]# cat start.yml
- name: start server
  service: name=httpd state=started enabled=yes
[root@ansible tasks]# cat user.yml
- name: create user
  user: name=httpd group=httpd system=yes shell=/sbin/nologin
[root@ansible tasks]# cat yum.yml
- name: install
  yum: name=httpd
用main.yml来规定这些剧本的执行顺序
[root@ansible tasks]# cat main.yml
- include: group.yml   
- include: user.yml
- include: yum.yml
- include: copy.yml
- include: start.yml
#include是固定词
[root@ansible tasks]#
[root@ansible templates]# cat httpd.conf.j2 | grep httpd_server
Listen {{ httpd_server }}
[root@ansible templates]#
定义模板,端口在hosts里面提前定义
[root@ansible playbook]# cat httpd.yml
---
- hosts: web
  remote_user: root
  roles:  #用关键字来调用roles角色下面的httpd,一层层的执行剧本。
    - role: httpd
在roles平级的目录下写一个总调用的剧本
[root@ansible playbook]# ansible-playbook httpd.yml

PLAY [web] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************
ok: [192.168.219.137]
ok: [192.168.219.136]

TASK [httpd : copy httpd.conf] **********************************************************************************************
changed: [192.168.219.136]
changed: [192.168.219.137]

TASK [httpd : start server] *************************************************************************************************
changed: [192.168.219.136]
changed: [192.168.219.137]

PLAY RECAP ******************************************************************************************************************
192.168.219.136            : ok=3    changed=2    unreachable=0    failed=0
192.168.219.137            : ok=3    changed=2    unreachable=0    failed=0
[root@ansible playbook]# ansible all -m shell -a 'netstat -anptu | grep httpd'
192.168.219.137 | SUCCESS | rc=0 >>
tcp        0      0 :::9090                     :::*                        LISTEN      3560/httpd

192.168.219.136 | SUCCESS | rc=0 >>
tcp        0      0 :::8080                     :::*                        LISTEN      3382/httpd

[root@ansible playbook]#
这样我们通过roles把流程规划的很清晰,不会再出现单一playbook出现的问题而难以调整或者复用的问题
roles案例
[root@ansible ansible.test]# tree roles
roles
├── keepalived
│   ├── handlers
│   │   └── restart.yml
│   ├── tasks
│   │   ├── copy.yml
│   │   ├── main.yml
│   │   ├── start.yml
│   │   └── yum.yml
│   ├── templates
│   │   └── keepalived.conf.j2
│   └── vars
│       └── main.yml
├── nginx
│   ├── handlers
│   ├── tasks
│   │   ├── copy.yml
│   │   ├── main.yml
│   │   ├── start.yml
│   │   └── yum.yml
│   ├── templates
│   │   └── nginx.conf.j2
│   └── vars
└── redis
    ├── handlers
    ├── tasks
    ├── templates
    └── vars

15 directories, 12 files
[root@ansible ansible.test]# cat roles/keepalived/vars/main.yml
ip_addr: 192.168.219.130/24   把keep需要的虚拟ip通过变量存在vars里面
[root@ansible ansible.test]# cat roles/keepalived/templates/keepalived.conf.j2

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority {{ vrrp }}
    advert_int 210
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        {{ ip_addr }}
    }
}
[root@ansible ansible.test]# cat setup.yml
---
- hosts: all
  remote_user: root
  roles:
    - { role: keepalived,tags: ['keepalived','web'],when:ansible_nodename == "www8080.http.com"}
    - { role: nginx,tags: ['nginx','web']}
[root@ansible ansible.test]#
在总控制的剧本里面加入tags标签,并加入判断,当主机名等于指定的名称之后才装keep。
ansible-playbook --tags="keepalived" setup.yml  在执行的时候加上标签来执行
[root@ansible ansible.test]# ansible all -m shell -a 'ps -aux | grep keepalived'
192.168.219.137 | SUCCESS | rc=0 >>
root       8382  0.0  0.1 106096  1128 pts/1    S+   08:24   0:00 /bin/sh -c ps -aux | grep keepalived
root       8384  0.0  0.0 103308   852 pts/1    S+   08:24   0:00 grep keepalivedWarning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ

192.168.219.136 | SUCCESS | rc=0 >>
root      10398  0.0  0.1 110276  1088 ?        Ss   08:23   0:00 /usr/sbin/keepalived -D
root      10400  0.0  0.2 114584  2944 ?        S    08:23   0:00 /usr/sbin/keepalived -D
root      10401  0.0  0.2 114452  2024 ?        S    08:23   0:00 /usr/sbin/keepalived -D
root      10440  0.0  0.1 106096  1128 pts/1    S+   08:24   0:00 /bin/sh -c ps -aux | grep keepalived
root      10442  0.0  0.0 103308   856 pts/1    S+   08:24   0:00 grep keepalivedWarning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ

[root@ansible ansible.test]# ansible all -m shell -a 'hostname'
192.168.219.137 | SUCCESS | rc=0 >>
www9090.http.com

192.168.219.136 | SUCCESS | rc=0 >>
www8080.http.com
可以看到总程序能自动判断并安装相应的程序
roles案例2,tags的使用

posted on 2019-03-01 16:53  ppc_server  阅读(233)  评论(0编辑  收藏  举报

导航