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)]
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,建议取消注释
/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-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参数就不需要加了,也不需要输入密码了
上面我们说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这行取消注释
非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用户登录需要那些操作
♣二: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模块不是默认模块,-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 ~]#
在传统的模式下,我们要在被控机执行本机(主控端)上一个写好的脚本,及时有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]#
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)
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]#
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]#
[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的目录,抓取的文件都放在这个目录下面,方便区分
[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 ~]#
[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 ~]#
[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 发现新加的计划任务就是注释的
[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
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)
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]# 可以看到服务都已经安装成功了
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 ~]#
[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 ~]#
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]#
[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目录已经删除了
[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]# 可以看到递归创建成功了
[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'
因模块很多,本篇不做过多的介绍,需要查找可以通过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命令安装网站的角色名就可以
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的语法和基本格式。
[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的其它命令,不断的去解决这个问题。
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]# 加密状态下剧本是无法执行的
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]# 加密文件要执行必须解密才能继续使用
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参数去查看,还是需要输入口令。
ansible-vault edit 后面是playbook的脚本 【编辑加密文件】
[root@ansible playbook]# ansible-vault edit playbook.test.yml Vault password: [root@ansible playbook]# 如果不想解密就把文件内容直接调整了,就可以使用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]# 直接修改老的密码,
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参数,进去剧本直接编写。
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]#
在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]#
脚本写好了,跑的过程我想知道也可以在执行的时候末尾加上-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也只能对应一个操作。
上面我们执行的时候都是按照流程去操作的,不存在判断的过程,这个我要判断机器的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端口修改也生效了
notify可以通知多个handlers的操作:
notify: - 空格之后接第一个handlers的名字 - 空格之后接第二个handlers的名字
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服务就启动了
[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的配置文件下发同时也触发了重启任务的操作。
[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还支持多个操作共用一个标签
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过滤出来的数值我们到时候就可以进行调用,以满足其他的需求。
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程序安装并启动成功。
在脚本里面使用变量可以使得脚本更加灵活,当然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 可以看到都安装成功
也可以在脚本里面把变量进行赋值,在执行的时候就直接执行,无需在执行的时候给参数。
在/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里面事先定义好了的端口
[web:vars] #也可以在组里面使用变量,指需要在组名后面添加vars即可 name1=www name2=httpd.com 在 hostname: name={{name1}}{{httpd_server}}.{{name2}}位置使用变量,也可以
在变量使用中,普通变量(单条命令)比公共组(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 可以看到创建成功
注: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的还能完成根据机器的资源配置下发与机器相关匹配的配置文件。
变量的优先级是命令行里面的-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服务
迭代: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]# 可以看到迭代帮忙创建的空的目录
迭代嵌套子变量:
如果想重复完成多件事情,单纯的嵌套写起来会比较麻烦。
[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 %} ......
迭代是我们定义好的参数,但是如果是想要产出的数字有规律且很多的情况下迭代很显然就不适合了,就需要使用循环来处理。
[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被模板循环赋值的结果。
[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]#
字典的好处就是我能在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增加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的
♣三: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出现的问题而难以调整或者复用的问题
[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 可以看到总程序能自动判断并安装相应的程序
posted on 2019-03-01 16:53 ppc_server 阅读(234) 评论(0) 编辑 收藏 举报