Ansible批量自动化管理工具

工具与环境介绍

  1. ansible简介
    批量管理服务器的工具
    无需部署agent,通过ssh进行管理
    流行的自动化运维工具:https://github.com/ansible/ansible
  2. jenkins简介
    可视化运维(主要用在可视化部署)
    持续构建,可以和git,svn结合
    可结合ssh实现可视化运维
    可结合ansible实现可视化运维
  3. 环境说明
    Centos7.3(yum -y install net-tools vim)
    关闭防火墙(systemctl stop firewalld,systemctl disable firewalld)
    关闭selinux

Python3与ansible的安装

#安装支持包
[root@localhost ~]# yum -y install lrzsz vim net-tools gcc gcc-c++ ncurses ncurses-devel unzip zlib-devel zlib openssl-devel openssl  openssh-clients*
#源码编译Python3.5
[root@localhost Python-3.5.2]# tar xf Python-3.5.2.tgz -C /usr/src/
[root@localhost Python-3.5.2]# cd /usr/src/Python-3.5.2/
[root@localhost Python-3.5.2]# ./configure --prefix=/usr/local/python/
[root@localhost Python-3.5.2]# make && make install
[root@localhost Python-3.5.2]# ln -s /usr/local/python/bin/python3 /usr/bin/python3
[root@localhost Python-3.5.2]# which python3
/usr/bin/python3
[root@localhost Python-3.5.2]# python3 -V
Python 3.5.2

使用pip3安装ansible

#安装ansible最新版本
[root@localhost bin]# /usr/local/python/bin/pip3 install --upgrade pip                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
[root@localhostPython-3.5.2]# /usr/local/python/bin/pip3 install ansible
#静心等待ansible安装完毕后
[root@localhost python]# ln -s /usr/local/python/bin/ansible /usr/local/bin/
[root@localhost python]# which ansible
/usr/local/bin/ansible
[root@localhost python]# ansible --version
ansible 2.5.4
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/python/lib/python3.5/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.5.2 (default, Jun 13 2018, 09:13:32) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]

ansible查看帮助
[root@ansible ~]# /usr/local/python/bin/ansible-doc -l 查看总帮助
[root@ansible ~]# /usr/local/python/bin/ansible-doc -s shell 查看shell模块的帮助
[root@ansible ~]# /usr/local/python/bin/ansible-doc -s raw

使用公私钥实现ssh无密码登陆

Host IP
ansible 192.168.239.128
web1 192.168.239.135
web2 192.168.239.136

#生成密钥对
[root@localhost ~]# ssh-keygen -t rsa -f ~/.ssh/id_rsa -P ""
#分发密钥
[root@localhost ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.239.135
#进行免密码登陆测试
[root@localhost ~]# ssh 192.168.239.135
Last login: Fri Dec  7 21:02:15 2018 from 192.168.239.1
[root@localhost ~]# hostname -I
192.168.239.135 
[root@localhost ~]# exit
logout
Connection to 192.168.239.135 closed.
[root@localhost ~]# hostname -I
192.168.239.128 

ansible的简单配置和ping模块

  1. ansible的配置文件
[root@localhost ~]# mkdir -p /etc/ansible
[root@localhost ~]# cd /etc/ansible
[root@localhost ansible]# vim hosts        #ansible主机管理配置文件
[nginx]    #被管理的主机组名称
web1 ansible_ssh_host=192.168.239.135 ansible_ssh_port=22 ansible_ssh_user=root     #第一台主机
web2 ansible_ssh_host=192.168.239.136 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=123456    #第二台主机
#如果我们已经设置了ssh免密钥了。那么就不需要写密码了。例如:webA 
我们要是没有设置免密钥,那么就需要安装sshpass工具,并在/etc/ansible/hosts文件里写上主机的连接密码。例如webB
#下载epel源安装sshpass
root@localhost ansible]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
root@localhost ansible]# yum -y install sshpass
root@localhost ansible]# which sshpass
/usr/bin/sshpass
  1. 进行ansible远程执行命令测试
    语法:ansible nginx -m command -a 'uptime'
    ansible 主机组 -m ansible内置功能模块名 -a 命令

进行命令测试:

#进行ping模块的连接测试
[root@localhost ansible]# ansible nginx -m ping
web2 | FAILED! => {                       #我们发现webB还是没链接成功,这是因为本机的known_hosts文件还没有记录对方主机的信息。
    "msg": "Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this.  Please add this host's fingerprint to your known_hosts file to manage this host."
}
web1 | SUCCESS => {                #webA成功
    "changed": false, 
    "ping": "pong"
}

想要解决known_hosts的问题,只需要修改ssh的配置文件/etc/ssh/ssh_config即可

#修改ssh配置文件
[root@localhost ansible]# sed -n '35p' /etc/ssh/ssh_config
#   StrictHostKeyChecking ask
[root@localhost ansible]# vim /etc/ssh/ssh_config
[root@localhost ansible]# sed -n '35p' /etc/ssh/ssh_config
   StrictHostKeyChecking no     #去掉注释,修改成这样
#重启ssh服务
[root@localhost ansible]# systemctl reload sshd.service
#再次进行ansible连接测试
[root@localhost ansible]# ansible nginx -m ping
web1 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
web2 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

  1. ansible的简单使用方式
    ansible -i /etc/ansible/hosts 主机或主机组 -m 指定模块 -a 命令
    不用-i指定配置文件默认为/etc/ansible/hosts
  2. 使用ping模块用来查看服务器是否连接正常,ping模块不需要-a指定参数
    ansible all -m ping
    主机和主机组注意事项:
