一、ansible简介

1、ansible是什么?

   ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能

项目地址:https://github.com/ansible/ansible

最新稳定版:v2.2.2.0-1


2、特性

基于python语言实现,由paramiko,PyYAML和Jinjia2三个关键模块构建

no agents:不需要在被管控主机上安装任何客户端;   

no server:无服务器端,使用时直接运行命令即可;

modules in any languages:基于模块完成各种任务,可使用任意语言开发模块;

YAML,not code:使用yaml语言定制playbook(剧本);

ssh by default:基于SSH工作;

    1) 基于密钥认证

    2)在inventory文件中指定帐号和密码

主从模式:

  master:ansible,是ssh client

  slave:ssh server

支持Playbooks

strong multi-tier solution强大的多层解决方案


3、优点

  轻量级,部署简单,无需在客户端安装agent,更新时,只需在操作机上进行一次更新即可;
  批量任务执行可以写成脚本,而且不用分发到远程就可以执行;
  使用python编写,维护更简单,ruby语法过于复杂;
  命令或Playbooks执行结果幂等

  支持sudo。


4、组织结构

   ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。

如下图所示:

【Ansible】01、Ansible基础_第1张图片

主要包括:

 1)ansible core:ansible核心

 2)connection plugins:连接插件

     负责和被监控端实现通信;

 3)host inventory主机清单

     是一个配置文件;在里面定义被管理的主机。

     包括被管理的每个主机的IP,掩码,sshd监听的端口号、帐号和密码,可以将被管理的主机分组 

 4)各种核心模块自定义模块

 5)Playbooks:YAML文件格式,也支持jinjia2模版语言来定义模版

      剧本执行多个任务时,非必需可以让节点一次性运行多个任务

 6)借助于插件完成记录日志邮件等功能;


二、ansible安装和配置

1、ansible安装

    epel源中就提供了最新稳定的版本

[root@Node6 ~]# yum list|grep ansible
ansible.noarch                              2.2.1.0-1.el6                epel   

[root@Node6 ~]# yum install ansible   

[root@Node6 ~]# rpm -ql ansible|less
/etc/ansible
/etc/ansible/ansible.cfg
/etc/ansible/hosts
/etc/ansible/roles
/usr/bin/ansible
/usr/bin/ansible-console
/usr/bin/ansible-doc
/usr/bin/ansible-galaxy
/usr/bin/ansible-playbook
/usr/bin/ansible-pull
/usr/bin/ansible-vault
/usr/lib/python2.6/site-packages/

  ansible不是服务,没有任何服务需要运行,使用时直接使用ansible命令运行即可,所谓 no server 


2、ansible配置

ansible的配置文件:

[root@Node6 ~]# cd /etc/ansible/
[root@Node6 ansible]# ls
ansible.cfg  hosts  roles

ansible.cfg:ansible的主配置文件

hosts:主机清单配置文件

roles:角色配置目录


先看/etc/ansible/hosts:

[root@Node6 ansible]# cat hosts 
# This is the default ansible 'hosts' file.
#
# It should live in /etc/ansible/hosts
#
#   - Comments begin with the '#' character
#   - Blank lines are ignored
#   - Groups of hosts are delimited by [header] elements
#   - You can enter hostnames or ip addresses
#   - A hostname/ip can be a member of multiple groups

# Ex 1: Ungrouped hosts, specify before any group headers.   
  
   ### 可以使用主机名(要能解析)或ip地址列在这里

## green.example.com
## blue.example.com
## 192.168.100.1
## 192.168.100.10

# Ex 2: A collection of hosts belonging to the 'webservers' group

 ### 可以将多个主机定义组

## [webservers]
## alpha.example.org
## beta.example.org
## 192.168.1.100
## 192.168.1.110

# If you have multiple hosts following a pattern you can specify
# them like this:

  ### 某种风格主机名的集合;下面这个例子表示主机名:www001.example.com --> www006.example.com 6个主机 

## www[001:006].example.com


# Ex 3: A collection of database servers in the 'dbservers' group

## [dbservers]
## 
## db01.intranet.mydomain.net
## db02.intranet.mydomain.net
## 10.25.1.56
## 10.25.1.57

# Here's another example of host ranges, this time there are no
# leading 0s:

## db-[99:101]-node.example.com


###########################################################

# 需要已经做过密钥认证

node4               # 需要能解析该主机名

[xj]
192.168.10.1:22
192.168.10.2


四、ansible命令使用

1、获取帮助信息

    通过 ansible-doc 命令获取ansible使用帮助信息

