ansible-分享

概览

  • Ansible 用来对一组机器进行批量操作,如应用部署、配置管理等
  • Ansible 完全基于Python开发,Linux系统下基于OpenSSH通信
  • Ansible 是开源软件,目前由Red Hat维护 (商业版提供了可视化界面)
  • Ansible 不需要数据库、不需要运行后台进程,不需要客户端(客户端无需任何配置,由管理端配置好后即可使用),对目标主机几乎完全无侵入,简单高效,学习成本相对低

Ansible 工作机制

Ansible主要由6部分组成

  • ANSIBLE PLAYBOOKS:任务剧本(任务集),编排定义Ansible任务集的配置文件,由Ansible顺序依次执行,通常是JSON格式的YML文件;
  • INVENTORY:Ansible管理主机的清单;
  • MODULES:Ansible执行命令的功能模块,多数为内置的核心模块,也可自定义;
  • PLUGINS:模块功能的补充,如连接类型插件、循环插件、变量插件、过滤插件等,该功能不常用。
  • API:供第三方程序调用的应用程序编程接口

Ansible 组件调用关系

安装

下文以CentOS7为例,Ansible支持的系统有: Red Hat, Debian, CentOS, macOS (windows不支持作为管理节点)

前提条件
===================================
1. 无论是管理节点(安装Ansible的机器),还是被管理节点其上均需要安装好Python并且版本满足:
Python 2.7+ OR Python 3.5+
2. 启用EPEL源

开始安装
===================================
yum install ansible
安装过程中,可能会自动安装的依赖参考如下:
    PyYAML                              
    libyaml                             
    python-babel                        
    python-backports                    
    python-backports-ssl_match_hostname 
    python-cffi                         
    python-enum34                       
    python-httplib2                     
    python-idna                         
    python-ipaddress                    
    python-jinja2                       
    python-markupsafe                   
    python-paramiko                     
    python-ply                          
    python-pycparser                    
    python-setuptools                   
    python2-cryptography                
    python2-jmespath                    
    python2-pyasn1                      
    sshpass                             
如果系统里的python是2.7版本,依赖却显示安装了很多python3的依赖,一般是因为yum源不够新,建议更换

Getting Started

https://docs.ansible.com/ansible/latest/user_guide/intro_getting_started.html

################################################### 机器准备 ##################################
192.168.1.113  x1
192.168.1.114  x2
192.168.1.118  x3
x1机器安装了Ansible,3台机器已经互相做好免密,并且
ssh x1 echo hello
ssh x2 echo hello
ssh x3 echo hello
如上的yes应当已经搞好

##################################### 添加主机清单 ###################################################
cat << 'EOF' >> /etc/ansible/hosts
x1
x2
x3
EOF

##################################### 测试连通性 ###################################################
[root@x1 ~]# ansible all -m ping
x2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
x3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
x1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

#####################################################  其它测试 ##################################
ansible all -a "/bin/echo hello"

#####################################################  查看版本号 ##################################
ansible --version

ad-hoc commands

直接在命令批量远程操作机器(ad-hoc),另外还有一种是书写playbook的方式

ansible [pattern] -m [module] -a "[module options]"

ansible atlanta -m copy -a "src=/etc/hosts dest=/tmp/hosts"

ansible webservers -m file -a "dest=/srv/foo/b.txt mode=600 owner=mdehaan group=mdehaan"

ansible all -m shell -a 'cat /etc/os-release'

ansible all  -m copy -a "src=/root/node_exporter-1.0.0.linux-amd64.tar.gz  dest=/tmp"
ansible all  -m copy -a "src=/root/node_exporter-1.0.0.linux-amd64.tar.gz  dest=/tmp"
上面的命令连续执行两次,可以发现第1次输出为黄色,表明系统已经做了修改,第2次输出为绿色表明无任何修改,具有一定的幂等性

ansible foo.example.com -m yum -a "name=httpd state=installed"

ansible all -m setup

主机清单 inventory

基础

在Ansible里把需要管理的机器提前放到配置文件里,默认是/etc/ansible/hosts

################################################ 简单主机分组 ##################################
[webservers]
10.0.0.31
10.0.0.41
10.0.0.61
[web01]
10.0.0.8

################################################# IP地址合并 ##################################
添加三台主机至webserver
[webservers]
web1.as4k.com
web2.as4k.com
web3.as4k.com

添加三台主机至webserver
[webservers]
web[1:3].as4k.com

####################################### 直接加上密码验证 ##################################
web1.as4k.com ansible_ssh_pass='123456'
web2.as4k.com ansible_ssh_pass='123456'
web3.as4k.com ansible_ssh_pass='123456'

动态获取 inventory

