第八章、ansible基于清单管理大项目
文章目录
- 一、引用主机清单
- 二、管理动态清单
- 三、配置并行
- 1、import_ 包含和导入
- 2、包含语句
一、引用主机清单
在playbook中引用hosts时,如果对应的主机清单中没有配置该主机,是无法执行的。
如果主机清单中配置的是主机名称,那么在playbook中hosts字段后写的是ip或者主机名,那么也是无法执行的,示例如下:
[student@server ansible]$ vim /etc/ansible/hosts
node1
node2
[student@server ansible]$ vim a.yml
---
- name: test
hosts: 172.16.30.10,node2.example.com
tasks:
- name: debug1
debug:
msg: chenyu
执行a.yml这个playbook的时候,你就会发现有问题了,直接跳过该任务,无法执行。
[root@server 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 *********************************************************************
[root@server ansible]$
所以在引用主机清单时,一定要注意playbook里面hosts字段后写的内容只能是主机清单里面编辑的内容。
使用playbook引用主机清单的方式有很多种,不过在使用这种的情况下需要清单里面有这些,如下
Hosts: node1
Hosts: node1.example.com
Hosts: 172.16.30.10
Hosts: all ----所有的受控主机
Hosts: net ----主机组[net]
Hosts: ‘’ ----所有的主机
Hosts: ‘.example.com’ 以example为后缀名
Hosts: ‘172.16.30.’ 以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@server ansible]$ Vim hosts
node1 myvars=aa 这里为node1赋予变量,输出时输出=aa
[net]
node2
[webserver:children]
net
我们可以通过ansible-inventory -i hosts --list 命令,将主机清单以JSON的格式显示出来
[root@server ansible]$ ansible-inventory -i hosts --list
{
"_meta": { //_meta下记录着主机中是那些是定义变量了
"hostvars": { //吧执行主机里面的事实放在一起
"node1": {
"myvars": "aa"
}
}
},
"all": { // all:列出我们所有的主机组,
"children": [ //子组
"ungrouped", 其中我们会发现我们并没有设置ungrouped主机组,这里怎么会有ungrouped组呢?这个ungrouped组意为是没有被分组的任何主机。
"webserver" //附加组
]
},
"net": { //net主机组下的成员
"hosts": [
"node2"
]
},
"ungrouped": { //ungrouped主机组就是不属于任何主机组的主机的
"hosts": [
"node1" //这里匹配到了没有被分组的node1
]
},
"webserver": { //附加组
"children": [
"net"
]
}
}
[root@server ansible]$
例子:
我们去下载一个动态的主机清单,然后做一个测试,测试下载下来的动态的主机清单能否使用?,这里是到网上找的一篇基本文件,但里面的内容可以修改
[root@server ansible]$ cat hosts.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': { //定义三个变量,dev组和组成员可以用
'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()
//测试清单
[root@server 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了,也是有两种方法
-i 手动指定使用哪个主机清单
方法一: 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软件包时,安装成功后会重启服务,那么我们使用触发器进行配置。
---
- 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,以此类推。设置如下:
---
- 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
1、import_ 包含和导入
导入playbook
导入 tasks
例子:部署httpd站点
先写一些部署yum仓库的playbook
[root@server ansible]$ cat 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
[root@server ansible]$ cat http.yml
---
- name: install http
yum:
name: httpd
state: present
[root@server ansible]$
最后写入一个部署web站点的playbook,其中导入playbook和在tasks中导入任务
[root@server ansible]$ cat web.yml
---
- name: web station
import_playbook: repo.yml //这里是首先执行好yum仓库
- name: test
hosts: all
tasks:
- import_tasks: http.yml
- name: start httpd
service:
name: httpd
state: started
enabled: yes
2、包含语句
使用include命令在tasks中只能包含任务(也就是简单的启动服务)
安装vsftpd软件包,并启动vsftpd服务
[root@server ansible]$ cat vsftpd.yml
---
- name: abc
yum:
name: vsftpd
state: present
[root@server ansible]$
[root@server ansible]$ cat a.yml
---
- name: test
hosts: all
tasks:
- include_tasks: vsftpd.yml
- name: start vsftpd
service:
name: vsftpd
state: started
enabled: yes