管理大项目
一、利用主机模式选择主机
1. 引用清单主机(用法)
主机模式用于指定要作为play或临时命令的目标的主机。在最简单的形式中,清单中受管主机或主机组的名称就是指定该主机或主机组的主机模式。
在play中,hosts指定要针对其运行play的受管主机。对于临时命令,以命令行参数形式将主机模式提供给ansible命令。
2. 获取受管主机
方法一: 直接写ip(常用)
[root@ansible ansible ~]# vim playbook.yml
---
- hosts: 192.168.47.129
方法二:通过IP地址引用受管主机。但是这种方式存在一个问题那就是难以记住play或临时命令所针对的主机使用了哪个IP地址。但是,如果没有可解析的主机名,我们可能必须按IP地址指定主机以进行连接,因为没有映射。
//如果有映射,可以根据主机名定义
[root@ansible ansible]# cat inventory
node
[root@ansible ansible]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.47.129 node
[root@ansible ansible]# ping node
PING node (192.168.47.129) 56(84) bytes of data.
64 bytes from node (192.168.47.129): icmp_seq=1 ttl=64 time=0
[root@ansible ansible]# ansible all -m ping //前提是做了免密登录
The authenticity of host 'node (192.168.47.129)' can't be established.
ECDSA key fingerprint is SHA256:R1sHsPUKGqzvhsHbbdaEr0NcNxutf4OEUT3JuAss6m4.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
node | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
主机模式用于指定要作为play或临时命令的目标的主机。在最简单的形式中,清单中受管主机或主机组的名称就是指定该主机或主机组的主机模式。
在play中,hosts指定要针对其运行play的受管主机。对于临时命令,以命令行参数形式将主机模式提供给ansible命令。
3. 使用组指定主机
当组名称用作主机模式时,它指定Ansible将对属于该组的成员的主机执行操作。
---
- hosts: lab
记住,有一个名为all的特别组,它匹配清单中的所有受管主机。
---
- hosts: all
还有一个名为ungrouped的特别组,它包括清单中不属于任何其他组的所有受管主机:
---
- hosts: ungrouped //不受组管理的
4. 使用通配符匹配多个主机
若要达成与all主机模式相同的目标,另一种方法是使用*通配符,它将匹配任意字符串。如果主机模式只是带引号的星号,则清单中的所有主机都将匹配。
---
- hosts: '*'
---
- hosts: '!test1.example.com,development' //不要test1这台主机,除了这台主机之外的所有的主机加上development这个组的主机
也可使用*字符匹配包含特定子字符串的受管主机或组。
例如,以下通配符主机模式匹配以.example.com结尾的所有清单名称:
---
- hosts: '*.example.com' //所有以.example.com结尾的所有主机
以下示例使用通配符主机模式来匹配开头为192.168.2.的主机或主机组的名称:
---
- hosts: '192.168.2.*' //192.168.2这个网段的所有主机
5. 列表
能够精确的用精确的,如果是要针对所有主机的,用all或者*;如果是针对某一个组里面的所有主机的话,就直接写组;如果是要在多个组里面的,就直接用逗号把多个组隔开;如果是在某一个组里面,不要组里面某一台,组里面的其他主机都要,那就先把组写上去,然后用逗号感叹号把要摘开的那台主机写上去,感叹号替换。
可以通过逻辑列表来引用清单中的多个条目。主机模式的逗号分隔列表匹配符合任何这些主机模式的所有主机。
如果提供受管主机的逗号分隔列表,则所有这些受管主机都将是目标:
---
- hosts: labhost1.example.com,test2.example.com,192.168.2.2
如果提供组的逗号分隔列表,则属于任何这些组的所有主机都将是目标:
---
- hosts: lab,datacenter1
也可以混合使用受管主机、主机组和通配符,如下所示:
---
- hosts: 'lab,data*,192.168.2.2'
注意
也可以用冒号(:)取代逗号。不过,逗号是首选的分隔符,特别是将IPv6地址用作受管主机名称时。
如果列表中的某一项以与符号(&)开头,则主机必须与该项匹配才能匹配主机模式。它的工作方式类似于逻辑AND。
例如,根据我们的示例清单,以下主机模式将匹配lab组中同时也属于datacenter1组的计算机:
---
- hosts: lab,&datacenter1 只有同时在lab和datacenterl这两个组的主机才匹配
我们也可以通过主机模式&lab,datacenter1或datacenter,&lab指定datacenter1组中的计算机只有在同时也属于lab组时才匹配。
通过在主机模式的前面使用感叹号(!)表示从列表中排除匹配某一模式的主机。它的工作方式类似于逻辑NOT。
根据示例清单,以下示例匹配datacenter组中定义的所有主机,但test2.example.com除外:
---
- hosts: datacenter,!test2.example.com
也可以使用模式’!test2.example.com,datacenter’来获得相同的结果。
最后一个示例演示了使用匹配测试清单中的所有主机的主机模式,datacenter1组中的受管主机除外。
---
- hosts: all,!datacenter1
二、动态清单
静态清单是直接写到文件里面直接写死的,而动态清单是需要去执行一个程序,通过这个程序去生成一个文件,而这个文件里面写了我们要受管的主机有哪些。这个程序的写法使用Python写的
三、配置并行
1. 使用分叉在ansible中配置并行
当Ansible处理playbook时,会按顺序运行每个play。确定play的主机列表之后,Ansible将按顺序运行每个任务。通常,所有主机必须在上一个任务执行成功才能启动下一个任务。
理论上,Ansible可以同时连接到play中的所有主机以执行每项任务。这非常适用于小型主机列表。但如果该play以数百台主机为目标,则可能会给控制节点带来沉重负担。
Ansible所进行的最大同时连接数由Ansible配置文件中的forks参数控制。默认情况下设为5,这可通过以下方式之一来验证。
[root@localhost ~]# grep forks /etc/ansible/ansible.cfg
#forks = 5
[root@localhost ~]# ansible-config dump|grep -i forks
DEFAULT_FORKS(default) = 5
[root@localhost ~]# ansible-config list|grep -i forks
DEFAULT_FORKS:
description: Maximum number of forks Ansible will use to execute tasks on target
- {name: ANSIBLE_FORKS}
- {key: forks, section: defaults}
name: Number of task forks
Ansible所进行的最大同时连接数由Ansible配置文件中的forks参数控制。
例如,假设Ansible控制节点配置了5个forks的默认值,并且play具有10个受管主机。Ansible将在前5个受管主机上执行play中的第一个任务,然后在其他5个受管主机上对第一个任务执行第二轮。在所有受管主机上执行第一个任务后,Ansible将继续一次在5受管主机的组中的所有受管主机上执行下一个任务。Ansible将依次对每个任务执行此操作,直到play结束。
forks的默认值设置得非常保守。如果你的控制节点正在管理Linux主机,则大多数任务将在受管主机上运行,并且控制节点的负载较少。在这种情况下,通常可以将forks的值设置得更高,可能接近100,然后性能就会提高。
如果playbook在控制节点上运行很多代码,则应明智地提高forks限值。如果使用Ansible管理网络路由器和交换机,则大多数模块在控制节点上运行而不是在网络设备上运行。由于这会增加控制节点上的负载,因此其支持forks数量增加的能力将显著低于仅管理Linux主机的控制节点。
可以从命令行覆盖Ansible配置文件中forks的默认设置。ansible和ansible-playbook命令均提供-f或–forks选项以指定要使用的forks数量。
2. 滚动更新
通常,当Ansible运行play时,它会确保所有受管主机在启动任何主机进行下一个任务之前已完成每个任务。在所有受管主机完成所有任务后,将运行任何通知的处理程序。
但是,在所有主机上运行所有任务可能会导致意外行为。例如,如果play更新负载均衡Web服务器集群,则可能需要在进行更新时让每个Web服务器停止服务。如果所有服务器都在同一个play中更新,则它们可能全部同时停止服务。
避免此问题的一种方法是使用serial关键字,通过play批量运行主机。在下一批次启动之前,每批主机将在整个play中运行。
在下面的示例中,Ansible一次在两个受管主机上执行play,直至所有受管主机都已更新。Ansible首先在前两个受管主机上执行play中的任务。如果这两个主机中的任何一个或两个都通知了处理程序,则Ansible将根据这两个主机的需要运行处理程序。在这两个受管主机上执行完play时,Ansible会在接下来的两个受管主机上重复该过程。Ansible继续以这种方式运行play,直到所有受管主机都已更新。
---
- name: Rolling update
hosts: webservers
serial: 2
tasks:
- name: latest apache httpd package is installed
yum:
name: httpd
state: latest
notify: restart apache
handlers:
- name: restart apache
service:
name: httpd
state: restarted
假设上一示例中的webservers组包含5个Web服务器,它们位于负载均衡器后面。将serial参数设置为2后,play一次将运行两台Web服务器。因此,5台Web服务器中的大多数服务器将始终可用。
相反,如果不使用serial关键字,将同时在5台Web服务器上执行play和生成的处理程序。这可能会导致服务中断,因为Web服务将在所有Web服务器上同时重新启动。
重要
出于某些目的,每批主机算作在主机子集上运行的完整play。这意味着,如果整个批处理失败,play就会失败,这将导致整个playbook运行失败。
在设置了serial: 2的上一个场景中,如果出现问题并且处理的前2个主机的play失败,则playbook将中止,其余3个主机将不会通过play运行。这是一个有用的功能,因为只有一部分服务器会不可用,使服务降级而不是中断。
serial关键字也可以指定为百分比。此百分比应用于play中的主机总数,以确定滚动更新批处理大小。无论百分比为何,每一工序的主机数始终为1或以上。
四、包含和导入文件
Ansible可以使用两种操作将内容带入playbook。可以包含内容,也可以导入内容。
包含内容是一个动态操作。在playbook运行期间,Ansible会在内容到达时处理所包含的内容。
导入内容是一个静态操作。在运行开始之前,Ansible在最初解析playbook时预处理导入的内容。
1. 导入playbook
import_playbook指令允许将包含play列表的外部文件导入playbook。换句话说,可以把一个或多个额外playbook导入到主playbook中。
由于导入的内容是一个完整的playbook,因此import_playbook功能只能在playbook的顶层使用,不能在play内使用。如果导入多个playbook,则将按顺序导入并运行它们。
[root@ansible ~]# cd /etc/ansible/playbook/
[root@ansible playbook]# ls
apache files passwd tasks template vars
[root@ansible playbook]# cd apache/
[root@ansible apache]# ls
files //存放配置文件
[root@ansible apache]# touch install.yml
[root@ansible apache]# touch config.yml
[root@ansible apache]# touch main.yml
[root@ansible apache]# cat install.yml //安装apache
---
- hosts: apache
tasks:
- name: install apache
yum:
name: httpd
state: present
[root@ansible apache]# cat config.yml //配置apache
--- //分隔
- hosts: apache
vars: //定义变量
- port: 8080
tasks:
- name: config httpd
template: //改端口号
src: files/httpd.conf.j2 //相对路径
dest: /etc/httpd/conf/httpd.conf //目标
[root@ansible apache]# cd files/
[root@ansible files]# scp apache:/etc/httpd/conf/httpd.conf httpd.conf.j2
httpd.conf 100% 12KB 10.9MB/s 00:00
[root@ansible files]# ls
httpd.conf.j2
[root@ansible files]# vim httpd.conf.j2
44 #Listen 12.34.56.78:80
45 Listen {{ port }} //引用变量
[root@ansible apache]# cat main.yml
- name: import playbook for install //导入playbook安装
import_playbook: install.yml //包含install.yml,因为install.yml很config.yml和main.yml在同一级别,所以直接写名字就可以了
- name: import playbook for config
import_playbook: config.yml
[root@ansible playbook]# ansible-playbook apache/main.yml
PLAY [apache] ******************************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.47.129]
TASK [install apache] **********************************************************
ok: [192.168.47.129]
[WARNING]: Found variable using reserved name: port
PLAY [apache] ******************************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.47.129]
TASK [config httpd] ************************************************************
changed: [192.168.47.129]
PLAY RECAP *********************************************************************
192.168.47.129 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@apache ~]# grep 80 /etc/httpd/conf/httpd.conf //过滤80端口
#Listen 12.34.56.78:80
Listen 8080
#ServerName www.example.com:80
使用导入的playbook在主playbook中交替play
[root@ansible playbook]# cat apache/main.yml
--- //---必须写在文件的最开始。不能放在后面,放在后面,因为导入是在文件之后写的,如果先导入的话,后面写的东西就冲突了
- hosts: apache
gather_facts: no
tasks:
- name: start service //只是示例,正常情况下不可以这样写
service:
name: httpd
state: started
enabled: yes
- name: import playbook for install
import_playbook: install.yml
- name: import playbook for config
import_playbook: config.yml
[root@ansible playbook]# ansible-playbook apache/main.yml
PLAY [apache] ******************************************************************
TASK [start service] ***********************************************************
changed: [192.168.47.129]
PLAY [apache] ******************************************************************
TASK [install apache] **********************************************************
ok: [192.168.47.129]
[WARNING]: Found variable using reserved name: port
PLAY [apache] ******************************************************************
TASK [config httpd] ************************************************************
ok: [192.168.47.129]
PLAY RECAP *********************************************************************
192.168.47.129 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2. 导入和包含任务
可以将任务文件中的任务列表导入或包含在play中。任务文件是包含一个任务平面列表的文件
写一个任务文件,只写任务,告诉怎么安装怎么配置,但是不告诉在哪个地方安装哪个地方配置
任务文件
[root@ansible playbook]# cat apache/install.yml
- name: install apache
yum:
name: httpd
state: present
[root@ansible playbook]# cat apache/config.yml
- name: config httpd
template:
src: files/httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
2.1 导入任务文件
可以使用import_tasks功能将任务文件静态导入playbook内的play中。导入任务文件时,在解析该playbook时将直接插入该文件中的任务。Playbook中的import_tasks的位置控制插入任务的位置以及运行多个导入的顺序。
[root@ansible apache]# mkdir tasks
[root@ansible apache]# mv install.yml config.yml tasks/
[root@ansible apache]# ls
files main.yml tasks
[root@ansible playbook]# cat apache/main.yml
---
- hosts: apache
gather_facts: no
vars:
port: 80
tasks:
- import_tasks: tasks/install.yml //导入任务
- import_tasks: tasks/config.yml
- name: start service
service:
name: httpd
state: started
enabled: yes
[root@ansible playbook]# ansible-playbook apache/main.yml
[WARNING]: Found variable using reserved name: port
PLAY [apache] ******************************************************************
TASK [install apache] **********************************************************
ok: [192.168.47.129]
TASK [config httpd] ************************************************************
changed: [192.168.47.129]
TASK [start service] ***********************************************************
ok: [192.168.47.129]
PLAY RECAP *********************************************************************
192.168.47.129 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@apache ~]# grep 80 /etc/httpd/conf/httpd.conf
#Listen 12.34.56.78:80
Listen 80
#ServerName www.example.com:80
导入任务文件时,在解析该playbook时将直接插入该文件中的任务。由于import_tasks在解析playbook时静态导入任务,因此对其工作方式有一些影响:
- 使用import_tasks功能时,导入时设置的when等条件语句将应用于导入的每个任务
- 无法将循环用于import_tasks功能
- 如果使用变量来指定要导入的文件的名称,则将无法使用主机或组清单变量
2.2 包含任务文件
可以使用include_tasks功能将任务文件动态导入playbook内的play中。
[root@ansible playbook]# cat apache/main.yml
---
- hosts: apache
gather_facts: no
vars:
port: 80
tasks:
- include_tasks: tasks/install.yml
- include_tasks: tasks/config.yml
- name: start service
service:
name: httpd
state: started
enabled: yes
[root@ansible playbook]# ansible-playbook apache/main.yml
[WARNING]: Found variable using reserved name: port
PLAY [apache] ******************************************************************
TASK [include_tasks] ***********************************************************
included: /etc/ansible/playbook/apache/tasks/install.yml for 192.168.47.129
TASK [install apache] **********************************************************
ok: [192.168.47.129]
TASK [include_tasks] ***********************************************************
included: /etc/ansible/playbook/apache/tasks/config.yml for 192.168.47.129
TASK [config httpd] ************************************************************
ok: [192.168.47.129]
TASK [start service] ***********************************************************
ok: [192.168.47.129]
PLAY RECAP *********************************************************************
192.168.47.129 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
在play运行并且这部分play到达前,include_tasks功能不会处理playbook中的内容。Playbook内容的处理顺序会影响包含任务功能的工作方式。
- 使用include_tasks功能时,包含时设置的when等条件语句将确定任务是否包含在play中,when条件在包含的文件里面有when就判断,如果没有就不判断
- 如果运行ansible-playbook–list-tasks以列出playbook中的任务,则不会显示已包含任务文件中的任务。将显示包含任务文件的任务。相比之下,import_tasks功能不会列出导入任务文件的任务,而列出已导入任务文件中的各个任务
[root@ansible playbook]# cat apache/main.yml
---
- hosts: apache
gather_facts: no
vars:
port: 80
tasks:
- import_tasks: tasks/install.yml
- include_tasks: tasks/config.yml
- name: start service
service:
name: httpd
state: started
enabled: yes
[root@ansible ansible]# ansible-playbook --list-tasks playbook/apache/main.yml
playbook: playbook/apache/main.yml
play #1 (apache): apache TAGS: []
tasks:
install apache TAGS: [] //导入的能看见被导入文件的里面的任务名称
include_tasks TAGS: [] //包含只能看见包含任务,看不见被包含这个任务里面主机的名称
start service TAGS: []
- 不能使用ansible-playbook --start-at-task从已包含任务文件中的任务开始执行playbook,执行的时候是按照顺序来执行的,导入和包含的时候是严格按照顺序来执行的。
- 不能使用notify语句触发已包含任务文件中的处理程序名称。可以在包含整个任务文件的主playbook中触发处理程序,在这种情况下,已包含文件中的所有任务都将运行。只能在主程序里面定义一个handlers(在所有任务完成之后才会去执行),在这里面去触发,不能在任务文件里面去写一个handlers,写了也没用,不会执行。
2.3 管理任务文件
为方便管理,可以创建专门用于任务文件的目录,并将所有任务文件保存在该目录中。然后playbook就可以从该目录包含或导入任务文件。这样就可以构建复杂的playbook,同时简化其结构和组件的管理。
[root@ansible ansible]# tree playbook/apache/
playbook/apache/
├── files
│ └── httpd.conf.j2
├── main.yml
└── tasks
├── config.yml
└── install.yml
2 directories, 4 files
3. 为外部play和任务定义变量
使用Ansible的导入和包含功能将外部文件中的play或任务合并到playbook中极大地增强了在Ansible环境中重用任务和playbook的能力。为了最大限度地提高重用可能性,这些任务和play文件应尽可能通用。变量可用于参数化play和任务元素,以扩大任务和play的应用范围。
这种用法的前提下install和config用到的值是不一样的。那个变量是通用的直接写进vars里面就可以
[root@ansible playbook]# cat apache/tasks/install.yml
- name: install {{ package}}
yum:
name: "{{ package}}"
state: present
[root@ansible playbook]# vim apache/main.yml
---
- hosts: apache
gather_facts: no
vars:
port: 80
tasks:
- import_tasks: tasks/install.yml
vars:
package: httpd
- include_tasks: tasks/config.yml
- name: start service
service:
name: httpd
state: started
enabled: yes
[root@ansible playbook]# ansible-playbook apache/main.yml
[WARNING]: Found variable using reserved name: port
PLAY [apache] ******************************************************************
TASK [install httpd] ***********************************************************
ok: [192.168.47.129]
TASK [include_tasks] ***********************************************************
included: /etc/ansible/playbook/apache/tasks/config.yml for 192.168.47.129
TASK [config httpd] ************************************************************
ok: [192.168.47.129]
TASK [start service] ***********************************************************
ok: [192.168.47.129]
PLAY RECAP *********************************************************************
192.168.47.129 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0