ansible 安装与配置介绍

一、ansible介绍

1、ansible概述

ansible是新出现的运维工具是基于Python开发的,揉合了众多老牌运维工具的优点实现了批量操作系统配置、批量程序的部署、批量运行命令等功能。它的运行结构可以描述为C/S模式,在Server端安装ansible软件包,然后批量管理Client端。ansible被定义为配置管理工具,并且通常与Chef、Puppet以及Salt相提并论。

2、ansible的特点:

a、易用的语法

ansible的配置管理脚本叫playbook,并且playbook的语法是基于YAML开发的,而YAML是一种以易于人类读写为设计理念的数据格式语言。

b、远程主机不需要安装任何依赖

使用ansible管理的服务器需要安装ssh和Python 2.5(或更新版本),或者安装simplejson库的Python 2.4.除此之外,不再需要预装任何agent程序或其他软件。

注意:Server端(用于控制远程主机的那台主机)需要安装Python2.6或更高版本

c、基于推送模式

以Puppet、Saltstack为代表的使用agent程序的配置管理系统默认使用“拉取模式”,安装在服务器上的agent程序定期向中心服务报备状态并拉取相应的配置信息。相比ansible的推送模式较为复杂,话费时间相对较多。不过如果你真的更喜欢拉取模式,ansible官方版本也可以支持,可以使用一个名为ansible-pull的工具,它是在ansible官方版本内一起发布的。

d、内置模块

你可以使用ansible在你管理的远程服务器上执行任意shell命令,但是ansible真正强大的地方在于内置的一系列模块,通过模块你可以执行像安装软件包,重启服务或者复制配置文件这样的任务。

二、安装配置

实验环境介绍:

server: ansible          Centos 6.6 x86_64   
client1: web1            Centos 6.6 x86_64   
client2: web2            Centos 6.6 x86_64

ansible中文权威指南访问地址http://www.ansible.com.cn/           

1、在服务端安装ansible,不需要在客户端安装(这里介绍yum安装方式,可以利用gitlab源码安装,这里不做介绍)

首先安装epel源

#rpm -ivh http://fr2.rpmfind.net/linux/epel/6/x86_64/epel-release-6-8.noarch.rpm
安装ansible
#yum install ansible -y

2、ansible管理客户端的方式(普通用户和root用户)

a、普通用户

在server端和客户端分别创建ansible用户并添加密码(添加用户步骤不在描述)

使用ansible登录到服务端并生成公钥:

$ssh-keygen -t rsa     #一直回车即可
将公钥拷贝到客户端
$/usr/bin/ssh-copy-id -i /home/ansible/.ssh/id_rsa.pub [email protected]  #期间需要输入客户端ansible的密码

测试:

$cat /etc/ansible/hosts
[web]
10.0.90.25
$ansible web -m ping           #ping命令可以执行
10.0.90.25 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
$ansible web -m shell -a "ls /tmp" #查看/tmp目录可以执行
10.0.90.25 | SUCCESS | rc=0 >>
hsperfdata_root
yum.log
zabbix_agentd.log
zabbix_agentd.pid
$ansible web -m shell -a "ls /root" #查看/root目录是提示权限不足,因为是普通用户
10.0.90.25 | FAILED | rc=2 >>
ls: cannot open directory /root: Permission denied
$ansible web -m shell -a "touch /tmp/test.txt"  #普通用户可以操作/tmp目录 
10.0.90.25 | SUCCESS | rc=0 >>
$ansible web -m shell -a "touch /usr/local/src/test.txt"  #没权限创建文件 
10.0.90.25 | FAILED | rc=1 >>
touch: cannot touch `/usr/local/src/test.txt': Permission denied

注:由以上可以判定用普通用户管理客户端会因为权限的问题,导致错误,所以要切换到root来执行。

介绍几个参数,如下:

-S, --su              run operations with su (deprecated, use become)
-R SU_USER, --su-user=SU_USER
                      run operations with su as this user (default=root)
                      (deprecated, use become)
-K, --ask-sudo-pass   ask for sudo password (deprecated, use become)

一个在普通用户下su到root执行的命令,如下:

$ansible web -m shell -a "touch /usr/local/src/test.txt" -S -R root -K 
SU password:    #这里提示输入su密码,也就是root的密码
10.0.90.25 | SUCCESS | rc=0 >>

有些朋友更倾向于使用普通用户进行连接并使用sudo命令实现root权限,格式如下:

#ansible web -m ping -u ansible -s

但是使用以上命令,需要将ansible配置为sudo用户,不然也会提示错误,如下:

$ansible web -m ping -u ansible -s     
10.0.90.25 | FAILED! => {
    "changed": false, 
    "failed": true, 
    "module_stderr": "", 
    "module_stdout": "sudo: a password is required\r\n", 
    "msg": "MODULE FAILURE", 
    "parsed": false
}

将ansible配置为sudo用户(在客户端服务器10.0.90.25上操作)

#vim /etc/sudoers   ##在末尾添加如下两行:
ansible ALL=(ALL) NOPASSWD: ALL      
Defaults:ansible !requiretty      #指定ansible用户不需要tty

再次执行刚才的命令,就不会提示错误了,如下:

$ansible web -m ping -u ansible -s
10.0.90.25 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
$ansible web -m shell -a "touch /root/test_sudo.txt" -u ansible -s   #执行这些命令都是OK的
10.0.90.25 | SUCCESS | rc=0 >>

b、root用户

以root权限登录server端,生成公钥

#ssh-keygen -t rsa     #一直回车即可
将公钥拷贝到客户端
#/usr/bin/ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]  #期间需要输入客户端root的密码

如果不建立服务端与客户端的ssh信任,相对安全,但是每次执行命令都需要输入远端服务器root密码,而且客户端root密码必须一致,也挺麻烦,操作命令如下:

#ansible 10.0.90.25 -m ping -k    
SSH password:   --输入远端服务器的root密码
10.0.90.25 | success >> {
    "change": false,
    "ping": "pong"
}

注:服务端与客户端没有配置SSH信任,需要在执行ansible命令时添加 -k (小写字母k等价于--ask-pass)参数,需要提供root(默认)账号密码。由此可以断定使用sudo切换到root执行命令或者直接使用root执行命令本质没什么区别,但是由于root权限可以干任何事情,所以在操作的时候一定要检查确保自己的命令不会对客户端造成不可恢复的错误之后才能执行!!!(我本人直接使用root用户管理) 

PS:有一次在centos 6.5 x86_64 系统安装ansible之后,执行如下命令报错:

#ansible --version --报错如下:
Traceback (most recent call last):
  File "/usr/bin/ansible", line 36, in 
    from ansible.runner import Runner
ImportError: No module named ansible.runner

在google找了好多资料,也没解决,后来看到一篇说是修改/usr/bin/ansible文件的第一行python解释器(ansible依赖python 2.6以上版本)将/usr/bin/python 改为/usr/bin/python2.6 之后,就不报错了!原因是之前在这台服务器上编译安装过python2.7版本,使用#which python 命令查看到的是/usr/local/python2.7/bin/python,而/usr/bin/ansible的第一行调用的是/usr/bin/python,所以报错!)

3、批量拷贝服务端公钥到客户端的配置

参考:http://linuxg.blog.51cto.com/4410110/1710110

4、服务端配置:

[root@ansible ansible]# cat hosts
[web]
10.0.90.24
10.0.90.25
[hosts1]
172.16.29.193

注:hosts文件定义:如果没有配置服务端通过ssh无密码登录客户端,hosts文件配置如下:

[webhosts]  
172.16.10.22 ansible_ssh_user=root ansible_ssh_pass=mima
172.16.10.33 ansible_ssh_user=root ansible_ssh_pass=mima
解释  
#ansible_ssh_user=root 是ssh登陆用户  
#ansible_ssh_pass=mima 是ssh登陆密码

简单测试:

[root@ansible ansible]# ansible 172* -m shell -a "hostname"
172.16.29.193 | success | rc=0 >>
guang
[root@ansible ansible]# ansible host1 -m shell -a "hostname"
172.16.29.193 | success | rc=0 >>
guang
以上两种方式都可以,其中模块shell也可以换成command
[root@ansible ~]# ansible host1 -m command -a 'date'
172.16.29.193 | success | rc=0 >>
Wed Jun 10 22:37:20 CST 2015
默认的模块名为command ,即“-m command” 可以省略
如:ansible host1 -m command -a "uptime"  等价与  ansible host1 -a "uptime"
#ansible host1 -m service -a  "name=httpd state=restarted"

5、ansible 管理系统用户

首先生成密码:

#openssl passwd -1 -salt 12345678
Password:    --输入密码,就会生成加密字符串
创建:
#ansible web -m user -a 'name=test1 comment="add a test user" password="$1$12345678$qT.Vr20lsSaufZbuk4JIb."'
删除:
#ansible web -m user -a "name=test1 state=absent"   --使用这种方式删除用户,不会删除用户的家目录
#ansible web -m user -a "name=test1 state=absent remove=yes" --使用这种方式删除用户,可以删除用户的家目录

6、playbook简单介绍

使用ansible时,绝大部分时间都将花费在编写playbook上,playbook是一个ansible术语,它指的是用于配置管理的脚本,可以将一些列命令整合到playbook中,统一进行执行,节省时间.playbook 是一个不同于使用ansible命令行执行方式的模式,功能强大灵活,是一个简单的配置管理和多主机部署系统,不同于任何已经存在的模式,可作为一个适合部署复杂应用程序的基础,playbook可以定制配置,可以按指定的操作步骤有序执行,支持同步以及异步方式。同时playbook是由一个或多个“play”组成的列表,可以让它们联同起来按事先编排的机制执行;所谓task无非是调用ansible的一个module,而在模块参数中可以使用变量;模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致。

下面是一个简单的例子:批量创建用户,并且将用户添加到wheel组,如果不想添加到wheel组,去掉groups=wheel即可(未设置密码)

#cat add_user.yml 
---
- hosts: web
  remote_user: root
  gather_facts: true
  tasks:
  - name: Add several users
    user: name={{ item }} state=present groups=wheel
    with_items:
      - testuser1
      - testuser2

测试:检测编写的yml文件是否有语法错误

#ansible-playbook add_user.yml --syntax-check 
playbook: add_user.yml                   ##如果没有语法错误就会出现这样的结果,有错误会提示

执行:

#ansible-playbook add_user.yml

批量删除用户:   --可以将用户家目录也删除,从whell组中删除

#cat del_user.yml 
---
- hosts: web
  remote_user: root
  gather_facts: true
  tasks:
  - name: del several users
    user: name={{ item }} state=absent remove=yes
    with_items:
       - testuser1
       - testuser2

执行:(执行之前先用--syntax-check检测yml文件是否有语法错误,养成一个好习惯)

#ansible-playbook del_user.yml

批量创建用户并且设置密码

#cat add_user.yml 
---
- hosts: web
  remote_user: root
  gather_facts: true
  tasks:
  - name: Add several users
    user: name={{ item }} state=present password="$1$1234567$IElhfIqK0wF7y.p/fYkzb/"
    with_items:
       - testuser1
       - testuser2
执行:
#ansible-playbook add_user.yml

另一种方法:

# cat adduser.yml 
---
- hosts: web
  remote_user: root
  vars:                     ##变量
    username: testuser9        
    password: testusermima
  tasks:
    - name: add user
      user: name={{ username }} state=present
    - name: set password
      shell: /bin/echo {{ password }} | /usr/bin/passwd --stdin {{ username }}

前面说到为客户端配置sudo用户,是直接操作的/etc/sudoers文件,客户端少的情况还可以考虑这种方式,但是假如有几十台,甚至几百台客户端,这种方式就行不通了;这里就介绍通过playbook批量添加sudo用户得方式:

#cat add_sudoer.yml 
---
- hosts: all
  vars:
    user: test
    password: "$1$123456$fDuma3GXwl8ivHL9oSoCm1"
  remote_user: root
  tasks:
    - name: Add User test
      user: name={{ user }} comment="manage user" password={{ password }}
    - name: Config /etc/sudoers
      lineinfile: dest=/etc/sudoers state=present line='`item`' validate='visudo -cf %s'
      with_items:
             - "{{ user }} ALL=(ALL) NOPASSWD: ALL"
             - "Defaults:`user` !requiretty"

密码可以使用前面介绍的openssl生成,也可以使用如下方式生成:

#yum install python-pip -y
#pip install passlib -y
#python -c "from passlib.hash import sha512_crypt; import getpass; print sha512_crypt.encrypt(getpass.getpass())"
Password:    #这里会提示让你输入密码,就是你想为用户设置的密码,然后回车,就会看到加密后的密码串
$6$rounds=656000$CvpbftENezrD7dtl$hROqRJu77oQST2iqmk24ZaNtZ8OMFohLWRum63EDyqWwYgfaI5kLBHoVGzwdTJLyxOhY5hA9dNtJrBSQMk4cH1

测试是否有语法错误,如下表示正确。

#ansible-playbook add_sudoer.yml --syntax-check
playbook: add_sudoer.yml

执行:--需要输入客户端root的密码,所以客户端的root密码最好保持一致!

#ansible-playbook add_sudoer.yml -k
SSH password: 
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [10.0.90.26]
ok: [10.0.90.25]
TASK [Add User test] ***********************************************************
changed: [10.0.90.25]
changed: [10.0.90.26]
TASK [Config /etc/sudoers] *****************************************************
changed: [10.0.90.25] => (item=test ALL=(ALL) NOPASSWD: ALL)
changed: [10.0.90.25] => (item=Defaults:test  !requiretty)
changed: [10.0.90.26] => (item=test ALL=(ALL) NOPASSWD: ALL)
changed: [10.0.90.26] => (item=Defaults:test  !requiretty)
PLAY RECAP *********************************************************************
10.0.90.25                 : ok=3    changed=2    unreachable=0    failed=0   
10.0.90.26                 : ok=3    changed=2    unreachable=0    failed=0

如上可以看到添加sudo用户OK了!

多项目同时更新的一个例子:

#cat multi_copy.yml
---
- hosts: host1
  remote_user: root
  gather_facts: false
  tasks:
  - name: copy local server file to clint /tmp
    template: src=/root/install.log dest=/tmp/test-`item`
    with_items:
      - install.log-1
      - install.log-2
      - install.log-3
执行:     
#ansible-playbook  multi_copy.yml

根据条件进行删除:

#cat delete.yml
---
- hosts: host1
  remote_user: root
  gather_facts: true
  tasks:
  - name: if system is centos ,then rm /tmp/install.log-1
    shell: rm -f /tmp/test-install.log-3
    when: ansible_os_family == "Ubuntu"
执行:
#ansible-playbook delete.yml
注:当条件语句when:ansible_os_family == "Ubuntu" 成立时,shell:rm -rf /tmp/test-install.log-3命令才会运行,而且gather_facts: true必须为true。
redhat系列的系统:
when: ansible_os_family == "RedHat"

优化ansible-playbook运行时间:

默认playbook是进行客户端fact搜集,一般如果你配置里没有使用fact的话,可以关闭这样就能减少运行时间!

参考文档:http://www.jb51.net/article/52154.htm

注:本人ansible在学习和摸索中,不足之处请多多指教!
补充:
默认ansible是使用key验证的,如果是使用密码登陆的服务器,使用ansible的话,要不就修改ansible.cfg配置文件的ask_pass = True给取消注释,要不就在运行命令时候加上-k,这个意思是-k, --ask-pass   #ask for SSH password
如果客户端不在know_hosts里将会报错,如下:

#ansible web1 -m shell -a "hostname"
paramiko: The authenticity of host 'web1' can't be established.
The ssh-rsa key fingerprint is 59b5a079b9680998ad40c56166be96cc.
Are you sure you want to continue connecting (yes/no)?
如果出现上面的报错,说明客户端不在know_hosts里面
需要修改ansible.cfg文件,将#host_key_checking = False取消注释,即可!