主机组范围 解释
all 代表所有主机
web1:web2 可以指定多台主机
all:!web1 指定all但不包含web2,注意!前需要加转意符号\
[root@localhost ansible]# ansible all -m ping
web2 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
web1 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
[root@localhost ansible]# ansible web1:web2 -m ping
web1 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
web2 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
[root@localhost ansible]# ansible all:\!web2 -m ping
web1 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

ansible的三个命令模块

  1. ansible模块command(不支持管道,不建议使用)
[root@localhost ansible]# ansible all -m command -a "pwd"
web2 | SUCCESS | rc=0 >>
/root

web1 | SUCCESS | rc=0 >>
/root

  1. ansible模块shell(支持管道,支持重定向)
#shell模块支持管道符
[root@localhost ansible]# ansible all -m shell -a "echo test ansible | grep a"
web1 | SUCCESS | rc=0 >>
test ansible

web2 | SUCCESS | rc=0 >>
test ansible
#shell支持重定向
[root@localhost ansible]# ansible all -m shell -a "echo bb >> /tmp/testansible"
webA | SUCCESS | rc=0 >>
webB | SUCCESS | rc=0 >>
#如果遇到特殊符号需要加入\转义,这样子ansible才能正常运行
[root@localhost ansible]# ansible all -m shell -a "cat /etc/passwd | awk -F":" '{print \$1}'"
webB | SUCCESS | rc=0 >>
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator
games
ftp
nobody
systemd-bus-proxy
systemd-network
dbus
polkitd
tss
postfix
sshd
chrony
webA | SUCCESS | rc=0 >>
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator
games
ftp
nobody
systemd-bus-proxy
systemd-network
dbus
polkitd
tss
postfix
sshd
chrony
  1. ansible模块raw,最原始的方式运行命令(不依赖python,仅通过ssh实现)
#清除yum缓存
[root@localhost ansible]# ansible all -m raw -a "yum -y clean all"
webB | SUCCESS | rc=0 >>
Loaded plugins: fastestmirror
Cleaning repos: c7-media
Cleaning up everything
Shared connection to 192.168.239.136 closed.
webA | SUCCESS | rc=0 >>
Loaded plugins: fastestmirror
Cleaning repos: c7-media epel
Cleaning up everything
Cleaning up list of fastest mirrors
Shared connection to 192.168.200.132 closed.
#建立yum缓存
[root@localhost ansible]# ansible all -m raw -a "yum makecache"
webA | SUCCESS | rc=0 >>
Loaded plugins: fastestmirror
c7-media                                                 | 3.6 kB     00:00     
Loading mirror speeds from cached hostfile
 * c7-media: 
Metadata Cache Created
Shared connection to 192.168.200.132 closed.
webB | SUCCESS | rc=0 >>
Loaded plugins: fastestmirror
c7-media                                                 | 3.6 kB     00:00     
Loading mirror speeds from cached hostfile
 * c7-media: 
Metadata Cache Created
Shared connection to 192.168.200.138 closed.
#yum装nmap包
[root@localhost ansible]# ansible all -m raw -a "yum -y install nmap"

ansible的copy模块批量下发文件或文件夹

copy模块概述
  1. copy模块的参数
    ansible 主机组 -m 模块 -a 命令

src:指定源文件或目录
dest:指定目标服务器的文件或目录
backup:是否要备份
owner:拷贝到目标服务器后,文件或目录的所属用户
group:拷贝到目标服务器后,文件或目录的所属群组
mode:文件或目录的权限
2. 准备工作

[root@localhost ansible]# mkdir -p /service/scripts
[root@localhost ansible]# echo "aaa" > /service/scripts/test.txt
[root@localhost ansible]# echo "bbb" > /service/scripts/test2.txt

  1. 所有被管理端节点必须安装libselinux-python包
[root@localhost ansible]# yum -y install libselinux-python
copy模块拷贝文件

特别提示:如果目标路径不存在会自动创建
src===>源文件路径 dest=目标路径位置

[root@localhost ansible]# ansible all -m copy -a "src=/service/scripts/test.txt dest=/service/scripts/"
webB | FAILED! => {     #节点未安装libselinux-python
    "changed": false,
    "checksum": "972a1a11f19934401291cc99117ec614933374ce",
    "msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!"
}
webA | SUCCESS => {
    "changed": true,
    "checksum": "972a1a11f19934401291cc99117ec614933374ce",
    "dest": "/service/scripts/test.txt",
    "gid": 0,
    "group": "root",
    "md5sum": "5c9597f3c8245907ea71a89d9d39d08e",
    "mode": "0644",
    "owner": "root",
    "secontext": "system_u:object_r:svc_svc_t:s0",
    "size": 4,
    "src": "/root/.ansible/tmp/ansible-tmp-1529035954.8010113-22928023490467/source",
    "state": "file",
    "uid": 0
}
#节点安装libselinux-python后在进行发送测试
[root@localhost ansible]# ansible webB -m copy -a "src=/service/scripts/test.txt dest=/service/scripts/"
webB | SUCCESS => {         #发送成功
    "changed": true,
    "checksum": "972a1a11f19934401291cc99117ec614933374ce",
    "dest": "/service/scripts/test.txt",
    "gid": 0,
    "group": "root",
    "md5sum": "5c9597f3c8245907ea71a89d9d39d08e",
    "mode": "0644",
    "owner": "root",
    "secontext": "system_u:object_r:svc_svc_t:s0",
    "size": 4,
    "src": "/root/.ansible/tmp/ansible-tmp-1529036146.1609693-94270890826089/source",
    "state": "file",
    "uid": 0
}
copy模块拷贝文件夹

