Ansible 自动化工具(剧本 playbook)
一、playbooks 概述
1. playbook 简介
Playbooks 是 Ansible的配置、部署、编排语言,他们可以被描述为一个需要希望远程主机执行命令的方案,或者一组IT程序运行的命令集合
Playbooks 与 ad-hoc相比,是一种完全不同的运用ansible的方式,是非常之强大的。
简单来说,playbooks是一种简单的配置管理系统与多机器部署系统的基础,与现有的其他系统有不同之处,且非常适合于复杂应用的部署。
playbook是ansible用于配置,部署,和管理被控节点的剧本。
通过playbook的详细描述,执行其中的一系列tasks,可以让远端主机达到预期的状态。playbook就像Ansible控制器给被控节点列出的的一系列to-do-list,而被控节点必须要完成。
也可以这么理解,playbook 字面意思,即剧本,现实中由演员按照剧本表演,在Ansible中,这次由计算机进行表演,由计算机安装,部署应用,提供对外服务,以及组织计算机处理各种各样的事情
2. playbook 使用场景
执行一些简单的任务,使用ad-hoc命令可以方便的解决问题,但是有时一个设施过于复杂,需要大量的操作时候,执行的ad-hoc命令是不适合的,这时最好使用playbook。
就像执行shell命令与写shell脚本一样,也可以理解为批处理任务,不过playbook有自己的语法格式。
使用playbook你可以方便的重用这些代码,可以移植到不同的机器上面,像函数一样,最大化的利用代码。在你使用Ansible的过程中,你也会发现,你所处理的大部分操作都是编写playbook。可以把常见的应用都编写成
playbook,之后管理服务器会变得十分简单。
3. playbooks 的组成
playbooks 本身由以下各部分组成
- Tasks:任务,即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行
- Variables:变量
- Templates:模板
- Handlers:处理器,当changed状态条件满足时,(notify)触发执行的操作
- Roles:角色
4. yaml 基本语法规则
大小写敏感
使用缩进表示层级关系
缩进时不允许使用tab键、只允许使用空格
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
5. yaml 关键字
关键字 | 含义 |
---|---|
hosts | 定义节点,可以是组:在 Ansible 中,hosts 关键字用于定义执行任务的主机节点,可以是单个主机或主机组。 |
remote_user | 指定以哪个用户身份进行登录:remote_user 关键字用于指定在执行任务时要使用的远程用户身份。 |
tasks | 定义任务:tasks 关键字用于定义要在主机上执行的任务列表。 |
become:yes | 表示切换用户:become 关键字用于表示在执行任务之前要切换到特定用户。 |
become_user:mysql | 表示切换到 mysql 用户,配合上一条使用:become_user 关键字用于指定要切换到的特定用户。 |
-name | 为下面执行的操作起名:在任务列表中,使用 - name 来为每个任务指定一个描述性的名称。 |
6. yaml 支持的数据结构
在 YAML 中,支持以下三种基本数据结构:
(1)对象(Mapping)
对象是键值对的集合,用于表示复杂的数据结构或属性集。对象使用冒号和缩进来表示键值对关系,键值对之间使用换行分隔。例如:
yamlCopy Codeperson:
name: John Doe
age: 30
上述示例中的 person
就是一个对象,包含了 name
和 age
两个键值对。
(2)数组(Sequence)
数组是一组按次序排列的值,用于表示有序的元素列表。数组使用短横线 -
和缩进来表示每个元素,元素之间使用换行分隔。例如:
yamlCopy Codefruits:
- apple
- orange
- banana
上述示例中的 fruits
是一个数组,包含了三个元素 apple
、orange
和 banana
。
(3)纯量(Scalar)
纯量是单个的、不可再分的值,用于表示简单的数据类型,如字符串、整数、布尔值等。纯量可以直接写在一行中。例如:
yamlCopy Codename: John Doe
age: 30
active: true
上述示例中的 name
、age
和 active
都是纯量。
通过组合和嵌套这些基本数据结构,YAML 可以表达复杂的数据模型和层次结构。这使得 YAML 成为一种方便且易读的数据序列化格式。
二、playbook 操作示例
主机清单
webserver 组:
dbservers 组:
1. 示例:安装 Apache 服务
vim /opt/test1.yuml
--- #yaml文件以---开头,以表明这是一个yaml文件,可省略
- 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,从而避免多次重启。
# 上传 httpd.conf 配置文件到 /opt
cd /opt
rz -E
# 运行playbook
ansible-playbook test1.yaml
# 补充参数:
-k(–ask-pass) # 用来交互输入ssh密码
-K(-ask-become-pass) # 用来交互输入sudo密码
-u # 指定用户
ansible-playbook test1.yaml --syntax-check #检查yaml文件的语法是否正确
ansible-playbook test1.yaml --list-task #检查tasks任务
ansible-playbook test1.yaml --list-hosts #检查生效的主机
ansible-playbook test1.yaml --start-at-task='install httpd' #指定从某个task开始运行
# 检查yaml文件的语法是否正确
ansible-playbook test1.yaml --syntax-check
# 检查tasks任务
ansible-playbook test1.yaml --list-task
# 检查生效的主机
ansible-playbook test1.yaml --list-hosts
# 指定从某个task开始运行
ansible-playbook test1.yaml --start-at-task='install httpd'
浏览器访问:http://192.168.23.15
2. 示例:定义、引用变量
vim /opt/test2.yaml
---
- name: second play
hosts: dbservers
remote_user: root
vars: #定义变量
- groupname: mysql #格式为 key: value
- username: nginx
tasks:
- name: create group
group: name={{groupname}} system=yes gid=123 #使用 {{key}} 引用变量的值
- name: create user
user: name={{username}} uid=123 group={{groupname}}
- name: copy file
copy: content="{{ansible_default_ipv4}}" dest=/opt/vars.txt #在setup模块中可以获取facts变量信息
ansible-playbook test1.yaml
# 在命令行里定义变量
ansible-playbook test1.yaml -e "username=testuser"
3. 示例:指定远程主机 sudo 切换用户
vim test3.yaml
---
- name: sudo play
gather_facts: fales
hosts: dbservers
remote_user: testuser
become: yes # 2.6版本以后的参数,之前是sudo,意思为切换用户运行
become_user: root # 指定sudo用户为root
# 设置 testuser 用户密码
ansible dbservers -m shell -a 'echo user123 | passwd --stdin testuser'
# 执行playbook时
ansible-playbook test1.yml -K
user123
4. 示例:when 条件判断
在Ansible中,提供的唯一一个通用的条件判断是when指令,当when指令的值为true时,则该任务执行,否则不执行该任务。
when一个比较常见的应用场景是实现跳过某个主机不执行任务或者只有满足条件的主机执行任务
vim /opt/test4.yaml
---
- hosts: all
remote_user: root
tasks:
- name: shutdown host
command: /sbin/shutdown -r now
when: ansible_default_ipv4.address == "192.168.23.15"
# when指令中的变量名不需要手动加上 {{}}
# 或
when: inventory_hostname == "<主机名>"
ansible-playbook test2.yaml
5. 示例:迭代
Ansible提供了很多种循环结构,一般都命名为with_items,作用等同于 loop 循环。
vim /opt/test5.yaml
---
- name: items_play # Playbook 的名称,用于标识此 Play 的目的或用途
hosts: dbservers # 要执行此 Play 的主机组,可以是主机名、IP地址或主机组名
gather_facts: false # 是否收集远程主机的事实信息
tasks: # 定义任务列表
- name: create directories # 任务名称,用于描述此任务的目的或功能
file: # 使用 file 模块来创建目录
path: '{{item}}' # 目录的路径,从变量 item 中获取值
state: directory # 确保目录存在且是一个目录
with_items: # 遍历 item 列表中的每个元素执行任务
- /tmp/test1 # 第一个目录的路径
- /tmp/test2 # 第二个目录的路径
- name: add users # 任务名称,用于描述此任务的目的或功能
user: # 使用 user 模块来创建用户
name: {{item.name}} # 用户名,从变量 item.name 中获取值
state: present # 确保用户存在
groups: {{item.groups}} # 用户所属的组,从变量 item.groups 中获取值
with_items: # 遍历 item 列表中的每个元素执行任务
- name: test1 # 第一个用户的用户名
groups: wheel # 第一个用户所属的组
- name: test2 # 第二个用户的用户名
groups: root # 第二个用户所属的组
ansible-playbook test5.yaml
三、Templates 模块
Jinja是基于Python的模板引擎。Template类是Jinja的一个重要组件,可以看作是一个编译过的模板文件,用来产生目标文本,传递Python的变量给模板去替换模板中的标记。
1. 准备 .j2 模板文件
先准备一个以 .j2 为后缀的 template 模板文件,设置引用的变量
cp /opt/httpd.conf /opt/httpd.conf.j2
vim /opt/httpd.conf.j2
Listen {{http_port}} # 42行,修改
ServerName {{server_name}} # 95行,修改
DocumentRoot "{{root_dir}}" # 119行,修改
<Directory "{{root_dir}}"> # 131行,配置访问目录权限
2. 修改主机清单
修改主机清单文件,使用主机变量定义一个变量名相同,而值不同的变量
vim /etc/ansible/hosts
[webservers]
192.168.23.15 http_port=192.168.23.15:80 server_name=www.15server.com:80 root_dir=/etc/httpd/htdocs
[dbservers]
192.168.23.20 http_port=192.168.23.20:80 server_name=www.20server.com:80 root_dir=/etc/httpd/htdocs
3. 编写 playbook
vim /opt/apache.yaml
---
- hosts: all
remote_user: root
vars:
- package: httpd
- service: httpd
tasks:
- name: install httpd package
yum: name={{package}} state=latest
- name: install configure 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 server
service: name={{service}} enabled=true state=started
handlers:
- name: restart httpd
service: name={{service}} state=restarted
ansible-playbook apache.yaml