柴少的官方网站 技术在学习中进步,水平在分享中升华

ansible的role扩展(六)

role是将playbook分割为多个文件的主要机制。它大大简化了复杂playbook的编写,同时还使得他们非常易于复用。roles就是将一个大的playbook拆开放到不同的目录中,可以方便调用。

一、role介绍

1.1 roles结构

每个ansible的role都会有一个名字,比如“webservers”,与webservers role相关的文件都存放在roles/webservers目录中。

这个目录中包含以下文件及目录:

# tree -L 1 /etc/ansible/playbooks/roles/webservers/

|-- defaults  #可以被覆盖的默认变量
|-- files     #保存着需要上传到主机的文件
|-- handler   
|-- meta      #role的依赖信息
|-- tasks     #task
|-- templates #保存Jinja2模板文件
`-- vars      #不应该被覆盖的变量

#上面显示的都是目录。上面每个单独的文件都是可选的,如果你想用哪个就在哪个目录下面创建main.yaml,如果不包含,目录里面可以是空的。

1.2 例子解释

我们还是以一个TLS的nginx的创建过程为例

先查看下nginx的目录结构:

# tree  -L 2 /etc/ansible/playbooks/roles/nginx/

|-- default
|-- files  #keys认证的目录和nginx源码安装包都在files目录下面
|   |-- keys
|   `-- nginx-1.10.3.tar.gz
|-- handlers
|   `-- main.yaml
|-- meta
|-- tasks
|   |-- main.yaml
|   `-- ssl.yaml
|-- templates   #模板文件在这里面
|   |-- nginx.conf.j2
|   `-- nginx_index.html.j2
`-- vars
    `-- main.yaml

查看入口playbook文件:

# cat /etc/ansible/playbooks/nginx.yaml

---
- hosts: 7servers
  roles:
  - {role: nginx, username: www ,when: "ansible_distribution_major_version =='7' and ansible_distribution == 'CentOS'"}

注:

这里最简单的写法就是:

roles:

    - role: nginx   #这就是引用roles目录下的nginx目录,然后一执行ansible-playbook nginx.yaml就会去/etc/ansible/playbooks/roles/nginx目录下找相关的yaml内容了。当然引用多个roles文件的话,就照着这个格式写多行就可以了。

下面就是我们上面的那种写法也是常用的:

role: nginx  #就是引用roles目录下的nginx目录下的相关yaml。

username: www   #就是给username变量赋值www。当然也可以这这个单拿出来,写到role:nginx的下面,以 username:  www的形式。

when: "ansible_distribution_major_version =='7' and ansible_distribution == 'CentOS'"  #这就是判断,当两个条件都为真的时候才会执行前面的role: nginx。当然也可以定义一个条件。当然有很多种比较方式:http://www.jianshu.com/p/04516cbba336都有举例。这里我们知道这个when,根据不同的条件执行的不同的playbook,如不同的操作系统,不同的系统版本,不同的主机环境等等,来执行不同的playbook,非常有用。

博文来自:www.51niux.com

查看下模板index文件:

# cat /etc/ansible/playbooks/roles/nginx/templates/nginx_index.html.j2  #等待被引用,里面有一些变量是取自vars目录下的定义。

<html>
  <head>
    <title>welcome to ansible</title>
  </head>
  <body>
    <p>key_file: {{ key_file }} </p>
    <p>cert_file:  {{ cert_file }} </p>
    <p>nginx_html: {{ nginx_html }} </p>
    <p>CPU_NUM: {{ CPU_NUM }} </p>
  </body>
</html>

查看vars目录下的文件:

# cat /etc/ansible/playbooks/roles/nginx/vars/main.yaml   #这里就是把我们用到的变量都写到这个文件中去了

CPU_NUM: "{{ansible_processor_vcpus}}" 
key_file: /opt/nginx/ssl/ca.crt 
cert_file: /opt/nginx/ssl/ca_nopass.key
conf_file: /opt/nginx/conf/nginx.conf
nginx_file: /opt/nginx
nginx_html: "{{nginx_file}}/html"

查看tasks目录下的文件:

# cat /etc/ansible/playbooks/roles/nginx/tasks/main.yaml   #这里面就是tasks的内容

- name: yum install package
  shell: yum install gcc gcc-c++ openssl* pcre pcre-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel ncurses ncurses-devel curl curl-devel gd gd2 gd-devel gd2-devel -y 
  tags:     #注意这里有的地方打标签了
    - packages      
- name: create nginx user
  user: name={{username}} uid=500   #这里的username在入口yaml哪里引用的。
  tags:
    - user  #都作了一些标签,这在测试的时候是很有用的
- name: create tools dir
  file: path=/opt/tools state=directory
  tags:
    - dir  
- name: cp nginx package    
  copy: src=nginx-1.10.3.tar.gz dest=/tools/  #注意这里的src,它的根目录就是/etc/ansible/playbooks/roles/nginx/files
  tags:
    - package
- name: nginx install 
  shell:  cd /tools && tar zxf nginx-1.10.3.tar.gz && cd nginx-1.10.3 && ./configure  --prefix=/opt/nginx-1.10.3 --with-http_ssl_module && make && make install
  tags:
    - install
- name: nginx link 
  file: >
        src=/opt/nginx-1.10.3
        dest=/opt/nginx
        state=link
- name: copy nginx conf
  template: src=nginx.conf.j2 dest={{ conf_file }}
  notify:
  - restart nginx
- include: ssl.yaml  #这里include,引用其他的yaml。因为main.yaml只能算是我们的入口文件,应该是多做一些yaml文件,用这种方式来引用,这样其他的yaml也可以调用,较少重复编写。

# cat /etc/ansible/playbooks/roles/nginx/tasks/ssl.yaml  #这就是被上面引用的ssl.yaml

- name: create ssl dir
  file: path=/opt/nginx/ssl state=directory
- name: copy TLS key
  copy: src=keys/ca_nopass.key dest={{ key_file }} owner=root mode=0600
- name: copy TLS certificate
  copy: src=keys/ca.crt dest={{ cert_file }}  #注意这里keys/ca.crt就是相对路径就可以了,就直接去/etc/ansible/playbooks/roles/nginx/files下面找了
- name: copy nginx file
  template: src=nginx.conf.j2 dest={{ conf_file }}
- name: copy index html
  template: src=nginx_index.html.j2 dest={{ nginx_html }}/index.html #这里j2文件直接去/etc/ansible/playbooks/roles/nginx/templates目录下面去找了。
  notify: #注意上面的内容是发送了变化才会去调用notify里面的内容,如果只是执行了但是发现不用去改变也就是ok状态不是change就不会notify。
   - restart nginx

查看handlers目录下的文件:

# cat /etc/ansible/playbooks/roles/nginx/handlers/main.yaml  #这里就是notify调用执行的地方。

- name: restart nginx
  shell: "[ -f /opt/nginx/logs/nginx.pid ] && /opt/nginx/sbin/nginx -s reload || /opt/nginx/sbin/nginx"  #像这种稍微负载点的写法要用双引号括起来

执行一下:

# ansible-playbook -l 192.168.1.122 /etc/ansible/playbooks/nginx.yaml --skip-tags packages 
#上面的命令稍微复杂点,-l 是限制192.168.1.122这台主机来执行# ansible-playbook -l 192.168.1.122 /etc/ansible/playbooks/nginx.yaml

# --skip-tags packages  我们上面不是打了一些tags标签吗,这里是跳过了packages标签,也就是跳过了yum步骤。

图片.png
#从上面的执行结果可以看出,我们该执行的都执行了,除了yum那个name没有执行,这就是tags的作用,在测试的时候可以避免某些东西重复执行。

查看下客户端的nginx进程启动情况:

图片.png

#从进程上面可以看出我们设置的那个变量www用户也被创建并引用了。

查看下浏览器:

 图片.png

博文来自:www.51niux.com

1.2 再写两个个tags的用法

# ansible-playbook -l 192.168.1.122 /etc/ansible/playbooks/nginx.yaml --tags package  #只运行package标签里的task语句

图片.png

# ansible-playbook -l 192.168.1.122 /etc/ansible/playbooks/nginx.yaml --tags "package,user" #同时执行多个tags标签,用""括起来,里面逗号隔开

图片.png

#虽然引用的顺序是反的,但是还是会按照playbook里面定义的上下先后顺序去执行。

另外tags 和role 结合使用的方法: - { role: nginx,  tags: [ 'user', 'package' ] }   #这样就不用在外部执行那里加了不过一般也不这么写。

系统内置tags(always、tagged、untagged、all 是四个系统内置的tag,有自己的特殊意义):

  always: 指定这个tag 后,task任务将永远被执行,而不用去考虑是否使用了--skip-tags标记
  tagged: 当 --tags 指定为它时,则只要有tags标记的task都将被执行,--skip-tags效果相反
  untagged: 当 --tags 指定为它时,则所有没有tag标记的task 将被执行,--skip-tags效果相反
  all: 这个标记无需指定,ansible-playbook 默认执行的时候就是这个标记.所有task都被执行

#上面的roles的例子虽然少,但是大部分的应该用到的东西都在里面了,下面就是根据实际情况来编写,并且需要用到其他的地方可以去官网查看。

二、ansible的性能调优(记录部分)

第一点:关闭gathering fact

前面说过了要么配置文件里面配置,要么在playbook里面加上gather_facts:  false,对于那些不用获取客户端fact的操作。

第二点:开启pipelining
首先再回忆一下ansible执行一个task的过程:

基于调用的模块生产一个python的脚本,然后将python脚本复制到主机上,最后它执行这个python脚本。

然后就说到pipelining了:

ansible支持一个优化,叫做pipelining.pipelining模式下ansible执行python脚本的时候并不会复制它,而是通过管道传递给SSH会话。因为这会让ansible使用的SSH会话由两个减少到一个,所以可以节省时间。

ssh pipelining 默认是关闭,之所以默认关闭是为了兼容不同的 sudo 配置,主要是 requiretty 选项。如果不使用 sudo,建议开启。打开此选项可以减少 ansible 执行没有传输时 ssh 在被控机器上执行任务的连接数。不过,如果使用 sudo,必须关闭 requiretty 选项。修改 /etc/ansible/ansible.cfg 文件可以开启 pipelining,改为:pipelining=True。

第三点:并发数

前面的配置文件和命令中都已经介绍了,ansible的并发数是5,可以通过更改配置文件或者命令加参数来增大并发数。
第四点:添加ControlMaster的配置

第五点:ansible的异步执行

在playbook的任务中加入两个参数:async和poll

第六点:fact的各种缓存

三、简单使用Callback

#如果我想改变ansible的默认输出,加点json输出之类的呢?

3.1  简单使用Callback功能

配置文件开启callback功能:

# cat /etc/ansible/ansible.cfg

callback_plugins=/etc/ansible/callbacks
bin_ansible_callbacks=True

#还有一个参数:“CALLBACK_NEEDS_WHITELIST”,如果设置为False,则无需设置callback_whitelist选项,反之,则必须在callback_whitelist选项中指定“CALLBACK_NAME”。

创建简单的callback脚本:

#mkdir /etc/ansible/callbacks

# cat /etc/ansible/callbacks/command.py

import os

from ansible.plugins.callback import CallbackBase

class CallbackModule(CallbackBase):
    '''
    This callback puts results into a host specific file in a directory in json format.
    '''
    #重新定义触发状态函数
    def v2_runner_on_ok(self, result):
        #打印结果输出
        print (result.__dict__.items())  
        for key in result.__dict__.keys():  
            print(key)
        #如何使用结果的某一个key
        print(result._host)

    def playbook_on_stats(self, stats):
        #打印剧本的状态
        print(stats.__dict__.items())

    def v2_runner_on_skipped(self, result):
        #打印跳过的状态
        print(result.__dict__.items())

#定义测试的yaml剧本

# cat test.yml

- hosts: all
  #gather_facts: no
  tasks:
  - name: test echo
    shell: echo 'haha No.1 PLAYBOOK' >>/tmp/hoc_test
    when:
      - env_type == "online" and host_type == "web"

#重新执行ansible-playbook查看结果:

# ansible-playbook -i test_hosts.txt test.yml --extra-vars "env_type=online,host_type=web"  #下面是摘取的结果输出

PLAY [all] ***********************************************************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************************************************
ok: [192.168.1.164]
dict_items([('_host', 192.168.1.164), ('_task', TASK: Gathering Facts), ('_result', {'ansible_facts': {'ansible_dns': {'options': {'timeout': '2', 'attempts': '3', 'rotate': True, 'single-requ
......
 []})])
_host
_task
_result
_task_fields
192.168.1.164

TASK [test echo] *********
skipping: [192.168.1.164]
dict_items([('_host', 192.168.1.164), ('_task', TASK: test echo), ('_result', {'changed': False, 'skip_reason': 'Conditional result was False', '_ansible_no_log': False}), ('_task_fields', 
......
PLAY RECAP ***********************************************************************************************************************************************************************************
192.168.1.164                : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

dict_items([('processed', {'192.168.1.164': 1}), ('failures', {}), ('ok', {'192.168.1.164': 1}), ('dark', {}), ('changed', {}), ('skipped', {'192.168.1.164': 1}), ('rescued', {}), ('ignored', {}), ('custom', {})])

result的几个key:

result._host: 主机名
result._task:任务名
result._result:结果
result._task_fields:执行信息

那么都有哪些触发函数可以替换呢?

参考:https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/callback/__init__.py

作者:忙碌的柴少 分类:ansible系列 浏览:6586 评论:0
留言列表
发表评论
来宾的头像