ansible使用指北(三)

2016-12-14 14:48:45

前言
在前面文章中我们认识了ansible的常用功能,今天我们来了解下ansible变量和ansible api,之后我们来看看如何优化ansible的性能

ansible变量
  • 在Inventory中定义变量

    • 主机与组

      1
      2
      3
      4
      5
      6
      7
      8
      mail.example.com
      [webservers]
      foo.example.com
      bar.example.com
      [dbservers]
      one.example.com
      two.example.com
      three.example.com
    • 主机变量

      1
      2
      3
      [atlanta]
      host1 http_port=80 maxRequestsPerChild=808
      host2 http_port=303 maxRequestsPerChild=909
    • 组的变量

      1
      2
      3
      4
      5
      6
      [atlanta]
      host1
      host2
      [atlanta:vars]
      ntp_server=ntp.atlanta.example.com
      proxy=proxy.atlanta.example.com
    • 把一个组作为另一个组的子成员

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      [atlanta]
      host1
      host2
      [raleigh]
      host2
      host3
      [southeast:children]
      atlanta
      raleigh
      [southeast:vars]
      some_server=foo.southeast.example.com
      halon_system_timeout=30
      self_destruct_countdown=60
      escape_pods=2
      [usa:children]
      southeast
      northeast
      southwest
      northwest
  • 在playbook中定义变量

    1
    2
    3
    - hosts: webservers
    vars:
    http_port: 80
  • 在文件和role中定义变量
    角色默认变量允许你为 included roles 或者 dependent roles(见下) 设置默认变量。要创建默认变量,只需在 roles 目录下添加 defaults/main.yml 文件。这些变量在所有可用变量中拥有最低优先级,可能被其他地方定义的变量(包括 inventory 中的变量)所覆盖

  • 使用变量: 关于Jinja2

    1
    2
    3
    4
    5
    #简单的模板中
    My amp goes to {{ max_amp_value }}

    #在playbook中直接这样用
    template: src=foo.cfg.j2 dest={{ remote_install_path }}/foo.cfg
  • 注册变量
    变量的另一个主要用途是在运行命令时,把命令结果存储到一个变量中.不同模块的执行结果是不同的.运行playbook时使用-v选项可以看到可能的结果值. 在ansible执行任务的结果值可以保存在变量中,以便稍后使用它.在 条件选择 章节有一些示例.

    1
    2
    3
    4
    5
    6
    7
    - hosts: web_servers
    tasks:
    - shell: /usr/bin/foo
    register: foo_result
    ignore_errors: True
    - shell: /usr/bin/bar
    when: foo_result.rc == 5
ansible api

ansible api 方便对结果输出的整形,以及二次开发及和其他程序之间的耦合调用,先来个简单例子

1
2
3
4
5
6
7
8
import ansible.runner
runner = ansible.runner.Runner(
module_name='ping',
module_args='',
pattern='web*',
forks=10
)
datastructure = runner.run()

结果如下,有兴趣的同学可以对比用命令行输出的结果

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

看个详细的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/python

import ansible.runner
import sys

# construct the ansible runner and execute on all hosts
results = ansible.runner.Runner(
pattern='*', forks=10,
module_name='command', module_args='/usr/bin/uptime',
).run()

if results is None:
print "No hosts found"
sys.exit(1)

print "UP ***********"
for (hostname, result) in results['contacted'].items():
if not 'failed' in result:
print "%s >>> %s" % (hostname, result['stdout'])

print "FAILED *******"
for (hostname, result) in results['contacted'].items():
if 'failed' in result:
print "%s >>> %s" % (hostname, result['msg'])

print "DOWN *********"
for (hostname, result) in results['dark'].items():
print "%s >>> %s" % (hostname, result)

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

ansible 性能优化
  • 优化前的准备—收集数据
    在 github 发现一个 Ansible 任务计时插件“ansible-profile”,安装这个插件后会显示 ansible-playbook 执行每一个任务所花费的时间。Github 地址:https://github.com/jlafon/ansible-profile。 这个插件安装很简单,只需要简单的三个命令即可完成安装。在你的 playbook 文件的目录下创建一个目录,目录名 callback_plugins 然后将下载的 profile_tasks.py 文件放到该目录下

    1
    2
    3
    4
    cd /etc/ansible 
    mkdir callback_plugins
    cd callback_plugins
    wget https://raw.githubusercontent.com/jlafon/ansible-profile/master/callback_plugins/profile_tasks.py
  • 关闭 gathering facts
    如果您观察过 ansible-playbook 的执行过程中,您会发现 ansible-playbook 的第 1 个步骤总是执行 gather facts,不论你有没有在 playbook 设定这个 tasks。如果你不需要获取被控机器的 fact 数据的话,你可以关闭获取 fact 数据功能。关闭之后,可以加快 ansible-playbook 的执行效率,尤其是你管理很大量的机器时,这非常明显。关闭获取 facts 很简单,只需要在 playbook 文件中加上“gather_facts: no”即可。如下

    1
    2
    3
    4
    5
    6
    7
    --- 
    - hosts: 172.16.64.240
    gather_facts: no
    remote_user: liheng
    sudo: yes
    roles:
    - {role: profile_test}
  • SSH PIPElinING
    SSH pipelining 是一个加速 Ansible 执行速度的简单方法。ssh pipelining 默认是关闭,之所以默认关闭是为了兼容不同的 sudo 配置,主要是 requiretty 选项。如果不使用 sudo,建议开启。打开此选项可以减少 ansible 执行没有传输时 ssh 在被控机器上执行任务的连接数。不过,如果使用 sudo,必须关闭 requiretty 选项。修改 /etc/ansible/ansible.cfg 文件可以开启 pipelining

    1
    2
    3
    pipelining=False
    修改为
    pipelining=True
  • ControlPersist
    ControlPersist 特性需要高版本的 SSH 才支持,CentOS 6 默认是不支持的,如果需要使用,需要自行升级 openssh。ControlPersist 即持久化 socket,一次验证,多次通信。并且只需要修改 ssh 客户端就行,也就是 Ansible 机器即可

    1
    2
    3
    4
    5
    6
    7
    8
    cat ~/.ssh/config 
    Host *
    Compression yes
    ServerAliveInterval 60
    ServerAliveCountMax 5
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 4h

    在开启了 ControlPersist 特性后,SSH 在建立了 sockets 之后,节省了每次验证和创建的时间。在网络状况不是特别理想,尤其是跨互联网的情况下,所带来的性能提升是非常可观的。有这边需求的,试试就知道了