特别提示:
如果目标路径里有与我拷贝的文件同名文件的话,会直接覆盖目标路径下的文件

#拷贝/service/scripts/  目录下所有内容到dest的路径下(注意两条命令的对比)
[root@localhost ansible]# ansible webA -m copy -a "src=/service/scripts/ dest=/service/scripts/"
webA | SUCCESS => {
    "changed": true,
    "dest": "/service/scripts/",
    "src": "/service/scripts/"
}
#拷贝/service/scripts目录本身及其内部的所有内容到dest的路径下(注意两条命令的对比)
[root@localhost ansible]# ansible webA -m copy -a "src=/service/scripts dest=/service/scripts/"
webA | SUCCESS => {
    "changed": true,
    "dest": "/service/scripts/",
    "src": "/service/scripts"
}
copy模块自动备份

特别提示:
参数:backup=yes ===>意思是,如果目标路径下,有与我同名但不同内容的文件时,在覆盖前,对目标文件先进行备份。

[root@localhost ~]# ansible webB -m copy -a "src=/service/scripts/ dest=/service/scripts/ backup=yes"
webB | SUCCESS => {
    "changed": true,
    "dest": "/service/scripts/",
    "src": "/service/scripts/"
}
copy模块指定用户和属主
[root@localhost ~]# ansible webA -m copy -a "src=/service/scripts/ dest=/service/scripts/ owner=nobody group=nobody mode=0600"
webA | SUCCESS => {
    "changed": true,
    "dest": "/service/scripts/",
    "src": "/service/scripts/"
}

ansible的script模块批量运行脚本

ansible的script模块能够实现远程服务器批量运行本地的shell脚本。

#操作示例-->远程批量分发并自动部署nginx
#所有被管理端需要挂载光盘,并创建本地yum配置文件
[root@localhost ansible]# mkdir -p /server/scripts
[root@localhost ansible]# cd /server/scripts
[root@localhost scripts]# rz
[root@localhost scripts]# ls | xargs -n1
auto_nginx.sh   #自动安装nginx脚本
fenfa.sh        #批量分发脚本
nginx-1.10.2.tar.gz #nginx源码包
[root@localhost scripts]# cat auto_nginx.sh   #nginx安装脚本
#!/bin/sh
#nginx install shell scripts
test -d /media/cdrom || mkdir -p /media/cdrom
mount /dev/sr0 /media/cdrom &>/dev/null
yum -y install gcc gcc-c++ make pcre pcre-devel zlib zlib-devel openssl  openssl-devel &>/dev/null
test -d /service/scripts || exit 3
cd /service/scripts/
tar xf nginx-1.10.2.tar.gz -C /usr/src/
cd /usr/src/nginx-1.10.2/
./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module &>/dev/null
make &>/dev/null
make install &>/dev/null
exit 0
[root@localhost scripts]# cat fenfa.sh    #源码包和安装脚本的批量分发脚本
#!/bin/sh
#批量分发脚本
Group=$1
ansible $Group -m copy -a "src=/server/scripts/ dest=/service/scripts/ mode=0755"   #无需判断对方有无目标目录,会自动创建
ansible $Group -m script -a "/bin/sh /service/scripts/auto_nginx.sh"
[root@localhost scripts]# sh fenfa.sh all #激活脚本
web2 | CHANGED => {
    "changed": true,
    "dest": "/service/scripts/",
    "src": "/server/scripts/"
}
web1 | CHANGED => {
    "changed": true,
    "dest": "/service/scripts/",
    "src": "/server/scripts/"
}

web2 | CHANGED => {
    "changed": true,
    "rc": 0,
    "stderr": "Shared connection to 192.168.239.136 closed.\r\n",
    "stderr_lines": [
        "Shared connection to 192.168.239.136 closed."
    ],
    "stdout": "",
    "stdout_lines": []
}
web1 | CHANGED => {
    "changed": true,
    "rc": 0,
    "stderr": "Shared connection to 192.168.239.135 closed.\r\n",
    "stderr_lines": [
        "Shared connection to 192.168.239.135 closed."
    ],
    "stdout": "",
    "stdout_lines": []
}
[root@localhost scripts]# 

ansible-playbook的初步使用

ansible客户端

[root@localhost ~]# yum install -y libselinux-python

playbook的使用,playbook可以把ansible的模块进行组合
ln -s /usr/local/python/bin/ansible-playbook /usr/local/bin/

  1. playbook的简单shell模块的使用
[root@localhost scripts]# cat test_shell.yaml  #playbook的执行模板
---         #开头三个小-开头
- hosts: web2   
  tasks:        
  - name: test
    shell: echo "welcome to yunjisuan" >> /tmp/username
  - name: test2
    shell: echo "welcome to yunjisuan" >> /tmp/username
