前言
在我之前的博客中不管是什么集群服务服务器的数量都并不是很多所以我每台机器逐一配置问题也不大,但是在实际生产中面对的服务器可不止就这几台,当面对如此多的服务器的时候别说做什么服务搭建了,就是切换个目录都异常繁琐,所以我们需要利用自动化运维工具来解决这个问题。
演示环境
四台CentOS7.3,IP地址分别为:172.16.2.{101-104},在172.16.2.104上演示ansible的操作
为了连接方便,我直接用秘钥方式验证登陆
ansible
概念
ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。
安装
ansible存在于epel源中,可以直接运行yum安装
[root@testsrv4 ~]# yum install ansible -y
命令行使用基本格式
ansible HOST-PATTERN -m MOD_ARGS -f FORKS -C -u USERNAME -c CONNECTION ansible-doc -l #查看所有模块 ansible-doc -s modname #查看modname模块的用法
定义远程主机地址
ansible是一个自动化运维工具,那么ansible既然是被用来管理服务器的就需要定义多台主机。定义方法就是配置ansible配置文件中的hosts清单文件。
[root@testsrv4 ~]# vim /etc/ansible/hosts #打开清单文件在最后添加如下内容 [mysrvs] #定义组名 172.16.2.101 172.16.2.102 172.16.2.103
定义完成后可以在命令行使用命令查询
[root@testsrv4 ~]# ansible all --list #列出服务器列表 hosts (3): 172.16.2.101 172.16.2.102 172.16.2.103 [root@testsrv4 ~]#
这里也可以用-m加上ping来指定使用ping模块来测试服务器在线状态
[root@testsrv4 ~]# ansible all -m ping -C #这里的-C是测试运行的意思 172.16.2.101 | SUCCESS => { "changed": false, "ping": "pong" } 172.16.2.102 | SUCCESS => { "changed": false, "ping": "pong" } 172.16.2.103 | SUCCESS => { "changed": false, "ping": "pong" } [root@testsrv4 ~]#
ansible命令行用法
ansible用户管理
ansible有非常多的模块可以使用,我们可以用ansible-doc -l来查看有哪些模块,这里我先从最简单的开始演示。
那么我先创建批量在三台服务器上创建一个组试试
[root@testsrv4 ~]# ansible all -m group -a "gid=2000 name=testgrp1 state=present system=no" #这段代码中all的意思是所有的服务器,-m group表示调用group模块,-a是定义键值的内容,其中gid和name顾名思义,state=present表示的是创建而删除是absent,system=no的意思是不创建为系统组 172.16.2.102 | SUCCESS => { "changed": true, "gid": 2000, "name": "testgrp1", "state": "present", "system": false } 172.16.2.101 | SUCCESS => { "changed": true, "gid": 2000, "name": "testgrp1", "state": "present", "system": false } 172.16.2.103 | SUCCESS => { "changed": true, "gid": 2000, "name": "testgrp1", "state": "present", "system": false } [root@testsrv4 ~]#
在ansible的使用中,我们定义的是期望的目标状态,举个例子在上面的实例中state就是一个目标状态,意思是说我们要将目标定义为什么样的状态而不是进行什么样的操作,换句话说如果我们要启动服务需要定义的是started而不是start,因为我们要的是状态而不是动作。
既然创建好了组,还是检查一下吧
[root@testsrv1 ~]# getent group testgrp1 testgrp1:x:2000: [root@testsrv2 ~]# getent group testgrp1 testgrp1:x:2000: [root@testsrv3 ~]# getent group testgrp1 testgrp1:x:2000:
试过了组的操作接下来试试用户的操作吧
[root@testsrv4 ~]# ansible all -m user -a "uid=2000 name=testuser1 state=present groups=testgrp1 shell=/bin/tcsh" 172.16.2.103 | SUCCESS => { "changed": true, "comment": "", "createhome": true, "group": 2001, "groups": "testgrp1", "home": "/home/testuser1", "name": "testuser1", "shell": "/bin/tcsh", "state": "present", "system": false, "uid": 2000 } 172.16.2.102 | SUCCESS => { "changed": true, "comment": "", "createhome": true, "group": 2001, "groups": "testgrp1", "home": "/home/testuser1", "name": "testuser1", "shell": "/bin/tcsh", "state": "present", "system": false, "uid": 2000 } 172.16.2.101 | SUCCESS => { "changed": true, "comment": "", "createhome": true, "group": 2001, "groups": "testgrp1", "home": "/home/testuser1", "name": "testuser1", "shell": "/bin/tcsh", "state": "present", "system": false, "uid": 2000 } [root@testsrv4 ~]#
和组的创建的格式是一样的,其中groups和shell的意思相信在看这篇博客的朋友没有人是不明白的。
copy 模块
copy模块是一个非常重要的模块,在更改配置文件的时候有着至关重要的作用,顾名思义copy就是用于复制文件的。下面我用随便复制一个配置文件到三台机器上测试效果
[root@testsrv4 ~]# ansible all -m copy -a "src=/etc/fstab dest=/app/fstab.ansible mode=600" 172.16.2.101 | SUCCESS => { "changed": true, "checksum": "ebd3720d52181b0d7df9caceb8750a6dac3646cc", "dest": "/app/fstab.ansible", "gid": 0, "group": "root", "md5sum": "7e62ef79f9fea2284e7e4082d72e1fe6", "mode": "0600", "owner": "root", "secontext": "system_u:object_r:default_t:s0", "size": 629, "src": "/root/.ansible/tmp/ansible-tmp-1505789314.01-262080661174880/source", "state": "file", "uid": 0 } 172.16.2.103 | SUCCESS => { "changed": true, "checksum": "ebd3720d52181b0d7df9caceb8750a6dac3646cc", "dest": "/app/fstab.ansible", "gid": 0, "group": "root", "md5sum": "7e62ef79f9fea2284e7e4082d72e1fe6", "mode": "0600", "owner": "root", "secontext": "system_u:object_r:default_t:s0", "size": 629, "src": "/root/.ansible/tmp/ansible-tmp-1505789313.99-48299942246410/source", "state": "file", "uid": 0 } 172.16.2.102 | SUCCESS => { "changed": true, "checksum": "ebd3720d52181b0d7df9caceb8750a6dac3646cc", "dest": "/app/fstab.ansible", "gid": 0, "group": "root", "md5sum": "7e62ef79f9fea2284e7e4082d72e1fe6", "mode": "0600", "owner": "root", "secontext": "system_u:object_r:default_t:s0", "size": 629, "src": "/root/.ansible/tmp/ansible-tmp-1505789313.91-234405401860789/source", "state": "file", "uid": 0 } [root@testsrv4 ~]#
如上面的代码所示,文件复制也成功了,其中src是只文件的源路径,dest是目标地址,mode是只复制过去后的文件的权限,这里其实还可以定义属主和属组,只需要在-a中加上owner和group键值对
[root@testsrv1 ~]# ll /app/ total 4 -rw-------. 1 root root 629 Sep 19 10:48 fstab.ansible [root@testsrv1 ~]#
找了任意一台机器查看结果
关于文件夹复制一定要注意的问题:
文件夹复制如果在源地址目录的最后加上了/,那么意思是复制该目录下的所有文件复制到目标地址,如果不加上/的话就是将整个目录带文件夹一起复制过去。
copy也可以直接定义一个文本文件到目标地址,方法为
[root@testsrv4 ~]# ansible all -m copy -a "content='hi there\n' dest=/app/test1.txt" #执行结果就不贴出来了
fetch 模块
那么刚才利用copy实现了从本地复制文件到远程主机,而我们想从远程主机复制文件到本地又要怎么做呢?这里可以利用fetch模块
[root@testsrv1 ~]# touch /app/test2.txt #在testsrv1上创建test2.txt [root@testsrv1 ~]# ls /app fstab.ansible test1.txt test2.txt [root@testsrv4 ~]# ansible 172.16.2.101 -m fetch -a "dest=/app/test2.txt src=/app/test2.txt" #复制远程主机的文件到本地,要注意源地址和目标地址的定义 172.16.2.101 | SUCCESS => { "changed": true, "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "dest": "/app/test2.txt/172.16.2.101/app/test2.txt", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "remote_checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "remote_md5sum": null } [root@testsrv4 ~]# tree /app #这里注意复制过来的文件存放的路径 /app └── test2.txt └── 172.16.2.101 └── app └── test2.txt 3 directories, 1 file [root@testsrv4 ~]#
command 模块
command模块主要功能是在远程主机上执行命令,我直接演示吧
[root@testsrv4 ~]# ansible all -m command -a "ls /app" 172.16.2.101 | SUCCESS | rc=0 >> fstab.ansible test1.txt test2.txt 172.16.2.103 | SUCCESS | rc=0 >> fstab.ansible test1.txt 172.16.2.102 | SUCCESS | rc=0 >> fstab.ansible test1.txt [root@testsrv4 ~]#
这个模块就很好理解了,-a后的内容就是要执行的命令,并且各主机要返回输出给本地。
command模块还有其他的选项如chdir,切换目录
[root@testsrv4 ~]# ansible all -m command -a "chdir=/app ls" 172.16.2.101 | SUCCESS | rc=0 >> fstab.ansible test1.txt test2.txt 172.16.2.103 | SUCCESS | rc=0 >> fstab.ansible test1.txt 172.16.2.102 | SUCCESS | rc=0 >> fstab.ansible test1.txt [root@testsrv4 ~]#
如上面,这里的chdir=/app ls 和之前的 ls /app是一样的效果,不一样的是chdir是切换到/app下后再ls
要注意的是command有不小的缺点,比如command无法执行passwd更改密码。
shell 模块
shell模块是基于shell本身执行命令的,和command是不同的,那么shell模块就能用来更改密码
[root@testsrv4 ~]# ansible all -m shell -a "echo testpassword | passwd --stdin testuser1" 172.16.2.101 | SUCCESS | rc=0 >> Changing password for user testuser1. passwd: all authentication tokens updated successfully. 172.16.2.103 | SUCCESS | rc=0 >> Changing password for user testuser1. passwd: all authentication tokens updated successfully. 172.16.2.102 | SUCCESS | rc=0 >> Changing password for user testuser1. passwd: all authentication tokens updated successfully. [root@testsrv4 ~]#
file 模块
file是一个功能比较强大的模块,包括创建文件创建符号链接也可以更改文件属性。
下面我先演示创建目录
[root@testsrv4 ~]# ansible all -m file -a "path=/app/testdir state=directory" #返回信息有点长而且没有意义我就不贴出来了,这里已经创建好了文件夹,我直接去testsrv1上检查一下是否创建成功就行了 [root@testsrv1 ~]# ls /app fstab.ansible test1.txt test2.txt testdir #没有问题
file不仅可以创建文件夹还可以创建符号链接,也就是软链接,下面我用在各机器上已有的文件test1.txt来创建软链接
[root@testsrv4 ~]# ansible all -m file -a "src=/app/test1.txt dest=/app/test1.link state=link" #命令执行完毕后会返回文件属性信息如下 172.16.2.103 | SUCCESS => { "changed": true, "dest": "/app/test1.link", "gid": 0, "group": "root", "mode": "0777", "owner": "root", "secontext": "unconfined_u:object_r:etc_runtime_t:s0", "size": 14, "src": "/app/test1.txt", "state": "link", "uid": 0 } 172.16.2.102 | SUCCESS => { "changed": true, "dest": "/app/test1.link", "gid": 0, "group": "root", "mode": "0777", "owner": "root", "secontext": "unconfined_u:object_r:etc_runtime_t:s0", "size": 14, "src": "/app/test1.txt", "state": "link", "uid": 0 } 172.16.2.101 | SUCCESS => { "changed": true, "dest": "/app/test1.link", "gid": 0, "group": "root", "mode": "0777", "owner": "root", "secontext": "unconfined_u:object_r:etc_runtime_t:s0", "size": 14, "src": "/app/test1.txt", "state": "link", "uid": 0 } #这个时候还是老规矩去任意一台机器上检查执行结果 [root@testsrv2 ~]# ll /app total 8 -rw-------. 1 root root 629 Sep 19 10:48 fstab.ansible lrwxrwxrwx. 1 root root 14 Sep 19 11:27 test1.link -> /app/test1.txt -rw-r--r--. 1 root root 9 Sep 19 10:54 test1.txt drwxr-xr-x. 2 root root 6 Sep 19 11:22 testdir
cron 模块
cron模块,顾名思义肯定是计划任务的模块,这个很好理解我直接演示
[root@testsrv4 ~]# ansible all -m cron -a "minute=* job='wall 'successful''" #这个计划任务很简单,执行完了之后等待一分钟看看结果 172.16.2.101 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "None" ] } 172.16.2.103 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "None" ] } 172.16.2.102 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "None" ] } #计划任务结果 [root@testsrv2 ~]# Broadcast message from root@testsrv2 (Tue Sep 19 11:35:01 2017): successful #因为刚才没有定义计划任务的name所以定义成功的计划任务名字为none,下面我删掉这些计划任务 [root@testsrv4 ~]# ansible all -m cron -a "name=None state=absent" 172.16.2.101 | SUCCESS => { "changed": true, "envs": [], "jobs": [] } 172.16.2.103 | SUCCESS => { "changed": true, "envs": [], "jobs": [] } 172.16.2.102 | SUCCESS => { "changed": true, "envs": [], "jobs": [] } [root@testsrv4 ~]#
yum 模块
这个模块显然就是用来利用yum来安装程序的,同样是非常好理解的模块这里也直接演示
[root@testsrv4 ~]# ansible all -m yum -a "name=httpd state=installed" #给所有的机器安装tree程序,这里的返回信息都是安装的时候的信息,非常多就不贴出来了 #检验一下 [root@testsrv1 ~]# rpm -q httpd httpd-2.4.6-45.el7.centos.x86_64 [root@testsrv1 ~]#
这里是不用担心重复安装的,如果程序已经安装过的话将不会做任何操作。
service 模块
service模块无疑是用于管理服务的模块,这个模块可以启动或者关闭服务也可以查看服务状态。和我之前提过的一样要注意的是,我们定义的期望目标的状态,如上面的yum模块在安装程序的时候state是installed而不是install,这里也是同样的。
[root@testsrv4 ~]# ansible all -m service -a "name=httpd state=started enabled=true" #启动服务会返回大量信息,这里不贴出。这里的name和started以及enabled都好理解 #验证 [root@testsrv2 ~]# ss -ntl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:22 *:* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 128 :::80 :::* LISTEN 0 128 :::22 :::* LISTEN 0 100 ::1:25 :::* [root@testsrv2 ~]# #可以看到80端口已开启
script 模块
这个模块是自动把本地的脚本复制到远程主机上然后执行,只需要指定脚本路径就可以了。
下面我写一个输出一句话重定向到指定目录生成文本文件的脚本然后让各机器执行
[root@testsrv4 ~]# vim test.sh #脚本 #!/bin/bash echo 'Hello ansible' > /app/hello.txt [root@testsrv4 ~]# ansible all -m script -a "/root/test.sh" #执行 172.16.2.103 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 172.16.2.103 closed.\r\n", "stdout": "", "stdout_lines": [] } 172.16.2.101 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 172.16.2.101 closed.\r\n", "stdout": "", "stdout_lines": [] } 172.16.2.102 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 172.16.2.102 closed.\r\n", "stdout": "", "stdout_lines": [] } [root@testsrv4 ~]# #在testsrv2上查看一下 [root@testsrv2 ~]# cat /app/hello.txt Hello ansible [root@testsrv2 ~]#
ansible还有非常多的模块,可谓是功能非常强大,这里我就介绍以上这些比较常用的模块。
ansible playbook
概念
ansible的功能多么强大我已经在上面描述过了,但是有一个问题,上面只是在命令行执行的操作,万一多台服务器宕了要换新机那命令又要重新执行一遍,命令有文档记载还好说,如果没有任何记载的话那就麻烦了。所以,ansible的playbook功能正好解决这个问题。
palybook需要把要执行的任务写成YAML格式的配置文件,YAML是一个可读性高,用来表达数据序列的格式。语法和其他高级语言类似,并且可以简单表达清单、散列表、标量等数据形态。
playbook的核心元素
Hosts:主机
Tasks:任务列表
Variables:变量
Templates:包含了模板语法的文本文件
Handlers:有特定条件出发的任务
Roles:角色
简单实例
[root@testsrv4 ~]# mkdir playbook #创建一个存放YAML文件的目录 [root@testsrv4 ~]# vim playbook/test1.yaml #创建一个YAML配置文件 - hosts: all #指定主机为全部 remote_user: root #指定用户为root tasks: - name: install nginx #任务名称 yum: name=nginx state=installed #安装nginx发 - name: start nginx service: name=nginx state=started #启动nginx服务 #-C测试运行 [root@testsrv4 ~]# ansible-playbook -C playbook/test1.yaml PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [172.16.2.101] ok: [172.16.2.103] ok: [172.16.2.102] TASK [install nginx] *********************************************************** changed: [172.16.2.103] changed: [172.16.2.102] changed: [172.16.2.101] TASK [start nginx] ************************************************************* fatal: [172.16.2.101]: FAILED! => {"changed": false, "failed": true, "msg": "Could not find the requested service nginx: cannot check nor set state"} fatal: [172.16.2.102]: FAILED! => {"changed": false, "failed": true, "msg": "Could not find the requested service nginx: cannot check nor set state"} fatal: [172.16.2.103]: FAILED! => {"changed": false, "failed": true, "msg": "Could not find the requested service nginx: cannot check nor set state"} to retry, use: --limit @/root/playbook/test1.retry PLAY RECAP ********************************************************************* 172.16.2.101 : ok=2 changed=1 unreachable=0 failed=1 172.16.2.102 : ok=2 changed=1 unreachable=0 failed=1 172.16.2.103 : ok=2 changed=1 unreachable=0 failed=1
这里一定要注意一个问题,在以上测试运行中有一个错误,提示的大概意思是没有指定的服务所以无法启动,其实这里是正常的,一个简单的逻辑,因为这是测试运行,所以在安装服务的时候是不会真正把服务安装上去的,所以在启动服务的时候是必然报错的,如果我现在直接正常运行一次就没有任何错误信息,因为真正运行的时候是真的把程序安装上了,所以正常启动是肯定没有问题的。
#接下来我演示正常启动 [root@testsrv4 ~]# ansible-playbook playbook/test1.yaml PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [172.16.2.102] ok: [172.16.2.101] ok: [172.16.2.103] TASK [install nginx] *********************************************************** changed: [172.16.2.101] changed: [172.16.2.103] changed: [172.16.2.102] TASK [start nginx] ************************************************************* changed: [172.16.2.103] changed: [172.16.2.102] changed: [172.16.2.101] PLAY RECAP ********************************************************************* 172.16.2.101 : ok=3 changed=2 unreachable=0 failed=0 172.16.2.102 : ok=3 changed=2 unreachable=0 failed=0 172.16.2.103 : ok=3 changed=2 unreachable=0 failed=0 [root@testsrv4 ~]#
如上,一切正常运行。
handler条件触发
如果我要实现更改了远程主机的配置文件之后自动执行重启服务的操作的话YAML需要这样写
- hosts: all remote_user: root tasks: - name: install nginx yum: name=nginx state=installed - name: copy config file copy: src=/path/nginx.conf dest=/etc/nginx/nginx.conf #复制配置文件 notify: restart nginx #通知handlers - name: start nginx service: name=nginx state=started handlers: - name: restart nginx service: name=nginx state=restarted
配置文件写成上面这样的话,当配置文件发生改动的时候便会重启服务,配置文件没有改动的话就不会触发重启nginx
那么来测试一下吧,注意配置文件中我写的path路径是指代不同路径的
[root@testsrv4 ~]# vim playbook/nginx.conf #对已经复制到本地的配置文件进行改动 [root@testsrv4 ~]# vim playbook/test1.yaml #配置文件更改无误 [root@testsrv4 ~]# ansible-playbook playbook/test1.yaml PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [172.16.2.103] ok: [172.16.2.102] ok: [172.16.2.101] TASK [install nginx] *********************************************************** ok: [172.16.2.103] ok: [172.16.2.102] ok: [172.16.2.101] TASK [copy config file] ******************************************************** changed: [172.16.2.103] changed: [172.16.2.101] changed: [172.16.2.102] TASK [start nginx] ************************************************************* ok: [172.16.2.103] ok: [172.16.2.102] ok: [172.16.2.101] RUNNING HANDLER [restart nginx] ************************************************ changed: [172.16.2.103] changed: [172.16.2.102] changed: [172.16.2.101] PLAY RECAP ********************************************************************* 172.16.2.101 : ok=5 changed=2 unreachable=0 failed=0 172.16.2.102 : ok=5 changed=2 unreachable=0 failed=0 172.16.2.103 : ok=5 changed=2 unreachable=0 failed=0 [root@testsrv4 ~]#
如上可以清楚地看到,当配置文件改动了之后handler被触发了,所以服务重启了,那么如果配置文件没有改动handler会不会被触发呢?我再运行一次就知道了
[root@testsrv4 ~]# ansible-playbook playbook/test1.yaml PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [172.16.2.103] ok: [172.16.2.102] ok: [172.16.2.101] TASK [install nginx] *********************************************************** ok: [172.16.2.101] ok: [172.16.2.103] ok: [172.16.2.102] TASK [copy config file] ******************************************************** ok: [172.16.2.103] ok: [172.16.2.102] ok: [172.16.2.101] TASK [start nginx] ************************************************************* ok: [172.16.2.101] ok: [172.16.2.103] ok: [172.16.2.102] PLAY RECAP ********************************************************************* 172.16.2.101 : ok=4 changed=0 unreachable=0 failed=0 172.16.2.102 : ok=4 changed=0 unreachable=0 failed=0 172.16.2.103 : ok=4 changed=0 unreachable=0 failed=0 [root@testsrv4 ~]#
如果配置文件没有改动的话那么handler是不会被触发的。
从之前运行playbook可以看到不管我想执行什么操作都会按照顺序全部执行,但是如果我只想执行更改配置文件操作不想执行其他的操作又怎么办呢?
这里就用到了tags的概念,我们可以在YAML中添加tags标签然后在运行的时候指定标签就可以了
- hosts: all remote_user: root tasks: - name: install nginx yum: name=nginx state=installed - name: copy config file copy: src=/root/playbook/nginx.conf dest=/etc/nginx/nginx.conf notify: restart nginx tags: changeconfig #在更改配置文件的地方加上changeconfig这样的标签 - name: start nginx service: name=nginx state=started handlers: - name: restart nginx service: name=nginx state=restarted #执行测试 [root@testsrv4 ~]# ansible-playbook -t changeconfig playbook/test1.yaml PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [172.16.2.103] ok: [172.16.2.101] ok: [172.16.2.102] TASK [copy config file] ******************************************************** ok: [172.16.2.101] ok: [172.16.2.102] ok: [172.16.2.103] PLAY RECAP ********************************************************************* 172.16.2.101 : ok=2 changed=0 unreachable=0 failed=0 172.16.2.102 : ok=2 changed=0 unreachable=0 failed=0 172.16.2.103 : ok=2 changed=0 unreachable=0 failed=0 [root@testsrv4 ~]#
如上面所示,除了必要的内容要执行外,只执行了复制配置文件的操作。
变量的运用
关于变量的运用演示我直接写一个新的YAML文件方便演示,变量的运用也算是比较好理解的问题。
#配置文件如下 - hosts: all remote_user: root tasks: - name: install {{ pkgname }} #这里的变量后面将会演示如何定义 yum: name={{ pkgname }} state=installed #执行 [root@testsrv4 ~]# ansible-playbook -e pkgname=lrzsz playbook/test2.yaml #-e加上配置文件中的变量名然后赋值就可以定义 PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [172.16.2.101] ok: [172.16.2.103] ok: [172.16.2.102] TASK [install lrzsz] *********************************************************** changed: [172.16.2.102] changed: [172.16.2.101] changed: [172.16.2.103] PLAY RECAP ********************************************************************* 172.16.2.101 : ok=2 changed=1 unreachable=0 failed=0 172.16.2.102 : ok=2 changed=1 unreachable=0 failed=0 172.16.2.103 : ok=2 changed=1 unreachable=0 failed=0 [root@testsrv4 ~]#
上面就是变量的简单用法。
如果要在YAML文件里定义变量的话可以这样写
- hosts: all remote_user: root vars: - pkgname: lrzsz tasks: - name: install {{ pkgname }} yum: name={{ pkgname }} state=installed
这里要注意:invertory参数,用于定义ansible远程连接目标主机时使用的参数,并非传递给playbook的变量
ansible_ssh_host 地址 ansible_ssh_port 端口 ansible_ssh_user 用户 ansible_ssh_pass 用户密码 ansible_sudo_pass 使用sudo的用户密码
这里的用户和用户密码可以直接在ansible的hosts文件中写入,就可以实现ansible对各主机的连接,我这里是用秘钥实现的,所以ansible的hosts文件里没有写这些参数
template 模板
模板主要是用来给不同的主机配置特定不同的值,比如服务的监听地址,实现方法也比较简单。
实例
[root@testsrv4 ~]# vim playbook/test3.yaml #写配置文件 - hosts: all remote_user: root tasks: - name: config template: src=/root/playbook/test.conf dest=/app/test.conf #这里先别急,test.conf里的内容是要传递的变量,template和copy的主要区别也就在于这里 [root@testsrv4 ~]# vim playbook/test.conf #写文件 This is {{ ansible_ens33.ipv4.address }} #双大括号里的内容是ansible ipadress -m setup获得的 #运行 [root@testsrv4 ~]# ansible-playbook playbook/test3.yaml PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [172.16.2.103] ok: [172.16.2.102] ok: [172.16.2.101] TASK [config] ****************************************************************** changed: [172.16.2.101] changed: [172.16.2.103] changed: [172.16.2.102] PLAY RECAP ********************************************************************* 172.16.2.101 : ok=2 changed=1 unreachable=0 failed=0 172.16.2.102 : ok=2 changed=1 unreachable=0 failed=0 172.16.2.103 : ok=2 changed=1 unreachable=0 failed=0 #可以看到ansible执行没有错误,那么现在去各主机上看看效果 [root@testsrv1 ~]# cat /app/test.conf This is 172.16.2.101 [root@testsrv1 ~]# [root@testsrv2 ~]# cat /app/test.conf This is 172.16.2.102 [root@testsrv2 ~]# [root@testsrv3 ~]# cat /app/test.conf This is 172.16.2.103 [root@testsrv3 ~]#
上面各主机IP地址一一对应,说明变量传递没有问题。
一些高级用法
条件测试
when语句:在task中使用,jinja2语法格式
实例演示
- hosts: all remote_user: root tasks: - name: install httpd yum: name=httpd state=installed when: ansible_os_family == "RedHat" - name: install apache2 apt: name=apache2 state=installed when: ansible_os_family == "Debian"
这个配置实例的意思是判断系统是什么系列的系统然后选择使用不同的安装程序来安装不同名称的服务
[root@testsrv4 ~]# ansible-playbook playbook/test4.yaml PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [172.16.2.103] ok: [172.16.2.102] ok: [172.16.2.101] TASK [install httpd] *********************************************************** ok: [172.16.2.101] ok: [172.16.2.103] ok: [172.16.2.102] TASK [install apache2] ********************************************************* skipping: [172.16.2.101] skipping: [172.16.2.102] skipping: [172.16.2.103] PLAY RECAP ********************************************************************* 172.16.2.101 : ok=2 changed=0 unreachable=0 failed=0 172.16.2.102 : ok=2 changed=0 unreachable=0 failed=0 172.16.2.103 : ok=2 changed=0 unreachable=0 failed=0 [root@testsrv4 ~]#
执行之后可以看到debian部分的操作直接就skipping了。
循环迭代
当我们需要一次性安装多个程序的时候显然如果一条一条地配置那就非常不高效了。解决办法如下
[root@testsrv4 ~]# vim playbook/test5.yaml #配置新的YAML文件 - hosts: all remote_user: root tasks: - name: install {{ item }} yum: name={{ item }} state=installed with_items: - php - php-mysql - mariadb #运行结果 [root@testsrv4 ~]# ansible-playbook playbook/test5.yaml PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [172.16.2.103] ok: [172.16.2.102] ok: [172.16.2.101] TASK [install {{ item }}] ****************************************************** changed: [172.16.2.101] => (item=[u'php', u'php-mysql', u'mariadb']) changed: [172.16.2.102] => (item=[u'php', u'php-mysql', u'mariadb']) changed: [172.16.2.103] => (item=[u'php', u'php-mysql', u'mariadb']) PLAY RECAP ********************************************************************* 172.16.2.101 : ok=2 changed=1 unreachable=0 failed=0 172.16.2.102 : ok=2 changed=1 unreachable=0 failed=0 172.16.2.103 : ok=2 changed=1 unreachable=0 failed=0 [root@testsrv4 ~]#
没有任何报错,运行成功,如果可以的话去其他三个主机检查一下比较好。
roles角色
在我们的正常工作中若ansible坏了,那我们的playbook就丢了,即使playbook没有丢但是却有各种各样的配置文件存在,如果配置文件也丢了那么一样是麻烦事,为了管理方便我们可以将某一种特定的程序或者服务所需要的所有配置内容定义为一个自包含目录结构,这无疑减轻了我们的管理成本,而这个所谓的自包含目录结构就是roles角色。
目录结构
files/:存放由copy或者script模块等调用的文件
templates/:templates模块查找所需要模板文件的目录
task/:至少应该包含一个为名main.yml的文件,其他的文件需要在此文件中通过include进行包含
handlers/:至少应该包含一个名为main.yml的文件,其他文件需要在此文件中通过include进行包含
vars/:至少应该包含一个名为main.yml的文件,其他文件需要在此文件中通过include进行包含
meta/:至少应该包含一个名为main.yml的文件,定义当前角色的特殊设定以及其依赖关系,其他文件需要在此文件中通过include进行包含
default/:设定默认变量时使用此目录中的main.yml文件
在playbook中调用角色的方法
- hosts: all remote_user: root roles: - role1 - role2
角色存放路径:/etc/ansible/roles
实例说明
[root@testsrv4 ~]# mkdir -pv /etc/ansible/roles/nginx/{files,templates,tasks,vars,handlers,meta,default} #创建角色路径 [root@testsrv4 ~]# vim /etc/ansible/roles/nginx/tasks/main.yml #写tasks配置 - name: install nginx yum: name=nginx state=installed [root@testsrv4 ~]# vim playbook/nginx.yaml #写nginx角色的playbook - hosts: all remote_user: root roles: - nginx #下面运行测试一下 [root@testsrv4 ~]# ansible-playbook playbook/nginx.yaml PLAY [all] ********************************************************************* TASK [setup] ******************************************************************* ok: [172.16.2.102] ok: [172.16.2.101] ok: [172.16.2.103] TASK [nginx : install nginx] *************************************************** ok: [172.16.2.103] ok: [172.16.2.102] ok: [172.16.2.101] PLAY RECAP ********************************************************************* 172.16.2.101 : ok=2 changed=0 unreachable=0 failed=0 172.16.2.102 : ok=2 changed=0 unreachable=0 failed=0 172.16.2.103 : ok=2 changed=0 unreachable=0 failed=0 [root@testsrv4 ~]#
完全OK,不过因为nginx已经安装过了所以没有进行任何操作。
ansible的常用元素差不多都已经演示完毕,这里本来打算做一个实战例子,但是整篇博客的篇幅有点过长了,而且实战例子其实只要把这上面的各个内容组合在一起就是一个完整的实战例子,所以实战例子我就不贴出来了。
转载请注明源地址,谢谢。