一、playbook基本语法
---
- hosts: all
tasks:
- name: Install nginx package
yum: name=nginx state=present
- name: copy nginx.conf
template: src=./nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=0644 validate='nginx -t -c %s'
notify:
- restart nginx service
handlers:
- name: restart nginx service
service: name=nginx state=restarted
[root@localhost ~]# cat hosts
[nginx]
192.168.198.10[1:2]
[nginx:vars]
ansible_python_interpreter=/usr/bin/python2
[root@localhost ~]# cat nginx.conf.j2
user nginx;
worker_processes {{ ansible_processor_cores }};
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
#检查语法
ansible-playbook nginx.yaml --syntax-check
#列出task
ansible-playbook nginx.yaml --list-task
#列出主机
ansible-playbook nginx.yaml --list-hosts
#-i指定hosts文件,运行playbook
ansible-playbook -i hosts nginx.yaml -f 3
#检查nginx服务状态,并确认生成的worker_processes参数
ansible -i hosts all -m shell -a 'netstata -tpln|grep :80' -f 3
ansible -i hosts all -m shell -a 'grep worker_processes /etc/nginx/nginx.conf' -f 3
#指定task运行,也可以交互式的执行task指定--step参数即可。
ansible-playbook -i hosts nginx.yaml -f 3 --start-at-task='copy nginx.conf'
日常playbook例子
二、playbook变量与引用
1、通过inventory文件定义主机以及主机组变量
vim /etc/ansible/hosts
192.168.198.101 key=101
192.168.198.102 key=102
[nginx]
192.168.198.10[1:2]
[nginx:vars]
ansible_python_interpreter=/usr/bin/python2
vim variable.yaml
---
- hosts: all
gather_facts: False
tasks:
- name: display Host variable from hostfile
debug: msg='The {{ inventory_hostname }} Value is {{ key }}'
ansible-playbook variable.yaml
vim hosts
192.168.198.101
192.168.198.102
[nginx]
192.168.198.10[1:2]
[nginx:vars]
ansible_python_interpreter=/usr/bin/python2
key=nginx
ansible-playbook variable.yaml
2、通过/etc/ansible/下的文件定义主机以及主机组变量
tree /etc/ansible/
.
|-ansible.cfg
|-group_vars
|_nginx
|-hosts
|_host_vars
|-192.168.198.101
|_192.168.198.102
head host_vars/*
host_vars/192.168.198.101
---
key: 192.168.198.101
host_vars/192.168.198.102
---
key: 192.168.198.102
cat group_vars/nginx
---
key: NGINX
ansible-playbook /root/variable.yaml
rm -rf host_vars/*
ansible-playbook /root/variable.yaml
3、通过ansible-playbook命令行传入
ansible-playbook /root/variable.yaml -e 'key=KEY'
#还支持指定文件的方式传入变量,变量的内容支持YAML和JSON。
cat var.yaml
---
key: YAML
cat var.json
{"key":"JSON"}
ansible-playbook /root/variable.yaml -e "@var.json"
ansible-playbook /root/variable.yaml -e "@var.yaml"
4、在playbook文件内使用vars
cat variable.yaml
---
- hosts: all
gather_facts: False
vars:
key: Ansible
tasks:
- name: display Host Variable from hostfile
debug: msg="The {{ inventory_hostname }} Value is {{ key }}"
ansible-playbook /root/variable.yaml
4、在playbook文件内使用vars_files
cat variable.yaml
---
- hosts: all
gather_facts: False
vars_files:
- var.yaml
tasks:
- name: display Host Variable from hostfile
debug: msg="The {{ inventory_hostname }} Value is {{ key }}"
ansible-playbook /root/variable.yaml
6、使用register内的变量
---
- hosts: all
gather_facts: False
tasks:
- name: register variable
shell: hostname
register: info# 把返回的值传递给info
- name: display variable
debug: msg='The variable is {{ info }}'#打印info变量的内容
ansible-playbook variable.yaml -l 192.168.198.101
register的输出结果都是python字典,可以指定输出的信息。
---
- hosts: all
gather_facts: False
tasks:
- name: register variable
shell: hostname
register: info# 把返回的值传递给info
- name: display variable
debug: msg='The variable is {{ info['stdout'] }}'
ansible-playbook variable.yaml -l 192.168.198.101
7、使用vars_prompt传入
Ansible还支持在运行playbook的时候通过交互式的方式给定义好的参数传入变量值,只需要在playbook中定义vars_prompt的变量名和交互式提示内容即可。也可加密内容,需安装python的passlib库。
---
- hosts: all
gather_facts: False
vars_prompt:
- name: "one"
prompt: "please input one value"
private: no
- name: "two"
prompt: "Please input two value"
default: 'good'
private: yes
tasks:
- name: display one value
debug: msg="one value is {{ one }}"
- name: display two value
debug: msg="two value is {{ two }}"
ansible-playbook variable.yaml -l 192.168.198.101
注意,相同的变量名会发生覆盖。
三、playbook循环
1、标准loops
vim loops.yaml
---
- hosts: all
gather_facts: False
tasks:
- name: debug loops
debug: msg="name -----> {{ item }}"
with_items:
- one
- two
#运行,-l 只执行这个主机
ansible-playbook loops.yaml -l 192.168.198.101
#with_items的值是python list数据结构,可以理解为每个task会循环读取list里面的值,然后key的名称是item,当然list里面也支持python字典。如下
vim loops.yaml
---
- hosts: all
gather_facts: False
tasks:
- name: debug loops
debug: msg="name -----> {{ item.key }} value -----> {{ item.value }}"
with_items:
- {key: 'one', value: 'VALUE1'}
- {key: 'two', value: 'VALUE2'}
ansible-playbook loops.yaml -l 192.168.198.101
2、嵌套loops
---
- hosts: all
gather_facts: False
tasks:
- name: debug loops
debug: msg="name --> {{ item[0] }} value --> {{ item[1] }}"
with_nested:
- ['A']
- ['a','b','c']
ansible-playbook loops.yaml -l 192.168.198.101
可以通过一个Python例子来解释这个例子,定义两个list的代码如下:
In: one=['A']
In: two=['a','b','c']
In: [i + y for i in one for y in two]
Out: ['Aa','Ab','Ac']
3、散列loops
---
- hosts: all
gather_facts: False
vars:
user:
shencan:
name: shencan
shell: bash
ruifengyun:
name: ruifengyun
shell: zsh
tasks:
- name: debug loops
debug: msg='name --> {{ item.key }} value --> {{ item.value.name }} shell --> {{ item.value.shell }}'
with_dict: user
ansible-playbook loops.yaml -l 192.168.198.101
#fatal: [192.168.198.101]: FAILED! => {"msg": "with_dict expects a dict"}
#为什么失败了
原理,with_dict是接收一个python字典(经过yaml.load后)的格式的变量。
In: user
out: {'shencan': {'name': 'shencan','shell': 'bash'},
'ruifengyun':{'name': 'ruifengyun','shell': 'zsh'}}
In: for key,value in user.items():
print(key,value['name'],value['shell'])
out: shencan shencan bash
ruifengyun ruifengyun zsh
4、文件匹配l
目录下指定格式的文件进行处理,这个时候直接引用with_fileglob循环取匹配我们需要处理的文件即可。
---
- hosts: all
gather_facts: False
tasks:
- name: debug loops
debug: msg="files --> {{ item }}"
with_fileglob:
- /root/*.yaml
#with_fileglob会匹配root目录下所有以yam结尾的文件,当做{{item}}变量。
ansible-playbook loops.yaml -l 192.168.198.101
原理,使用glob.glob(’/root/*.yaml’)取做文件模糊匹配。(os.listdir())
5、随机选择loops
---
- hosts: all
gather_facts: False
tasks:
- name: debug loops
debug: msg="files --> {{ item }}"
with_random_choice:
- 'ansible1'
- 'ansible2'
- 'ansible3'
ansible-playbook loops.yaml -l 192.168.198.101
with_random_choice就是在传入的list 中随机选择一个,与使用python random实现原理一样。
In: import random
In: list=['ansible1','ansible2','ansible3']
In: print(random.choice(list))
ansible1
6、条件判断loops
有时候执行一个task之后,我们需要检测这个task的结果是否达到了预想状态,如果没有达到我们预想的状态时,就需要退出整个playbook执行,这个时候我们就需要对某个task结果一直循环检测了,如下所示:
---
- hosts: all
gather_facts: False
tasks:
- name: debug loops
shell: cat /root/Ansible
register: host
until: host.stdout.startswith("Master")
retries: 5
delay: 5
每隔5秒执行一次cat /root/Ansible将结果register给host然后判断host.stdout.startswith的内容是否Master字符串开头,如果条件成立,此task运行完成,如果条件不成立5秒后重试,5秒后还不成立,此task运行失败。
7、文件优先loops
---
- hosts: all
gather_facts: False
tasks:
- name: debug loops
debug: msg="files --> {{ item }}"
with_first_found:
- '{{ansible_distribution}}.yaml'
- 'default.yaml'
ansible-playbook loops.yaml -l 192.168.198.101
with_first_found会从list里面定义的文件从上往下一个一个匹配,匹配后就是item的值。
8、register loops
---
- hosts: all
gather_facts: True
tasks:
- name: debug loops
shell: '{{ item }}'
with_items:
- hostname
- uname
register: ret
- name:display loops
debug: msg='{% for i in ret.results %} {{ i.stdout }} {% endfor %}'
ansible-playbook loops.yaml -l 192.168.198.101
四、playbook lookups
1、lookups file
open是经常使用的lookups方式,打开文件把结果返回给变量。
---
- hosts: all
gather_facts: False
vars:
contents: '{{ lookup('open','/etc/sysconfig/network') }}'
tasks:
- name: debug lookups
debug: msg='The contents is {% for i in contents.split('\n') %}{{ i }}{% endfor %}'
ansible-playbook lookups.yaml -l 192.168.198.101
2、lookups password
password也是我们使用的一种lookups方式,对传入的内容进行加密处理。
---
- hosts: all
gather_facts: False
vars:
contents: '{{ lookup('password','ansible_book') }}'
tasks:
- name: debug lookups
debug: msg='The contents is {{ contents }}'
ansible-playbook lookups.yaml -l 192.168.198.101
3、lookups pipe
控制python的subprocess.Popen函数,将命令的结果传递给变量。
---
- hosts: all
gather_facts: False
vars:
contents: '{{ lookup('pipe','date +%Y-%m-%d') }}'
tasks:
- name: debug lookups
debug: msg=msg='The contents is {% for i in contents.split('\n') %}{{ i }}{% endfor %}'
#contents的内容就是在Ansible控制机器上执行date +%Y-%m-%d的结果。
ansible-playbook lookups.yaml -l 192.168.198.101
4、lookups redis_kv
redis_kv是从Redis数据库中get数据,需要安装redis的python库。这里在本地安装一个Redis服务器,然后给Ansible设置一个值。
redis-cli
redis 127.0.0.1:6379> set 'ansible' 'good'
redis 127.0.0.1:6379> get 'ansible'
#修改playbook并运行
---
- hosts: all
gather_facts: False
vars:
contents: '{{ lookup('redis_kv','redis://localhost:6379,ansible') }}'
tasks:
- name: debug lookups
debug: msg='The contents is {% for i in contents.split('\n') %}{{ i }}{% endfor %}'
ansible-playbook lookups.yaml -l 192.168.198.101
5、lookups template
template和open方式有点类似,都是读取文件,但是template在读取文件之前需要把jinja模板渲染完后再读取,下面我们制定一个jinja模板文件:
worker_processes {{ ansible_processor_cores }}
IPaddress {{ ansible_eth0.ipv4.address }}
---
- hosts: all
gather_facts: False
vars:
contents: '{{ lookup('template','./lookups.j2') }}'
tasks:
- name: debug lookups
debug: msg='The contents is {% for i in contents.split('\n') %}{{ i }}{% endfor %}'
ansible-playbook lookups.yaml
五、playbook conditionals
条件判断,在实际应用中经常会碰到不同的主机可能要执行不同的task。目前Ansible的所有条件方式都是使用when进行判断,条件成立就执行。
---
- hosts: all
tasks:
- name: Host 192.168.198.101 run this task
debug: msg='{{ ansible_default_ipv4.address }}'
when: ansible_default_ipv4.address == '192.168.198.101'
- name: memtotal < 200M and processor_cores == 2 run this task
debug: msg='{{ ansible_fqdn }}'
when: ansible_memtotal_mb < 500 and ansible_processor_cores == 2
- name: all host run thos task
shell: hostname
register: info
- name: Hostname is python Machie run this task
debug: msg='{{ ansible_fqdn }}'
when: info['stdout'] == 'python'
- name: Hostname is startswith M run this task
debug: msg='{{ ansible_fqdn }}'
when: info['stdout'].startswith('M')
#执行这个语句
ansible-playbook conditionals.yaml
#skipping表示跳过的主机,没有执行
六、Jinja2 filter(管道)
语法: {{ value|filter_name:参数 }}
Jinja2 filter扩展 Ansible默认支持Jinja2语言的内置filter,Jinja2官网也提供了很多filter。一下是常用的filter:
---
- hosts: all
gather_facts: False
vars:
list: [1,2,3,4,5]
one: '1'
str: 'string'
tasks:
- name: run commands
shell: df -h
register: info
- name: debug conditionals filter
debug: msg='The run commands status is changed'
when: info|changed
- name: debug int capitalize filter
debug: msg='The int value {{ one|int }} The ower value is {{ str|capitalize }}'
- name: debug default filter
debug: msg='The Variable value is {{ ansible | default('ansible is not define') }}'
- name: debug list max and min filter
debug: msg='The list max valye is {{ list | max }} The list min value is {{ list | min }}'
- name: debug ramdom filter
debug: msg='The list random value is {{ list | random }} and generate a random value is {{ 1000 | random(1,10) }}'
- name: debug join filter
debug: msg='The join filter value is {{ list | join('+') }}'
- name: msg='The replace value is {{ str |replace('t','T') }} The regex_replace value is {{ str | regex_replace('.*tr(.*)$','\\1') }}'
七、playbook内置变量
playbook默认已经内置变量,掌握这些变量就可以实现关于主机的逻辑判断。
1、groups和group_names
groups变量是一个全局变量,他会打印出Inventory文件里面的所有主机以及主机组信息,返回的是json字符串,可以直接调用{{ groups }},也可以引用其中的数据,比如docker组的host主机,{{ groups[‘docker’] }},他会返回一个list列表,group_names变量会打印当前主机所在的groups名称。如果没有定义会返回ungrouped,它返回的也是夜歌组名称的list列表。
2、hostvars
hostvars是用来调用指定主机变量,需要传入主机信息,返回结果也是一个JSON字符串,同样也可以直接引用JSON字符串内的指定信息。
3、inventory_hostname和inventory_hostname_short
inventory_hostname变量是返回Inventory文件里面定义的主机名,
inventory_hostname_short会返回Inventory文件中主机名的第一部分。
4、play_hosts和inventory_dir
play_hosts变量是用来返回当前playbook运行的主机信息,返回格式是主机list结构。
inventory_dir变量是用来返回当前playbook使用的Inventory目录。
5、应用案例
看一个Jinja2的模板文件,这个文件包含以上playbook内置变量。
=============
groups info
{{ groups }}
=============
=======docker groups info =========
{% for host in groups['docker'] %}
={{ hostvars[host]['inventory_hostname'] }} eth0 IP is {{ hostvars[host]['ansible_default_ipv4']['address'] }}
+{{ hostvars[host]['inventory_hostname'] }} groups is {{ group_names }}
-{{ hostvars[host]['inventory_hostname'] }} short is {{ inventory_hostname_short }}
*{{ play_hosts }}
@{{ inventory_dir }}
{% endfor %}
[root@localhost ~]# tree .
.
|-docker
|_hosts
[root@localhost ~]# cat docker
[docker]
172.17.42.10[1:3]
[docker:vars]
ansible_ssh_pass='123456'
[ansible:children]
docker
[root@localhost ~]# cat hosts
172.17.42.101 ansible_ssh_pass='123456'
172.17.42.102 ansible_ssh_pass='123456'
172.17.42.1 ansible_ssh_pass='123456'
[root@localhost ~]# cat template.yaml
---
- hosts: all
tasks:
- name: test template
template: src=jinja.j2 dest=/tmp/cpis
#开始执行playbook
ansible-playbook template.yaml
[root@localhost ~]# cat /tmp/cpis
可以参考结果来看每个变量的含义。