ansible小结(一)ansible的安装

常用的自动化运维工具:

CFengine 最早的自动化运维工具

Chef

Puppet       –基于Ruby开发,采用C/S架构,扩展性强,基于SSL认证 (远程命令执行比较短板)

SaltStack   –基于Python开发,采用C/S架构,比Puppet更轻量级,配置YMAL,配置更为简单

Ansible      –基于Python开发,无客户端,更轻量级,配置语言也是采用YMAL,无守护进程 (目前市场上运用较多的还是puppet和saltstack)

 一、简介

Ansible 是一个配置管理和应用部署工具,功能类似于目前业界的配置管理工具 Chef,Puppet,Saltstack。Ansible 是通过 Python 语言开发。Ansible 平台由 Michael DeHaan 创建,他同时也是知名软件 Cobbler 与 Func 的作者。Ansible 的第一个版本发布于 2012 年 2 月。Ansible 默认通过 SSH 协议管理机器,所以 Ansible 不需要安装客户端程序在服务器上。您只需要将 Ansible 安装在一台服务器,在 Ansible 安装完后,您就可以去管理控制其它服务器。不需要为它配置数据库,Ansible 不会以 daemons 方式来启动或保持运行状态。Ansible 可以实现以下目标:

 

 

  • 自动化部署应用
  • 自动化管理配置
  • 自动化的持续交付
  • 自动化的(AWS)云服务管理。

 

根据 Ansible 官方提供的信息,当前使用 Ansible 的用户有:evernote、rackspace、NASA、Atlassian、twitter 等。

 

注:以上简介来自于ibm developerworks 中国的介绍。

二、Ansible的安装

1、yum源安装

以centos为例,默认在源里没有ansible,不过在fedora epel源里有ansible,配置完epel 源后,可以直接通过yum 进行安装。这里以centos6.8为例:

yum install http://mirrors.sohu.com/fedora-epel/6/x86_64/epel-release-6-8.noarch.rpm
yum install ansible

     2、apt-get安装

在ubuntu及其衍生版中,可以通过增加ppa源进行apt-get安装,具体如下:

 sudo apt-get install software-properties-common
 sudo apt-add-repository ppa:ansible/ansible
 sudo apt-get update
 sudo apt-get install ansible

     3、源码安装

源码安装需要python2.6以上版本,其依赖模块paramiko、PyYAML、Jinja2、httplib2、simplejson、pycrypto模块,以上模块可以通过pip或easy_install 进行安装,不过本部分既然提到的是源码安装,主要针对的无法上外网的情况下,可以通过pypi 站点搜索以上包,下载后通过python setup.py install 进行安装。

最后通过githubpypi上下载ansible源码包,通过python setup.py install 安装即可。由于安装过程相对简单,这里略过,主要介绍安装后,可能遇到的问题。

 python setup.py install
 libyaml is not found or a compiler error: forcing --without-libyaml
 (if libyaml is installed correctly, you may need to
 specify the option --include-dirs or uncomment and
 modify the parameter include_dirs in setup.cfg)
 running install_lib
 running install_egg_info
 Removing /usr/lib64/python2.6/site-packages/PyYAML-3.11-py2.6.egg-info
 Writing /usr/lib64/python2.6/site-packages/PyYAML-3.11-py2.6.egg-info

在centos6.8系统中,可以通过yum -y install libyaml 包解决,或者从ISO文件中提供该包,通过rpm -ivh进行安装。

b、安装完ansible是,报错如下:

[root@361way.com ansible-1.9.1]# ansible -h
Traceback (most recent call last):
File "/usr/local/src/ansible-devel/bin/ansible", line 36, in <module>
 from ansible.runner import Runner
File "/usr/local/src/ansible-devel/lib/ansible/runner/__init__.py", line 62, in <module>
 from Crypto.Random import atfork
File "/usr/lib64/python2.6/site-packages/Crypto/Random/__init__.py", line 29, in <module>
from Crypto.Random import _UserFriendlyRNG
File "/usr/lib64/python2.6/site-packages/Crypto/Random/_UserFriendlyRNG.py", line 38, in <module>
from Crypto.Random.Fortuna import FortunaAccumulator
File "/usr/lib64/python2.6/site-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 39, in <module>
import FortunaGenerator
File "/usr/lib64/python2.6/site-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 34, in <module>
from Crypto.Util.number import ceil_shift, exact_log2, exact_div
File "/usr/lib64/python2.6/site-packages/Crypto/Util/number.py", line 56, in <module>
if _fastmath is not None and not _fastmath.HAVE_DECL_MPZ_POWM_SEC:
AttributeError: \'module\' object has no attribute \'HAVE_DECL_MPZ_POWM_SEC\'

import paramiko包时,报错如下:

>>> import paramiko
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.6/site-packages/paramiko/__init__.py", line 69, in <module>
from transport import randpool, SecurityOptions, Transport
File "/usr/lib/python2.6/site-packages/paramiko/transport.py", line 32, in <module>
from paramiko import util
File "/usr/lib/python2.6/site-packages/paramiko/util.py", line 32, in <module>
from paramiko.common import *
File "/usr/lib/python2.6/site-packages/paramiko/common.py", line 98, in <module>
from rng import StrongLockingRandomPool
File "/usr/lib/python2.6/site-packages/paramiko/rng.py", line 22, in <module>
from Crypto.Util.randpool import RandomPool as _RandomPool
File "/usr/lib64/python2.6/site-packages/Crypto/Util/randpool.py", line 30, in <module>
import Crypto.Random
File "/usr/lib64/python2.6/site-packages/Crypto/Random/__init__.py", line 29, in <module>
from Crypto.Random import _UserFriendlyRNG
File "/usr/lib64/python2.6/site-packages/Crypto/Random/_UserFriendlyRNG.py", line 38, in <module>
19.    from Crypto.Random.Fortuna import FortunaAccumulator
20.  File "/usr/lib64/python2.6/site-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 39, in <module>
21.    import FortunaGenerator
22.  File "/usr/lib64/python2.6/site-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 34, in <module>
23.    from Crypto.Util.number import ceil_shift, exact_log2, exact_div
24.  File "/usr/lib64/python2.6/site-packages/Crypto/Util/number.py", line 56, in <module>
25.    if _fastmath is not None and not _fastmath.HAVE_DECL_MPZ_POWM_SEC:
26.AttributeError: \'module\' object has no attribute \'HAVE_DECL_MPZ_POWM_SEC\'

经网上查找,确认为pycrypto包安装时依赖的GMP版本不对的问题,具体可以通过以下步骤验证:

[root@361way.com pycrypto-2.6.1]# python setup.py build
running build
running build_py
running build_ext
running build_configure
warning: GMP or MPIR library not found; Not building Crypto.PublicKey._fastmath.

解决方法:

打开 /usr/lib64/python2.6/site-packages/Crypto/Util/number.py 文件,可以 看到 56 行上的注释说明,要求 libgmp 为 v5 以上版本。而系统现有版本为 4.1.4,把以下两行暂时注释掉,Ansible 执行正常。

if _fastmath is not None and not _fastmath.HAVE_DECL_MPZ_POWM_SEC:
     _warn("Not using mpz_powm_sec.  You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.", PowmInsecureWarning)

不过,此方法只是临时加以解决,更好的方式是去将 libgmp 升级到符合要求的版本

c、执行时报错

[root@361way.com src]# ansible test -m raw -a \'uptime\'
10.212.52.14 | FAILED => to use the \'ssh\' connection type with passwords, you must install the sshpass program
10.212.52.16 | FAILED => to use the \'ssh\' connection type with passwords, you must install the sshpass program

安装sshpass程序。默认源里没有,我这里选择直接从sohu源里下载安装。

三、Ansible的配置与验证

这里以pypi上下载的源码内有一个examles包,可以将使用该示例文件做为默认配置,具体如下:

[root@361way.com ansible-1.9.1]# mkdir -p /etc/ansible
[root@361way.com ansible-1.9.1]# cp -rp examples/*  /etc/ansible/
[root@361way.com ansible-1.9.1]# cd /etc/ansible/

使用默认示例配置文件后,编辑/etc/ansible/hosts文件,通过以下方式验证ansible是否可用:

[root@361way.com ~]# cat /etc/ansible/hosts
[test]
10.212.52.252 ansible_ssh_user=root ansible_ssh_pass=361way.com
10.212.52.14 ansible_ssh_user=root ansible_ssh_pass=abc123
10.212.52.16 ansible_ssh_user=root ansible_ssh_pass=91it.org

以上的配置中,我配置了一个test组,该组下有三台主机,三台都使用root验证,三台的密码分别是361way.com、abc123、91it.org 。

注:后面的用户和密码项是非必须的,在配置key认证的情况下,不使用密码也可以直接操作 。未使用key的,也可以在ansible通过 -k参数在操作前询问手动输入密码。

[root@361way.com ~]# ansible test -a \'uptime\'
10.212.52.252 | success | rc=0 >>
18:01pm  up 21 days  3:24,  3 users,  load average: 0.39, 0.38, 0.35
10.212.52.16 | success | rc=0 >>
18:09pm  up 329 days  1:01,  2 users,  load average: 0.08, 0.03, 0.05
10.212.52.14 | success | rc=0 >>
18:08pm  up 329 days  0:23,  2 users,  load average: 0.06, 0.06, 0.05

执行以上指令后,有结果输出,证明安装成功。

 

ansible小结(二)ansible架构

一、Ansible基本架构

Ansible 是一个模型驱动的配置管理器,支持多节点发布、远程任务执行。默认使用 SSH 进行远程连接。无需在被管理节点上安装附加软件,可使用各种编程语言进行扩展。

上图为ansible的基本架构,从上图可以了解到其由以下部分组成:

  • 核心:ansible
  • 核心模块(Core Modules):这些都是ansible自带的模块 
  • 扩展模块(Custom Modules):如果核心模块不足以完成某种功能,可以添加扩展模块
  • 插件(Plugins):完成模块功能的补充
  • 剧本(Playbooks):ansible的任务配置文件,将多个任务定义在剧本中,由ansible自动执行
  • 连接插件(Connectior Plugins):ansible基于连接插件连接到各个主机上,虽然ansible是使用ssh连接到各个主机的,但是它还支持其他的连接方法,所以需要有连接插件
  • 主机群(Host Inventory):定义ansible管理的主机

二、ansible工作原理

ansible-principleansible-principle2

以上是从网上找到的两张ansible工作原理图,两张图基本都是在架构图的基本上进行的拓展。从上面的图上可以了解到:

1、管理端支持local 、ssh、zeromq 三种方式连接被管理端,默认使用基于ssh的连接---这部分对应基本架构图中的连接模块;

2、可以按应用类型等方式进行Host Inventory(主机群)分类,管理节点通过各类模块实现相应的操作---单个模块,单条命令的批量执行,我们可以称之为ad-hoc;

3、管理节点可以通过playbooks 实现多个task的集合实现一类功能,如web服务的安装部署、数据库服务器的批量备份等。playbooks我们可以简单的理解为,系统通过组合多条ad-hoc操作的配置文件 。

 

三、ansible的七个命令

安装完ansible后,发现ansible一共为我们提供了七个指令:ansible、ansible-doc、ansible-galaxy、ansible-lint、ansible-playbook、ansible-pull、ansible-vault 。这里我们只查看usage部分,详细部分可以通过 “指令 -h”  的方式获取。

1、ansible 

[root@localhost ~]# ansible -h
Usage: ansible <host-pattern> [options]

ansible是指令核心部分,其主要用于执行ad-hoc命令,即单条命令。默认后面需要跟主机和选项部分,默认不指定模块时,使用的是command模块。如:

[root@localhost ~]# ansible 127.0.0.1 -a \'date\'
127.0.0.1 | SUCCESS | rc=0 >>
Sun May 28 11:00:40 CST 2017

默认使用的模块是可以在ansible.cfg 中进行修改的。ansible命令下的参数部分解释如下

参数:
  -a \'Arguments\', --args=\'Arguments\' 命令行参数
  -m NAME, --module-name=NAME 执行模块的名字,默认使用 command 模块,所以如果是只执行单一命令可以不用 -m参数
  -i PATH, --inventory=PATH 指定库存主机文件的路径,默认为/etc/ansible/hosts.
  -u Username, --user=Username 执行用户,使用这个远程用户名而不是当前用户
  -U --sud-user=SUDO_User  sudo到哪个用户,默认为 root
  -k --ask-pass  登录密码,提示输入SSH密码而不是假设基于密钥的验证
  -K --ask-sudo-pass 提示密码使用sudo
  -s --sudo sudo运行
  -S --su 用 su 命令
  -l  --list 显示所支持的所有模块
  -s --snippet 指定模块显示剧本片段
  -f  --forks=NUM 并行任务数。NUM被指定为一个整数,默认是5。 #ansible testhosts -a "/sbin/reboot" -f 10 重启testhosts组的所有机器,每次重启10台
  --private-key=PRIVATE_KEY_FILE 私钥路径,使用这个文件来验证连接
  -v --verbose 详细信息
  all  针对hosts 定义的所有主机执行
  -M MODULE_PATH, --module-path=MODULE_PATH 要执行的模块的路径,默认为/usr/share/ansible/
  --list-hosts 只打印有哪些主机会执行这个 playbook 文件,不是实际执行该 playbook 文件
  -o --one-line 压缩输出,摘要输出.尝试一切都在一行上输出。
  -t Directory, --tree=Directory 将内容保存在该输出目录,结果保存在一个文件中在每台主机上。
  -B 后台运行超时时间
  -P 调查后台程序时间
  -T Seconds, --timeout=Seconds 时间,单位秒s
  -P NUM, --poll=NUM 调查背景工作每隔数秒。需要- b
  -c Connection, --connection=Connection  连接类型使用。可能的选项是paramiko(SSH),SSH和地方。当地主要是用于crontab或启动。
  --tags=TAGS 只执行指定标签的任务    例子:ansible-playbook test.yml --tags=copy  只执行标签为copy的那个任务
  --list-hosts 只打印有哪些主机会执行这个 playbook 文件,不是实际执行该 playbook 文件
  --list-tasks 列出所有将被执行的任务
  -C, --check 只是测试一下会改变什么内容,不会真正去执行;相反,试图预测一些可能发生的变化
  --syntax-check 执行语法检查的剧本,但不执行它
  -l SUBSET, --limit=SUBSET 进一步限制所选主机/组模式  --limit=192.168.0.15 只对这个ip执行
  --skip-tags=SKIP_TAGS 只运行戏剧和任务不匹配这些值的标签  --skip-tags=copy_start
  -e EXTRA_VARS, --extra-vars=EXTRA_VARS  额外的变量设置为键=值或YAML / JSON
        #cat update.yml
        ---
        - hosts: {{ hosts }}
          remote_user: {{ user }}
        ..............
        #ansible-playbook update.yml --extra-vars "hosts=vipers user=admin"   传递{{hosts}}、{{user}}变量,hosts可以是 ip或组名
  -l,--limit 对指定的 主机/组 执行任务  --limit=192.168.0.10,192.168.0.11 或 -l 192.168.0.10,192.168.0.11 只对这个2个ip执行任务