概述
==============================
Ansible启用动态Inventory的机制是通过调用外部脚本(任何脚本都可以,二进制文件也可以,只要运行结果返回的是JSON串就行)生成指定格式的JSON串。Ansible可以对JSON格式的字符串进行解析,并最终将其转化为Ansible可用的Inventory文件格式。所以,所谓的动态Inventory文件脚本开发,其实就是编写脚本根据具体环境将主机信息及关系(这些数据可以通过抓取数据库,调用外部API或者直接读取文件获得)以JSON格式来表示出来,并将其做为脚本输出结果传给Ansible。
需要注意的是,用于生成JSON代码的脚本必须支持两个选项:--list和--host。

--list:返回所有的主机组信息,每个组都应该包含字典形式的主机列表、子组列表,如果需要的话还应该有组变量,最简单的信息是只包含主机列表。返回的数据格式是JSON格式。
--host:返回该主机的变量列表,或者是返回一个空的字典,使用JSON格式

参考python脚本

#!/usr/bin/python3
# -*- coding: utf-8 -*-

#!/usr/bin/python3

import os
import sys
import json
import argparse


class ExampleInventory(object):
    def __init__(self):
        self.inventory = {}
        self.read_cli_args()

        # 定义 --list 选项
        if self.args.list:
            self.inventory = self.example_inventory()
        elif self.args.host:
            self.inventory = self.empty_inventory()
        else:
            self.inventory = self.empty_inventory()
        print(json.dumps(self.inventory))

    def example_inventory(self):
        return {
            'group': {
                'hosts': ['10.222.32.121', '10.222.32.122']
            }
        }

    def empty_inventory(self):
        return {'_meta': {
            'hostvars': {}
        }}

    def read_cli_args(self):
        parser = argparse.ArgumentParser()
        parser.add_argument('--list', action='store_true')
        parser.add_argument('--host', action='store')
        self.args = parser.parse_args()


ExampleInventory()

使用

chmod +x inventory.py

[root@node3 /etc/ansible]# ./inventory.py  --list
{"group": {"hosts": ["10.222.32.121", "10.222.32.122"]}}

[root@node3 /etc/ansible]# ansible all -i inventory.py  -m ping

手动指定 inventory 文件

inventories/        
    inventory-prod        
    inventory-dev

ansible-playbook playbook.yml -i inventories/inventory-dev

剧本 playbook

基础

剧本是用人类易读语言YAML,来描述对被管理机器需要进行的一些列操作。模块是商店,被管理机器是原材料,剧本就是做菜手册。上面按顺序记录了,如何把被管理机器一步步打造成指定的样子。

########################################### 快速入门 ###################################################
cat test.yaml
---
- hosts: nfs
  tasks:
    - name: copy files form m01 to nfs
      copy: src=/root/httpd.conf dest=/root/ mode=777

ansible-playbook test.yaml

上面的命令也可以换行书写
- hosts: nfs
  tasks:
    - name: copy files form m01 to nfs
      copy: src=/root/httpd.conf
            dest=/root/
            mode=777

########################################### 在剧本中使用shell模块 ##################################
shell和command模块比较特殊,其后直接接命令,不再是key=value的形式。

tasks:
  - name: show ip address
    command: hostname -I

########################################### Handlers - 事件处理器 ##################################
Running Operations On Change,在很多时候我们需要配置一些服务启动,但这些服务的启动  
往往都是有条件的,那就是**当配置文件发生变化时**自动重启,这时就需要使用notify,  
只要检测到配置文件变化,即调用对应的handlers处理,示例如下:

- name: template configuration file
  template:
    src: template.j2
    dest: /etc/foo.conf
  notify:
     - restart memcached
     - restart apache

handlers:
    - name: restart memcached
      service:
        name: memcached
        state: restarted
    - name: restart apache
      service:
        name: apache
        state: restarted

使用handlers要注意,此时name名称就是一一对应的,不仅仅是注释。

########################################### 语法检查和预执行 ###################################################
YAML对空格的要求非常严格,可使用:

    ansible-playbook --syntax-check test.yaml

模拟执行(不对目标机器实际进行操作):

    ansible-playbook -C test.yaml

大C参数表示预执行,不会真正修改被管理机器的东西,会提前告知我们可能发生的变化,错误。

看一下哪些机器被该剧本管理:

    ansible-playbook playbook.yaml --list-hosts

执行时输出详细信息:

    ansible-playbook test.yaml -C --verbose

Ansible 语法特性总结
=======================================
总的来说,Playbook语法具有如下一些特性。
1)需要以“---”(3个减号)开始,且需顶行首写。
2)次行开始正常写Playbook的内容,但笔者建议写明该Playbook的功能。
3)使用#号注释代码。
4)缩进必须是统一的,不能将空格和Tab混用。
5)缩进的级别必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的。
6)YAML文件内容和Linux系统大小写判断方式保持一致,是区别大小写的,k/v的值均需大小写敏感。
7)k/v的值可同行写也可换行写。同行使用“:”分隔,换行写需要以“-”分隔。
8)一个完整的代码块功能需最少元素,需包括name:task。
9)一个name只能包括一个task