[root@Node6 ~]# ansible-doc --help
Usage: ansible-doc [options] [module...]

Options:
  -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
  
  
[root@Node6 ~]# ansible-doc -s yum       # 查看 yum 模块的帮助信息 
- name: Manages packages with the `yum' package manager
  action: yum
      conf_file         # The remote yum configuration file to use for the transaction.
      disable_gpg_check # Whether to disable the GPG checking of signatures of packages
                               being installed. Has an effect
                               only if state is `present' or
                               `latest'.
      disablerepo       # `Repoid' of repositories to disable for the install/update
                               operation. These repos will
                               not persist beyond the
                               transaction. When specifying
                               multiple repos, separate them
                               with a ",".
      enablerepo       # `Repoid' of repositories to enable for the install/update
                               operation. These repos will
                               not persist beyond the
                               transaction. When specifying
                               multiple repos, separate them
                               with a ",".
      exclude                # Package name(s) to exclude when state=present, or latest
      list                   # Various (non-idempotent) commands for usage with
                               `/usr/bin/ansible' and `not'
                               playbooks. See examples.
      name=                  # Package name, or package specifier with version, like
                               `name-1.0'. When using
                               state=latest, this can be '*'
                               which means run: yum -y
                               update. You can also pass a
                               url or a local path to a rpm
                               file (using state=present).
                               To operate on several packages
                               this can accept a comma
                               separated list of packages or
                               (as of 2.0) a list of
                               packages.
      state              # Whether to install (`present' or `installed', `latest'), or
                               remove (`absent' or `removed')
                               a package.
      update_cache       # Force updating the cache. Has an effect only if state is
                               `present' or `latest'.
      validate_certs     # This only applies if using a https url as the source of the
                               rpm. e.g. for localinstall. If
                               set to `no', the SSL
                               certificates will not be
                               validated. This should only
                               set to `no' used on personally
                               controlled sites using self-
                               signed certificates as it
                               avoids verifying the source
                               site. Prior to 2.1 the code
                               worked as if this was set to
                               `yes'.


2、ansbile 命令使用语法

       ansible [-f forks] [-m module_name] [-a args]

host-patten:

    对哪些主机生效多个ip之间使用","分隔,all表示所有主机,也可以使用主机清单文件中定义的组名;在主机清单文件中指定的是ip这里就使用ip,指定是主机名就使用主机名

-f forks:启动的并发线程数

-m module_name:要使用的模块

-a args:模块特有的参数


例子:

[root@Node6 ~]# ansible node4 -a "hostname"
node4 | SUCCESS | rc=0 >>
Node4

[root@Node6 ~]# ansible 192.168.10.1,192.168.10.2 -a "hostname"
192.168.10.1 | SUCCESS | rc=0 >>
Node1

192.168.10.2 | SUCCESS | rc=0 >>
Node2

[root@Node6 ~]# ansible all -a 'hostname'
192.168.10.1 | SUCCESS | rc=0 >>
Node1

192.168.10.2 | SUCCESS | rc=0 >>
Node2

node4 | SUCCESS | rc=0 >>
Node4

[root@Node6 ~]# ansible All -a 'hostname'
 [WARNING]: No hosts matched, nothing to do

[root@Node6 ~]# ansible ALL -a 'hostname'
 [WARNING]: No hosts matched, nothing to do


五、ansible常用模块

1、command 命令模块

         默认模块,用于在远程主机上执行命令

[root@Node6 ~]# ansible node4 -m command -a "hostname -I"
node4 | SUCCESS | rc=0 >>
192.168.10.4 192.168.100.13 

[root@Node6 ~]# ansible all -a "hostname -I"  
                                      # command是默认模块,-m moudule_name 可以省略
192.168.10.2 | SUCCESS | rc=0 >>
192.168.10.2 192.168.100.4 

192.168.10.1 | SUCCESS | rc=0 >>
192.168.10.1 192.168.100.9 

node4 | SUCCESS | rc=0 >>
192.168.10.4 192.168.100.13

[root@Node6 ~]# ansible all,localhost -a "hostname -I"
localhost | SUCCESS | rc=0 >>
192.168.10.8 192.168.100.17 

192.168.10.2 | SUCCESS | rc=0 >>
192.168.10.2 192.168.100.4 

node4 | SUCCESS | rc=0 >>
192.168.10.4 192.168.100.13 

192.168.10.1 | SUCCESS | rc=0 >>
192.168.10.1 192.168.100.9


2、cron  计划任务模块

    用于在远程主机设置计划任务

[root@Node6 ~]# ansible-doc -s cron
- name: Manage cron.d and crontab entries.
  action: cron
      backup          # If set, create a backup of the crontab before it is modified.
                               The location of the backup is
                               returned in the `backup_file'
                               variable by this module.
      cron_file       # If specified, uses this file instead of an individual user's
                               crontab. If this is a relative
                               path, it is interpreted with
                               respect to /etc/cron.d. (If it
                               is absolute, it will typically
                               be /etc/crontab). To use the
                               `cron_file' parameter you must
                               specify the `user' as well.
      day             # Day of the month the job should run ( 1-31, *, */2, etc )
                                   ### 指定天
      disabled        # If the job should be disabled (commented out) in the crontab.
                               Only has effect if
                               state=present
      env                    # If set, manages a crontab's environment variable. New
                               variables are added on top of
                               crontab. "name" and "value"
                               parameters are the name and
                               the value of environment
                               variable.
      hour                   # Hour when the job should run ( 0-23, *, */2, etc )
                                   ### 指定小时
      insertafter            # Used with `state=present' and `env'. If specified, the
                               environment variable will be
                               inserted after the declaration
                               of specified environment
                               variable.
      insertbefore           # Used with `state=present' and `env'. If specified, the
                               environment variable will be
                               inserted before the
                               declaration of specified
                               environment variable.
      job                    # The command to execute or, if env is set, the value of
                               environment variable. Required
                               if state=present.
                                   ### 指定要执行任务
      minute                 # Minute when the job should run ( 0-59, *, */2, etc )
                                   ### 指定分钟
      month              # Month of the year the job should run ( 1-12, *, */2, etc )
                                   ### 指定月份
      name            # Description of a crontab entry or, if env is set, the name of
                               environment variable. Required
                               if state=absent. Note that if
                               name is not set and
                               state=present, then a new
                               crontab entry will always be
                               created, regardless of
                               existing ones.
      reboot                 # If the job should be run at reboot. This option is
                               deprecated. Users should use
                               special_time.
      special_time           # Special time specification nickname.
      state             # Whether to ensure the job or environment variable is present
                               or absent.
                                    ### 添加或者移除任务
                               
      user                   # The specific user whose crontab should be modified.
      weekday          # Day of the week that the job should run ( 0-6 for Sunday-
                               Saturday, *, etc )
                                   ### 指定星期几
