Playbook概述
1.Playbook简介
playbook是 一个不同于使用Ansible命令行执行方式的模式,其功能更强大灵活。 简单来说,playbook是一个非常简单的配置管理和多主机部署系统,不同于任何已经存在的模式,可作为一个适合部署复杂应用程序的基础。**Playbook可以定制配置,可以按照指定的操作步骤有序执行,支持同步和异步方式。**我们完成一个任务,例如安装部署一个httpd服务,我们需要多个模块(一个模块也可以称之为task)提供功能来完成。而playbook就是组织多个task的容器,他的实质就是一个文件,有着特定的组织格式,它采用的语法格式是YAML(Yet Another Markup Language)。
2.Playbook组成部分
- **Tasks(任务): 包含要在目标主机上执行的操作,使用模块定义这些操作。**每个任务都是一个模块调用,可以是系统命令、包管理器操作、文件操作等。
- **Variables(变量): 用于存储和传递数据。**在 Playbooks 中,您可以定义变量,这些变量可以是全局的、主机特定的或任务特定的。
- **Templates(模板): 用于生成配置文件等。**模板是包含占位符的文件,这些占位符在执行时由 Ansible 替换为实际的变量值。
- **Handlers(处理器): 用于在任务中某些条件满足时触发的操作。**通常用于在任务中发生 "changed" 状态时执行重启服务等操作。
- **Roles(角色): 是一种组织和封装 Playbooks 的方式。**Roles 允许将相关的任务、变量、模板和处理器组织成一个可重用的单元,使 Playbooks 更清晰和模块化。
3.实例:apache的yum安装部署
vim test1.yml
- name: first play
#定义一个play的名称,可省略
gather_facts: false
#设置不进行facts信息收集,这可以加快执行速度,可省略
hosts: webservers
#指定要执行任务的被管理主机组,如多个主机组用冒号分隔
remote_user: root
#指定被管理主机上执行任务的用户
tasks:
#定义任务列表,任务列表中的各任务按次序逐个在hosts中指定的主机上执行
- name: test connection
#自定义任务名称
ping:
#使用 module: [options] 格式来定义一个任务
- name: disable selinux
command: '/sbin/setenforce 0'
#command模块和shell模块无需使用key=value格式
ignore_errors: True
#如执行命令的返回值不为0,就会报错,tasks停止,可使用ignore_errors忽略失败的任务
- name: disable firewalld
service: name=firewalld state=stopped
#使用 module: options 格式来定义任务,option使用key=value格式
- name: install httpd
yum: name=httpd state=latest
- name: install configuration file for httpd
copy: src=/opt/httpd.conf dest=/etc/httpd/conf/httpd.conf
#这里需要一个事先准备好的/opt/httpd.conf文件
notify: "restart httpd"
#如以上操作后为changed的状态时,会通过notify指定的名称触发对应名称的handlers操作
- name: start httpd service
service: enabled=true name=httpd state=started
handlers:
#handlers中定义的就是任务,此处handlers中的任务使用的是service模块
- name: restart httpd #notify和handlers中任务的名称必须一致
service: name=httpd state=restarted
#Ansible在执行完某个任务之后并不会立即去执行对应的handler,而是在当前play中所有普通任务都执行完后再去执行handler,这样的好处是可以多次触发notify,但最后只执行一次对应的handler,从而避免多次重启。
- name: first play
gather_facts: false
hosts: webservers
remote_user: root
tasks:
- name: test connection
ping:
- name: disable selinux
command: '/sbin/setenforce 0'
ignore_errors: True
- name: disable firewalld
service: name=firewalld state=stopped
- name: install httpd
yum: name=httpd state=latest
- name: start httpd service
service: enabled=true name=httpd state=started
- name: editon index.html
shell: echo "this is httpd" > /var/www/html/index.html
notify: restart httpd
handlers:
- name: restart httpd
service: name=httpd state=restarted
//运行playbook
ansible-playbook test1.yml
//补充参数:
-k(--ask-pass):用来交互输入ssh密码
-K(-ask-become-pass):用来交互输入sudo密码
-u:指定用户
ansible-playbook test1.yml --syntax-check #检查yaml文件的语法是否正确
ansible-playbook test1.yml --list-task #检查tasks任务
ansible-playbook test1.yml --list-hosts #检查生效的主机
ansible-playbook test1.yml --start-at-task='install httpd' #指定从某个task开始运行
Playbook的定义、引用变量
vim test2.yml
- name: second play
hosts: webservers
remote_user: root
vars:
groupname: group1
username: user1
tasks:
- name: create group
group:
name: "{{ groupname }}"
system: yes
gid: 301
- name: create user
user:
name: "{{ username }}"
uid: 301
group: "{{ groupname }}"
shell: /sbin/nologin
- name: copy file
copy:
content: "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}"
dest: /opt/vars.txt
-------------------------------------------------------------------------------------------
hostvars: 这是一个包含所有主机变量的字典。
inventory_hostname: 这是当前主机的名字。
['ansible_default_ipv4']['address']: 这是一个嵌套的索引,用于获取当前主机的 ansible_default_ipv4 字典中的 address 键的值,即 IPv4 地址。
表示获取当前主机的 IPv4 地址复制到/opt/vars.txt。
-------------------------------------------------------------------------------------------
//运行playbook
ansible-playbook test2.yml
ansible 20.0.0.20 -a 'cat /opt/vars.txt'
ansible 20.0.0.20 -a 'cat /etc/passwd'
ansible 20.0.0.20 -a 'cat /etc/group'
//也可以在命令行里传参
ansible-playbook test2.yml -e 'username=user2 groupname=group2'
ansible 20.0.0.30 -a 'cat /opt/vars.txt'
ansible 20.0.0.30 -a 'cat /etc/passwd'
ansible 20.0.0.30 -a 'cat /etc/group'
Playbook的when条件判断
在Ansible中,提供的唯一一个通用的条件判断是when指令,当when指令的值为true时,则该任务执行,否则不执行该任务。
//when一个比较常见的应用场景是实现跳过某个主机不执行任务或者只有满足条件的主机执行任务
vim test3.yml
hosts: all
remote_user: root
tasks:
- name: test when
debug: msg='判断位置'
#满足条件的测试输出
when: ansible_default_ipv4.address == "20.0.0.20"
#不等于为 !=
#或者inventory_hostname == "<主机名>"
//运行playbook
ansible-playbook test3.yml
Playbook的迭代
Ansible提供了很多种循环结构,一般都命名为with_items,作用等同于 loop 循环。
1.with_item 循环输出
示例1:
//with_item单循环输出
vim test4.yml
- name: item test
hosts: dbservers
remote_user: root
gather_facts: no
tasks:
- debug:
msg: "{{item}}"
with_items: [a, b, c, d]
//运行playbook
ansible-playbook test4.yml
示例2:
- name: item test
hosts: dbservers
remote_user: root
gather_facts: no
tasks:
- debug:
msg: "{{item}}"
with_items:
- [a, b, c, d]
- [1, 2, 3, 4]
#with_item会把所有列表当成一个整体,实际上就是输出一个列表中的所有数据。
//运行playbook
ansible-playbook test4.yml
2.with_list 每组列表一起循环输出
- name: item test
hosts: dbservers
remote_user: root
gather_facts: no
tasks:
- debug:
msg: "{{item}}"
with_items:
- [a, b, c, d]
- [1, 2, 3, 4]
#with_item会把所有列表当成一个整体,实际上就是输出一个列表中的所有数据。
//运行playbook
ansible-playbook test4.yml
3.with_together 同一列表位置数据组合输出循环
示例1:
- name: item test
hosts: dbservers
remote_user: root
gather_facts: no
tasks:
- debug:
msg: "{{item}}"
with_together:
- [a, b, c, d]
- [1 ,2, 3, 4]
//运行playbook
ansible-playbook test4.yml
示例2:
- name: item test
hosts: dbservers
remote_user: root
gather_facts: no
tasks:
- debug:
msg: "{{item}}"
with_together:
- [a, b, c, d]
- [1 ,2, 3, 4]
- [A, B, C]
//运行playbook
ansible-playbook test4.yml
4.with_nested 列表数据循环匹配的循环
根据列表个数定义有多少层的循环
- name: item test
hosts: dbservers
remote_user: root
gather_facts: no
tasks:
- debug:
msg: "{{item}}"
with_nested:
- [a, b, c, d]
- [1 ,2, 3, 4]
//运行playbook
ansible-playbook test4.yml
5.综合实操
实操1:
vim test5.yml
- name: play1
hosts: dbservers
gather_facts: false
tasks:
- name: create file
file:
path: "{{item}}"
state: touch
with_items: [ /opt/a, /opt/b, /opt/c, /opt/d ]
- name: play2
hosts: dbservers
gather_facts: false
vars:
test:
- /opt/test1
- /opt/test2
- /opt/test3
- /opt/test4
tasks:
- name: create directories
file:
path: "{{item}}"
state: directory
with_items: "{{test}}"
- name: play3
hosts: dbservers
gather_facts: false
tasks:
- name: add users
user: name={{item.name}} state=present groups={{item.groups}}
with_items:
- name: test1
groups: carrot
- name: test2
groups: root
//运行playbook
ansible-playbook test5.yml
实操2:
vim test6.yml
- name: play1
hosts: 20.0.0.20
gather_facts: false
vars:
test:
- /opt/test1
- /opt/test2
- /opt/test3
tasks:
- name: create directory
file:
path: "{{ item }}"
state: directory
with_items: "{{ test }}"
- name: play2
hosts: 20.0.0.20
gather_facts: false
tasks:
- name: copy files
copy:
src: "/opt/{{ item.src }}"
dest: "/opt/{{ item.dest }}"
with_items:
- { src: 123, dest: test1 }
- { src: 456, dest: test2 }
- { src: 789, dest: test3 }
//运行playbook
ansible-playbook test6.yml
Templates 模块
Jinja是基于Python的模板引擎。Template类是Jinja的一个重要组件,可以看作是一个编译过的模板文件,用来产生目标文本,传递Python的变量给模板去替换模板中的标记。
1.准备模板文件
以Apache配置文件为例,部署apache服务
//先准备一个以 .j2 为后缀的 template 模板文件,设置引用的变量
cp /etc/httpd/conf/httpd.conf /opt/httpd.conf.j2
vim /opt/httpd.conf.j2
Listen {{http_port}} #42行,修改
ServerName {{server_name}} #95行,修改
DocumentRoot "{{root_dir}}" #119行,修改
2.修改主机清单文件
//修改主机清单文件,使用主机变量定义一个变量名相同,而值不同的变量
vim /etc/ansible/hosts
[webservers]
20.0.0.20 http_port=80 server_name=www.accp.com:80 root_dir=/etc/httpd/htdocs
[dbservers]
20.0.0.30 http_port=80 server_name=www.benet.com:80 root_dir=/etc/httpd/htdocs
3.编写playbook
vim apache.yml
- hosts: all
remote_user: root
vars:
- package: httpd
- service: httpd
tasks:
- name: instll httpd
yum: name={{package}}
- name: install configuration file
template: src=/opt/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
notify:
- restart httpd
- name: create root_dir
file:
path: /etc/httpd/htdocs
state: directory
- name: start httpd
service: name={{service}} enabled=true state=started
handlers:
- name: restart httpd
service: name={{service}} state=restarted
//运行playbook
ansible-playbook apache.yml
Tags模块
可以在一个playbook中为某个或某些任务定义"标签",在执行此playbook时通过ansible-playbook命令使用--tags选项能实现仅运行指定的tasks。
playbook还提供了一个特殊的tags为always。 作用就是当使用always作为tags的task时,无论执行哪一个tags时,定义有always的tags都会执行。
在Ansible中,标记(Tags)是用于标记任务的标识符,可以用来选择性地运行或忽略特定标记的任务。 在执行Ansible Playbook时,你可以使用--tags和--skip-tags选项来指定要运行或跳过的标记。
以下是一些常见的标记,
你也可以自定义标记以满足特定需求:
- always: 始终运行的任务,即使没有使用--tags选项。
- never: 从不运行的任务,即使使用了--tags选项。
- debug: 用于调试目的的任务。
- setup: 收集主机的事实(facts)信息的任务。
- pre_tasks: 在主任务之前运行的任务。
- post_tasks: 在主任务之后运行的任务。
- 也可以自定义标签。
示例:
vim test11.yml
- hosts: all
tasks:
- name: This task has the tag 'debug'
debug:
msg: "Debug message"
tags:
- debug
- name: This task has the tag 'setup'
setup:
tags:
- setup
- name: This task always runs
debug:
msg: "Always run"
tags:
- always
- name: This task never runs
debug:
msg: "Never run"
tags:
- never
//运行playbook
ansible-playbook test11.yml
//指定运行playbook中debug标签的内容
ansible-playbook test11.yml --tags=debug
//指定运行playbook中never标签的任务
ansible-playbook test11.yml --tags=never
Roles模块
Ansible为了层次化、结构化地组织Playbook,使用了角色(roles),roles可以根据层次型结构自动装载变量文件、task以及handlers等。
简单来讲,**roles就是通过分别将变量、文件、任务、模块及处理器放置于单独的目录中,**并可以便捷地include它们。
roles一般用于基于主机构建服务的场景中,但也可以用于构建守护进程等场景中。
1.roles 的目录结构
cd /etc/ansible/
tree roles/
roles/
├── web/ #相当于 playbook 中的 每一个 play 主题
│ ├── files/
│ ├── templates/
│ ├── tasks/
│ ├── handlers/
│ ├── vars/
│ ├── defaults/
│ └── meta/
└── db/
├── files/
├── templates/
├── tasks/
├── handlers/
├── vars/
├── defaults/
└── meta/
web/: 是一个角色的名称,可能是用于配置和管理Web服务器的角色。在这个目录下,有以下子目录和文件:
files/: 用于存放要在目标主机上复制的文件。
这些文件可以通过 Ansible 的 copy 模块或者 template 模块进行复制。
templates/: 用于存放 Jinja2 模板文件,这些模板文件可以通过 Ansible 的 template 模块渲染,
并将结果复制到目标主机上。
tasks/: 包含要执行的任务的主要文件,通常是 main.yml。这些任务文件中定义了角色的主要功能。
handlers/: 包含要在角色中使用的处理器的主要文件,通常也是 main.yml。
处理器是在特定条件下触发的操作,通常是在任务执行完成后。
vars/: 包含角色特定的变量。这些变量可以在角色的任务和处理器中使用。
defaults/: 包含默认变量值。这些变量在没有在 inventory 文件或者 Playbook 中定义的情况下使用。
meta/: 包含元信息,通常用于描述依赖关系和其他元数据。
roles/
├── my_role/ # 角色名称,根据实际情况取名
│ ├── files/ # 存放由 copy 模块或 script 模块调用的文件
│ ├── templates/ # 存放 Jinja2 模板文件
│ ├── tasks/ # 包含任务的目录
│ │ └── main.yml # 角色的主要任务定义
│ ├── handlers/ # 包含处理器的目录
│ │ └── main.yml # 角色的主要处理器定义
│ ├── vars/ # 包含变量的目录
│ │ └── main.yml # 角色的主要变量定义
│ ├── defaults/ # 包含默认变量的目录
│ │ └── main.yml # 角色的默认变量定义
│ └── meta/ # 包含元信息的目录
│ └── main.yml # 角色的元信息定义
2.实例:集中式lamp的role编写
playbook中使用roles的步骤
(1)创建以 roles 命名的目录
mkdir /etc/ansible/roles/ -p #yum装完默认就有
(2)创建全局变量目录(可选)
mkdir /etc/ansible/group_vars/ -p
touch /etc/ansible/group_vars/all #文件名自己定义,引用的时候注意
(3)在 roles 目录中分别创建以各角色名称命名的目录,如 httpd、mysql
mkdir /etc/ansible/roles/httpd
mkdir /etc/ansible/roles/mysql
(4)在每个角色命名的目录中分别创建files、handlers、tasks、templates、meta、defaults和vars目录,
用不到的目录可以创建为空目录,也可以不创建
mkdir /etc/ansible/roles/httpd/{files,templates,tasks,handlers,vars,defaults,meta}
mkdir /etc/ansible/roles/mysql/{files,templates,tasks,handlers,vars,defaults,meta}
(5)在每个角色的 handlers、tasks、meta、defaults、vars 目录下创建 main.yml 文件,千万不能自定义文件名
touch /etc/ansible/roles/httpd/{defaults,vars,tasks,meta,handlers}/main.yml
touch /etc/ansible/roles/mysql/{defaults,vars,tasks,meta,handlers}/main.yml
(6)修改 site.yml 文件,针对不同主机去调用不同的角色
vim /etc/ansible/site.yml
---
- hosts: webservers
remote_user: root
roles:
- httpd
- hosts: dbservers
remote_user: root
roles:
- mysql
(7)运行 ansible-playbook
cd /etc/ansible
ansible-playbook site.yml
实操
mkdir /etc/ansible/roles/httpd/{files,templates,tasks,handlers,vars,defaults,meta} -p
mkdir /etc/ansible/roles/mysql/{files,templates,tasks,handlers,vars,defaults,meta} -p
mkdir /etc/ansible/roles/php/{files,templates,tasks,handlers,vars,defaults,meta} -p
touch /etc/ansible/roles/httpd/{defaults,vars,tasks,meta,handlers}/main.yml
touch /etc/ansible/roles/mysql/{defaults,vars,tasks,meta,handlers}/main.yml
touch /etc/ansible/roles/php/{defaults,vars,tasks,meta,handlers}/main.yml
------编写httpd模块------
写一个简单的tasks/main.yml
vim /etc/ansible/roles/httpd/tasks/main.yml
- name: install apache
yum: name={{pkg}} state=latest
- name: start apache
service: enabled=true name={{svc}} state=started
//定义变量:可以定义在全局变量中,也可以定义在roles角色变量中,一般定义在角色变量中
vim /etc/ansible/roles/httpd/vars/main.yml
pkg: httpd
svc: httpd
-------编写mysql模块-------
vim /etc/ansible/roles/mysql/tasks/main.yml
- name: install mysql
yum: name={{pkg}} state=latest
- name: start mysql
service: enabled=true name={{svc}} state=started
vim /etc/ansible/roles/mysql/vars/main.yml
pkg:
- mariadb
- mariadb-server
svc: mariadb
-------编写php模块-----
vim /etc/ansible/roles/php/tasks/main.yml
- name: install php
yum: name={{pkg}} state=latest
- name: start php-fpm
service: enabled=true name={{svc}} state=started
vim /etc/ansible/roles/php/vars/main.yml
pkg:
- php
- php-fpm
svc: php-fpm
-----编写roles示例-----
vim /etc/ansible/site.yml
---
- hosts: webservers
remote_user: root
roles:
- httpd
- mysql
- php
cd /etc/ansible
ansible-playbook site.yml