模板说明:
---  #开头必须有三个小-,顶格写
- hosts:   #正文配置代码的第一级,必须有两个空格(-占一个空格位)
- host: web2   #web2是host参数的值,值和hosts:之间要有一个空格
  tasks:        #tasks:表示接下来要执行的具体任务
  - name:     #相对于tasks再多缩进两个格(-占一个空格位),表示属于tasks的下一级
  - name: test  #test只是要执行的具体命令的名字可以随便写。name:后还是有一个空格要注意
    shell:  #表示调用shell模块执行命令相对于tasks仍旧要多缩进两个空格
    shell: echo "xxx" >> xxx     #shell:后边还是要有个空格,需要注意。

执行playbook配置文件

[root@localhost scripts]# ansible-playbook test_shell.yaml #执行playbook配置文件
PLAY [web2] ********************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************
ok: [web2]
TASK [test] ********************************************************************************************************
changed: [web2]
TASK [test2] *******************************************************************************************************
changed: [web2]
PLAY RECAP *********************************************************************************************************
web2                       : ok=3    changed=2    unreachable=0    failed=0   
  1. playbook的简单copy模块的使用
[root@localhost scripts]# echo "welcome to yunjisuan" >> /tmp/test_copy
[root@localhostscripts]# cat test_copy.yaml 
---
- hosts: all
  tasks:
  - name: test copy
    copy: src=/tmp/test_copy dest=/tmp/
[root@localhost scripts]# ansible-playbook /server/scripts/test_copy.yaml 
PLAY [all] *********************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************
ok: [web1]
ok: [web2]
TASK [test copy] ***************************************************************************************************
changed: [web1]
changed: [web2]
PLAY RECAP *********************************************************************************************************
web1                       : ok=2    changed=1    unreachable=0    failed=0   
web2                      : ok=2    changed=1    unreachable=0    failed=0   
  1. playbook使用register输出命令运行结果

我们在用playbook进行ansible模块操作的时候,并没有命令的执行结果输出,默认被隐藏了。
我们可以通过register模块加载输出命令的执行结果

[root@localhost scripts]# cat test_register.yaml 
---
- hosts: all
  tasks:
  - name: test register
    shell: echo "welcome to yunjisuan"
    register: print_result          #将之前命令的输出结果保存在变量print_result里
  - debug: var=print_result         #将变量的值作为debug输出出来。
[root@localhost scripts]# ansible-playbook test_register.yaml 

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

TASK [Gathering Facts] ****************************************************************
ok: [web2]
ok: [web1]

TASK [test register] ******************************************************************
changed: [web2]
changed: [web1]

TASK [debug] **************************************************************************
ok: [web1] => {
    "print_result": {
        "changed": true,
        "cmd": "echo \"welcome to yunjisuan\"",
        "delta": "0:00:00.003203",
        "end": "2018-12-08 01:22:01.287935",
        "failed": false,
        "rc": 0,
        "start": "2018-12-08 01:22:01.284732",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "welcome to yunjisuan",
        "stdout_lines": [
            "welcome to yunjisuan"
        ]
    }
}
ok: [web2] => {
    "print_result": {
        "changed": true,
        "cmd": "echo \"welcome to yunjisuan\"",
        "delta": "0:00:00.003450",
        "end": "2018-12-08 01:22:01.160379",
        "failed": false,
        "rc": 0,
        "start": "2018-12-08 01:22:01.156929",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "welcome to yunjisuan",
        "stdout_lines": [
            "welcome to yunjisuan"
        ]
    }
}

PLAY RECAP ****************************************************************************
web1                       : ok=3    changed=1    unreachable=0    failed=0   
web2                       : ok=3    changed=1    unreachable=0    failed=0   

  1. nginx配置下发并检测
[root@localhost scripts]# vim /tmp/nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    upstream default_server {   
        server 192.168.239.130;     
        server 192.168.239.131;
    }
    server {
        listen       80;
        server_name  www.yunjisuan.com;
        location / {
            proxy_pass http://default_server;  
            proxy_set_header host $host;  
            proxy_set_header x-forward-for $remote_addr;  
        }
    }
}
[root@localhost scripts]# cat test_nginx_conf.yaml 
---
- hosts: all
  tasks:
  - name: copy nginx.conf
    copy: src=/tmp/nginx.conf dest=/usr/local/nginx/conf/ backup=yes
  - name:
    shell: /usr/local/nginx/sbin/nginx -t
    register: nginx_result
  - debug: var=nginx_result
[root@localhost scripts]# ansible-playbook test_nginx_conf.yaml

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

TASK [Gathering Facts] ****************************************************************
ok: [web1]
ok: [web2]

TASK [copy nginx.conf] ****************************************************************
changed: [web1]
changed: [web2]

TASK [shell] **************************************************************************
changed: [web2]
changed: [web1]

TASK [debug] **************************************************************************
ok: [web1] => {
    "nginx_result": {
        "changed": true,
        "cmd": "/usr/local/nginx/sbin/nginx -t",
        "delta": "0:00:00.007621",
        "end": "2018-12-08 01:28:58.493810",
        "failed": false,
        "rc": 0,
        "start": "2018-12-08 01:28:58.486189",
        "stderr": "nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok\nnginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful",
        "stderr_lines": [
            "nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok",
            "nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful"
        ],
        "stdout": "",
        "stdout_lines": []
    }
}
ok: [web2] => {
    "nginx_result": {
        "changed": true,
        "cmd": "/usr/local/nginx/sbin/nginx -t",
        "delta": "0:00:00.008844",
        "end": "2018-12-08 01:28:58.323927",
        "failed": false,
        "rc": 0,
        "start": "2018-12-08 01:28:58.315083",
        "stderr": "nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok\nnginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful",
        "stderr_lines": [
            "nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok",
            "nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful"
        ],
        "stdout": "",
        "stdout_lines": []
    }
}