剧本书写常用技巧

标签、限制具体机器、指定操作系统
=================================================
---
- name: copy node-ps single binary to remote machine /etc/dAppCluster
  copy: src=node-ps dest=/etc/dAppCluster/node-ps mode=0744
  tags:
  - INSTALL_OR_UPDATE
  when: 
  - ansible_distribution == "CentOS"
  - ansible_distribution_major_version == "6" or ansible_distribution_major_version == "7"

- name: copy node-ps manager scripts CENTOS7
  copy: src=node-ps.service dest=/usr/lib/systemd/system/node-ps.service
  tags:
  - INSTALL_OR_UPDATE
  when: 
  - ansible_distribution == "CentOS"
  - ansible_distribution_major_version == "7"


ansible-playbook  playbooks/dpool_zsh.yml --limit 10.13.32.39 (换成分组名即限制某一组机器)
ansible-playbook  playbooks/node_ps.yml --tags INSTALL --limit ${new-nodeps-ip}

https://git.staff.sina.com.cn/dpool/ansible/blob/master/roles/node_ps/tasks/main.yml


使用非root用户执行
=================================================
[root@node3 /etc/ansible/playbooks]# cat none-root-user.yml
- hosts: test
  tasks:
    - name: debug info 4
      become: yes
      become_user: www
      shell: whoami > /tmp/tmp2.txt

提示颜色信息说明

  • 翔黄色:对远程节点进行相应修改
  • 帽子绿:对远程节点不进行相应修改,或者只是对远程节点信息进行查看
  • 深红色:操作执行命令有异常
  • 浅紫色:表示对命令执行发出警告信息(可能存在的问题,给你一下建议)

Playbook 与 Shell 脚本差异对比

  1. Ansible 执行时会留下清晰到痕迹,告诉我们Ansible都干了什么
  2. 如果设计合理,Ansible能提供相当程度都幂等性支持 (至少能够校验该playbook是否实现了幂等)
  3. 提供了类似 --check 参数,在之际执行前,进行模拟执行,不会对目标机器实际作出改变
  4. 大型Shell脚本含义维护,一旦出问题难以debug,而设计良好的Ansible Playbook几乎无此问题

角色 roles

快速入门

[root@astest ~]# cat /etc/ansible/hosts
astest
[root@astest ~]# cat /etc/hosts | grep astest
10.0.0.230	astest	astest
[root@astest ~]# ansible all -m ping

