在playbook中引用hosts时,如果对应的主机清单中没有配置该主机,是无法执行的。
如果主机清单中配置的是主机名称,那么在playbook中hosts字段后写的是ip或者主机名,那么也是无法执行的,示例如下
[student@ansible ansible]$ vim /etc/ansible/hosts
node1
node2
[student@ansible ansible]$ vim a.yml
---
- name: test
hosts: 172.16.30.10,node2.example.com
tasks:
- name: debug1
debug:
msg: haha
执行a.yml这个playbook的时候,你就会发现有问题了,直接跳过该任务,无法执行。
[student@ansible ansible]$ ansible-playbook a.yml
[WARNING]: Could not match supplied host pattern, ignoring: 172.16.30.10
[WARNING]: Could not match supplied host pattern, ignoring: node2.example.com
PLAY [test] ********************************************************************
skipping: no hosts matched
PLAY RECAP *********************************************************************
[student@ansible ansible]$
所以在引用主机清单时,一定要注意playbook里面hosts字段后写的内容一定只能时主机清单里面编辑的内容。
使用playbook引用主机清单的方式有很多种,如下
Hosts: node1
Hosts: node1.example.com
Hosts: 172.16.30.10
Hosts: all ----所有的受控主机
Hosts: net ----主机组net
Hosts: ‘*’ ----所有的主机
Hosts: ‘*.example.com’
Hosts: ‘172.16.30.*’
Hosts: ‘web*’ ---通过匹配符*来匹配
Hosts: net:webserver ----取net组和webserver组的并集
Hosts: net:&webserver ---取交集
Hosts: net:!node1 ---net组所有的主机,但是除了node1
Hosts: node[1-5]
Hosts: node[a-d]
从我们学习到现在为止,我们使用最多的就是静态主机清单,我们ansible也支持动态的主机清单。在什么时候我们才会用到动态的主机清单呢?
比如说,我们受控主机比较少的情况下,我们建议是使用静态主机清单的,但当我们处于一个大型的环境下,受控主机非常非常多的时候,而且受控主机更替非常快的时候,我们使用静态主机清单就很难来管理了,我们使用动态的主机清单更便于我们管理了。
Ansible支持动态清单脚本,这些脚本在每当ansible执行时从这些类型的来源检索当前的信息,使清单能够实时得到更新。这些清单脚本是可执行的程序,能够从一些外部来源收集信息,并以JSON格式输出清单。
[student@ansible ansible]$ vim hosts
node1 myvars=aa
[net]
node2
[webserver:children]
net
我们可以通过ansible-inventory -i hosts --list 命令,将主机清单以JSON的格式显示出来
[student@ansible ansible]$ ansible-inventory -i hosts --list
{
"_meta": { //_meta下记录着我们主机中是否定义变量
"hostvars": {
"node1": {
"myvars": "aa"
}
}
},
"all": { // all:列出我们所有的主机组,其中我们会
"children": [ 发现我们并没有设置ungrouped主机组,这
"ungrouped", 里怎么会有ungrouped组呢?
"webserver" 这个ungrouped组是那些不属于任何主机组
] 的主机。
},
"net": { //net主机组下的成员
"hosts": [
"node2"
]
},
"ungrouped": { //ungrouped主机组就是不属于任何主机组
"hosts": [ 的主机的
"node1"
]
},
"webserver": {
"children": [
"net"
]
}
}
[student@ansible ansible]$
例子
我们去下载一个动态的主机清单,然后做一个测试,测试下载下来的动态的主机清单能否使用?
[student@ansible ansible]$ cat inventory.py
#!/usr/bin/env python3
'''
Example custom dynamic inventory script for Ansible, in Python.
'''
import os
import sys
import argparse
import json
class ExampleInventory(object):
def __init__(self):
self.inventory = {}
self.read_cli_args()
# Called with `--list`.
if self.args.list:
self.inventory = self.example_inventory()
# Called with `--host [hostname]`.
elif self.args.host:
# Not implemented, since we return _meta info `--list`.
self.inventory = self.empty_inventory()
# If no groups or vars are present, return empty inventory.
else:
self.inventory = self.empty_inventory()
print(json.dumps(self.inventory));
# Example inventory for testing.
def example_inventory(self):
return {
'net': {
'hosts': ['node1'],
'vars': {
'mytest_var1': 'hello1',
'mytest_var2': 'hello2',
'mytest_common': 'hello_common'
}
},
'hr': {
'hosts': ['node2'],
'vars': {
'mytest_var3': 'hello3',
'mytest_common': 'hello_common'
}
},
'_meta': {
'hostvars': {
'node1': {
'host_specific_var': 'foo100'
},
'node2': {
'host_specific_var': 'bar101'
}
}
}
}
# Empty inventory for testing.
def empty_inventory(self):
return {'_meta': {'hostvars': {}}}
# Read the command line args passed to the script.
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()
# Get the inventory.
ExampleInventory()
[student@ansible ansible]$ cat test1.yaml
---
- hosts: all
tasks:
- name: debug test 1
debug:
msg: "just for test ansible dynamic inventory"
- name: debug test 2
debug:
msg: "{{ mytest_common }}"
执行test1.yml这个playbook之前,我们首先需要给hosts.py这个动态的主机清单一个执行的权限,因为它是一个脚本,需要执行。chmod +x hosts.py
接着就是执行这个playbook了,也是有两种方法
方法一: ansible-playbook test1.yaml -i hosts.py 手动指定使用哪个主机清单
方法二:
修改ansible.cfg配置文件
Inventory = /etc/ansible/hosts.py
然后使用ansible-playbook test1.yaml命令执行该playbook
PLAY [all] *********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [node1]
ok: [node2]
TASK [debug test 1] ************************************************************
ok: [node1] => {
"msg": "just for test ansible dynamic inventory"
}
ok: [node2] => {
"msg": "just for test ansible dynamic inventory"
}
TASK [debug test 2] ************************************************************
ok: [node1] => {
"msg": "hello_common"
}
ok: [node2] => {
"msg": "hello_common"
}
PLAY RECAP *********************************************************************
node1 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
当ansible处理playbook时,会按顺序运行每个play。确定play的主机列表后,ansible将按顺序运行每个任务。通常,所有主机必须在任何主机在play中启动下一个任务之前完成任务。
理论上,ansible可以连接到play中的所有主机以执行每项任务。这非常适用于小型主机列表。但如果该play以数百台主机为目标,则可能会给控制节点带来沉重的负担。
Ansible所进行的最大同时连接数由ansible配置文件的forks参数控制。默认值为5.
比如一个play具有10台受控主机,我们forks的值为5,那么,首先前5台主机,同时运行第一个play,当这5台主机运行完成第一个play后,后5台主机同时运行第一个play,等到10台主机运行完成第一个play时,才开始运行第二个play,以此类推。
当受控主机时linux主机时,我们执行play的时候,大多数的任务是在受控主机上运行的,ansible控制节点得负载就少了,这种情况下呢,我们可以把forks的值设置得更高一些。
当受控主机是网络设备时,比如路由器交换机时,大多数得模块是在控制节点中运行的,这时控制节点的负载就会很高,这个时候我们就不能去提高forks的值了。
通常情况下,ansible运行play时,它会确保所有的受控主机在启动任何进行下一项任务之前已完成了每个任务。在所有受控主机完成所有任务后,将运行任何通知的处理器。
比如说,给所有webserver主机组安装httpd软件包时,安装成功后会重启服务,那么我们使用触发器进行配置。
[student@ansible ansible]$ vim haha.yml
---
- name: web station
hosts: webserver
tasks:
- name: install httpd
yum:
name: httpd
state: latest
notify:
- restart httpd
handlers:
- name: restart httpd
service:
name: httpd
state: restarted
那么加入webserver此时有非常非常多的主机,当我们中间某个更新httpd失败时,那么handlers还会执行吗?我们都知道,handlers它是在我们执行完所有的play之后,才会去执行的,但是node1、node2都更新成功了,node3这里失败了,那么整个play就直接中断了,不会去执行handlers了。那么就有问题了,那我们如何来解决这个问题呢?
就可以用到serial参数,加入给该参数设置为2,那么意思就是先从webserver中选择2台主机进行运行play,当我这两台主机全部运行play完成后,再从webserver中未执行play的主机中选择2台进行执行play,以此类推。设置如下
[student@ansible ansible]$ vim hehe.yml
---
- name: web station
hosts: webserver
serial: 2
tasks:
- name: install httpd
yum:
name: httpd
state: latest
notify:
- restart httpd
handlers:
- name: restart httpd
service:
name: httpd
state: restarted
例子:部署httpd站点
先写一些部署yum仓库的playbook
[student@ansible ansible]$ vim repo.yml
---
- name: configure repo
hosts: node1,node2
tasks:
- name: repo1
yum_repository:
file: server
name: baseos
description: rhel8
baseurl: file:///mnt/BaseOS
enabled: yes
gpgcheck: no
- name: repo2
yum_repository:
file: server
name: appstream
description: RHEL8
baseurl: file:///mnt/AppStream
enabled: yes
gpgcheck: no
- name: mount cdrom
mount:
src: /dev/cdrom
path: /mnt
fstype: iso9660
state: mounted
然后再写一个安装httpd软件包的playbook
[student@ansible ansible]$ vim http.yml
---
- name: install http
yum:
name: httpd
state: present
[student@ansible ansible]$
最后写入一个部署web站点的playbook,其中导入playbook和在tasks中导入任务
[student@ansible ansible]$ cat web.yml
---
- name: web station
import_playbook: repo.yml
- name: test
hosts: all
tasks:
- import_tasks: http.yml
- name: start httpd
service:
name: httpd
state: started
enabled: yes
在tasks中包含任务
安装vsftpd软件包,并启动vsftpd服务
[student@ansible ansible]$ vim vsftpd.yml
---
- name: abc
yum:
name: vsftpd
state: present
[student@ansible ansible]$
[student@ansible ansible]$ cat a.yml
---
- name: test
hosts: all
tasks:
- include_tasks: vsftpd.yml
- name: start vsftpd
service:
name: vsftpd
state: started
enabled: yes
使用动态清单模板,修改其内容,要求如下:
(1)node1是test主机组的成员,其中test主机组可以使用变量:
aa=11
bb=22
(2)node2和node3是prod主机组的成员,其中prod主机组可以使用的变量:
cc=33
dd=44
(3)node1还可以使用的变量:nodevar=name1
(4)node2还可以使用的变量:nodevar=name2
(5)node3还可以使用的变量:nodevar=name3
(6)撰写一个test.yml的playbook,要求所有的受控主机输出变量nodevar的值
[student@ansible ansible]$ rz -E
rz waiting to receive.
[student@ansible ansible]$ ls
ansible.cfg inventory inventory.php inventory.py README.md roles test1.yaml
[student@ansible ansible]$ vim inventory.py
[student@ansible ansible]$ cat inventory.py
#!/usr/bin/env python3
'''
Example custom dynamic inventory script for Ansible, in Python.
'''
import os
import sys
import argparse
import json
class ExampleInventory(object):
def __init__(self):
self.inventory = {}
self.read_cli_args()
# Called with `--list`.
if self.args.list:
self.inventory = self.example_inventory()
# Called with `--host [hostname]`.
elif self.args.host:
# Not implemented, since we return _meta info `--list`.
self.inventory = self.empty_inventory()
# If no groups or vars are present, return empty inventory.
else:
self.inventory = self.empty_inventory()
print(json.dumps(self.inventory));
# Example inventory for testing.
def example_inventory(self):
return {
'test': {
'hosts': ['node1'],
'vars': {
'aa': '11',
'bb': '22',
}
},
'prod': {
'hosts': ['node2','node3'],
'vars': {
'cc': '33',
'dd': '44'
}
},
'_meta': {
'hostvars': {
'node1': {
'nodevar': 'haha1'
},
'node2': {
'nodevar': 'haha2'
},
'node3': {
'nodevar': 'haha3'
}
}
}
}
# Empty inventory for testing.
def empty_inventory(self):
return {'_meta': {'hostvars': {}}}
# Read the command line args passed to the script.
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()
# Get the inventory.
ExampleInventory()
[student@ansible ansible]$ chmod +x inventory.py
[student@ansible ansible]$ ll
总用量 40
-rw-r--r--. 1 student student 20003 10月 25 01:41 ansible.cfg
-rw-rw-r--. 1 student student 61 10月 25 01:38 inventory
-rw-r--r--. 1 student student 1769 11月 1 23:27 inventory.php
-rwxr-xr-x. 1 student student 2023 11月 2 00:18 inventory.py
-rw-r--r--. 1 student student 1673 11月 1 23:27 README.md
drwxrwxr-x. 2 student student 6 10月 25 01:38 roles
-rw-r--r--. 1 student student 301 11月 1 23:27 test1.yaml
[student@ansible ansible]$ vim test.yml
[student@ansible ansible]$ cat test.yml
---
- name: test1 debug
hosts: all
tasks:
- name: debug
debug:
msg: "{{ nodevar }}"
[student@ansible ansible]$ ansible-playbook test.yml -i inventory.py
PLAY [test1 debug] ******************************************************************************************
TASK [Gathering Facts] **************************************************************************************
ok: [node2]
ok: [node1]
ok: [node3]
TASK [debug] ************************************************************************************************
ok: [node3] => {
"msg": "haha3"
}
ok: [node2] => {
"msg": "haha2"
}
ok: [node1] => {
"msg": "haha1"
}
PLAY RECAP **************************************************************************************************
node1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node3 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0