PLAY RECAP ****************************************************************************
web1                       : ok=4    changed=2    unreachable=0    failed=0   
web2                       : ok=4    changed=2    unreachable=0    failed=0   


playbook的自定义变量和内置变量

  1. 在Playbook中使用自定义变量
[root@localhost scripts]# cat test_vars.yaml 
---
- hosts: all
  vars:         #定义变量
  - name: "yunjisuan"   #第一个name变量
    age: "3"            #第二个age变量
  tasks:
  - name: "{{ name }}"      #{{}}两对大括号引用变量,变量名两头空格
    shell: echo "myname {{ name }},myage {{ age }}"
    register: var_result
  - debug: var=var_result
特别提示:
引用变量需要在双引号中引用。
[root@localhost scripts]# ansible-playbook /server/scripts/test_vars.yaml 
 [WARNING]: Found variable using reserved name: name        #这里提示,name是一个保留的内置变量,我们在自定义时不能用
PLAY [all] ****************************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [web1]
ok: [web2]

TASK [yunjisuan] **********************************************************************
changed: [web1]
changed: [web2]

TASK [debug] **************************************************************************
ok: [web1] => {
    "var_result": {
        "changed": true,
        "cmd": "echo \"myname yunjisuan,myage 3\"",
        "delta": "0:00:00.002816",
        "end": "2018-12-08 09:03:15.150245",
        "failed": false,
        "rc": 0,
        "start": "2018-12-08 09:03:15.147429",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "myname yunjisuan,myage 3",
        "stdout_lines": [
            "myname yunjisuan,myage 3"
        ]
    }
}
ok: [web2] => {
    "var_result": {
        "changed": true,
        "cmd": "echo \"myname yunjisuan,myage 3\"",
        "delta": "0:00:00.002469",
        "end": "2018-12-08 09:03:14.711524",
        "failed": false,
        "rc": 0,
        "start": "2018-12-08 09:03:14.709055",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "myname yunjisuan,myage 3",
        "stdout_lines": [
            "myname yunjisuan,myage 3"
        ]
    }
}

PLAY RECAP ****************************************************************************
web1                       : ok=3    changed=1    unreachable=0    failed=0   
web2                       : ok=3    changed=1    unreachable=0    failed=0   


#我们修改一下name这个变量再发送,就不会出警告了
[root@localhost scripts]# cat test_vars.yaml 
---
- hosts: all
  vars:
  - names: "yunjisuan"
    age: "3"
  tasks:
  - name: "{{ names }}"
    shell: echo "myname {{ names }},myage {{ age }}"
    register: var_result
  - debug: var=var_result
  1. 在playbook中使用ansible内置变量

我们可以使用ansible all -m setup | less查看ansible内置变量

[root@localhost scripts]# cat test_setupvars.yaml 
---
- hosts: all
  gather_facts: True    #使用ansible内置变量
  tasks:
  - name: setup var
    shell: echo "ip {{ ansible_all_ipv4_addresses[0] }} cpu {{ ansible_processor_count }}"
    register: var_result
  - debug: var=var_result
[root@localhost scripts]# 
[root@localhost scripts]# ansible-playbook test_setupvars.yaml 
PLAY [all] ****************************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [web1]
ok: [web2]

TASK [setup var] **********************************************************************
changed: [web2]
changed: [web1]

TASK [debug] **************************************************************************
ok: [web1] => {
    "var_result": {
        "changed": true,
        "cmd": "echo \"ip 192.168.239.135 cpu 1\"",
        "delta": "0:00:00.002772",
        "end": "2018-12-08 09:24:22.913678",
        "failed": false,
        "rc": 0,
        "start": "2018-12-08 09:24:22.910906",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "ip 192.168.239.135 cpu 1",
        "stdout_lines": [
            "ip 192.168.239.135 cpu 1"
        ]
    }
}
ok: [web2] => {
    "var_result": {
        "changed": true,
        "cmd": "echo \"ip 192.168.239.136 cpu 1\"",
        "delta": "0:00:00.002615",
        "end": "2018-12-08 09:24:22.419994",
        "failed": false,
        "rc": 0,
        "start": "2018-12-08 09:24:22.417379",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "ip 192.168.239.136 cpu 1",
        "stdout_lines": [
            "ip 192.168.239.136 cpu 1"
        ]
    }
}

PLAY RECAP ****************************************************************************
web1                       : ok=3    changed=1    unreachable=0    failed=0   
web2                       : ok=3    changed=1    unreachable=0    failed=0   

简单演示一下ansible内置变量的取用方法ansible all -m setup | less

[root@localhost scripts]# cat test_setupvars.yaml
---
- hosts: all
  gather_facts: True    #开启系统变量
  tasks:
  - name: setup var
    shell: echo "ip {{ ansible_all_ipv4_addresses[0] }} cpu {{ ansible_processor_count }}" >> /tmp/test
  - name: setup var2
    shell: echo "time {{ ansible_date_time["date"] }}" >> /tmp/test
    register: var_result
  - debug: var=var_result