Ansible-playbook 技巧
  • 获取执行命令的输出 –Register
    register 关键字可以存储指定命令的输出结果到一个自定义的变量中,我们通过访问这个自定义变量就可以获取到命令的输出结果。Register 的使用很方便,只需要在 task 声明 register 关键字,并自定义一个变量名就可以。如下

    1
    2
    3
    4
    5
    6
    7
    - name: echo date 
    command: date
    register: date_output

    - name: echo date_output
    command: echo "30"
    when: date_output.stdout.split(' ')[2] == "30"

    这里第 1 个 task 是执行了一个 date 命令,register 关键字将 date 命令的输出存储到 date_output 变量名。第 2 个 task 对输出进行分析,并使用 when 对关键字对分析后的进行判断,如果匹配,则执行这个 task,不匹配就不执行。这里要重点说下的,因为 register 获取到的输出内容都是字符串,而 ansible 又是 python 写的,你可以使用 python 字符串的方法对其做处理,比如本文中使用的 split,还可以使用 find 方法。个人觉得,真是非常灵活方便

  • task委托
    在对一组服务器 server_group1 执行操作过程中,需要在另外一台机器 A 上执行一个操作,比如在 A 服务器上添加一条 hosts 记录,这些操作必须要在一个 playbook 联动完成。也就是是说 A 服务器这个操作与 server_group1 组上的服务器有依赖关系。Ansible 默认只会在定义好的一组服务器上执行相同的操作,这个特性对于执行批处理是非常有用的。但如果在这过程中需要同时对另外 1 台机器执行操作时,就需要用到 Ansible 的任务委派功能(delegate_to)。使用 delegate_to 关键字可以委派任务到指定的机器上运行。在 playbook 的操作如下:

    1
    2
    3
    4
    5
    6
    - name: add host record 
    shell: 'echo "192.168.1.100 test.xyz.com" >> /etc/hosts'

    - name: add host record to center server
    shell: 'echo "192.168.1.100 test.xyz.com " >> /etc/hosts'
    delegate_to: 192.168.1.1

    任务委派功能还可以用于以下场景:
    a)在部署之前将一个主机从一个负载均衡集群中删除
    b)当你要对一个主机做改变之前去掉相应 dns 的记录
    c)当在一个存储设备上创建 iscsi 卷的时候
    d)当使用外的主机来检测网络出口是否正常的时候

  • 本地操作功能 –local_action
    Ansible 默认只会对控制机器执行操作,如果需要在 Ansible 本机执行操作,可以使用 delegate_to( 任务委派 ),可以使用另外一外功能实现,这就是 local_action 关键字

    1
    2
    - name: add host record to center server 
    local_action: shell 'echo "192.168.1.100 test.xyz.com " >> /etc/hosts'

    当然您也可以使用 connection:local 方法,如下:

    1
    2
    3
    - name: add host record to center server 
    shell: 'echo "192.168.1.100 test.xyz.com " >> /etc/hosts'
    connection: local
  • 选择性执行 task–Tag(标签)
    您可能因为某些原因,会创建一个很大型的 playbook,但是你可能只想想运行其中特定部分的配置而无需要运行整个 playbook 。那么这时你可能需要用到 tag 功能。示例如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    - name: yun install package 
    yum: name={{ item }} state=installed
    with_items:
    - httpd
    - memcached
    tags:
    - packages

    - name: configuration modity
    template: src=templates/src.j2 dest=/etc/foo.conf
    tags:
    - configuration

    如果你只想运行 playbook 中的”configuration”和”packages”,你可以这样做

    1
    ansible-playbook example.yml – tags "configuration,packages"

    如果你只想执行 playbook 中某个特定任务之外的所有任务,你可以这样做:

    1
    ansible-playbook example.yml – skip-tags "configuration"

    tag 特性是一个不错的功能,但如果真的是要维护一个大型的 playbook,还是建议将 playbook 按功能或应用拆分成多个 playbook,然后再在主 playbook include 其他子 playbook,这样即既利于维护也方便管理

ref
python-api-2-0
Inventory
Docs Variables
Ansible 进阶技巧
ansible小结(九)playbook进阶


您的鼓励是我写作最大的动力

俗话说,投资效率是最好的投资。 如果您感觉我的文章质量不错,读后收获很大,预计能为您提高 10% 的工作效率,不妨小额捐助我一下,让我有动力继续写出更多好文章。