[root@astest ~]# tree .
.
|-- testrole
|   `-- tasks
|       `-- main.yml
`-- test.yml

[root@astest ~]# cat test.yml 
- hosts: astest
  roles:
  - testrole
[root@astest ~]# cat testrole/tasks/main.yml 
- debug:
    msg: "hello world!"

ansible-playbook test.yml

目录结构

tasks目录:角色需要执行的主任务文件放置在此目录中,默认的主任务文件名为main.yml,当调用角色时,默认会执行main.yml文件中的任务,你也可以将其他需要执行的任务文件通过include的方式包含在tasks/main.yml文件中。
handlers目录:当角色需要调用handlers时,默认会在此目录中的main.yml文件中查找对应的handler
defaults目录:角色会使用到的变量可以写入到此目录中的main.yml文件中,通常,defaults/main.yml文件中的变量都用于设置默认值,以便在你没有设置对应变量值时,变量有默认的值可以使用,定义在defaults/main.yml文件中的变量的优先级是最低的。
vars目录:角色会使用到的变量可以写入到此目录中的main.yml文件中,看到这里你肯定会有疑问,vars/main.yml文件和defaults/main.yml文件的区别在哪里呢?区别就是,defaults/main.yml文件中的变量的优先级是最低的,而vars/main.yml文件中的变量的优先级非常高,如果你只是想提供一个默认的配置,那么你可以把对应的变量定义在defaults/main.yml中,如果你想要确保别人在调用角色时,使用的值就是你指定的值,则可以将变量定义在vars/main.yml中,因为定义在vars/main.yml文件中的变量的优先级非常高,所以其值比较难以覆盖。
meta目录:如果你想要赋予这个角色一些元数据,则可以将元数据写入到meta/main.yml文件中,这些元数据用于描述角色的相关属性,比如 作者信息、角色主要作用等等,你也可以在meta/main.yml文件中定义这个角色依赖于哪些其他角色,或者改变角色的默认调用设定,在之后会有一些实际的示例,此处不用纠结。
templates目录: 角色相关的模板文件可以放置在此目录中,当使用角色相关的模板时,如果没有指定路径,会默认从此目录中查找对应名称的模板文件。
files目录:角色可能会用到的一些其他文件可以放置在此目录中,比如,当你定义nginx角色时,需要配置https,那么相关的证书文件即可放置在此目录中。

配置文件

Changes can be made and used in a configuration file which will be searched for in the following order:  

1  ANSIBLE_CONFIG (environment variable if set)
2  ansible.cfg (in the current directory)
3  ~/.ansible.cfg (in the home directory)
4  /etc/ansible/ansible.cfg

Ansible will process the above list and use the first file found, all others are ignored.

################################ 配置文件注释 ########################################################
The configuration file is one variant of an INI format. Both the hash sign (#) and semicolon (;) are allowed as comment markers when the comment starts the line. However, if the comment is inline with regular values, only the semicolon is allowed to introduce the comment. For instance:

# some basic default values...
inventory = /etc/ansible/hosts  ; This points to the file that lists your hosts

在线配置帮助  https://docs.ansible.com/ansible/latest/reference_appendices/config.html
默认配置文件  https://github.com/ansible/ansible/blob/devel/examples/ansible.cfg


一份常用配置文件参考
==============================
[root@node3 /etc/ansible]# cat ansible.cfg
[defaults]
inventory          =  /etc/ansible/hosts
roles_path         =  /etc/ansible/roles
interpreter_python =  /usr/bin/python

[inventory]
[privilege_escalation]
[paramiko_connection]
[ssh_connection]
[persistent_connection]
[accelerate]
[selinux]
[colors]
[diff]

ps: 如果要管理机器非常多,inventory 可以配置成一个目录,目录里放多个文件,文件的格式保持不变,中括号里面的内容是实际用的清单名称

变量

系统内置变量 ansible node -m setup
========================================
ansible node -m setup

获取IP地址
========================================
cat  playbooks/debug-info.yml
- hosts: test
  tasks:
    - name: debug info1
      debug:
        msg: "{{ ansible_default_ipv4.address }}"

    - name: debug info2
      debug:
        msg: "{{ ansible_host }}"

ansible-playbook playbooks/debug-info.yml

使用group_vars里的变量
========================================
# ls
ansible.cfg  group_vars  hosts  playbooks  roles  template
# group_vars 与 hosts 处在同级目录
# cat hosts
[test]
node1
node2
node3
# cat group_vars/test
foo:
  name: onesdkfjsdlkfj
  field2: two
  role: web5
  region: bx
# cat playbooks/group-vars-test.yml
- hosts: test
  tasks:
    - name: test1
      shell: echo {{ foo.role }} > /tmp/tmp.txt

    - name: test2
      shell: echo {{ foo.region }} >> /tmp/tmp.txt
# ansible-playbook playbooks/group-vars-test.yml
# ssh node3 cat /tmp/tmp.txt
web5
bx
# group_vars 是固定用法,目录名不能更换,目录必须和inventory在同级目录,文件名要和主机清单中括号里面的名称对应上

模板

[root@node3 /etc/ansible]# ifconfig  | grep -A1 eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.222.32.122  netmask 255.255.254.0  broadcast 10.222.33.255

[root@node3 /etc/ansible]# cat playbooks/template-test.yml
- hosts: test
  tasks:
    - name: test template
      vars:
        testnum: 5
      template:
        src: /etc/ansible/templates/test.j2
        dest: /tmp/test.j2

[root@node3 /etc/ansible]# cat /etc/ansible/templates/test.j2
{% if testnum > 3 %}
大与3的模板
{% endif %}

bind {{ ansible_default_ipv4.address }}
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no

[root@node3 /etc/ansible]# ansible-playbook playbooks/template-test.yml > /tmp/tmp2.txt
[root@node3 /etc/ansible]# cat /tmp/test.j2
大与3的模板

bind 10.222.32.122
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no

Ansible Pull 工作模式

Ansible 默认工作模式是Push,即主机通过SSH远程推送相关资源到目标机器,目标机器执行

具体示例参考如下:
*/20 * * * * root /usr/local/bin/ansible-pull -o -C 2.1.0 -d /srv/www/king-gw/ -i /etc/ansible/hosts -U git:// git.kingifa.com/king-gw-ansiblepull >> /var/log/ansible-pull.log 2>&1

ansible-pull通常在配置大批量机器的场景下会使用,灵活性稍有欠缺,但效率几乎可以无限提升,对运维人员的技术水平和前瞻性规划有较高要求

参考资料

ansible puppet saltstack三款自动化运维工具的对比
https://blog.csdn.net/qq_26848099/article/details/79400801

https://docs.ansible.com/

https://www.zsythink.net/archives/tag/ansible/

https://docs.ansible.com/ansible/2.4/shell_module.html