[root@localhost scripts]# ansible-playbook test_setupvars.yaml 

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

TASK [Gathering Facts] ****************************************************************
ok: [web1]
ok: [web2]

TASK [setup var] **********************************************************************
changed: [web2]
changed: [web1]

TASK [setup var2] *********************************************************************
changed: [web1]
changed: [web2]

TASK [debug] **************************************************************************
ok: [web1] => {
    "var_result": {
        "changed": true,
        "cmd": "echo \"time 2018-12-08\" >> /tmp/test",
        "delta": "0:00:00.002968",
        "end": "2018-12-08 10:27:53.776367",
        "failed": false,
        "rc": 0,
        "start": "2018-12-08 10:27:53.773399",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "",
        "stdout_lines": []
    }
}
ok: [web2] => {
    "var_result": {
        "changed": true,
        "cmd": "echo \"time 2018-12-08\" >> /tmp/test",
        "delta": "0:00:00.002446",
        "end": "2018-12-08 10:27:53.342519",
        "failed": false,
        "rc": 0,
        "start": "2018-12-08 10:27:53.340073",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "",
        "stdout_lines": []
    }
}

PLAY RECAP ****************************************************************************
web1                       : ok=4    changed=2    unreachable=0    failed=0   
web2                       : ok=4    changed=2    unreachable=0    failed=0   


(1)Python的位置变量

[root@localhost scripts]# python3
Python 3.5.2 (default, Dec  8 2018, 08:18:46) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-23)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [1,3,5]      
>>> a
[1, 3, 5]
>>> a[0]    #出现第一个值
1
>>> a[1]    #出现第二个值
3
>>> a[2]	  #出现第三个值
5
>>> a[3]    #因为只附了三个值,故报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> a[-1]    #显示倒数第一个值
5
>>> a[-2]	   #显示倒数第二个值
3
>>> a[-3]    #显示倒数第三个值
1

#Ctrl-D 退出

(2)Python的字典(键值key-values)
一个键可以对应一个值,也可以对应多个值,但不能多个键对应一个值

[root@localhost scripts]# python3
Python 3.5.2 (default, Dec  8 2018, 08:18:46) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-23)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = {"user":[1,2,3,4,5],"user1":123456}        #  字典用{},键值对:"用户名":密码
>>> a["user"]
[1, 2, 3, 4, 5]
>>> a["user"][0] 
1
>>> a["user1"]
123456

#Ctrl-D 退出

Playbook下发可变配置文件

配置文件如果使用copy模块去下发的话,那配置都是一样的;
如果下发的配置文件里有可变的配置,需要用到template模块。

  1. 利用template模块下发可变的配置文件
[root@localhost scripts]# cat /tmp/test 
my name is {{ myname }} #自定义变量
my name is {{ ansible_all_ipv4_addresses[0] }}  #系统变量
[root@localhost scripts]# cat test_filevars.yaml 
---
- hosts: all
  gather_facts: True    #开启系统变量
  vars:
  - myname: "yunjisuan" #自定义变量
  tasks:
  - name: template test
    template: src=/tmp/test dest=/root/test #使用template下发可变配置文件
[root@localhost scripts]# ansible-playbook test_filevars.yaml

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

TASK [Gathering Facts] ****************************************************************
ok: [web1]
ok: [web2]

TASK [template test] ******************************************************************
changed: [web2]
changed: [web1]

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


  1. 下发配置文件里面使用判断语法
[root@localhost scripts]# cat /tmp/if.j2 
{% if PORT %}       #if PORT存在   {% if 变量 %}    如果PORT变量为真,就赋值ip=0.0.0.0:{{ PORT }}
ip=0.0.0.0:{{ PORT }}
{% else %}          #否则的话(PORT变量为空),就赋值ip=0.0.0.0:80
ip=0.0.0.0:80
{% endif %}         #结尾
[root@localhost scripts]# cat test_ifvars.yaml 
---
- hosts: all
  gather_facts: True    #开启系统内置变量
  vars:
  - PORT: 90        #自定义变量
  tasks:
  - name: jinja2 if test         #jinja2是j2的全称
    template: src=/tmp/if.j2 dest=/root/test
[root@localhost scripts]# ansible-playbook test_ifvars.yaml
PLAY [all] ****************************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [web2]
ok: [web1]

TASK [jinja2 if test] *****************************************************************
changed: [web2]
changed: [web1]

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

如果我们将变量PORT值为空的话,就会是另外的结果

[root@localhost scripts]# cat test_ifvars.yaml 
---
- hosts: all
  gather_facts: True
  vars:
  - PORT:       #置空
  tasks:
  - name: jinja2 if test
    template: src=/tmp/if.j2 dest=/root/test
[root@localhost scripts]# ansible-playbook test_ifvars.yaml

Playbook的notify通知和下发nginx配置

#实战下发可执行动作的可变的nginx配置文件
[root@localhost scripts]# head -1 /tmp/nginx.j2 
worker_processes  {{ ansible_processor_count }};    #可变的参数
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    upstream default_server {
        server 192.168.239.130;
        server 192.168.239.131;
    }
    server {
        listen       80;
        server_name  www.yunjisuan.com;
        location / {
            proxy_pass http://default_server;
            proxy_set_header host $host;
            proxy_set_header x-forward-for $remote_addr;
        }
    }
}

