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步骤。
#从上面的执行结果可以看出,我们该执行的都执行了,除了yum那个name没有执行,这就是tags的作用,在测试的时候可以避免某些东西重复执行。
查看下客户端的nginx进程启动情况:
#从进程上面可以看出我们设置的那个变量www用户也被创建并引用了。
查看下浏览器:
博文来自:www.51niux.com
1.2 再写两个个tags的用法
# ansible-playbook -l 192.168.1.122 /etc/ansible/playbooks/nginx.yaml --tags package #只运行package标签里的task语句
# ansible-playbook -l 192.168.1.122 /etc/ansible/playbooks/nginx.yaml --tags "package,user" #同时执行多个tags标签,用""括起来,里面逗号隔开
#虽然引用的顺序是反的,但是还是会按照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