(END)

例子:

[root@Node6 ~]# ansible xj -f 2 -m cron -a 'minute=*/3 job="echo hello &> /root/cron.txt" name="test cron job of xj"'
192.168.10.2 | SUCCESS => {
    "changed": true, 
    "envs": [], 
    "jobs": [
        "test cron job of xj"
    ]
}
192.168.10.1 | SUCCESS => {
    "changed": true, 
    "envs": [], 
    "jobs": [
        "test cron job of xj"
    ]
}

[root@Node1 ~]# crontab -l            # 调用crond服务
#Ansible: test cron job of xj     
*/3 * * * * echo hello &> /root/cron.txt

[root@Node1 ~]# cat cron.txt
hello

[root@Node6 ~]# ansible xj -f 2 -m cron -a 'name="test cron job of xj" state="absent"'
                                       # 移除cron 任务
192.168.10.1 | SUCCESS => {
    "changed": true, 
    "envs": [], 
    "jobs": [
        "None"
    ]
}
192.168.10.2 | SUCCESS => {
    "changed": true, 
    "envs": [], 
    "jobs": [
        "None"
    ]
}


3、user,group 用户,组管理

[root@Node6 ~]# ansible xj -m user -a "name=user4 createhome=no system=yes shell=/sbin/nologin" 

192.168.10.2 | SUCCESS => {
    "changed": true, 
    "comment": "", 
    "createhome": false, 
    "group": 495, 
    "home": "/home/user4", 
    "name": "user4", 
    "shell": "/sbin/nologin", 
    "state": "present", 
    "system": true, 
    "uid": 496
}
192.168.10.1 | SUCCESS => {
    "changed": true, 
    "comment": "", 
    "createhome": false, 
    "group": 496, 
    "home": "/home/user4", 
    "name": "user4", 
    "shell": "/sbin/nologin", 
    "state": "present", 
    "system": true, 
    "uid": 496
}



4、copy    复制或新建文件

src=     定义本地源文件路径可以是相对路径和绝对路径

dest=    定义远程目标文件路径,只能是绝对路径

content=  取代src选项,用此处指定的信息生成为目标文件内容