[root@localhost scripts]# cat test_nginxvars.yaml 
---
- hosts: all
  gather_facts: True    #开启系统内置变量
  tasks:
  - name: nginx conf
    template: src=/tmp/nginx.j2 dest=/usr/local/nginx/conf/nginx.conf backup=yes  #对对方电脑做出改变才会触发notify,未做出改变不会触发notify
    notify:
    - reload nginx  #下发通知给handlers模块执行名字叫做reload nginx的动作
  handlers: #定义动作
  - name: reload nginx  #动作的名字
    shell: /usr/local/nginx/sbin/nginx -s reload
  - name: stop nginx  
    shell: /usr/local/nginx/sbin/nginx -s stop
  - name: start nginx  
    shell: /usr/local/nginx/sbin/nginx
[root@localhost scripts]# ansible-playbook test_nginxvars.yaml 

使用roles标准化Playbook

roles功能可以用来规范playbook的编写,可以降低耦合度

  1. 创建所需要的roles原型目录结构
#创建roles基本原型的目录结构
创建目录/myroles/
[root@ansible myroles]# tree /myroles/
 /myroles/
 ├── nginx.yaml	 #入口触发配置文件
 └── roles 	#playbook的原型配置目录
	 └── nginx 	#nginx相关模组配置目录
		 ├── files 	#copy模块和script模块的参数src默认会从这个文件夹查找
		 ├── handlers 	#用来存放notify的
		 ├── tasks 	#用来存放ansible模块任务的
		 ├── templates	 #用来存放j2的
		 └── vars 	#用来存放变量的

 7 directories, 1 file
 #入口触发配置文件
 [root@ansible myroles]# cat /myroles/nginx.yaml
 ---
 - hosts: all #执行的主机范围
 gather_facts: True #开启系统内置变量
 roles: #启用roles原型配置
 - nginx #执行nginx原型模组

  1. roles中tasks任务编排模组的使用
 #在nginx模组添加tasks任务配置文件
 [root@ansible myroles]# cat roles/nginx/tasks/main.yaml
 ---
 - name: check alived #任务1的名字
   ping: #执行ping模块
 - name: #任务2的名字
   shell: ls / #执行shell模块
   register: ls_result #将执行结果保存给变量
 - debug: var=ls_result #变量的值赋值给debug进行输出

 #完成后的目录结构如下所示
 [root@ansible myroles]# tree /myroles/
 /myroles/
 ├── nginx.yaml #nginx模组入口配置文件
 └── roles
 	└── nginx #nginx原型模组目录
		 ├── files
		 ├── handlers
		 ├── tasks
		 │ └── main.yaml #nginx模组的tasks任务配置文件
		 ├── templates
		 └── vars

7 directories, 2 files
  1. 执行简单的roles任务模型
#执行nginx入口配置文件
[root@ansible myroles]# ansible-playbook nginx.yaml

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

 TASK [Gathering Facts]
**************************************************************************
**************
 ok: [webA]
 ok: [webB]

 TASK [nginx : check alived]
**************************************************************************
*********
 ok: [webA]
 ok: [webB]

 TASK [nginx : shell]
**************************************************************************
****************
 changed: [webA]
 changed: [webB]

TASK [nginx : debug]
**************************************************************************
****************
 ok: [webA] => {
 "ls_result": {
 "changed": true,
 "cmd": "ls /",
 "delta": "0:00:00.002805",
 "end": "2018-06-21 11:52:29.343592",
 "failed": false,
 "rc": 0,
 "start": "2018-06-21 11:52:29.340787",
 "stderr": "",
 "stderr_lines": [],
 "stdout":
"bin\nboot\ndev\netc\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroo\nroot\n
run\nsbin\nservice\nsrv\nsys\ntmp\nusr\nvar",
 "stdout_lines": [
 "bin",
 "boot",
 "dev",
 "etc",
 "home",
 "lib",
 "lib64",
 "media",
 "mnt",
 "opt",
 "proc",
 "roo",
 "root",
 "run",
 "sbin",
 "service",
 "srv",
 "sys",
 "tmp",
 "usr",
 "var"
 ]
 }
 }
 ok: [webB] => {
 "ls_result": {
 "changed": true,
 "cmd": "ls /",
 "delta": "0:00:00.002708",
 "end": "2018-06-21 11:52:29.359754",
 "failed": false,
 "rc": 0,
 "start": "2018-06-21 11:52:29.357046",
 "stderr": "",
 "stderr_lines": [],
 "stdout":
"bin\nboot\ndev\netc\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroo\nroot\n
run\nsbin\nservice\nsrv\nsys\ntmp\nusr\nvar",
 "stdout_lines": [
 "bin",
 "boot",
 "dev",
 "etc",
 "home",
 "lib",
 "lib64",
 "media",
 "mnt",
 "opt",
 "proc",
 "roo",
 "root",
 "run",
 "sbin",
 "service",
 "srv",
 "sys",
 "tmp",
 "usr",
 "var"
 ]
 }
 }

 PLAY RECAP
**************************************************************************
**************************
 webA : ok=4 changed=1 unreachable=0
failed=0
 webB : ok=4 changed=1 unreachable=0
failed=0

ansible-playbook执行入口配置文件nginx.yaml后,它会自动在roles目录下查找
nginx目录并进入后查找tasks任务目录并执行main.yaml的任务配置文件。
其实,这个roles的操作等效于以下配置

