Playbook高级技巧
Includes、Handlers、Files、Templates、Roles、Jinja、Galaxy
Includes 引用
includes可引用yml文件,vars、handlers、files也支持includes的引用
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 |
# RestartPhpfpm.yml --- - name: restart php-fpm service: name=php-fpm state=restarted # b.yml --- - hosts: phpserver remote_user: test tasks: - name: restart php-fpm service: name=php-fpm state=restarted --- - hosts: webserver remote_user: root tasks: - name: 引用restart php-fpm include: RestartPhpfpm.yml - name: 其他任务 shell: echo "xx" - hosts: all tasks: - debug: msg: task1 - name: 引用另外一个yml include: b.yml |
使用 include可以把playbook按功能进行拆分,最后再拼合
在 Sysinit.yml 中引用 user-config.yml create_dir.yml static_git_pull.yml git_checkout.yml
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
##### user-config.yml ##### --- - name: 为用户添加配置文件 copy: src: example_profile dest: "/home/{{ username }}/.profile" owner: "{{ username }}" group: "{{ username }}" mode: 0744 - name: 添加 private keys copy: src: "{{ item.src }}" dest: "/home/.ssh/{{ item.dest }}" owner: "{{ username }}" group: "{{ username }}" mode: 0600 with_items: ssh_private_keys - name: 重启某个服务 service: name=example state=restarted ##### create_dir.yml ##### --- - name: 创建目录 file: path: "{{ package_dir }}{{ project }}-release-{{ git_commit }}/{{ flag }}-{{ project }}" owner: www group: www mode: 0755 recurse: yes state: directory ##### static_git_pull.yml ##### --- - name: Git pull git: repo: "{{ repository_static }}" dest: "{{ package_dir }}{{ project }}-release-{{ git_commit }}/{{ flage }}-{{ project }}" version: "{{ git_commit }}" force: yes ##### git_checkout.yml ##### --- - name: Git fetch command: /usr/bin/git fetch args: chdir: "{{ package_dir }}{{ project }}-release-{{ git_commit }}/{{ flag }}-{{ project }}" - name: Git reset command: /usr/bin/git reset --hard args: chdir: "{{ package_dir }}{{ project }}-release-{{ git_commit }}/{{ flag }}-{{ project }}" - name: Git checkout command: /usr/bin/git checkout {{ git_commit }} args: chdir: "{{ package_dir }}{{ project }}-release-{{ git_commit }}/{{ flag }}-{{ project }}" ##### Sysinit.yml ##### --- - hosts: all tasks: - name: 初始化用户 include: user-config.yml vars: username: johndoe ssh_private_keys: - { src: /path/to/key1, dest: id_rsa } - { src: /path/to/key2, dest: id_rsa_2 } - name: 初始化目录 include: create_dir.yml - name: 拉取代码 include: static_git_pull.yml - name: 检出代码 include: git_checkout.yml |
动态 Includes
在满足一定条件时加载 include 可提高灵活性和可扩展性
import_tasks 静态加载
include_tasks 动态加载
include 加 static: no 动态加载 将废弃
1 2 3 4 5 6 7 8 9 10 11 12 |
- hosts: 127.0.0.1 tasks: - name: 判断extra-tasks.yml是否存在 stat: path: extra/extra-tasks.yml register: extra_file - debug: var: extra_file - name: 若存在 extra-tasks.yml则执行 include: extra/extra-tasks.yml static: no when: extra_file.stat.exists |
Handler Includes 技巧
handler由notify触发,用于当资源发生变化时一次性执行指定操作
handler支持include 需要写在handlers区域
Playbooks Includes 技巧
导入一个playbook
1 2 3 4 5 6 7 |
--- - hosts: all remote_user: root tasks: [ ... ] - include: web.yml - include: db.yml |
Roles 角色
Ad-Hoc用于临时命令;Playbook适合中小项目;Roles常用于大型项目
Roles 包括 vars_files tasks handlers meta templates
构建 Roles
Roles依赖于目录的命名和摆放, tasks/main.yml 是任务入口
每个目录下均由 main.yml 定义该功能的任务集,tasks/main.yml 默认执行所有指定的任务
Roles的调用文件 playbook_role.yml
1 2 3 4 |
--- - hosts: all roles: - role_name |
roles目录可在 /etc/ansible//ansible.cfg中roles_path定义 也可以和入口playbook文件存放在同级目录
Roles调用结构
group_vars目录下的文件定义roles中调用的变量
Roles的功能集:
roles/x/tasks/main.yml 主函数 包括在其中的所有任务都会被执行
roles/x/handlers/main.yml 所有包括其中的handlers将被执行
roles/x/vars/main.yml 所有包括在其中的变量将在roles中生效
roles/x/meta/main.yml roles所有依赖将被正常登入
roles/x/{files,templates,tasks}/xxx 存放所有文件/模板 使用时不需要指定绝对路径
Roles技巧之Files 文件传输
Files和Templates均用于文件处理;存放于files目录下的文件不需要指定绝对路径
Roles技巧之Templates 模板替换
模板由Jinja2格式渲染 文件后缀 .j2
两个花括号中间写变量名且花括号和变量名间有空格分隔 如 {{ variable }}
变量存放于 vars/main.yml
1 2 |
- name: 模板传输 template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf |
tasks/main.yml 中include引用的yml文件除了本目录 也可以引用其他roles下的yml
跨平台 Roles
同时管理多平台
/etc/ansible/hosts
[cross-platform]
192.168.1.10 ansible_ssh_user="stanley" #Debian
192.168.1.12 #RedHat 默认使用root
在play中 可添加 remote_user: root 覆盖 yml 中对整个playbook 的定义,即对某一个play指定特定的执行用户
总调度文件中要使用 when: ansible_os_family == 'RedHat'
1 2 3 4 5 6 7 |
--- - name: 跨平台执行某任务 hosts: all remote_user: root roles: - { role: httpd_debian, when: ansible_os_family == 'Debian' } - { role: httpd_redhat, when: ansible_os_family == 'RedHat' } |
Jinja2 模板
Jinja2 For 循环
Jinja2 执行语句 {% statement execution %}
{{ item }}
{% endfor %}
示例:
192.168.1.{{ id }} web{{ "%02d"|format(id-200) }}.example
{% endfor %}
# 生成192.168.1.201 web01.example 到 192.168.1.210 web10.example
# 注意 range生成的范围是 x<= value < y
Jinja2 If 条件
...
{% endif %}
##
{% if item.gateway is defined %}
...
{% endif %}
示例:
bind-address=0.0.0.0:{{ PORT }}
{% else %}
bind-address=0.0.0.0:3306
{% endif %}
# 若变量PORT存在,则使用变量的值,否则使用默认值3306
Jinja 多值合并
将多个items合并成一个list
{{ node | join("") }}:5672
{% if not loop.last %} # loop.last为最后一次迭代时 为true
{% endif %}
{% endfor %}
说明:
groups是ansible内置变量,db是inventory文件中的主机组
loop.index 当前循环迭代次数 默认从1开始
loop.index0 当前循环迭代次数 默认从0开始
loop.revindex 到循环结束需要迭代的次数 默认从1开始
loop.revindex0 到循环结束需要迭代的次数 默认从0开始
loop.first 若为第一次迭代 返回true
loop.last 若为最后一次迭代 返回true
loop.length 序列中的项目数
loop.depth 显示渲染的递归循环的层级数 默认从1开始
loop.depth0 显示渲染的递归循环的层级数 默认从0开始
loop.cycle 在一串序列间期取值的辅助函数
Jinja default() 设定
设置默认值
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 |
--- - name: var测试 hosts: 127.0.0.1 gather_facts: no vars: myvariable: false tasks: - name: param1 set_fact: myvariable: "{{ param1 }}" when: param1 is defined - name: param2 set_fact: myvariable: "{{ param1 if not myvariable else myvariable +','+param2 }}" when: param2 is defined - name: param3 set_fact: myvariable: "{{ param1 if not myvariable else myvariable +','+param3 }}" when: param3 is defined - name: default set_fact: myvariable: "default" when: not myvariable - name: 查看变量 debug: var: myvariable |
运行:
ansible-playbook bb.yml # "myvariable": "default"
ansible-playbook bb.yml -e "param1=value1" # "myvariable": "value1"
ansible-playbook bb.yml -e "param1=value1 param2=value2" # "myvariable": "value1,value2"
ansible-playbook bb.yml -e "param1=value1 param2=value2 param3=value3" # "myvariable": "value1,value2,value3"
Ansible Galaxy
Ansible官方Roles分享平台,可以免费上传或下载Roles
默认下载的Roles存放于 /etc/ansible/roles 目录下,可在ansible.cfg中定义存放目录
ansible-galaxy
galaxy网址:https://galaxy.ansible.com
ansible-galaxy [delete|import|info|init|install|list|login|remove|search|setup] [--help] [options] ...
说明:
init 创建空roles,ansible-galaxy init myroles 目录myroles将位于当前目录下
info 显示roles信息,ansible-galaxy info manala.mysql
install 安装roles,ansible-galaxy install manala.mysql 默认存放于 ~.ansible/roles/manala.mysql
list 更出本地已安装的roles,ansible-galaxy list
remove 删除本地的roles,ansible-galaxy remove manala.mysql
galaxy上roles命名 username.rolename
Inventory 文件扩展
默认文件: /etc/ansible/hosts
指定特定的inventory文件: -i
ansible-playbook playbook.yml -i /path/to/hostsfiles
192.168.1.1
[servercheck-web:vars]
ansible_ssh_user=root
# 按操作系统对主机组进行分组
[centos:children]
servercheck-web
定义主机组之间的继承关系使用 children
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
--- #对所有主机进行基础配置 - hosts: all sudo: true roles: - security - logging - firewall #配置web主机 - hosts: servercheck-web roles: - nginx - php #配置数据库主机 - hosts: servercheck-db roles: - pgsql - db-tuning |
一个yml文件中 可多次指定 hosts 以限定不同的任务对不同的主机进行应用
host_vars/group_vars 目录
可放置在 /etc/ansible目录下,也可与playbook文件放在同一目录,或与inventory文件同一个目录
host_vars目录内放置和主机同名的yaml文件,该文件中定义的变量会覆盖在其他任何playbook和role中定义的同名变量的值
动态 Inventory
Ansible通过调用第三方脚本来动态配置Inventory文件
任何脚本都可以,二进制文件也可以,只需要运行结果返回的是指定格式的 json 串
注意:用于生成 json 代码的脚本必须支持两个选项 --list 和 --host
--list 返回所有的主机组信息,每个组都包含字典形式的主机列表、子组列表或组变量
--host <hostname> 返回该主机的变量列表,或返回一个空的字典
ansible 使用 -i 选项来调用脚本: ansible all -i my-inventory-script -m ping
--list 返回的 json 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{ "databases":{ # 主机组名 "hosts":[ # 固定字段 "192.168.1.19", "192.168.1.20" ], "vars":{ "ansible_ssh_user":"johndoe", "ansible_ssh_private_key_file":"~/.ssh/mykey", "example_variable":"value" } }, "_meta":{ # 主机变量 "hostvars":{ # 固定字段 "192.168.1.19":{ "host_specific_var":"bar" }, "192.168.1.20":{ "host_specific_var":"foo" } } } } |
动态inventory脚本的python实现
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#!/usr/bin/env python #-*- encoding:utf-8 -*- import os import sys import argparse try: import json except ImportError: import simplejson as json class ExampleInventory(object): def __init__(self): self.inventory = {} self.read_cli_args() # 定义 --list 选项 if self.args.list: self.inventory=self.example_inventory() # 定义 --host [hostname] 选项 elif self.args.host: self.inventory=self.empty_inventory() # 若没有主机组或变量要设置 返回一个空inventory else: self.inventory=self.empty_inventory() print(json.dumps(self.inventory)) # 用于展示效果的 json inventory def example_inventory(self): return { 'group':{ 'hosts':['192.168.1.77','192.168.1.78'], 'vars':{ 'ansible_ssh_user':'test', # 'ansible_ssh_private_key_file':'~/.vagrant.d/insecure_private_key', 'example_variable':'value' } }, '_meta':{ 'hostvars':{ '192.168.1.77':{ 'host_specific_var':'foo' }, '192.168.1.78':{ 'host_specific_var':'bar' } } } } # 返回仅用于测试的空inventory 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() |
赋予inventory.py 可执行权限 chmod +x inventory.py
ansible all -i inventory.py -m ping
ansible all -i inventory.py -m debug -a "var=host_specific_var" # 测试主机变量是否生效
动态inventory脚本的php实现
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
#!/usr/bin/php <?php /** * @file *基于PHP的动态Inventory脚本举例 */ /** * @return array *生成用于展示效果的JSON格式的Inventory文件内容 */ function example_inventory() { return [ 'group' => [ 'hosts' => ['192.168.28.71', '192.168.28.72'], 'vars' => [ 'ansible_ssh_user' => 'test', 'ansible_ssh_private_key_file' => '~/.test.d/insecure_private_key', 'example_variable' => 'value', ], ], '_meta' => [ 'hostvars' => [ '192.168.1.77' => [ 'host_specific_var' => 'foo', ], '192.168.1.78' => [ 'host_specific_var' => 'bar', ], ], ], ]; } /** * @return array * 生成用于测试的空Inventory */ function empty_inventory() { return ['_meta' => ['hostvars' => new stdClass()]]; } /** * 获取Inventory * @param array $argv * 以数组形式传入变量(as returned by $_SERVER['argv']). * @return array */ function get_inventory($argv = []) { $inventory = new stdClass(); // 设置`--list`选项 if (!empty($argv[1]) && $argv[1] == '--list') { $inventory = example_inventory(); } // 定义`--host [hostname]` 选项 elseif ((!empty($argv[1]) && $argv[1] == '--host') && !empty($argv[2])) { //未部署,我们这里只演示--list选项功能 $inventory = empty_inventory(); } //如果没有主机组或变量要设置,就返回一个空Inventory else { $inventory = empty_inventory(); } print json_encode($inventory); } // 获取Iventory. get_inventory($_SERVER['argv']); ?> |
赋予inventory.php 可执行权限 chmod +x inventory.php
ansible all -i inventory.php -m ping
ansible all -i inventory.php -m debug -a "var=host_specific_var" # 测试主机变量是否生效