自动化运维工具ansible的使用

前言

在我之前的博客中不管是什么集群服务服务器的数量都并不是很多所以我每台机器逐一配置问题也不大,但是在实际生产中面对的服务器可不止就这几台,当面对如此多的服务器的时候别说做什么服务搭建了,就是切换个目录都异常繁琐,所以我们需要利用自动化运维工具来解决这个问题。


演示环境

四台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是不会被触发的。


tags 标签

从之前运行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的常用元素差不多都已经演示完毕,这里本来打算做一个实战例子,但是整篇博客的篇幅有点过长了,而且实战例子其实只要把这上面的各个内容组合在一起就是一个完整的实战例子,所以实战例子我就不贴出来了。

转载请注明源地址,谢谢。