[root@Node6 ~]# ansible xj -m copy -a "src=/etc/fstab dest=/tmp/fstab.txt mode=640"
192.168.10.2 | SUCCESS => {
    "changed": true, 
    "checksum": "b43214f5dd10c80fa48a7c99875c21ff263eaa94", 
    "dest": "/tmp/fstab.txt", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "f317c0c8d83588c44c02c84041dfc287", 
    "mode": "0640", 
    "owner": "root", 
    "size": 805, 
    "src": "/root/.ansible/tmp/ansible-tmp-1491217643.44-272268881932863/source", 
    "state": "file", 
    "uid": 0
}
192.168.10.1 | SUCCESS => {
    "changed": true, 
    "checksum": "b43214f5dd10c80fa48a7c99875c21ff263eaa94", 
    "dest": "/tmp/fstab.txt", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "f317c0c8d83588c44c02c84041dfc287", 
    "mode": "0640", 
    "owner": "root", 
    "size": 805, 
    "src": "/root/.ansible/tmp/ansible-tmp-1491217643.49-272925226793088/source", 
    "state": "file", 
    "uid": 0
}


[root@Node6 ~]# ansible xj -m copy -a 'content="1\n2" dest=/tmp/src/1.txt'
192.168.10.2 | FAILED! => {
    "changed": false, 
    "checksum": "860bfb3bbfa6c2aa4a3a0c99b5ef584e98954612", 
    "failed": true, 
    "msg": "Destination directory /tmp/src does not exist"
}
192.168.10.1 | SUCCESS => {
    "changed": true, 
    "checksum": "860bfb3bbfa6c2aa4a3a0c99b5ef584e98954612", 
    "dest": "/tmp/src/1.txt", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "a1fe7d8e64a2b3f20e90b79387bff527", 
    "mode": "0644", 
    "owner": "root", 
    "size": 3, 
    "src": "/root/.ansible/tmp/ansible-tmp-1491218012.26-77327477558155/source", 
    "state": "file", 
    "uid": 0
}

[root@Node1 src]# cat 1.txt 
1
2[root@Node1 src]#        ### 需要自己添加换行符



5、file  设置文件属性

[root@Node6 ~]# ansible xj -m file -a "owner=sshd group=sshd mode=755 path=/tmp/src/1.txt"
                   ### 修改文件权限,属有者,属有组
192.168.10.2 | FAILED! => {
    "changed": false, 
    "failed": true, 
    "msg": "file (/tmp/src/1.txt) is absent, cannot continue", 
    "path": "/tmp/src/1.txt", 
    "state": "absent"
}
192.168.10.1 | SUCCESS => {
    "changed": true, 
    "gid": 74, 
    "group": "sshd", 
    "mode": "0755", 
    "owner": "sshd", 
    "path": "/tmp/src/1.txt", 
    "size": 3, 
    "state": "file", 
    "uid": 74
}


[root@Node6 ~]# ansible xj -m file -a "src=/etc/fstab path=/tmp/src/111 state=link"
                                   ### 创建软链接文件
192.168.10.2 | FAILED! => {
    "changed": false, 
    "failed": true, 
    "msg": "Error while linking: [Errno 2] No such file or directory", 
    "path": "/tmp/src/111", 
    "state": "absent"
}
192.168.10.1 | SUCCESS => {
    "changed": true, 
    "dest": "/tmp/src/111", 
    "gid": 0, 
    "group": "root", 
    "mode": "0777", 
    "owner": "root", 
    "size": 10, 
    "src": "/etc/fstab", 
    "state": "link", 
    "uid": 0
}


6、ping 测试指定主机是否能连接

[root@Node6 ~]# ansible xj -m ping
192.168.10.1 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
192.168.10.2 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}


7、service  管理服务

enabled=    是否开机自动启动,取值为 true 或者 false

name=      服务名称

state=     状态,取值有started,stopped,restarted

[root@Node6 ~]# ansible 192.168.10.1 -m service -a "name=httpd enabled=yes state=started"
192.168.10.1 | SUCCESS => {
    "changed": true, 
    "enabled": true, 
    "name": "httpd", 
    "state": "started"
}


8、shell  在远程主机上运行命令,尤其使用管道,变量等复杂命令

[root@Node6 ~]# ansible xj -a "tail /etc/passwd|tail -1 /etc/passwd"
192.168.10.2 | FAILED | rc=1 >>
tail: option used in invalid context -- 1

192.168.10.1 | FAILED | rc=1 >>
tail: option used in invalid context -- 1

[root@Node6 ~]# ansible xj -m shell -a "tail /etc/passwd|tail -1 /etc/passwd"
192.168.10.1 | SUCCESS | rc=0 >>
user4:x:496:496::/home/user4:/sbin/nologin

