Ansible之playbook剧本编写

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
相关推荐
紫晓宁2 天前
jmeter结合ansible分布式压测--3压测执行
分布式·jmeter·ansible
紫晓宁3 天前
jmeter结合ansible分布式压测--1数据准备
分布式·jmeter·ansible
紫晓宁3 天前
jmeter结合ansible分布式压测--2jmter环境准备
分布式·jmeter·ansible
SG.xf6 天前
ansible中的任务执行控制
ansible
赶紧回家去9 天前
Ansible基本使用
运维·ansible
我就是全世界9 天前
ansible详细介绍和具体步骤
ansible
福大大架构师每日一题9 天前
27.9 调用go-ansible执行playbook拷贝json文件重载采集器
golang·json·ansible·prometheus
SG.xf9 天前
Ansible
运维·ansible
避凉闲庭10 天前
ansible开局配置-openEuler
linux·运维·ansible·脚本·openeuler·免密登录·批量化
运维小白。。12 天前
Ansible 批量部署
ansible