使用 Ansible Playbooks

Posted by Sunday on 2018-05-18

ansible可以非常方便的控制多台机器,比如查看各台机器上的apache服务状态,ansible all -a "service apache status" --sudo,如果任务更复杂一点可以写成一个脚本,然后ansible会在每台机器上执行脚本。但是如果任务中子任务有有依赖那,任务判断任务的执行状态那,总之还是有一系列的问题,总结起来就是能不能做任务编排?当然能,如果没有任务编排就不能称之为配置管理工具,这个功能就叫playbook.

playbooks正如名字一样”剧本集”,通过剧本来讲述一个复杂的故事。在这里playbooks是对tasks的编排,从而完成非常复杂的任务。所谓的配置管理,其实就是说任务编排。 既然playbooks能够实现对任务的编排,那么playbook必然会有自己的语法,幸运的是playbook使用了yaml,非常简单的一种文件格式,下面来简单学习一下如何编写playbook。

Playbook的构成

  • hosts,用于指定目标
  • vars,使得任务更加灵活
  • tasks,具体的行为动作
  • Handler,用于通知
1
2
3
4
5
6
7
- name: setup openstack compent
hosts: all
remote_user: root

tasks:
- name: say hello
shell: echo Hello World.

如何在playbook中使用变量

变量命名

变量名可以为字母,数字以及下划线.

定义变量

在playbook中定义变量

1
2
3
- hosts: webservers
vars:
http_port: 80

在文件和role中定义变量

  1. hosts文件中定义变量时使用

    1
    2
    [groupname:vars]
    key = value
  2. group vars file

    1
    2
    cat group_vars/groupname
    key: value
  3. 如果是role的话,则在相应的defaults/main.yml或者vars/main.yml

注册变量

1
register: xxx

xxx.stdout.find(sdf)
xxx.stdout_lines

使用变量

Ansible允许你使用Jinja2模板系统在playbook中引用变量.

1
My amp goes to {{ max_amp_value }}

jinjia2过滤器

1
# default(*value*, *default_value=u''*, *boolean=False*)

http://jinja.pocoo.org/docs/dev/templates/#builtin-filters
http://ju.outofmemory.cn/entry/97893

使用Facts获取的信息
https://ansible-tran.readthedocs.io/en/latest/docs/playbooks_variables.html#inventory

1
2
ansible hostname -m setup
ansible all -m setup -a "filter=ansible_distribution*"

如果没有使用fact变量时可以强制地关闭,这样执行速度会快一点.

1
2
- hosts: whatever
gather_facts: no

魔法变量

  • hostvars允许你访问另一个主机的变量,当然前提是ansible已经收集到这个主机的变量了
  • group_names:是当前主机所在的group列表
  • groups: 是所有inventory的group列表
  • inventory_hostname: 是在inventory里定义的主机名
  • play_hosts是当前的playbook范围内的主机列表
  • inventory_dir和inventory_file是定义inventory的目录和文件

优先级

role defaults
inventory vars
inventory group_vars
inventory host_vars
playbook group_vars
playbook host_vars
host facts
registered vars
set_facts
play vars
play vars_prompt
play vars_files
role and include vars
block vars (only for tasks in block)
task vars (only for the task)
extra vars (always win precedence)

从低到高.ansible中可以定义变量的地方太多了,没啥不一样的地方,就是优先级不同而已.

一些常用的变量
inventory_hostname, 机器的hostname
inventory_address,ip地址

条件选择

when 可以各种条件组合, ==, >=, not, and, is defined, is not defined.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- hosts: webservers
roles:
- { role: debian_stock_config, when: ansible_os_family == 'Debian' }

tasks:
- command: /bin/false
register: result
ignore_errors: True
- command: /bin/something
when: result|failed
- command: /bin/something_else
when: result|success
- command: /bin/still/something_else
when: result|skipped

when后面可以接Jinja2的表达式或者过滤器

基于变量选择文件和模版

1
2
3
4
5
6
7
8
9
- name: template a file
template: src= dest=/etc/myapp/foo.conf
with_first_found:
- files:
- .conf
- default.conf
paths:
- search_location_one/somedir/
- /opt/other_location/somedir/

变量为空或者未定义的判断

1
2
3
4
5
when: varname is undefined
or
varname is none
or
varname | trim == ''

循环

基本的循环结构 with_items

1
2
3
4
5
6
7
8
9
10
11
12
# 迭代列表
- name: add several users
user: name= state=present groups=wheel
with_items:
- testuser1
- testuser2
# 迭代哈希
- name: add several users
user: name= state=present groups=
with_items:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }

字典循环 with_dict

1
2
3
4
5
6
7
8
9
10
11
12
---
users:
alice:
name: Alice Appleworth
telephone: 123-456-7890
bob:
name: Bob Bananarama
telephone: 987-654-3210
tasks:
- name: Print phone records
debug: msg="User is ()"
with_dict: ""

文件循环 with_fileglob

1
2
3
4
5
6
7
8
9
10
11
12
---
- hosts: all

tasks:

# first ensure our target directory exists
- file: dest=/etc/fooapp state=directory

# copy each file over that matches the given pattern
- copy: src= dest=/etc/fooapp/ owner=root mode=600
with_fileglob:
- /playbooks/files/fooapp/*

错误处理

通常情况下, 当出现失败时 Ansible 会停止在宿主机上执行.有时候,你会想要继续执行下去.为此 你需要像这样编写任务:

1
2
3
- name: this will not be counted as a failure
command: /bin/false
ignore_errors: yes

控制对失败的定义

假设一条命令的错误码毫无意义只有它的输出结果能告诉你什么出了问题,比如说字符串 “FAILED” 出 现在输出结果中.

1
2
3
4
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"

当一个 shell或命令或其他模块运行时,它们往往都会在它们认为其影响机器状态时报告 “changed” 状态.有时你可以通过返回码或是输出结果来知道它们其实并没有做出任何更改.你希望覆写结果的, “changed” 状态使它不会出现在输出的报告或不会触发其他处理程序:

1
2
3
4
5
6
7
8
9
tasks:

- shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"

# this will never report 'changed' status
- shell: wall 'beep'
changed_when: False

如何调试

1.当运行verbose模式时,会打印出所有模块运行后的变量.这对于你要使用register功能时候很重要.只需要在执行playbook命令时加上参数–verbose便可以.ansible-playbook –verbose playbook.yml

  1. debug模块
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # Example that prints the loopback address and gateway for each host
    - debug: msg="System has uuid "

    - debug: msg="System has gateway "
    when: ansible_default_ipv4.gateway is defined

    - shell: /usr/bin/uptime
    register: result

    - debug: var=result verbosity=2

    - name: Display all variables/facts known for a host
    debug: var=hostvars[inventory_hostname] verbosity=4

重复执行

常常用于软件安装或者重启的场景.

1
2
3
4
5
6
7
8
9
10
11
- name: Confirm service connectivity
command: "/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf ping"
changed_when: _mysql_running.rc != 0
register: _mysql_running
until: _mysql_running == 0
retries: ""
delay: ""
failed_when: false
changed_when: _mysql_running != 0
tags:
- galera-cluster-state-check