192.168.10.2 | SUCCESS | rc=0 >>
user3:x:501:501::/home/user3:/bin/bash


[root@Node6 ~]# ansible xj -a "echo '123'|passwd --stdin user3"
192.168.10.2 | SUCCESS | rc=0 >>
123|passwd --stdin user3

192.168.10.1 | SUCCESS | rc=0 >>
123|passwd --stdin user3

[root@Node6 ~]# ansible xj -m shell -a "echo '123'|passwd --stdin user3"
192.168.10.1 | SUCCESS | rc=0 >>
Changing password for user user3.
passwd: all authentication tokens updated successfully.

192.168.10.2 | SUCCESS | rc=0 >>
Changing password for user user3.
passwd: all authentication tokens updated successfully.


9、yum 安装程序包

name=   指明要安装的程序包,可以带上版本号

state=   present,latest表示安装,absent表示卸载

[root@Node6 ~]# ansible 192.168.10.2 -m yum -a "name=httpd"
192.168.10.2 | SUCCESS => {
    "changed": true, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n * base: mirrors.aliyun.com\n * epel: mirrors.aliyun.com\n * extras: mirrors.aliyun.com\n * updates: mirrors.aliyun.com\nSetting up Install Process\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.2.15-56.el6.centos.3 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package      Arch          Version                        Repository      Size\n================================================================================\nInstalling:\n httpd        x86_64        2.2.15-56.el6.centos.3         updates        834 k\n\nTransaction Summary\n================================================================================\nInstall       1 Package(s)\n\nTotal download size: 834 k\nInstalled size: 3.0 M\nDownloading Packages:\nRunning rpm_check_debug\nRunning Transaction Test\nTransaction Test Succeeded\nRunning Transaction\n\r  Installing : httpd-2.2.15-56.el6.centos.3.x86_64                          1/1 \n\r  Verifying  : httpd-2.2.15-56.el6.centos.3.x86_64                          1/1 \n\nInstalled:\n  httpd.x86_64 0:2.2.15-56.el6.centos.3                                         \n\nComplete!\n"
    ]
}


10、script 将本地脚本在远程节点上运行

[root@Node6 ~]# ls -l "/tmp/1.sh"
-rw-r--r-- 1 root root 44 Apr  3 19:49 /tmp/1.sh     ### 可以没有执行权限
[root@Node6 ~]# cat "/tmp/1.sh"
echo 111
echo 222
touch /tmp/src/script.txt
[root@Node6 ~]# ansible 192.168.10.1 -m script -a "/tmp/1.sh"
192.168.10.1 | SUCCESS => {
    "changed": true, 
    "rc": 0, 
    "stderr": "", 
    "stdout": "111\r\n222\r\n", 
    "stdout_lines": [
        "111", 
        "222"
    ]
}


11、setup 收集远程主机的facts

    每个被管理节点在接收并运行管理命令之前,会将自己主机相关信息,如果操作系统版本,IP地址等报告给ansible,得到的变量名和其值可以直接引用。

[root@Node6 ~]# ansible 192.168.10.1 -m setup
192.168.10.1 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [       ### facts变量:值
            "192.168.10.1", 
            "192.168.100.9"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::20c:29ff:fe99:d24e", 
            "fe80::20c:29ff:fe99:d258"
        ], 
        "ansible_architecture": "x86_64", 
        "ansible_bios_date": "07/02/2015", 
        "ansible_bios_version": "6.00", 
        "ansible_cmdline": {
            "KEYBOARDTYPE": "pc", 
            "KEYTABLE": "us", 
            ...
            
            
[root@Node6 ~]# ansible 192.168.10.1 -m setup|grep -A 5 ansible_processor 
                                                                 ### 查看cpu信息
        "ansible_processor": [
            "GenuineIntel", 
            "Intel(R) Core(TM) i5-3230M CPU @ 2.60GHz"
        ], 
        "ansible_processor_cores": 1, 
        "ansible_processor_count": 1, 
        "ansible_processor_threads_per_core": 1, 
        "ansible_processor_vcpus": 1, 
        "ansible_product_name": "VMware Virtual Platform", 
        "ansible_product_serial": "VMware-56 4d 9e 9d 56 67 65 5c-2f b3 ca 41 e1 99 d2 4e", 
        "ansible_product_uuid": "564D9E9D-5667-655C-2FB3-CA41E199D24E", 
        "ansible_product_version": "None", 
        "ansible_python": {


注意:

  使用ansible时,不一定一定要使用相应的模块,可以直接使用command、shell、script 模块更方便