#本配置和之前的roles配置等效
[root@ansible myroles]# cat /service/scripts/test.yaml
---
- hosts: all
  gather_facts: True
  tasks: #其实roles的本质就是将tasks任务单独写了。 
- name: check alived #并在入口文件里追加了roles去查找tasks配置文件路径
 ping:
- name:
  shell: ls /
  register: ls_result
- debug: var=ls_result
  1. roles中vars自定义变量模组的使用
 #创建自定义变量vars模组的配置文件
[root@ansible myroles]# cat roles/nginx/vars/main.yaml
 ---
my_name: yunjisuan
phone: 1800000000
[root@ansible myroles]# cat roles/nginx/tasks/main.yaml
---
- name: check alived
  ping:
- name:
  shell: ls /
  register: ls_result
- debug: var=ls_result
- name: #添加对变量引用的任务编排
  shell: echo my phone is {{ phone }}
  register: echo_result
- debug: var=echo_result
[root@ansible myroles]# ansible-playbook nginx.yaml #执行入口配置文件
  1. 使用copy,script模块的标准化
    roles模型里使用copy,script模块,默认从roles/nginx/files这里面找
[root@ansible myroles]# cat roles/nginx/files/test
welcome to yunjisuan
[root@ansible myroles]# cat roles/nginx/files/test.sh
echo "aaa" >> /tmp/test
[root@ansible myroles]# chmod +x roles/nginx/files/test.sh
[root@ansible myroles]# cat roles/nginx/tasks/main.yaml
---
- name: check alived
  ping:
- name:
  shell: ls /
  register: ls_result
- debug: var=ls_result
- name:
  shell: echo my phone is {{ phone }}
  register: echo_result
- debug: var=echo_result
- name: #添加copy模块
  copy: src=test dest=/root/
- name: #添加script模块(自动在目标IP机器上执行脚本)
  script: test.sh

[root@ansible myroles]# ansible-playbook nginx.yaml
  1. roles中template模块的使用
    roles模型里使用template模块,默认从roles/nginx/template里面找
[root@ansible myroles]# cat roles/nginx/templates/test.j2
myname is {{ my_name }},my phone is {{ phone }} #引用自定义变量
my ipaddress is {{ansible_all_ipv4_addresses[0]}} #引用内置变量
[root@ansible myroles]# cat roles/nginx/tasks/main.yaml
---
- name: check alived
  ping:
- name:
  shell: ls /
  register: ls_result
- debug: var=ls_result
- name:
  shell: echo my phone is {{ phone }}
  register: echo_result
- debug: var=echo_result
- name:
  copy: src=test dest=/root/
- name:
  script: test.sh
- name:
  template: src=test.j2 dest=/root/test2 #下发可变配置文件
[root@ansible myroles]# ansible-playbook nginx.yaml
  1. roles中notify模块的使用
    roles使用notify模块,默认从roles/nginx/handles里面找
[root@ansible myroles]# cat roles/nginx/handlers/main.yaml
---
- name: start_nginx #定义handlers的动作类型
  shell: /usr/local/nginx/sbin/nginx
- name: stop_nginx #定义handlers的动作类型
  shell: /usr/local/nginx/sbin/nginx -s stop
- name: reload_nginx #定义handlers的动作类型
  shell: /usr/local/nginx/sbin/nginx -s reload

[root@ansible myroles]# cat roles/nginx/tasks/main.yaml
---
- name: check alived
  ping:
- name:
  shell: ls /
  register: ls_result
- debug: var=ls_result
- name:
  shell: echo my phone is {{ phone }}
  register: echo_result
- debug: var=echo_result
- name:
  copy: src=test dest=/root/
- name:
  script: test.sh
- name:
  template: src=test.j2 dest=/root/test2
  notify: start_nginx #执行template任务后下发通知给handlers执行start_nginx
[root@ansible myroles]# ansible-playbook nginx.yaml

特别提示:
notify下发通知只有当之前的任务造成了变化那么才会被执行,如果没有发生任何改变,则notify不会被执行。例如:

#tasks任务造成改变,触发notify
[root@ansible myroles]# cat /tmp/test.yaml
---
- hosts: webA
  gather_facts: True
  tasks:
- name:
  copy: src=/tmp/test dest=/root/ #这步造成目标改变才能出发notify
  notify: start_nginx
  handlers:
- name: start_nginx
  shell: /usr/local/nginx/sbin/nginx
[root@ansible myroles]# ansible-playbook /tmp/test.yaml

PLAY [webA]
**************************************************************************
*************************

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

TASK [copy]
**************************************************************************
*************************
changed: [webA] #发生了改变

RUNNING HANDLER [start_nginx] #触发notify
**************************************************************************
*******
 changed: [webA]

 PLAY RECAP
**************************************************************************
**************************
 webA : ok=3 changed=2 unreachable=0
failed=0
#我们再次执行/tmp/test.yaml
[root@ansible myroles]# ansible-playbook /tmp/test.yaml

PLAY [webA]
**************************************************************************
*************************

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

TASK [copy]
**************************************************************************
*************************
ok: [webA] #没有造成任务改变,未触发notify通知

PLAY RECAP
**************************************************************************
**************************
webA : ok=2 changed=0 unreachable=0
failed=0

赞(52) 打赏
未经允许不得转载:优客志 » 系统运维
分享到:

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