2、ansible-doc

[root@localhost ~]# ansible-doc -h
Usage: ansible-doc [options] [module...]

Options:
  -a, --all             Show documentation for all modules
  -h, --help            show this help message and exit
  -l, --list            List available modules
  -M MODULE_PATH, --module-path=MODULE_PATH
                        specify path(s) to module library (default=None)
  -s, --snippet         Show playbook snippet for specified module(s)
  -v, --verbose         verbose mode (-vvv for more, -vvvv to enable
                        connection debugging)
  --version             show program\'s version number and exit

该指令用于查看模块信息,常用参数有两个-l 和 -s ,具体如下

//列出所有已安装的模块
# ansible-doc  -l
//查看具体某模块的用法,这里如查看command模块
# ansible-doc  -s command

3、ansible-galaxy

ansible-galaxy 指令用于方便的从https://galaxy.ansible.com/ 站点下载第三方扩展模块,我们可以形象的理解其类似于centos下的yum、python下的pip或easy_install 。如下示例:

[root@localhost ~]# ansible-galaxy install aeriscloud.docker
- downloading role \'docker\', owned by aeriscloud
- downloading role from https://github.com/AerisCloud/ansible-docker/archive/v1.0.0.tar.gz
- extracting aeriscloud.docker to /etc/ansible/roles/aeriscloud.docker
- aeriscloud.docker was installed successfully

这个安装了一个aeriscloud.docker组件,前面aeriscloud是galaxy上创建该模块的用户名,后面对应的是其模块。在实际应用中也可以指定txt或yml 文件进行多个组件的下载安装。这部分可以参看官方文档

4、ansible-lint

ansible-lint是对playbook的语法进行检查的一个工具。用法是ansible-lint playbook.yml 。

5、ansible-playbook

该指令是使用最多的指令,其通过读取playbook 文件后,执行相应的动作,这个后面会做为一个重点来讲。

6、ansible-pull

该指令使用需要谈到ansible的另一种模式---pull 模式,这和我们平常经常用的push模式刚好相反,其适用于以下场景:你有数量巨大的机器需要配置,即使使用非常高的线程还是要花费很多时间;你要在一个没有网络连接的机器上运行Anisble,比如在启动之后安装。这部分也会单独做一节来讲。

7、ansible-vault

ansible-vault主要应用于配置文件中含有敏感信息,又不希望他能被人看到,vault可以帮你加密/解密这个配置文件,属高级用法。主要对于playbooks里比如涉及到配置密码或其他变量时,可以通过该指令加密,这样我们通过cat看到的会是一个密码串类的文件,编辑的时候需要输入事先设定的密码才能打开。这种playbook文件在执行时,需要加上 –ask-vault-pass参数,同样需要输入密码后才能正常执行。具体该部分可以参查官方博客

 

ansible小结(三)Inventory与Patterns

Ansible的Inventory文件,可以理解为saltstack中的salt-key中的所有minion的列表以及用户自定义的nodegroup的概念,默认情况下这个文件是/etc/ansible/hosts ,后面还会讲到Dynamic Inventory,本节主要讲静态主机群部分。Patterns(模式)部分我们可以理解为正则表达式,通过Patterns我们可以匹配Inventory分组中的部分主机。

一、Hosts and Groups(主机与组)

对于/etc/ansible/hosts最简单的定义格式像下面:

1、简单的主机和组

mail.361way.com
[webservers]
web1.361way.com
web2.361way.com [dbservers]
db1.361way.com
db2.361way.com

a、中括号中的名字代表组名,你可以根据你自己的需求将庞大的主机分成具有标识的组,如上面我分了两个组webservers和dbservers组;

b、主机(hosts)部分可以使用域名、主机名、IP地址表示;当然使用前两者时,也需要主机能反解析到相应的IP地址,一般此类配置中多使用IP地址;

2、端口与别名

如果某些主机的SSH运行在自定义的端口上,ansible使用Paramiko进行ssh连接时,不会使用你SSH配置文件中列出的端口,但是如果修改ansible使用openssh进行ssh连接时将会使用:

192.168.0.10:5309

假如你想要为某些静态IP设置一些别名,类似于SaltStack中minion配置文件中id的参数配置。你可以这样做:

jumper ansible_ssh_port = 5555 ansible_ssh_host = 192.168.1.50

3、指定主机范围

[webservers]
www[01:50].361way.com
[databases]
db-[a:f].91it.org

上面指定了从web1到web50,webservers组共计50台主机;databases组有db-a到db-f共6台主机

4、使用主机变量

以下是Hosts部分中经常用到的变量部分

ansible_ssh_host # 要连接的主机名
ansible_ssh_port # 端口号默认是22
ansible_ssh_user # ssh连接时默认使用的用户名
ansible_ssh_pass # ssh连接时的密码
ansible_sudo_pass # 使用sudo连接用户是的密码
ansible_ssh_private_key_file # 秘钥文件如果不想使用ssh-agent管理时可以使用此选项
ansible_shell_type # shell的类型默认sh
ansible_connection # SSH 连接的类型: local , ssh , paramiko在 ansible 1.2 之前默认是 paramiko ,后来智能选择,优先使用基于 ControlPersist 的 ssh (支持的前提)
ansible_python _ interpreter #用来指定 python 解释器的路径,同样可以指定ruby 、perl 的路径

示例

[test]
10.212.52.252 ansible_ssh_user=root ansible_ssh_pass=\'361way.com\'
10.212.52.14 ansible_ssh_user=test1 ansible_ssh_pass=\'91it.org\'
10.212.52.16 ansible_ssh_user=test2 ansible_ssh_port=7788 ansible_ssh_pass=\'123456\'

上面的示例中指定了三台主机,三台主机的用密码分别是361way.com、91it.org、123456,指定的ssh连接的用户名分别为root、test1、test2,ssh 端口分别为22、22、7788 ,这样在ansible命令执行的时候就不用再指令用户和密码等了,执行结果如下:

[root@361way.com ~]# ansible test -a \'uptime\'
10.212.52.252 | success | rc=0 >>
01:34am  up 23 days 10:57,  2 users,  load average: 0.42, 0.39, 0.41
10.212.52.16 | success | rc=0 >>
01:41am  up 331 days  8:33,  2 users,  load average: 0.00, 0.01, 0.05
10.212.52.14 | success | rc=0 >>
01:40am  up 331 days  7:55,  2 users,  load average: 0.09, 0.03, 0.05

5、组内变量

变量也可以通过组名,应用到组内的所有成员:

[test]
host1
host2
[test:vars]
ntp_server=ntp.361way.com
proxy=proxy.361way.com

上面test组中包含两台主机,通过对test组指定vars变更,相应的host1和host2相当于相应的指定了ntp_server和proxy变量参数值 。

6、组的包含与组内变量

[hangzhou]
host1
host2
[jiaxing]
host2
host3
[zhejiang:children]
hangzhou
jiaxing
[zhejiang:vars]
some_server=foo.southeast.example.com
halon_system_timeout=30
self_destruct_countdown=60
escape_pods=2
[china:children]
zhejiang
henan
shandong
hebei

如上面的示例中,我指定了杭州组我有host1、hosts2;嘉兴组我有host3、host4主机;我又指定了一个组浙江组,同时包含杭州和嘉兴;同时为该组内的所有主机指定了四个vars变量。后面我又设定了一个组中国组,包含浙江、河南、山东、河北

注:由于vars变量在ansible ad-hoc部分中基本用不到,主要用在ansible-playbook中,后面的章节部分也会提到。

以上部分基本上是完全按照官方Inventory 文档部分进行了翻译和微小的变化。英文感觉还可以的可以直接查看官方页面。

二、Patterns(主机与组正则匹配部分)

把Patterns 直接理解为正则实际是不完全准确的,正常的理解为patterns意味着在ansible中管理哪些主机,也可以理解为,要与哪台主机进行通信。在探讨这个问题之前我们先看下ansible的用法:

ansible <pattern_goes_here> -m <module_name> -a <arguments>

直接上一个示例:

ansible webservers -m service -a "name=httpd state=restarted"

这里是对webservers 组或主机重启httpd服务 ,其中webservers 就是Pattern部分。而之所以上面我说Pattern(模式)可以理解为正则,主要针对下面经常用到的用法而言的。

1、表示所有的主机可以使用all 或 * 

2、通配符与逻辑或

   利用通配符还可以指定一组具有规则特征的主机或主机名,冒号表示or---逻辑或

one.361way.com
one.361way:two.361way.com
192.168.1.50
192.168.1.*

当然,这里的*通配符也可以用在前面,如:

*.361way.com
*.com

上面的用法,在多个组之间同样适用 ,如:

webservers
webservers:dbservers  //表示两个组中所有的主机

3、逻辑非与逻辑and

当然你可以做出非的表达式,例如,目标主机必须在组webservers但不在phoenix组中

webserver:!phoenix

你还可以做出交集的表达式,例如,目标主机必须即在组webservers中又在组staging中

webservers:&staging

一个更复杂的示例:

webserver:dbservers:&staging:!phoenix

上面这个复杂的表达式最后表示的目标主机必须满足:在webservers或者dbservers组中,必须还存在于staging组中,但是不在phoenix组中。这些可以看作是SaltStack中Compound matchers

4、混合高级用法

*.361way.com:*.org

还可以在开头的地方使用”~”,用来表示这是一个正则表达式:

~(web|db).*\.91it\.org

到这里估计你应该用能明白为什么前面我会提到Patterns 可以理解为正则的原因了。最后部分给两个ansible-playbook中具体可能用的用法:

a、在ansible-palybook命令中,你也可以使用变量来组成这样的表达式,但是你必须使用“-e”的选项来指定这个表达式(通常我们不这样用):

ansible-palybook -e webservers:!{{excluded}}:&{{required}}

b、在ansible和ansible-playbook中,还可以通过一个参数”–limit”来明确指定排除某些主机或组:

ansible-playbook site.yml --limit datacenter2

 

ansible小结(四)ansible.cfg与默认配置

Ansible默认安装好后有一个配置文件/etc/ansible/ansible.cfg,该配置文件中定义了ansible的主机的默认配置部分,如默认是否需要输入密码、是否开启sudo认证、action_plugins插件的位置、hosts主机组的位置、是否开启log功能、默认端口、key文件位置等等。

[defaults]
# some basic default values...
hostfile       = /etc/ansible/hosts   \\指定默认hosts配置的位置
# library_path = /usr/share/my_modules/
remote_tmp     = $HOME/.ansible/tmp
pattern        = *
forks          = 5
poll_interval  = 15
sudo_user      = root  \\远程sudo用户
#ask_sudo_pass = True  \\每次执行ansible命令是否询问ssh密码
#ask_pass      = True  \\每次执行ansible命令时是否询问sudo密码
transport      = smart
remote_port    = 22
module_lang    = C
gathering = implicit
host_key_checking = False    \\关闭第一次使用ansible连接客户端是输入命令提示
log_path    = /var/log/ansible.log \\需要时可以自行添加。chown -R root:root ansible.log
system_warnings = False    \\关闭运行ansible时系统的提示信息,一般为提示升级
# set plugin path directories here, separate with colons
action_plugins     = /usr/share/ansible_plugins/action_plugins
callback_plugins   = /usr/share/ansible_plugins/callback_plugins
connection_plugins = /usr/share/ansible_plugins/connection_plugins
lookup_plugins     = /usr/share/ansible_plugins/lookup_plugins
vars_plugins       = /usr/share/ansible_plugins/vars_plugins
filter_plugins     = /usr/share/ansible_plugins/filter_plugins
fact_caching = memory
[accelerate]
accelerate_port = 5099
accelerate_timeout = 30
accelerate_connect_timeout = 5.0
# The daemon timeout is measured in minutes. This time is measured
# from the last activity to the accelerate daemon.
accelerate_daemon_timeout = 30

本篇就结合一个示例对其进行下了解。我在对之前未连接的主机进行连结时报错如下:

[root@361way.com ~]# ansible test -a \'uptime\'
10.212.52.14 | FAILED => 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.
10.212.52.16 | FAILED => 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.

从上面的输出提示上基本可以了解到由于在本机的~/.ssh/known_hosts文件中并有fingerprint key串,ssh第一次连接的时候一般会提示输入yes 进行确认为将key字符串加入到  ~/.ssh/known_hosts 文件中。

方法1:

了解到问题原因为,我们了解到进行ssh连接时,可以使用-o参数将StrictHostKeyChecking设置为no,使用ssh连接时避免首次连接时让输入yes/no部分的提示。通过查看ansible.cfg配置文件,发现如下行:

[ssh_connection]
# ssh arguments to use
# Leaving off ControlPersist will result in poor performance, so use
# paramiko on older platforms rather than removing it
#ssh_args = -o ControlMaster=auto -o ControlPersist=60s

所以这里我们可以启用ssh_args 部分,使用下面的配置,避免上面出现的错误:

ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no 

方法2:

在ansible.cfg配置文件中,也会找到如下部分:

# uncomment this to disable SSH key host checking
host_key_checking = False  

默认host_key_checking部分是注释的,通过找开该行的注释,同样也可以实现跳过 ssh 首次连接提示验证部分。由于配置文件中直接有该选项,所以推荐用方法2 。

 

其他部分

由于官方给的说明比较详细,同时ansible.cfg 文件本身默认也有注释提示部分,所以不做过多说明,这里再举个例子,默认ansible 执行的时候,并不会输出日志到文件,不过在ansible.cfg 配置文件中有如下行:

# logging is off by default unless this path is defined
# if so defined, consider logrotate
log_path = /var/log/ansible.log

同样,默认log_path这行是注释的,打开该行的注释,所有的命令执行后,都会将日志输出到/var/log/ansible.log 文件,便于了解在何时执行了何操作及其结果,如下:

[root@361way.com ansible]# cat /var/log/ansible.log
2015-05-04 01:57:19,758 p=4667 u=root |
2015-05-04 01:57:19,759 p=4667 u=root |  /usr/bin/ansible test -a uptime
2015-05-04 01:57:19,759 p=4667 u=root |
2015-05-04 01:57:20,563 p=4667 u=root |  10.212.52.252 | success | rc=0 >>
01:57am  up 23 days 11:20,  2 users,  load average: 0.38, 0.38, 0.40
7.2015-05-04 01:57:20,831 p=4667 u=root |  10.212.52.14 | success | rc=0 >>
02:03am  up 331 days  8:19,  2 users,  load average: 0.08, 0.05, 0.05
2015-05-04 01:57:20,909 p=4667 u=root |  10.212.52.16 | success | rc=0 >>
02:05am  up 331 days  8:56,  2 users,  load average: 0.00, 0.01, 0.05

 

 

ansible小结(五)Dynamic Inventory

Ansible Inventory实际上是包含静态Inventory和动态Inventory两部分,静态Inventory指的是在文件/etc/ansible/hosts中指定的主机和组,Dynamic Inventory指通过外部脚本获取主机列表,并按照ansible 所要求的格式返回给ansilbe命令的。这部分一般会结合CMDB资管系统、zabbix 监控系统、crobble安装系统、云计算平台等获取主机信息。由于主机资源一般会动态的进行增减,而这些系统一般会智能更新。我们可以通过这些工具提供的API 或者接入库查询等方式返回主机列表。

一、最简单示例

由于Ansible在接受脚本动态获取主机信息时支持的是json格式,这里我也不从其他系统中取了,向通过一段代码打印一个段json格式的主机信息:

#!/usr/bin/env python
# coding=utf-8
import json
host1ip = [\'10.212.52.252\',\'10.212.52.14\']
host2ip = [\'10.212.52.16\']
group = \'test1\'
group2 = \'test2\'
hostdata = {group:{"hosts":host1ip},group2:{"hosts":host2ip}}
print json.dumps(hostdata,indent=4)

注:

1、主机部分必须是列表格式的;

2、hostdata行,其中的”hosts” 部分可以省略,但如果使用时,必须是”hosts” ,不能是其他如‘‘hostlist’’ 等字符串。省略后可以这样写:

 

hostdata = {group:host1ip,group2:host2ip} 

 

直接执行该段代码结果如下:

[root@361way.com ~]# python twogroup.py
{
    "test1": {
        "hosts": [
            "10.212.52.252",
            "10.212.52.14"
        ]
    },
    "test2": {
        "hosts": [
            "10.212.52.16"
        ]
    }
}

上面定义了两个主机组,test1组内包含主机10.212.52.252、10.212.52.14,test2组内包含主机10.212.52.16 。ansible可以通过如下方法调用:

[root@361way.com ~]# ansible -i twogroup.py test1 -m command -a \'uptime\' -k
SSH password:
10.212.52.252 | success | rc=0 >>
 23:01pm  up 24 days  8:24,  2 users,  load average: 0.21, 0.35, 0.39
10.212.52.14 | success | rc=0 >>
 23:08pm  up 332 days  5:23,  2 users,  load average: 0.00, 0.01, 0.05
[root@361way.com ~]# ansible -i twogroup.py test2 -m command -a \'uptime\' -k
SSH password:
10.212.52.16 | success | rc=0 >>
 23:09pm  up 332 days  6:00,  2 users,  load average: 0.08, 0.06, 0.05

二、复杂示例

在静态主机配置文件示例中,会有组变量(vars),组之间的包含,如下图(点击图片看大图):

 

 

 

如果以上部分想要,通过脚本获取实现,实现后返回的json格式应该如下图:

 

 

像上面这种复杂的返回格式,一般不会用在ad-hoc环境中,多数会用在ansible-playbook 中,应为playbook文件中有时假会涉及到vars 参数的传参。

三、从第三方平台获取主机示例

这个在本篇一开头就提到了,我们从如cobbler、cmdb等平台上获取的示例。由于ansible 的发起者(作者)同时又是cobbler软件的创建者,所以官方文档给了我们cobbler的示例,同时给出了一个从AWS 去上获取主机信息的示例 。如下:

cobbler 上获取主机信息代码

aws 云上获取主机信息代码

关于如何从aws上获取主机信息并入库,这个我之前也写过相关的篇章,具体也可以参看我之前的博文----AWS主机资产管理  (该篇也是纯python实现的)。

通过zabbix api 获取主机信息,我也写过一篇zabbix小结(八)Zabbix api ,想要通过zabbix平台上获取主机列表信息也可以参考下。

四、其他

1、ansible -i 参数后调用的脚本并非一定是py文件,也可以是其他脚本输出的结果,这里做了个简单的测试:

[root@361way.com yaml]# ansible -i group.sh test1 -m command -a \'uptime\' -k
SSH password:
10.212.52.16 | success | rc=0 >>
 00:18am  up 332 days  7:10,  2 users,  load average: 0.00, 0.01, 0.05
10.212.52.14 | success | rc=0 >>
 00:17am  up 332 days  6:32,  2 users,  load average: 0.01, 0.03, 0.05
10.212.52.252 | success | rc=0 >>
 00:11am  up 24 days  9:33,  2 users,  load average: 0.49, 0.42, 0.41
[root@localhost yaml]# cat group.sh
#!/bin/bash
groups=\'\'\'
{
    "test1": {
        "hosts": [
            "10.212.52.252",
            "10.212.52.14",
            "10.212.52.16"
        ]
    }
}
\'\'\'
echo $groups

2、-i 参数指定的脚本需要有可执行权限 ,不然会报错,如下:

[root@361way.com yaml]# ansible -i hostjson.py  AA  -a \'uptime\'
ERROR: The file hostjson.py looks like it should be an executable inventory script, but is not marked executable. Perhaps you want to correct this with `chmod +x hostjson.py`?

 

 

ansible小结(六)Ad-hoc与commands模块

 

Ad-Hoc 是指ansible下临时执行的一条命令,并且不需要保存的命令,对于复杂的命令后面会说playbook。讲到Ad-hoc 就要提到模块,所有的命令执行都要依赖于事先写好的模块,默认安装好的ansible 里面已经自带了很多模块,如:command、raw、shell、file、cron等,具体可以通过ansible-doc -l 进行查看 。

一、Ad-hoc 

1、直接执行

这里还是先来一个上几篇幅经常用到的一个例子:

root@361way ~]# ansible 10.212.52.252 -a \'uptime\' -k
SSH password:
10.212.52.252 | success | rc=0 >>
10:10am  up 27 days 19:33,  2 users,  load average: 0.39, 0.34, 0.33

一个ad-hoc命令的执行,需要按以下格式进行执行:

ansible 主机或组  -m 模块名 -a \'模块参数\'  ansible参数

主机和组,是在/etc/ansible/hosts 里进行指定的部分,当然动态Inventory 使用的是脚本从外部应用里获取的主机,这部分具体可以参考ansible小结(五)Dynamic Inventory

  • 模块名,可以通过ansible-doc -l 查看目前安装的模块,默认不指定时,使用的是command模块,具体可以查看/etc/ansible/ansible.cfg 的“#module_name = command                                                                                                                                            ” 部分,默认模块可以在该配置文件中进行修改;
  • 模块参数,可以通过 “ansible-doc 模块名” 查看具体的用法及后面的参数;
  • ansible参数,可以通过ansible命令的帮忙信息里查看到,这里有很多参数可以供选择,如是否需要输入密码、是否sudo等

2、后台执行

当命令执行时间比较长时,也可以放到后台执行,这里会用到-B、-P参数,如下:

 

ansible all -B 3600 -a "/usr/bin/long_running_operation --do-stuff" \\后台执行命令 3600s,-B 表示后台执行的时间
ansible all -m async_status -a "jid=123456789"  \\检查任务的状态
ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff" \\后台执行命令最大时间是 1800s 即 30 分钟,-P 每 60s 检查下状态默认 15s

示例如下:

 

[root@361way ~]# ansible 10.212.52.252 -B 3600 -P 0 -a \'watch ls\'
background launch...
10.212.52.252 | success >> {
    "ansible_job_id": "411650646689.13501",
    "results_file": "/root/.ansible_async/411650646689.13501",
    "started": 1
}
[root@361way ~]# ansible 10.212.52.252 -m async_status -a \'jid=411650646689.13501\'
10.212.52.252 | success >> {
    "ansible_job_id": "411650646689.13501",
    "changed": false,
    "finished": 0,
    "results_file": "/root/.ansible_async/411650646689.13501",
    "started": 1
}

不指定-P或-P参数为非0时,该任务就会按-P直接的参数一直刷新下去,直到超出-B参数指定的时间或命令执行完成:

 

[root@361way ~]# ansible 10.212.52.252 -B 3600  -a \'watch ls\'
background launch...
10.212.52.252 | success >> {
    "ansible_job_id": "397200656414.15008",
    "results_file": "/root/.ansible_async/397200656414.15008",
    "started": 1
}
10.212.52.252 | success >> {
    "ansible_job_id": "397200656414.15008",
    "changed": false,
    "finished": 0,
    "results_file": "/root/.ansible_async/397200656414.15008",
    "started": 1
}
<job 397200656414.15008> polling on 10.212.52.252, 3585s remaining
…………………………………………略

 

二、commands模块

上面已经提到,ansbile自身已经自带了很多模块,可以通过ansible-doc -l 进行查看。这里就结合command、shell、raw、script模块了解下其用法。

上面四个模块都属于commands 类

 

  • command模块,该模块通过-a跟上要执行的命令可以直接执行,不过命令里如果有带有如下字符部分则执行不成功 ,command模块不是调用的shell的指令,所以没有bash的环境变量,也不能使用shell的一些操作方式,其他和shell没有区别 “ so variables like $HOME and operations like “<“, “>”, “|”, and “&” will not work (use the shell module if you need these features).”;
  • shell 模块,用法其本和command一样,不过的是其是通过/bin/sh进行执行,所以shell 模块可以执行任何命令,就像在本机执行一样,“ It is almost exactly like the command module but runs the command through a shell (/bin/sh) on the remote node.”;
  • raw模块,用法和shell 模块一样 ,其也可以执行任意命令,就像在本机执行一样,raw很多地方和shell类似,更多的地方建议使用shell和command模块。但是如果是使用老版本python,需要用到raw,又或者是客户端是路由器,因为没有安装python模块,那就需要使用raw模块了 “Executes a low-down and dirty SSH command, not going through the module subsystem. There is no change handler support for this module. This module does not require python on the remote system,This is useful and should only be done in two cases. The first case is installing python-simplejson on older (Python 2.4 and before) hosts that need it as a dependency to run modules, since nearly all core modules require it. Another is speaking to any devices such as routers that do not have any Python installed. In any other case, using the shell or command module is much more appropriate. Arguments given to raw are run directly through the configured remote shell. Standard output, error output and return code are returned when available. There is no change handler support for this module. This module does not require python on the remote system, much like the script module.”
  • script模块,其是将管理端的shell 在被管理主机上执行,其原理是先将shell 复制到远程主机,再在远程主机上执行,原理类似于raw模块,“This module does not require python on the remote system, much like the raw module.” 。

    注:raw模块和comand、shell 模块不同的是其没有chdir、creates、removes参数,chdir参数的作用就是先切到chdir指定的目录后,再执行后面的命令,这在后面很多模块里都会有该参数 。

 

command模块包含如下选项: 

  • creates:一个文件名,当该文件存在,则该命令不执行 
  • free_form:要执行的linux指令 
  • chdir:在执行指令之前,先切换到该指定的目录 
  • removes:一个文件名,当该文件不存在,则该选项不执行
  • executable:切换shell来执行指令,该执行路径必须是一个绝对路径

command模块、raw模块、shell模块示例:

[root@361way ~]# ansible 10.212.52.252 -m command -a \'ps auxf|grep snmp\'
10.212.52.252 | FAILED | rc=1 >>
ERROR: Unsupported option (BSD syntax)
********* simple selection *********  ********* selection by list *********
-A all processes                      -C by command name
-N negate selection                   -G by real group ID (supports names)
-a all w/ tty except session leaders  -U by real user ID (supports names)
-d all except session leaders         -g by session OR by effective group name
-e all processes                      -p by process ID
T  all processes on this terminal     -s processes in the sessions given
a  all w/ tty, including other users  -t by tty
g  OBSOLETE -- DO NOT USE             -u by effective user ID (supports names)
r  only running processes             U  processes for specified users
x  processes w/o controlling ttys     t  by tty
*********** output format **********  *********** long options ***********
-o,o user-defined  -f full            --Group --User --pid --cols --ppid
-j,j job control   s  signal          --group --user --sid --rows --info
-O,O preloaded -o  v  virtual memory  --cumulative --format --deselect
-l,l long          u  user-oriented   --sort --tty --forest --version
-F   extra full    X  registers       --heading --no-heading --context
                    ********* misc options *********
-V,V  show version      L  list format codes  f  ASCII art forest
-m,m,-L,-T,H  threads   S  children in sum    -y change -l format
-M,Z  security data     c  true command name  -c scheduling class
-w,w  wide output       n  numeric WCHAN,UID  -H process hierarchy
[root@361way ~]# ansible 10.212.52.252 -m raw -a \'ps auxf|grep snmp\'
10.212.52.252 | success | rc=0 >>
root      5580 25.0  0.0  12876  1792 pts/2    Ss+  12:36   0:00      \_ bash -c ps auxf|grep snmp
root      5607  0.0  0.0   5720   832 pts/2    S+   12:36   0:00          \_ grep snmp
root     24364  0.0  0.0  70416  6696 ?        SNl  May15   0:22 /usr/sbin/snmpd -r -A -LF i /var/log/net-snmpd.log -p /var/run/snmpd.pid
[root@361way ~]# ansible 10.212.52.252 -m shell -a \'ps auxf|grep snmp\'
10.212.52.252 | success | rc=0 >>
root      5803  0.0  0.0  11308  1308 pts/2    S+   12:36   0:00              \_ /bin/sh -c ps auxf|grep snmp
root      5805  0.0  0.0   4260   572 pts/2    S+   12:36   0:00                  \_ grep snmp
root     24364  0.0  0.0  70416  6696 ?        SNl  May15   0:22 /usr/sbin/snmpd -r -A -LF i /var/log/net-snmpd.log -p /var/run/snmpd.pid


上面的执行结果可以看到,我这里加了管道,command模块执行时出错,而使用raw模块和shell 模块都正常。

使用chdir的示例:

[root@361way ~]# ansible 10.212.52.252 -m command -a \'chdir=/tmp/361way touch test.file\'
10.212.52.252 | success | rc=0 >>
[root@361way ~]# ansible 10.212.52.252 -m shell -a \'chdir=/tmp/361way touch test2.file\'
10.212.52.252 | success | rc=0 >>
[root@361way ~]# ansible 10.212.52.252 -m raw -a \'chdir=/tmp/361way touch test3.file\'
10.212.52.252 | success | rc=0 >>

从上面执行结果来看,三个命令都执行成功了。不过通过在远程主机上查看,前两个文件被成功创建

linux-wdh1:/tmp/361way # ls /tmp/361way
test.file  test2.file

使用raw模块的执行的结果文件也被正常创建了,不过不是在chdir 指定的目录,而是在当前执行用户的家目录。

linux-wdh1:~ # ls ~/test3.file
/root/test3.file

creates与removes示例:

这里我在测试主机上创建/tmp/361way/server.txt文件,执行结果如下:

 

[root@361way ~]# ansible 10.212.52.252 -a \'creates=/tmp/361way/server.txt uptime\'
10.212.52.252 | success | rc=0 >>
skipped, since /tmp/361way/server.txt exists
[root@361way ~]# ansible 10.212.52.252 -a \'removes=/tmp/361way/server.txt uptime\'
10.212.52.252 | success | rc=0 >>
 15:11pm  up 28 days  0:34,  2 users,  load average: 0.75, 0.46, 0.39

script模块示例:

[root@361way ~]# cat script.sh
#!/bin/bash
df -hl
ifconfig
ps auxf|grep snmp
[root@361way ~]# ansible 10.212.52.252 -m script -a \'scrip.sh\'
10.212.52.252 | FAILED => file or module does not exist: /root/scrip.sh
[root@361way ~]# ansible 10.212.52.252 -m script -a \'script.sh\'
10.212.52.252 | success >> {
    "changed": true,
    "rc": 0,
    "stderr": "OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\nControl socket connect(/root/.ansible/cp/ansible-ssh-10.212.52.252-22-root): Connection refused\r\ndebug1: Connecting to 10.212.52.252 [10.212.52.252] port 22.\r\ndebug1: fd 3 clearing O_NONBLOCK\r\ndebug1: Connection established.\r\ndebug1: permanently_set_uid: 0/0\r\ndebug1: identity file /root/.ssh/identity type -1\r\ndebug1: identity file /root/.ssh/identity-cert type -1\r\ndebug1: identity file /root/.ssh/id_rsa type -1\r\ndebug1: identity file /root/.ssh/id_rsa-cert type -1\r\ndebug1: identity file /root/.ssh/id_dsa type -1\r\ndebug1: identity file /root/.ssh/id_dsa-cert type -1\r\ndebug1: identity file /root/.ssh/id_ecdsa type -1\r\ndebug1: identity file /root/.ssh/id_ecdsa-cert type -1\r\ndebug1: Remote protocol version 2.0, remote software version OpenSSH_6.2\r\ndebug1: match: OpenSSH_6.2 pat OpenSSH*\r\ndebug1: Enabling compatibility mode for protocol 2.0\r\ndebug1: Local version string SSH-2.0-OpenSSH_5.3\r\ndebug1: SSH2_MSG_KEXINIT sent\r\ndebug1: SSH2_MSG_KEXINIT received\r\ndebug1: kex: server->client aes128-ctr hmac-md5 zlib@openssh.com\r\ndebug1: kex: client->server aes128-ctr hmac-md5 zlib@openssh.com\r\ndebug1: SSH2_MSG_KEX_DH_GEX_REQUEST(1024<1024<8192) sent\r\ndebug1: expecting SSH2_MSG_KEX_DH_GEX_GROUP\r\ndebug1: SSH2_MSG_KEX_DH_GEX_INIT sent\r\ndebug1: expecting SSH2_MSG_KEX_DH_GEX_REPLY\r\ndebug1: Host \'10.212.52.252\' is known and matches the RSA host key.\r\ndebug1: Found key in /root/.ssh/known_hosts:1\r\ndebug1: ssh_rsa_verify: signature correct\r\ndebug1: SSH2_MSG_NEWKEYS sent\r\ndebug1: expecting SSH2_MSG_NEWKEYS\r\ndebug1: SSH2_MSG_NEWKEYS received\r\ndebug1: SSH2_MSG_SERVICE_REQUEST sent\r\ndebug1: SSH2_MSG_SERVICE_ACCEPT received\r\ndebug1: Authentications that can continue: publickey,password,keyboard-interactive\r\ndebug1: Next authentication method: keyboard-interactive\r\ndebug1: Enabling compression at level 6.\r\ndebug1: Authentication succeeded (keyboard-interactive).\r\ndebug1: setting up multiplex master socket\r\nControlSocket /root/.ansible/cp/ansible-ssh-10.212.52.252-22-root already exists, disabling multiplexing\r\ndebug1: channel 0: new [client-session]\r\ndebug1: Requesting no-more-sessions@openssh.com\r\ndebug1: Entering interactive session.\r\ndebug1: Sending environment.\r\ndebug1: Sending env LANG = en_US.UTF-8\r\ndebug1: Sending command: LANG=C LC_CTYPE=C /root/.ansible/tmp/ansible-tmp-1431924855.88-242473611260231/script.sh \r\ndebug1: client_input_channel_req: channel 0 rtype exit-status reply 0\r\ndebug1: client_input_channel_req: channel 0 rtype eow@openssh.com reply 0\r\ndebug1: channel 0: free: client-session, nchannels 1\r\ndebug1: fd 1 clearing O_NONBLOCK\r\ndebug1: fd 2 clearing O_NONBLOCK\r\nConnection to 10.212.52.252 closed.\r\nTransferred: sent 1928, received 3920 bytes, in 0.1 seconds\r\nBytes per second: sent 37017.0, received 75262.7\r\ndebug1: Exit status 0\r\ndebug1: compress outgoing: raw data 537, compressed 375, factor 0.70\r\ndebug1: compress incoming: raw data 1837, compressed 1019, factor 0.55\r\n",
    "stdout": "Filesystem      Size  Used Avail Use% Mounted on\r\n/dev/sda2       9.9G  872M  8.5G  10% /\r\nudev            3.9G  128K  3.9G   1% /dev\r\ntmpfs           3.9G   76K  3.9G   1% /dev/shm\r\n/dev/sda3       5.0G  219M  4.5G   5% /boot\r\n/dev/sda8        40G   15G   23G  40% /home\r\n/dev/sda9       9.9G  5.2G  4.3G  55% /opt\r\n/dev/sda6       5.0G  2.7G  2.1G  57% /tmp\r\n/dev/sda5       9.9G  3.4G  6.0G  36% /usr\r\n/dev/sda7       9.9G  823M  8.6G   9% /var\r\neth0      Link encap:Ethernet  HWaddr 00:50:56:A8:65:7E  \r\n          inet addr:10.212.52.252  Bcast:10.212.52.255  Mask:255.255.255.0\r\n          inet6 addr: fe80::250:56ff:fea8:657e/64 Scope:Link\r\n          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1\r\n          RX packets:24112135 errors:0 dropped:792372 overruns:0 frame:0\r\n          TX packets:10697339 errors:0 dropped:0 overruns:0 carrier:0\r\n          collisions:0 txqueuelen:1000 \r\n          RX bytes:17137233328 (16343.3 Mb)  TX bytes:13390377826 (12770.0 Mb)\r\n\r\nlo        Link encap:Local Loopback  \r\n          inet addr:127.0.0.1  Mask:255.0.0.0\r\n          inet6 addr: ::1/128 Scope:Host\r\n          UP LOOPBACK RUNNING  MTU:16436  Metric:1\r\n          RX packets:3407332 errors:0 dropped:0 overruns:0 frame:0\r\n          TX packets:3407332 errors:0 dropped:0 overruns:0 carrier:0\r\n          collisions:0 txqueuelen:0 \r\n          RX bytes:262675450 (250.5 Mb)  TX bytes:262675450 (250.5 Mb)\r\n\r\nroot     25332  0.0  0.0   4260   568 pts/2    S+   12:54   0:00          \\_ grep snmp\r\nroot     24364  0.0  0.0  70416  6696 ?        SNl  May15   0:22 /usr/sbin/snmpd -r -A -LF i /var/log/net-snmpd.log -p /var/run/snmpd.pid\r\n"
}

输出结果很多,看起来也很乱,不过查下stdout部分,这个部分是实际上执行后的结果。这里可以配合管道一起使用,可以如下使用:

[root@361way ~]# ansible 10.212.52.252 -m script -a \'script.sh\' |egrep \'>>|stdout\'

 

 

 

ansible小结(七)常用模块

上一篇中介绍了commands部分模块,本篇承接上篇介绍下常用的模块。根据官方的分类,将模块按功能分类为:云模块、命令模块、数据库模块、文件模块、资产模块、消息模块、监控模块、网络模块、通知模块、包管理模块、源码控制模块、系统模块、单元模块、web设施模块、windows模块 ,具体可以参看官方页面

一、ping模块

测试主机是否是通的,用法很简单,不涉及参数:

[root@361way ~]# ansible 10.212.52.252 -m ping
10.212.52.252 | success >> {
    "changed": false,
    "ping": "pong"
}

二、setup模块

setup模块,主要用于获取主机信息,在playbooks里经常会用到的一个参数gather_facts就与该模块相关。setup模块下经常使用的一个参数是filter参数,具体使用示例如下(由于输出结果较多,这里只列命令不写结果):

[root@361way ~]# ansible 10.212.52.252 -m setup -a \'filter=ansible_*_mb\'   //查看主机内存信息
[root@361way ~]# ansible 10.212.52.252 -m setup -a \'filter=ansible_eth[0-2]\'   //查看地接口为eth0-2的网卡信息
[root@361way ~]# ansible all -m setup --tree /tmp/facts   //将所有主机的信息输入到/tmp/facts目录下,每台主机的信息输入到主机名文件中(/etc/ansible/hosts里的主机名)

三、file模块

file模块主要用于远程主机上的文件操作,file模块包含如下选项: 

  • force:需要在两种情况下强制创建软链接,一种是源文件不存在但之后会建立的情况下;另一种是目标软链接已存在,需要先取消之前的软链,然后创建新的软链,有两个选项:yes|no 
  • group:定义文件/目录的属组 
  • mode:定义文件/目录的权限
  • owner:定义文件/目录的属主
  • path:必选项,定义文件/目录的路径
  • recurse:递归的设置文件的属性,只对目录有效
  • src:要被链接的源文件的路径,只应用于state=link的情况
  • dest:被链接到的路径,只应用于state=link的情况 
  • state:  directory:如果目录不存在,创建目录 file:即使文件不存在,也不会被创建 link:创建软链接 hard:创建硬链接 touch:如果文件不存在,则会创建一个新的文件,如果文件或目录已存在,则更新其最后修改时间 absent:删除目录、文件或者取消链接文件

使用示例:

ansible test -m file -a "src=/etc/fstab dest=/tmp/fstab state=link"
ansible test -m file -a "path=/tmp/fstab state=absent"
ansible test -m file -a "path=/tmp/test state=touch"

四、copy模块

复制文件到远程主机,copy模块包含如下选项:

 

  • backup:在覆盖之前将原文件备份,备份文件包含时间信息。有两个选项:yes|no 
  • content:用于替代”src”,可以直接设定指定文件的值 
  • dest:必选项。要将源文件复制到的远程主机的绝对路径,如果源文件是一个目录,那么该路径也必须是个目录 
  • directory_mode:递归的设定目录的权限,默认为系统默认权限
  • force:如果目标主机包含该文件,但内容不同,如果设置为yes,则强制覆盖,如果为no,则只有当目标主机的目标位置不存在该文件时,才复制。默认为yes
  • others:所有的file模块里的选项都可以在这里使用
  • src:要复制到远程主机的文件在本地的地址,可以是绝对路径,也可以是相对路径。如果路径是一个目录,它将递归复制。在这种情况下,如果路径使用”/”来结尾,则只复制目录里的内容,如果没有使用”/”来结尾,则包含目录在内的整个内容全部复制,类似于rsync。 
  • validate :The validation command to run before copying into place. The path to the file to validate is passed in via \’%s\’ which must be present as in the visudo example below.

 

示例如下:

ansible test -m copy -a "src=/srv/myfiles/foo.conf dest=/etc/foo.conf owner=foo group=foo mode=0644"
ansible test -m copy -a "src=/mine/ntp.conf dest=/etc/ntp.conf owner=root group=root mode=644 backup=yes"
ansible test -m copy -a "src=/mine/sudoers dest=/etc/sudoers validate=\'visudo -cf %s\'"

五、service模块

用于管理服务 该模块包含如下选项: 

arguments:给命令行提供一些选项 

enabled:是否开机启动 yes|no

name:必选项,服务名称 

pattern:定义一个模式,如果通过status指令来查看服务的状态时,没有响应,就会通过ps指令在进程中根据该模式进行查找,如果匹配到,则认为该服务依然在运行

runlevel:运行级别

sleep:如果执行了restarted,在则stop和start之间沉睡几秒钟

state:对当前服务执行启动,停止、重启、重新加载等操作(started,stopped,restarted,reloaded)

使用示例:

# Example action to reload service httpd, in all cases
- service: name=httpd state=reloaded
# Example action to enable service httpd, and not touch the running state
- service: name=httpd enabled=yes
# Example action to start service foo, based on running process /usr/bin/foo
- service: name=foo pattern=/usr/bin/foo state=started
# Example action to restart network service for interface eth0
- service: name=network state=restarted args=eth0

六、cron模块

用于管理计划任务 包含如下选项: 

  • backup:对远程主机上的原任务计划内容修改之前做备份 
  • cron_file:如果指定该选项,则用该文件替换远程主机上的cron.d目录下的用户的任务计划 
  • day:日(1-31,*,*/2,……) 
  • hour:小时(0-23,*,*/2,……)  
  • minute:分钟(0-59,*,*/2,……) 
  • month:月(1-12,*,*/2,……) 
  • weekday:周(0-7,*,……)
  • job:要执行的任务,依赖于state=present 
  • name:该任务的描述 
  • special_time:指定什么时候执行,参数:reboot,yearly,annually,monthly,weekly,daily,hourly 
  • state:确认该任务计划是创建还是删除 
  • user:以哪个用户的身份执行

 

示例:

 

ansible test -m cron -a \'name="a job for reboot" special_time=reboot job="/some/job.sh"\'
ansible test -m cron -a \'name="yum autoupdate" weekday="2" minute=0 hour=12 user="root
ansible 10.212.52.252 -m cron  -a \'backup="True" name="test" minute="0" hour="2" job="ls -alh > /dev/null"\'
ansilbe test -m cron -a \'cron_file=ansible_yum-autoupdate state=absent\'

 

七、yum模块

使用yum包管理器来管理软件包,其选项有: 

 

  • config_file:yum的配置文件 
  • disable_gpg_check:关闭gpg_check 
  • disablerepo:不启用某个源 
  • enablerepo:启用某个源
  • name:要进行操作的软件包的名字,也可以传递一个url或者一个本地的rpm包的路径 
  • state:状态(present,absent,latest)

 

示例如下:

 

ansible test -m yum -a \'name=httpd state=latest\'
ansible test -m yum -a \'name="@Development tools" state=present\'
ansible test -m yum -a \'name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present\'

八、user模块与group模块

user模块是请求的是useradd, userdel, usermod三个指令,goup模块请求的是groupadd, groupdel, groupmod 三个指令,具体参数这里不再细讲,直接上示例。

1、user模块示例:

- user: name=johnd comment="John Doe" uid=1040 group=admin
- user: name=james shell=/bin/bash groups=admins,developers append=yes
- user: name=johnd state=absent remove=yes
- user: name=james18 shell=/bin/zsh groups=developers expires=1422403387
#生成密钥时,只会生成公钥文件和私钥文件,和直接使用ssh-keygen指令效果相同,不会生成authorized_keys文件。
- user: name=test generate_ssh_key=yes ssh_key_bits=2048 ssh_key_file=.ssh/id_rsa

注:指定password参数时,不能使用后面这一串密码会被直接传送到被管理主机的/etc/shadow文件中,所以需要先将密码字符串进行加密处理。然后将得到的字符串放到password中即可。

 

[root@361way ~]#  openssl passwd -1 -salt $(< /dev/urandom tr -dc \'[:alnum:]\' | head -c 32)
Password:
$1$YngB4z8s$atSVltYKnDxJmWZ3s.4/80
或者
[root@361way ~]# echo "123456" | openssl passwd -1 -salt $(< /dev/urandom tr -dc \'[:alnum:]\' | head -c 32) -stdin
$1$4P4PlFuE$ur9ObJiT5iHNrb9QnjaIB0
#经验证下面生成的密码串也可以正常使用,不过与/etc/shadow的格式不统一,不建议使用
[root@361way ~]# openssl passwd -salt -1 "123456"
-1yEWqqJQLC66
#使用上面的密码创建用户
[root@361way ~]#ansible all -m user -a \'name=foo password="$1$4P4PlFuE$ur9ObJiT5iHNrb9QnjaIB0

 

不同的发行版默认使用的加密方式可能会有区别,具体可以查看/etc/login.defs文件确认,centos 6.5版本使用的是SHA512加密算法,生成密码可以通过ansible官方给出的示例:

python -c "from passlib.hash import sha512_crypt; import getpass; print sha512_crypt

2、group示例

 

- group: name=somegroup state=present

 

九、synchronize模块

使用rsync同步文件,其参数如下:

 

  • archive: 归档,相当于同时开启recursive(递归)、links、perms、times、owner、group、-D选项都为yes ,默认该项为开启
  • checksum: 跳过检测sum值,默认关闭
  • compress:是否开启压缩
  • copy_links:复制链接文件,默认为no ,注意后面还有一个links参数
  • delete: 删除不存在的文件,默认no
  • dest:目录路径
  • dest_port:默认目录主机上的端口 ,默认是22,走的ssh协议
  • dirs:传速目录不进行递归,默认为no,即进行目录递归
  • rsync_opts:rsync参数部分
  • set_remote_user:主要用于/etc/ansible/hosts中定义或默认使用的用户与rsync使用的用户不同的情况
  • mode: push或pull 模块,push模的话,一般用于从本机向远程主机上传文件,pull 模式用于从远程主机上取文

 

另外还有其他参数,这里不再一一说明。上几个用法:

src=some/relative/path dest=/some/absolute/path rsync_path="sudo rsync"
src=some/relative/path dest=/some/absolute/path archive=no links=yes
src=some/relative/path dest=/some/absolute/path checksum=yes times=no
src=/tmp/helloworld dest=/var/www/helloword rsync_opts=--no-motd,--exclude=.git mode=pull

十、mount模块

 

  • 配置挂载点 选项: 
  • dump fstype:必选项,挂载文件的类型 
  • name:必选项,挂载点 
  • opts:传递给mount命令的参数
  • src:必选项,要挂载的文件 
  • state:必选项 

     

    present:只处理fstab中的配置 

    absent:删除挂载点 

    mounted:自动创建挂载点并挂载之 

    umounted:卸载

 

示例:

name=/mnt/dvd src=/dev/sr0 fstype=iso9660 opts=ro state=present
name=/srv/disk src=\'LABEL=SOME_LABEL\' state=present
name=/home src=\'UUID=b3e48f45-f933-4c8e-a700-22a159ec9077\' opts=noatime state=present
ansible test -a \'dd if=/dev/zero of=/disk.img bs=4k count=1024\'
ansible test -a \'losetup /dev/loop0 /disk.img\'
ansible test -m filesystem \'fstype=ext4 force=yes opts=-F dev=/dev/loop0\'
ansible test -m mount \'name=/mnt src=/dev/loop0 fstype=ext4 state=mounted opts=rw\'

十一、get_url 模块

该模块主要用于从http、ftp、https服务器上下载文件(类似于wget),主要有如下选项:

sha256sum:下载完成后进行sha256 check;

timeout:下载超时时间,默认10s

url:下载的URL

url_password、url_username:主要用于需要用户名密码进行验证的情况

use_proxy:是事使用代理,代理需事先在环境变更中定义

示例:

- name: download foo.conf
  get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf mode=0440
- name: download file with sha256 check
  get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf sha256sum=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c

官方提供的可能用到模块有git、svn版本控制模块,sysctl 、authorized_key_module系统模块,apt、zypper、pip、gem包管理模块,find、template文件模块,mysql_db、redis数据库模块,url 网络模块等。具体可以参看官方手册模块部分

 

 

 

ansible小结(八)ansible-playbook简单使用

 

ansbile-playbook是一系统ansible命令的集合,其利用yaml 语言编写,运行过程,ansbile-playbook命令根据自上而下的顺序依次执行。同时,playbook开创了很多特性,它可以允许你传输某个命令的状态到后面的指令,如你可以从一台机器的文件中抓取内容并附为变量,然后在另一台机器中使用,这使得你可以实现一些复杂的部署机制,这是ansible命令无法实现的。

playbook通过ansible-playbook命令使用,它的参数和ansible命令类似,如参数-k(–ask-pass) 和 -K (–ask-sudo) 来询问ssh密码和sudo密码,-u指定用户,这些指令也可以通过规定的单元写在playbook 。ansible-playbook的简单使用方法: ansible-playbook example-play.yml 。

一、一个简单的示例

下面给出一个简单的ansible-playbook示例,了解下其构成。

# cat user.yml
- name: create user
  hosts: all
  user: root
  gather_facts: false
  vars:
  - user: "test"
  tasks:
  - name: create  user
    user: name="{{ user }}"

上面的playbook 实现的功能是新增一个用户:

name参数对该playbook实现的功能做一个概述,后面执行过程中,会打印 name变量的值 ;

hosts参数指定了对哪些主机进行参作;

user参数指定了使用什么用户登录远程主机操作;

gather_facts参数指定了在以下任务部分执行前,是否先执行setup模块获取主机相关信息,这在后面的task会使用到setup获取的信息时用到;

vars参数,指定了变量,这里指字一个user变量,其值为test ,需要注意的是,变量值一定要用引号引住;

task指定了一个任务,其下面的name参数同样是对任务的描述,在执行过程中会打印出来。user提定了调用user模块,name是user模块里的一个参数,而增加的用户名字调用了上面user变量的值。具体执行结果如下:

[root@361way playbooks]# ansible-playbook user.yml
PLAY [create user] ************************************************************
TASK: [create  user ] **********************************************
changed: [10.212.52.252]
changed: [10.212.52.14]
changed: [10.212.52.16]
PLAY RECAP ********************************************************************
10.212.52.14               : ok=1    changed=1    unreachable=0    failed=0
10.212.52.16               : ok=1    changed=1    unreachable=0    failed=0
10.212.52.252              : ok=1    changed=1    unreachable=0    failed=0   

同样,如果想实现把这个新增的用户删除,只需将该playbook文件的最后一行替换为如下行再执行相应的playbook即可:

user: name="{{ user }}" state=absent remove=yes

二、一键修补bash shellcode示例

再给出一个稍微复杂的示例,通过ansible-playbook实现对N台主机同时修补bash shellcode 漏洞。需要注意的是,可能现网主机分布着不同的系统版本。这里假设现网同时存在centos5和6版本,具体playbook内容如下:

 

 

# cat update_bash.yml
- hosts: all
  remote_user: root
  gather_facts: True
  tasks:
  - name: update bash in redhat 6 version
    yum: name=http://mirrors.aliyun.com/centos/6.6/os/x86_64/Packages/bash-4.1.2-2el6.x86_64.rpm.rpm state=present
    when: ansible_os_family == "RedHat" and ansible_distribution_version|int >=6
  - name: update bash in redhat 5 version
    yum: name=http://mirrors.hustunique.com/centos/5/updates/x86_64/RPMS/bash-3.2-33.el5.1.x86_64.rpm state=present
    when: ansible_os_family == "RedHat" and ansible_distribution_version|int <=5

上面使用了when语句,同时也开启了gather_facts setup模块,这里的ansible_os_family变量和ansible_distribution_version变量就是直接使用的setup模块获取的信息。

如果有大量主机,就在运行的时候加上-f然后选择一个合适的并发主机数量即可,我这里使用了这个,很快的就升级完成bash了。

三、playbook的构成

playbook是由一个或多个“play”组成的列表。play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色。从根本上来讲所谓task无非是调用ansible的一个module。将多个play组织在一个playbook中即可以让它们联同起来按事先编排的机制同唱一台大戏。其主要有以下四部分构成

playbooks组成:

  •   Target section:   定义将要执行 playbook 的远程主机组
  •   Variable section: 定义 playbook 运行时需要使用的变量
  •   Task section:     定义将要在远程主机上执行的任务列表
  •   Handler section:  定义 task 执行完成以后需要调用的任务

下面介绍下构成playbook 的四层结构。

1、Hosts和Users

playbook中的每一个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务。

hosts 用于指定要执行指定任务的主机其可以是一个或多个由冒号分隔主机组。

remote_user 则用于指定远程主机上的执行任务的用户。 不过remote_user也可用于各task中。也可以通过指定其通过sudo的方式在远程主机上执行任务其可用于play全局或某任务。 此外甚至可以在sudo时使用sudo_user指定sudo时切换的用户。

 

示例:

- hosts: webnodes
  tasks:
    - name: test ping connection:
    remote_user: test
    sudo: yes

2、任务列表和action

play的主体部分是task list。

task list中的各任务按次序逐个在hosts中指定的所有主机上执行即在所有主机上完成第一个任务后再开始第二个。在运行自下而下某playbook时如果中途发生错误所有已执行任务都将回滚因此在更正playbook后重新执行一次即可。 

task的目的是使用指定的参数执行模块而在模块参数中可以使用变量。模块执行是幂等的这意味着多次执行是安全的因为其结果均一致。每个task都应该有其name用于playbook的执行结果输出建议其内容尽可能清晰地描述任务执行步骤。如果未提供name则action的结果将用于输出。 

定义task的可以使用“action: module options”或“module: options”的格式推荐使用后者以实现向后兼容。如果action一行的内容过多也中使用在行首使用几个空白字符进行换行。

 

 

tasks:
  - name: make sure apache is running
    service: name=httpd state=running
在众多模块中只有command和shell模块仅需要给定一个列表而无需使用“key=value”格式例如
tasks:
  - name: disable selinux
    command: /sbin/setenforce 0  如果命令或脚本的退出码不为零可以使用如下方式替代
tasks:
  - name: run this command and ignore the result
    shell: /usr/bin/somecommand || /bin/true
或者使用ignore_errors来忽略错误信息
tasks:
  - name: run this command and ignore the result
    shell: /usr/bin/somecommand
    ignore_errors: True   

3、handlers 

用于当关注的资源发生变化时采取一定的操作。 “notify”这个action可用于在每个play的最后被触发这样可以避免多次有改变发生时每次都执行指定的操作取而代之仅在所有的变化发生完成后一次性地执行指定操作。 在notify中列出的操作称为handler也即notify中调用 handler中定义的操作。 

 

注意:在 notify 中定义内容一定要和tasks中定义的 – name 内容一样,这样才能达到触发的效果,否则会不生效。

 

- name: template configuration file
  template: src=template.j2 dest=/etc/foo.conf
  notify:
  - restart memcached
  - restart apache
handler是task列表这些task与前述的task并没有本质上的不同。
handlers:
  - name: restart memcached
    service: name=memcached state=restarted
  - name: restart apache
    service: name=apache state=restarted 

 

4、tags

tags用于让用户选择运行或略过playbook中的部分代码。ansible具有幂等性因此会自动跳过没有变化的部分即便如此有些代码为测试其确实没有发生变化的时间依然会非常地长。 此时如果确信其没有变化就可以通过tags跳过此些代码片断。

5、示例

下面再给出一个安装httpd web服务的示例:

# cat /etc/ansible/playbook/install_web.yml
- hosts: webservers
  remote_user: root
  gather_fasks: False
  vars:
    packages: httpd
  tasks:
    - name: Install httpd
      yum: name={{ packages }} state=present
    - name: Cofiguration httpd
      copy: src=/root/httpd.conf dest=/etc/httpd/conf/httpd.conf
      tags: httpd_conf
    notify:
      - restart httpd
    - name: Start httpd
      service: name=httpd state=started enabled=no
      tags: start
    - name:Add centos user
      user: name={{ item }} state=absent
      tags: adduser
      with_items:
        - centos
        - admin
  handlers:
    - name: restart httpd
      service: name=httpd state=restart

上面的代码没有考虑ubuntu平台,仅仅考虑centos/redhat平台。

 

 

ansible小结(九)playbook进阶

 

并发运行

ansible默认只会创建5个进程,所以一次任务只能同时控制5台机器执行.那如果你有大量的机器需要控制,或者你希望减少进程数,那你可以采取异步执行.ansible的模块可以把task放进后台,然后轮询它.这使得在一定进程数下能让大量需要的机器同时运作起来.

使用async和poll这两个关键字便可以并行运行一个任务. async这个关键字触发ansible并行运作任务,而async的值是ansible等待运行这个任务的最大超时值,而poll就是ansible检查这个任务是否完成的频率时间.

如果你希望在整个集群里面平行的执行一下updatedb这个命令.使用下面的配置

- hosts: all
    tasks:
      - name: Install mlocate
        yum: name=mlocate state=installed
      - name: Run updatedb
        command: /usr/bin/updatedb
        async: 300
        poll: 10

你会发现当你使用上面的例子控制超过5台机器的时候,command.在上面yum模块会先在5台机器上跑,完成后再继续下面的机器.而上面command模块的任务会一次性在所有机器上都执行了,然后监听它的回调结果

如果你的command是控制机器开启一个进程放到后台,那就不需要检查这个任务是否完成了.你只需要继续其他的动作,最后再使用wait_for这个模块去检查之前的进程是否按预期中开启了便可.只需要把poll这个值设置为0,便可以按上面的要求配置ansible不等待job的完成.

最后,或者你还有一种需求是有一个task它是需要运行很长的时间,那你需要设置一直等待这个job完成.这个时候你把async的值设成0便可.

总结来说,大概有以下的一些场景你是需要使用到ansible的polling特性的

  1. 你有一个task需要运行很长的时间,这个task很可能会达到timeout.
  2. 你有一个任务需要在大量的机器上面运行
  3. 你有一个任务是不需要等待它完成的

当然也有一些场景是不适合使用polling特性的

  1. 你的这个任务是需要运行完后才能继续另外的任务的
  2. 你的这个任务能很快的完成

Looping

在ansible你能够通过不同的输入去重复的执行同一个模块,举个例子,你需要管理几个具有相同权限的文件.你能够用一个for循环迭代一个facts或者variables去减少你的重复劳动.

使用with_items这个关键字就可以完成迭代一个列表.列表里面的每个变量都叫做item.有一些模块譬如yum,它就支持使用with_items去安装一列表的包,而不需要写好多个yum的task

下面来一个with_items的例子

 

tasks:
  - name: Secure config files
    file: path=/etc/{{ item }} mode=0600 owner=root group=root
    with_items:
     - my.cnf
     - shadow
     - fstab

除了使用items轮训,ansible还有一种方式是lookup插件.这些插件可以让ansible从外部取得数据,例如,你或许希望可以通过一种特定模式去上传你的文件.

在这个例子里面,我们会上传所有的public keys到一个目录,然后聚合它们到一个authorized_keys文件

tasks:     #1
    - name: Make key directory     #2
      file: path=/root/.sshkeys ensure=directory mode=0700
      owner=root group=root     #3
    - name: Upload public keys     #4
      copy: src={{ item }} dest=/root/.sshkeys mode=0600
      owner=root group=root     #5
      with_fileglob:     #6
       - keys/*.pub     #7
    - name: Assemble keys into authorized_keys file     #8
      assemble: src=/root/.sshkeys dest=/root/.ssh/authorized_keys
      mode=0600 owner=root group=root     #9

loop模块一般在下面的场景中使用

  1. 类似的配置模块重复了多遍
  2. fact是一个列表
  3. 创建多个文件,然后使用assemble聚合成一个大文件
  4. 使用with_fileglob匹配特定的文件管理

条件语句

有一些模块,例如copy这个模块有一些机制能跳过本次模块的运行.其实我们也可以使用自己的条件语句去配置跳过模块,这样方便你服务能够选择使用不同的包管理(apt,yum)和不同的文件系统.并且你还可以使用set_fact这个模块做成更多的差异配置

你能够使用when这个关键字去达到跳过本次模块运行的效果,when关键字后面跟着的是python的表达式,在表达式中你能够使用任何的变量或者fact,当表达式的结果返回的是false,便会跳过本次的模块

下面一段配置就说明了如何在debian和redhat系统中选择apt还是yum包管理,并且如果不是以上两个系统,会用debug模块把系统打印出来

---
- name: Install VIM
  hosts: all
  tasks:
    - name: Install VIM via yum
      yum: name=vim-enhanced state=installed
      when: ansible_os_family == "RedHat"
    - name: Install VIM via apt
      apt: name=vim state=installed
      when: ansible_os_family == "Debian"
    - name: Unexpected OS family
      debug: msg="OS Family {{ ansible_os_family }} is not supported" fail=yes
      when: not ansible_os_family == "RedHat" or ansible_os_family == "Debian"

条件语句还有一种用法,它还可以让你当达到一定的条件的时候暂停下来,等待你的输入确认.一般情况下,当ansible遭遇到error时,它会直接结束运行.那其实你可以当遭遇到不是预期的情况的时候给使用pause模块,这样可以让用户自己决定是否继续运行任务

name: pause for unexpected conditions
pause: prompt="Unexpected OS"
when: ansible_os_family != "RedHat"

下面一些情景建议你使用条件语句做跳过动作

  1. job里面有不同操作系统的机器
  2. 提示用户,然后再执行操作请求
  3. 提高性能,避免运行一个需要执行一段时间模块,而且你知道这个模块不会返回changed

task委托

默认ansible的所有task是在我们的配置的管理机器上面运行的,当在一个独立的群集里面配置,那是适用的.而有一些情况是,某些任务运行的状态是需要传递给其他机器的,在同一个任务你需要在其他机器上执行,这时候你就许多要用task委托

使用delegate_to关键字便可以配置任务在其他机器上执行.其他模块还是在所有配置的管理机器上运行的,当到了这个关键字的任务就是使用委托的机器上运行.而facts还是适用于当前的host,下面我们演示一个例子,使用get_url模块去下载一个web集群的配置

 

---
  - name: Fetch configuration from all webservers
    hosts: webservers
    tasks:
      - name: Get config
        get_url: dest=configs/{{ ansible_hostname }} force=yes url=http://{{ ansible_hostname }}/diagnostic/config
        delegate_to: localhost

如果需要委托loaclhost执行任务,这里提供一个快捷的方式,只要使用local_action作为task的key便行.我们尝试使用这种方式来配置上面的例子,会更加简洁.

 

---    #1
   - name: Fetch configuration from all webservers     #2
     hosts: webservers     #3
     tasks:     #4
        - name: Get config
          local_action: get_url dest=configs/{{ ansible_hostname }}.cfg url=

 

委托不限于localhost,可以在你的inventory里面的任何host.下列一些场景适用使用委托

  1. 部署之前你希望从负载均衡里面把host移除
  2. 更改你的server时候更改dns的指向
  3. 创建一个iSCSI卷存储设备
  4. 使用一个外部服务器去检测一下服务

额外的变量

大家应该在之前的章节的例子里面有看到group_names这个变量.这个是ansible提供的一个很神奇变量.直至写本书的时候,有7个这样的变量,我会在下面的章节介绍

a.hostvars变量

hostvars允许你在当前的任务中应用所有host的变量.当setup模块没有运行的时候,只有这些变量将是可用.例如你配置 ${hostvars.hostname.fact}可以访问其他复杂的变量.例如你可以配置${hostvars.ns1.ansible_ distribution}得到ns1这个server的linux发型版本.

下面的例子设置了一个dns_master变量,这是ns1 server的ip.然后这个变量能够在所有机器上调用

---
- name: Setup DNS Servers
   hosts: allnameservers
   tasks:
     - name: Install BIND
       yum: name=named state=installed
- name: Setup Slaves     #7
  hosts: slavenamesservers     #8
  tasks:     #9
    - name: Get the masters IP
      set_fact: dns_master="{{ hostvars.ns1.ansible_default_ipv4.address }}"
    - name: Configure BIND
      template: dest=/etc/named.conf src/templates/named.conf.j2

b.groups变量

groups变量是inventory里面的group分组列表.这个是一个非常强大的工具,能够让你迭代你配置的所有的hosts.看下面的例子.

---
- name: Configure the database
  hosts: dbservers
  user: root
  tasks:
    - name: Install mysql
      yum: name={{ item }} state=installed
      with_items:
        - mysql-server
        - MySQL-python
    - name: Start mysql
      service: name=mysqld state=started enabled=true
    - name: Create a user for all app servers
      with_items: groups.appservers
      mysql_user: name=kate password=test host={{ hostvars[item].ansible_e

groups变量实际不是你的hosts变量的列表.它只是你hosts的name的列表.如果你需要调用host里面的变量还需要配合hostvars使用

下面的例子配置创建known_hosts文件

playbook配置

---
   hosts: all
   tasks:
   - name: Setup known hosts
     hosts: all
     tasks:
       - name: Create known_hosts
         template: src=templates/known_hosts.j2 dest=/etc/ssh/ssh_known_hosts owner

template模板

{% for host in groups[\'all\'] %}
{{ hostvars[host][\'ansible_hostname\'] }}
{{ hostvars[host][\'ansible_ssh_host_key_rsa_public\'] }}
{% endfor %}

c.group_names变量

group_names是当前host所属的组的列表.这可以用于在条件语句中调用成员的group关系,或者用于debugging.通常来说这变量大部分用于跳过一些task或者在模板中用于条件语句的变量.在下面的例子中,如果你有两套sshd的配置文件,一套用于安全性更加严谨的,一个安全性普通的.然后我们根据group名来配分host到哪个sshd配置下.

---
- name: Setup SSH
  hosts: sshservers
  tasks:
    - name: For secure machines
      set_fact: sshconfig=files/ssh/sshd_config_secure
      when: "\'secure\' in group_names"
    - name: For non-secure machines
      set_fact: sshconfig=files/ssh/sshd_config_default
      when: "\'secure\' not in group_names"
    - name: Copy over the config
      copy: src={{ sshconfig }} dest=/tmp/sshd_config

d.inventory_hostname变量

inventory_hostname是机器的hostname,当你没有使用setup模块,或者由于各种原因导致setup的变量是错误的,你可以选择使用这个变量.此变量可以帮助你初始化你的机器和改变hostname

e.inventory_hostname_short

inventory_hostname_short类似与上面的inventory_hostname变量,只是它是截取第一个句点的前面的字符,例如hostname是host.example.com,就会只截取到host

f.inventory_dir

此变量是inventory文件的路径,包括目录与文件名

g.inventory_file

类似上面的变量,只是它只有文件名

使用变量来查找文件

所有的模块都可以把变量作为参数的一部分,通过使用”{{}}”符号扩起来.譬如变量test就是”{{ test }}”.这样你就可以通过变量加载特定的文件.例如,你希望根据不同的机器architecture选择不同的NRPE(nagios的客户端)配置文件,那可以像这样的配置

 

---
- name: Configure NRPE for the right architecture
 hosts: ansibletest
 user: root
 tasks:
   - name: Copy in the correct NRPE config file
     copy: src=files/nrpe.{{ ansible_architecture }}.conf dest=/etc/nagios/nrpe.

在copy和tempalate模块里面,你能够使用ansible去查找一组的文件.然后默认使用第一个文件.这能够让你达到效果是,当第一个文件不存在时,会查找第二个文件,如此类推知道最后一个文件还不存在就报fail.使用first_available_file这个关键字便可以到上述效果.

 

---
- name: Install an Apache config file
  hosts: ansibletest
  user: root
  tasks:
   - name: Get the best match for the machine
     copy: dest=/etc/apache.conf src={{ item }}
     first_available_file:
      - files/apache/{{ ansible_os_family }}-{{ ansible_architecture }}.cfg
      - files/apache/default-{{ ansible_architecture }}.cfg
      - files/apache/default.cfg

环境变量

unix命令经常需要依赖环境变量,例如C makefiles,installers,和aws cli工具.很幸运,ansible很容易实现,譬如你现在需要控制远程的机器一个文件到s3,那你许多要配置aws的access key.下面我们的例子演示,安装pip,用pip安装aws cli,并且通过cli上传文件到s3

---
- name: Upload a remote file via S3
  hosts: ansibletest
  user: root
  tasks:
   - name: Setup EPEL
     command: rpm -ivh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm creates=/etc/yum.repos.d/epel.repo
   - name: Install pip
     yum: name=python-pip state=installed
   - name: Install the AWS tools
     pip: name=awscli state=present
   - name: Upload the file
     shell: aws s3 put-object --bucket=my-test-bucket --key={{ ansible_hostname }}/fstab --body=/etc/fstab --region=eu-west-1
  environment:
    AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXXXXXXX
    AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXX

一些模块例如get_url,yum,和apt是需要使用环境变量配置proxy的.下面一些场景也是需要配置环境变量的

  1. 运行application installers
  2. 当运行shell的时候需要添加一些额外的的变量在path里
  3. 需要load的一些库不在系统的library路径中
  4. 在运行模块时使用LD_PRELOAD hack

External data lookups

ansible在0.9版本开始引进了lookup插件,这些插件运行ansible在外围获取数据.ansible已经提供了几个插件,但它还是支持自己编写插件.这真的让你使用ansible配置更加伸缩自如

lookup是在master机器运行的python程序.下面一个例子是使用lookup插件获取环境变量里面的http_proxy,然后配置在远端机器,确保远端机器使用相同的proxy下载文件

---     #1
- name: Downloads a file using the same proxy as the controlling machine
  hosts: all
  tasks:
    - name: Download file
      get_url: dest=/var/tmp/file.tar.gz url=http://server/file.tar.gz
      environment:
       http_proxy: "{{ lookup(\'env\', \'http_proxy\') }}"

使用with_*能够使用lookup插件迭代出特别的东西.您可以使用任何这样的插件,但最好是返回一个列表.下面的例子让你自动注册webapp,使用下面的例子会创建出虚拟机并配置它

---
- name: Registers the app server farm
  hosts: localhost
  connection: local
  vars:
    hostcount: 5
  tasks:
    - name: Register the webapp farm
      local_action: add_host name={{ item }} groupname=webapp
      with_sequence: start=1 end={{ hostcount }} format=webapp%02x

在下面的场景,lookup非常有用

  1. 复制整个目录的apache配置到conf.d
  2. 使用环境变量调整playbook的运行
  3. 从DNS TXT记录中获取配置
  4. 获取一个命令的输出到一个变量中

保存结果

几乎所有的模块都是会outputs一些东西,甚至debug模块也会.大多数我们会使用的结果变量是changed.这个changed变量决定了是否要直接handlers和输出的颜色是什么.然而,结果变量还有其他的用途,譬如我需要保存我的结果变量,然后咋我的playbook的其他地方给使用.在下面的例子我们创建了一个/tmp目录,然后在后面我们创建一个/tmp/subtmp使用和前面目录一样的权限

---
- name: Using register
  hosts: ansibletest
  user: root
  tasks:
   - name: Get /tmp info
     file: dest=/tmp state=directory
     register: tmp
   - name: Set mode on /var/tmp
     file: dest=/tmp/subtmp mode={{ tmp.mode }} state=directory

一些模块,例如上面的file模块,是能够获取到一些简单的信息.结合register这个功能,可以让你在playbook里面检查你的环境和计算如何进行

register对于数多场景是很有用的

  1. 在一台远端的服务器获取一个目录下的一列表的文件,然后下载这些文件
  2. 在handler执行之前,发现前面一个task发生了changed,然后执行一个指定的task
  3. 获取远端服务器的ssh key的内容,构建出known_hosts文件

debugging playbook

有好几种方法去debug我们的playbook.ansible有verbose模式和debug模式,也可以使用例如fetch和get_url模块来协助debug.当你想学习怎样使用一些模块时,这些debugging技术能够帮助你.

a.debug模块

debug模块使用很简单.它具有两个参数,msg和fail.msg就是打印出来的信息,而当fail参数设置为yes时,会发送失败通知给ansible,然后ansible会停止运行任务.

在下面的例子,配置了使用debug模块去显示远端机器所有的network interface

---
- name: Demonstrate the debug module
  hosts: ansibletest
  user: root
  vars:
    hostcount: 5
  tasks:
    - name: Print interface
      debug: msg="{{ item }}"
      with_items: ansible_interfaces

运行上面的配置会出现这样的输出

PLAY [Demonstrate the debug module] *********************************
GATHERING FACTS *****************************************************
ok: [ansibletest]
TASK: [Print IP address] ********************************************
ok: [ansibletest] => (item=lo) => {"item": "lo", "msg": "lo"}
ok: [ansibletest] => (item=eth0) => {"item": "eth0", "msg": "eth0"}
PLAY RECAP **********************************************************
ansibletest                : ok=2    changed=0    unreachable=0 failed=0

如你说见,debug模块可以让你很容易看到在playbook运行期间一些变量

b.verbose模式

另外的debug选择是verbose模式.当运行verbose模式时,会打印出所有模块运行后的变量.这对于你要使用register功能时候很重要.只需要在执行playbook命令时加上参数–verbose便可以.ansible-playbook –verbose playbook.yml

c.check模式

除了verbose模式外,ansible还提供了check模式和diff模式.只需要执行playbook时添加参数–check和–diff.check模式运行时,ansible不会真正控制远程机器发生变更.这能够让你获得这次playbook任务中,将会发生changed事件的列表.

很重要的一点是check模式不是完美的.有一些模块是会跳过check模式的.尤其明显的限制是在运行command和shell模块

在diff模式下,当文件发现更变,会打印出变更文件的变更部分.配合check模式使用效果更好

d.pause模块

另外一个debug技巧是使用pause模块,它可以让你需要在某个地方需要检查远程机器的配置的时候暂停playbook的执行.这样可以让先观察一下运行到这里为止的效果,再判断是否继续运行下去.

总结

在这个章节我们更加深入探索了编写playbook的一些细节.现在你应该可以使用一些ansible的特性.例如delegation,looping,conditionals,和fact,registration等等,让你能够更容易的编写和维护你的playbook.我们也看到了如何获取其他host的信息,如何配置环境变量,如何从外围获取到数据.最后我们展示了一些debug技巧,让playbook能按你的预期来执行.

下一章节,我们会学习如何在大规模环境中使用ansible,也会讲到一些方法让你在一些需要运行很久的任务中提高你的性能.我们也会介绍一些特性让你的playbook如何更加可维护,更加解藕,让它们按目的分配到不同的地方.

 

 

ansible小结(十)ansible api

ansible api 与ansible-playbook api 本应该是后面放在 ansible条件与循环、ansible变量篇之后讲的,不过使用过后实在按捺不住提前写的冲动,这个插个队先讲讲API 部分。

一、ansible api

ansible api 的使用非常强大,也非常简单,只不过把模块需要使用的参数写到了脚本中,这里先来看下官方给的示例,不过同于官方的是,我这里增我将结果进行了json美化输出。

[root@361way api]# cat test_api.py
2.#!/usr/bin/env python
3.# coding=utf-8
4.import ansible.runner
5.import json
6.runner = ansible.runner.Runner(
7.       module_name=\'ping\',
8.       module_args=\'\',
9.       pattern=\'all\',
10.       forks=10
11.    )
12.datastructure = runner.run()
13.data = json.dumps(datastructure,indent=4)
14.print data

其输出结果如下:

 

注:如果主机是不通或失败的,结果将会输出到dark部分里,一个含有失败主机的结果类似如下:

 

{
2.    "dark" : {
3.       "web1.example.com" : "failure message"
4.    },
5.    "contacted" : {
6.       "web2.example.com" : 1
7.    }
8.}

再为看下第二个示例:

#!/usr/bin/python
2.import ansible.runner
3.import sys
4.# construct the ansible runner and execute on all hosts
5.results = ansible.runner.Runner(
6.    pattern=\'*\', forks=10,
7.    module_name=\'command\', module_args=\'/usr/bin/uptime\',
8.).run()
9.if results is None:
10.   print "No hosts found"
11.   sys.exit(1)
12.print "UP ***********"
13.for (hostname, result) in results[\'contacted\'].items():
14.    if not \'failed\' in result:
15.        print "%s >>> %s" % (hostname, result[\'stdout\'])
16.print "FAILED *******"
17.for (hostname, result) in results[\'contacted\'].items():
18.    if \'failed\' in result:
19.        print "%s >>> %s" % (hostname, result[\'msg\'])
20.print "DOWN *********"
21.for (hostname, result) in results[\'dark\'].items():
22.    print "%s >>> %s" % (hostname, result

上面的示例中对主机的输出结果进行了判断,并且结果的输出进行了定制化,上面执行的结果你可以和ansible all -m command -a \’uptime\’ 的结果进行下比对,看下有什么不同。

上面的示例基本上都是参照官方页面进行执行的,更多用法可以通过pydoc ansible或者通过python里的help(ansible)查看。另外在多主机执行时,可以使用async(异部)方式运行。

二、ansible_playbook api

ansible_playbook api 部分在官方文档上并没有提,不过通过查看ansible模块的帮助信息可以看到其是支持的。在ansible google论坛里(需FQ),有老外也给出里代码,其实它和执行ansible的api方式一样,只是多了个几个参数:

import ansible.playbook
2.from ansible import callbacks
3.from ansible import utils
4.stats = callbacks.AggregateStats()
5.playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
6.runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY)
7.pb = ansible.playbook.PlayBook(
8.    playbook="nseries.yml",
9.    stats=stats,
10.    callbacks=playbook_cb,
11.    runner_callbacks=runner_cb,
12.    check=True
13.)
14.for (play_ds, play_basedir) in zip(pb.playbook, pb.play_basedirs):
15.    import ipdb
16.    ipdb.set_trace()
17.    # Can play around here to see what\'s going on.
18.pb.run()

 

大致看了下代码,在用api的方式执行playbook的时候,playbook,stats,callbacks,runner_callbacks这几个参数是必须的。不使用的时候会报错。

 

arguments = []
2.if playbook is None:
3.    arguments.append(\'playbook\')
4.if callbacks is None:
5.    arguments.append(\'callbacks\')
6.if runner_callbacks is None:
7.    arguments.append(\'runner_callbacks\')
8.if stats is None:
9.    arguments.append(\'stats\')
10.if arguments:
11.    raise Exception(\'PlayBook missing required arguments: %s\' % \', \'.join(arguments))

 

playbook用来指定playbook的yaml文件

stats用来收集playbook执行期间的状态信息,最后会进行汇总

callbacks用来输出playbook执行的结果

runner_callbacks用来输出playbook执行期间的结果。但是它返回的结果太简单,我想让它详细点,如果用自定义callback的方法插入到mongo里面的话也行,或者是直接输出,但是我想所有task都执行完后,把每个task的详细信息输出到终端上,最后发现结果输出都是靠callbacks.py里的AggregateStats这个类,在每执行完一个task后,都会调用AggregateStats进行计算,汇总。

 

[root@361way api]# cat playbook_api.py
2.#!/usr/bin/env python
3.# coding=utf-8
4.import ansible.playbook
5.from ansible import callbacks
6.from ansible import utils
7.import json
8.stats = callbacks.AggregateStats()
9.playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
10.runner_cb = callbacks.PlaybookRunnerCallbacks(stats,verbose=utils.VERBOSITY)
11.res=ansible.playbook.PlayBook(
12.            playbook=\'/etc/ansible/playbooks/user.yml\',
13.            stats=stats,
14.            callbacks=playbook_cb,
15.            runner_callbacks=runner_cb
16.    ).run()
17.data = json.dumps(res,indent=4)
18.print data
19.# 执行结果如下:
20.[root@361way api]# python playbook_api.py
21.PLAY [create user] ************************************************************
22.TASK: [create test "{{ user }}"] **********************************************
23.changed: [10.212.52.16]
24.changed: [10.212.52.14]
25.{
26.    "10.212.52.16": {
27.        "unreachable": 0,
28.        "skipped": 0,
29.        "ok": 1,
30.        "changed": 1,
31.        "failures": 0
32.    },
33.    "10.212.52.14": {
34.        "unreachable": 0,
35.        "skipped": 0,
36.        "ok": 1,
37.        "changed": 1,
38.        "failures": 0
39.    }
40.}
41.[root@361way api]# 

 

三、总结

 

从上面的例子来看,感觉作用似乎有点鸡肋。多条ansible shell 指令的执行可以写成playbook 来执行,ansbile-playbook 也可以通过include 调用子playbook ,似乎API 部分用处并不大 。咋一听深感有理,不过细究一下,

1、当需要先对前一次作任务执行的结果进行处理,并将相应的结果对应的作为输入再在一次任务传入时,这里使用api 更方便;

2、需要对结果输出进行整形时,也比较api 方便;

3、playbook 之间进行调用或都playbook比较复杂时,想要理清任务之间的关系势必累显麻烦,而通过api,从上一层任务到下一层任务之间的调用关系明子。而且playbook之间可以是平行的关系。方便小的功能模块的复用。

4、方便二次开发及和其他程序之间的耦合调用----目前感觉这条是最实用的。

 

 

 

 

ansible小结(十一)ansible普通用户su切换问题

在之前我曾总结了一点ansible系列博文,本篇还承接之前的篇幅吧。在现网应用中,安全加固后的主机是不允许直接以root用户登陆的,而很多命令又需要root用户来执行,在不改造现网的情况下。希望通过一个普通用户先登陆,再su切到root执行。而且每台主机的普通用户和root用户的密码又不同。希望在通过ansible执行的时候不需要交互输入密码,而是直接执行后输出结果。

一、ansible hosts配置文件

在之前的系列文章中我们提到,可以把密码写到hosts配置文件,通过查询官网的相关信息了解了,其除了ansible_ssh_user、ansible_ssh_pass变量外,还为su切换提供了ansible_su_pass变量,通过该变量我们可以把root密码直接写到配置文件中。具体如下:

[root@361way.com ~]# cat /etc/ansible/hosts
2.[test01]
3.10.212.52.14 ansible_ssh_user=test ansible_ssh_pass=111111 ansible_su_pass=*I2145
4.10.212.52.16 ansible_ssh_user=test ansible_ssh_pass=xyz123  ansible_su_pass=mn1Pokm
5.10.212.52.252 ansible_ssh_user=amos ansible_ssh_pass=asdf ansible_su_pass=xyzp

注:我测试使用的ansible版本是1.9版的,在新的2.0版本中,变量也做了变化ansible_become_pass替换了之前的ansible_sudo_pass or ansible_su_pass ,具体可以参看官方文档

二、ansible命令参数

在执行ansible -h查看时,会看到如下条目:

-S, --su              run operations with su (deprecated, use become)
2.  -R SU_USER, --su-user=SU_USER
3.                        run operations with su as this user (default=root)
4.                        (deprecated, use become)

三、su切换执行

所以结合上面两块,我们做下简单的测试:

[root@361way.com ~]# ansible all  -S -R root -m shell -a "uptime"
2.10.212.52.252 | success | rc=0 >>
3. 16:13pm  up 34 days  5:40,  2 users,  load average: 0.08, 0.21, 0.30
4.10.212.52.16 | success | rc=0 >>
5. 16:26pm  up 538 days 23:17,  2 users,  load average: 0.00, 0.01, 0.05
6.10.212.52.14 | success | rc=0 >>
7. 16:24pm  up 538 days 22:39,  2 users,  load average: 0.00, 0.01, 0.05

这里需要注意的是,普通用户的家目录是要存在,并切该普通用户要有写的权限的,不然会出现类似如下的报错:

10.212.52.252 | FAILED => Authentication or permission failure.
2.In some cases, you may have been able to authenticate and did not have permissions on the remote directory.
3.Consider changing the remote temp path in ansible.cfg to a path rooted in "/tmp".
4.Failed command was: mkdir -p $HOME/.ansible/tmp/ansible-tmp-1449456070.96-212322517029279 && echo $HOME/.ansible/tmp/ansible-tmp-1449456070.96-212322517029279,
5.exited with result 1: mkdir: cannot create directory `/home/amos/.ansible\':

当然,如果这个普通用户没有家目录或者家目录没有写权限在不修改远端主机也有办法可以搞定,修改ansible主机的ansible.cfg配置文件,如下:

[root@361way.com ~]# vim /etc/ansible/ansible.cfg
2.找到如下行:
3.remote_tmp     = $HOME/.ansible/tmp
4.修改为
5.remote_tmp     = /tmp/.ansible/tmp
tmp目录一般都有写的权限吧,改成临时目录为/tmp下即可。

再下为我们再看看远程主机的message日志文件确认下是否真的是通过普通用户切换的:
Dec  3 11:36:20 linux su: (to root) test on /dev/pts/1     //由普通用户test切换为su切换为root的日志
2.Dec  3 11:36:20 linux ansible-command: Invoked with creates=None executable

功能实现了,最后要说的是,由于该配置文件中涉及到多台主机的用户名密码,所以该文件的安全工作一定要做好。

 

 

 

ansible小结(十 二)磁盘使用率筛选

 

年底了虽然有各种告警,不过还是需要做一下全网巡检,这里以磁盘使用率为例。由于目前平台上使用的是商业版的自动化工具bmc,其在执行分发都方面非常不存,不过在取值返回上做的不好---当然也和其是商业化工具有关,很多东西不开源,用shell 的语法概念写nsh时又执行不通过,NND总是要问原厂支撑 。刚好在测试环境上有之前安装的ansible环境,也测试下用ansible 来实现该功能。

一、实现目标

 

将所有主机的磁盘大于75%的主机获取到,并输出为下面的格式:

主机IP    主机名      磁盘挂载点信息     磁盘使用率

假如 host1上有多个分区都大于75% ,则写多条。当主机上没有一台符合时,则什么都不输出。

注:现网主机上有两个 bond 网卡,其中一个配置的是10段的IP,另一个配置的是192段的IP。这里要获取的是10段的IP。

二、 ansible + awk 简单输出

该问题我处理的结果是使用ansible api 写的一个python脚本实现的。先看下我在一个技术群和一个大牛的讨论的结果(点击图片看大图)。

 

如上图,大牛的做法用的是awk 的next方法进行的处理,awk 的next功能我也做过记录,具体参看awk next多行合并

这里有几个注意点需要说明下:

1、大牛的写法中在筛选上写的不严谨,$5前应该有int转化,不过不能正确的取到值

[root@361way ~]# df -hP|awk \'NR>1 && $5 > 20\'
2./dev/xvda1       20G  4.6G   15G  25% /
3./dev/xvdb        20G  645M   18G   4% /data1
4.[root@361way ~]# df -hP|awk \'NR>1 && int($5) > 20\'
5./dev/xvda1       20G  4.6G   15G  25% /

有没有看到,如果不做int格式转换取的结果是所有分区的信息,而加上int后获取的才是我们想要的结果

2、在ansible执行时$5前面要加上转义

# ansible all -m shell -a "df -hP|awk \'NR>1 && int($5) > 50\'"
2.10.212.52.252 | FAILED | rc=2 >>
3.awk: fatal: 0 is invalid as number of arguments for int
4.[root@361way ~]# df -hP|awk \'NR>1 && int(\$5) > 20\'
5.awk: NR>1 && int(\$5) > 20
6.awk:             ^ backslash not last character on line

如上面的结果,如果在ansible执行时不加转义时会有报错提示,如果加上转义在主机端执行时会自动是将转义符去掉的结果。而在主机端执行时,如果加上了转义也会报错。所以主机端一定不能加上转义。

3、在实际应用时,输出结果可能和大牛的输出有差距

[root@localhost ~]# ansible all -m shell -a "df -hP|awk \'NR>1 && int(\$5) > 30\'"|awk \'/success/{ip=$1;next}{print ip,$0}\'
2.10.212.52.252 /dev/sda9       9.9G  2.9G  6.5G  31% /opt
3.10.212.52.252 /dev/sda6       5.0G  1.9G  2.8G  41% /tmp
4.10.212.52.252 /dev/sda5       9.9G  3.9G  5.5G  42% /usr
5.10.212.52.252
6.10.212.52.14 /dev/cciss/c0d0p5  9.9G  3.2G  6.3G  34% /usr
7.10.212.52.14
8.10.212.52.16 /dev/cciss/c0d0p7  9.9G  4.0G  5.4G  43% /tmp
9.10.212.52.16 /dev/cciss/c0d0p5  9.9G  2.9G  6.5G  31% /usrdf: `/root/.gvfs\': Permission denied
10.10.212.52.16 

上面的结果是我在自己的测试环境上执行的结果。可以看到多出的空行也打印了主机的IP 。还会需要注意的,我这里ansible输出的success是小写的。

不知道以上的问题是不是使用的环境不同造成的。我运行的环境是ansible主机为redhat6,ansible版本为1.9,被取数据主机有redhat6和suse11 。不过这都是小问题,同样可以通过处理获取到正常的结果。

4、ansible api 执行

上面的大牛的结果,我使用ansible api 执行,如下,可以对比下:

[root@localhost ~]# cat /tmp/test.py
2.#!/usr/bin/env python
3.# coding=utf-8
4.# author : www.361way.com
5.# mail : itybku@139.com
6.import ansible.runner
7.#import json
8.runner = ansible.runner.Runner(
9.           module_name=\'shell\',
10.           module_args="df -hP|awk \'NR>1 && int($5)>30\'",
11.           pattern=\'all\',
12.           forks=10
13.        )
14.results = runner.run()
15.#print  results
16.for (hostname, result) in results[\'contacted\'].items():
17.    if not \'failed\' in result:
18.        for line in  result[\'stdout\'].split(\'\n\'):
19.            print "%s  %s" % (hostname, line)
20.# 执行结果如下
21.[root@localhost ~]# python /tmp/test.py
22.10.212.52.16  /dev/cciss/c0d0p7  9.9G  4.0G  5.4G  43% /tmp
23.10.212.52.16  /dev/cciss/c0d0p5  9.9G  2.9G  6.5G  31% /usr
24.10.212.52.252  /dev/sda9       9.9G  2.9G  6.5G  31% /opt
25.10.212.52.252  /dev/sda6       5.0G  1.9G  2.8G  41% /tmp
26.10.212.52.252  /dev/sda5       9.9G  3.9G  5.5G  42% /usr
27.10.212.52.14  /dev/cciss/c0d0p5  9.9G  3.2G  6.3G  34% /usr

三、还是ansible api 获取磁盘信息

上面的方法中实际执行时,比我们预期需要的效果少了主机名一项。这里我换做执行脚本实现,实现效果如下:

#/bin/bash
2.# author : www.361way.com
3.IP=`ip add show|grep inet|grep 10|awk \'{print $2}\'`
4.df -hl|grep \'^/\'|sed \'s/%//g\'|awk \'{if($5>30) print $0}\'|while read line
5.do
6.    echo $IP `hostname` $line
7.done
8.# 执行结果如下
9.# sh aa.sh
10.10.212.52.253/24 localhost /dev/sda3 9.5G 5.7G 3.4G 64 /
11.10.212.52.253/24 localhost /dev/sda2 39G 19G 18G 52 /home
12.10.212.52.253/24 localhost /dev/sda6 9.5G 7.1G 2.0G 78 /usr

使用ansible api 执行该脚本的结果如下:

# python dfscript.py
2.df: `/root/.gvfs\': Permission denied
3.df: `/root/.gvfs\': Permission denied
4.10.212.52.16/24 linux /dev/cciss/c0d0p7 9.9G 4.0G 5.4G 43 /tmp
5.10.212.52.16/24 linux /dev/cciss/c0d0p5 9.9G 2.9G 6.5G 31 /usr
6.10.212.52.252/24 zjhz-bmc-test /dev/sda9 9.9G 2.9G 6.5G 31 /opt
7.10.212.52.252/24 zjhz-bmc-test /dev/sda6 5.0G 1.9G 2.8G 41 /tmp
8.10.212.52.252/24 zjhz-bmc-test /dev/sda5 9.9G 3.9G 5.5G 42 /usr
9.10.212.52.14/24 linux /dev/cciss/c0d0p5 9.9G 3.2G 6.3G 34 /usr

dfscript.py脚本内容如下

# cat dfscript.py
2.#!/usr/bin/env python
3.# coding=utf-8
4.# author : www.361way.com
5.# mail : itybku@139.com
6.import ansible.runner
7.#import json
8.runner = ansible.runner.Runner(
9.           module_name=\'script\',
10.           module_args="aa.sh",
11.           pattern=\'all\',
12.           forks=10
13.        )
14.results = runner.run()
15.#print  results
16.for (hostname, result) in results[\'contacted\'].items():
17.    if not \'failed\' in result:
18.        for line in  result[\'stdout\'].split(\'\r\n\'):
19.            #print "%s  %s" % (hostname, line)
20.            print line

直接对该脚本执行后的结果进行grep 标准输出时,会发现其行与行之间是以\r\n这样的方式分行的。

所以在数据获取方面,尽量以ansible api 的方式进行获取,而api 的使用非常简单,无法是几个参数的替换后面再调用run方法,最终在对结果进行处理。而涉及多项信息获取时,建议使用自定义模块的方法,先将所需数据取回来,以json方式返回---ansible自定义模块所要求的格式。返回后可以再以api 或其他方式处理返回的数据即可。

 

版权声明:本文为gandefeng原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/gandefeng/p/6915478.html